From 0472474ad312b3c69d9be55061abb188f3988d7a Mon Sep 17 00:00:00 2001 From: s_kawamoto Date: Wed, 7 Aug 2013 00:56:21 +0100 Subject: [PATCH] Update PuTTY to 0.63. --- FFFTP_Eng_Release/FFFTP.exe | Bin 610304 -> 610304 bytes FFFTP_Eng_Release_64/FFFTP.exe | Bin 720384 -> 723456 bytes Release/FFFTP.exe | Bin 608768 -> 608768 bytes Release_64/FFFTP.exe | Bin 718848 -> 721920 bytes contrib/putty/BUILDSCR.CV | 39 + contrib/putty/CHARSET/CHARSET.H | 7 +- contrib/putty/CHARSET/FROMUCS.C | 3 +- contrib/putty/CHARSET/LOCALENC.C | 3 +- contrib/putty/CHARSET/MIMEENC.C | 5 + contrib/putty/CHARSET/SBCS.DAT | 22 + contrib/putty/CHARSET/SBCSDAT.C | 76 + contrib/putty/CHARSET/TOUCS.C | 3 +- contrib/putty/CHARSET/XENC.C | 1 + contrib/putty/CMDGEN.C | 71 +- contrib/putty/CMDLINE.C | 220 +- contrib/putty/CONF.C | 613 +++++ contrib/putty/CONFIG.C | 1184 ++++----- contrib/putty/CONFIGUR | 3 + contrib/putty/CONTRIB/CYGTERMD/PTY.C | 16 +- contrib/putty/CONTRIB/CYGTERMD/TELNET.C | 5 +- contrib/putty/CONTRIB/LOGPARSE.PL | 907 +++++++ contrib/putty/CPROXY.C | 11 +- contrib/putty/DIALOG.C | 159 +- contrib/putty/DIALOG.H | 75 +- contrib/putty/DOC/BLURB.BUT | 6 +- contrib/putty/DOC/CONFIG.BUT | 67 +- contrib/putty/DOC/ERRORS.BUT | 16 +- contrib/putty/DOC/FAQ.BUT | 19 +- contrib/putty/DOC/INDEX.BUT | 7 +- contrib/putty/DOC/LICENCE.BUT | 4 +- contrib/putty/DOC/MAN-PTEL.BUT | 8 +- contrib/putty/DOC/MAN-PTER.BUT | 19 +- contrib/putty/DOC/MAN-PUTT.BUT | 8 +- contrib/putty/DOC/PLINK.BUT | 4 +- contrib/putty/DOC/PSCP.BUT | 4 +- contrib/putty/DOC/PUBKEY.BUT | 14 +- contrib/putty/EMPTY.H | 1 + contrib/putty/IMPORT.C | 184 +- contrib/putty/LDISC.C | 42 +- contrib/putty/LDISC.H | 6 +- contrib/putty/LDISCUCS.C | 7 +- contrib/putty/LICENCE | 2 +- contrib/putty/LOGGING.C | 115 +- contrib/putty/MACOSX/MAKEFILE | 89 +- contrib/putty/MACOSX/OSXMAIN.M | 18 + contrib/putty/MACOSX/OSXWIN.M | 8 +- contrib/putty/MACOSX/README.OSX | 9 + contrib/putty/MINIBIDI.C | 12 +- contrib/putty/MISC.C | 189 +- contrib/putty/MISC.H | 5 + contrib/putty/MKAUTO.SH | 40 +- contrib/putty/MKFILES.PL | 201 +- contrib/putty/MKUNXARC.SH | 19 +- contrib/putty/NETWORK.H | 15 +- contrib/putty/NOTIMING.C | 2 +- contrib/putty/PINGER.C | 17 +- contrib/putty/PORTFWD.C | 205 +- contrib/putty/PPROXY.C | 2 +- contrib/putty/PROXY.C | 205 +- contrib/putty/PROXY.H | 5 +- contrib/putty/PSCP.C | 434 ++-- contrib/putty/PSFTP.C | 550 +++-- contrib/putty/PSFTP.H | 8 +- contrib/putty/PUTTY.H | 633 +++-- contrib/putty/PUTTYMEM.H | 12 +- contrib/putty/RAW.C | 87 +- contrib/putty/README | 52 +- contrib/putty/RECIPE | 48 +- contrib/putty/RLOGIN.C | 68 +- contrib/putty/SERCFG.C | 33 +- contrib/putty/SETTINGS.C | 1036 ++++---- contrib/putty/SFTP.C | 55 +- contrib/putty/SFTP.H | 19 +- contrib/putty/SSH.C | 2582 +++++++++++--------- contrib/putty/SSH.H | 28 +- contrib/putty/SSHAES.C | 4 +- contrib/putty/SSHARCF.C | 2 +- contrib/putty/SSHBN.C | 183 +- contrib/putty/SSHDES.C | 8 +- contrib/putty/SSHDSS.C | 119 +- contrib/putty/SSHDSSG.C | 6 +- contrib/putty/SSHGSS.H | 2 +- contrib/putty/SSHMD5.C | 8 +- contrib/putty/SSHNOGSS.C | 2 +- contrib/putty/SSHPRIME.C | 1804 ++++++-------- contrib/putty/SSHPUBK.C | 68 +- contrib/putty/SSHRAND.C | 94 +- contrib/putty/SSHRSA.C | 56 +- contrib/putty/SSHRSAG.C | 10 +- contrib/putty/SSHSH256.C | 110 + contrib/putty/SSHSHA.C | 42 +- contrib/putty/SSHZLIB.C | 1 + contrib/putty/STORAGE.H | 25 +- contrib/putty/TELNET.C | 175 +- contrib/putty/TERMINAL.C | 902 +++---- contrib/putty/TERMINAL.H | 62 +- contrib/putty/TESTBACK.C | 14 +- contrib/putty/TESTDATA/BIGNUM.PY | 10 + contrib/putty/TIMING.C | 98 +- contrib/putty/TREE234.C | 13 +- contrib/putty/UNIX/CONFIGUR.AC | 127 +- contrib/putty/UNIX/GTKCFG.C | 31 +- contrib/putty/UNIX/GTKCOLS.H | 2 +- contrib/putty/UNIX/GTKDLG.C | 95 +- contrib/putty/UNIX/GTKFONT.C | 697 ++++-- contrib/putty/UNIX/GTKFONT.H | 30 +- contrib/putty/UNIX/GTKWIN.C | 921 ++++--- contrib/putty/UNIX/MAKEFILE.AM | 338 +++ contrib/putty/UNIX/MAKEFILE.GTK | 138 +- contrib/putty/UNIX/MAKEFILE.IN | 877 ------- contrib/putty/UNIX/MAKEFILE.UX | 84 +- contrib/putty/UNIX/UNIX.H | 33 +- contrib/putty/UNIX/UXAGENTC.C | 6 +- contrib/putty/UNIX/UXCFG.C | 19 +- contrib/putty/UNIX/UXCONS.C | 104 +- contrib/putty/UNIX/UXGEN.C | 1 + contrib/putty/UNIX/UXGSS.C | 11 +- contrib/putty/UNIX/UXMISC.C | 179 +- contrib/putty/UNIX/UXNET.C | 144 +- contrib/putty/UNIX/UXPLINK.C | 318 +-- contrib/putty/UNIX/UXPROXY.C | 45 +- contrib/putty/UNIX/UXPTERM.C | 23 +- contrib/putty/UNIX/UXPTY.C | 345 +-- contrib/putty/UNIX/UXPUTTY.C | 30 +- contrib/putty/UNIX/UXSER.C | 41 +- contrib/putty/UNIX/UXSFTP.C | 71 +- contrib/putty/UNIX/UXSTORE.C | 151 +- contrib/putty/UNIX/UXUCS.C | 32 +- contrib/putty/UNIX/UX_X11.C | 2 +- contrib/putty/VERSION.C | 16 + contrib/putty/WCWIDTH.C | 90 +- contrib/putty/WILDCARD.C | 3 +- contrib/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV | 80 +- contrib/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV | 158 +- contrib/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV | 152 +- contrib/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV | 152 +- contrib/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV | 192 +- contrib/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV | 102 +- contrib/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV | 134 +- contrib/putty/WINDOWS/MAKEFILE.BOR | 259 +- contrib/putty/WINDOWS/MAKEFILE.CYG | 192 +- contrib/putty/WINDOWS/MAKEFILE.LCC | 213 +- contrib/putty/WINDOWS/MAKEFILE.VC | 277 ++- contrib/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP | 4 + contrib/putty/WINDOWS/MSVC/PLINK/PLINK.DSP | 4 + contrib/putty/WINDOWS/MSVC/PSCP/PSCP.DSP | 4 + contrib/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP | 4 + contrib/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP | 4 + contrib/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP | 4 + contrib/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP | 4 + contrib/putty/WINDOWS/PAGEANT.RC | 4 +- contrib/putty/WINDOWS/PUTTY.ISS | 10 +- contrib/putty/WINDOWS/PUTTYGEN.RC | 4 +- contrib/putty/WINDOWS/VERSION.RC2 | 4 +- contrib/putty/WINDOWS/WINCFG.C | 71 +- contrib/putty/WINDOWS/WINCONS.C | 49 +- contrib/putty/WINDOWS/WINCTRLS.C | 97 +- contrib/putty/WINDOWS/WINDEFS.C | 24 +- contrib/putty/WINDOWS/WINDLG.C | 26 +- contrib/putty/WINDOWS/WINDOW.C | 974 +++++--- contrib/putty/WINDOWS/WINGSS.C | 10 +- contrib/putty/WINDOWS/WINHANDL.C | 24 + contrib/putty/WINDOWS/WINHELP.C | 2 +- contrib/putty/WINDOWS/WINHELP.H | 1 + contrib/putty/WINDOWS/WINJUMP.C | 18 +- contrib/putty/WINDOWS/WINMISC.C | 168 +- contrib/putty/WINDOWS/WINNET.C | 130 +- contrib/putty/WINDOWS/WINNOISE.C | 33 +- contrib/putty/WINDOWS/WINPGEN.C | 134 +- contrib/putty/WINDOWS/WINPGNT.C | 181 +- contrib/putty/WINDOWS/WINPGNTC.C | 8 +- contrib/putty/WINDOWS/WINPLINK.C | 188 +- contrib/putty/WINDOWS/WINPRINT.C | 25 +- contrib/putty/WINDOWS/WINPROXY.C | 18 +- contrib/putty/WINDOWS/WINSER.C | 41 +- contrib/putty/WINDOWS/WINSFTP.C | 37 +- contrib/putty/WINDOWS/WINSTORE.C | 110 +- contrib/putty/WINDOWS/WINSTUFF.H | 29 +- contrib/putty/WINDOWS/WINUCS.C | 161 +- contrib/putty/WINDOWS/WINUTILS.C | 43 +- contrib/putty/WINDOWS/WINX11.C | 7 +- contrib/putty/WINDOWS/WIN_RES.RC2 | 4 +- contrib/putty/X11FWD.C | 52 +- putty/BUILDSCR.CV | 39 + putty/CHARSET/CHARSET.H | 7 +- putty/CHARSET/FROMUCS.C | 3 +- putty/CHARSET/LOCALENC.C | 3 +- putty/CHARSET/MIMEENC.C | 5 + putty/CHARSET/SBCS.DAT | 22 + putty/CHARSET/SBCSDAT.C | 76 + putty/CHARSET/TOUCS.C | 3 +- putty/CHARSET/XENC.C | 1 + putty/CMDGEN.C | 71 +- putty/CMDLINE.C | 220 +- putty/CONF.C | 613 +++++ putty/CONFIG.C | 1184 ++++----- putty/CONFIGUR | 3 + putty/CONTRIB/CYGTERMD/PTY.C | 16 +- putty/CONTRIB/CYGTERMD/TELNET.C | 5 +- putty/CONTRIB/LOGPARSE.PL | 907 +++++++ putty/CPROXY.C | 11 +- putty/DIALOG.C | 159 +- putty/DIALOG.H | 75 +- putty/DOC/BLURB.BUT | 6 +- putty/DOC/CONFIG.BUT | 67 +- putty/DOC/ERRORS.BUT | 16 +- putty/DOC/FAQ.BUT | 19 +- putty/DOC/INDEX.BUT | 7 +- putty/DOC/LICENCE.BUT | 4 +- putty/DOC/MAN-PTEL.BUT | 8 +- putty/DOC/MAN-PTER.BUT | 19 +- putty/DOC/MAN-PUTT.BUT | 8 +- putty/DOC/PLINK.BUT | 4 +- putty/DOC/PSCP.BUT | 4 +- putty/DOC/PUBKEY.BUT | 14 +- putty/EMPTY.H | 1 + putty/IMPORT.C | 184 +- putty/LDISC.C | 42 +- putty/LDISC.H | 6 +- putty/LDISCUCS.C | 7 +- putty/LICENCE | 2 +- putty/LOGGING.C | 115 +- putty/MACOSX/MAKEFILE | 89 +- putty/MACOSX/OSXMAIN.M | 18 + putty/MACOSX/OSXWIN.M | 8 +- putty/MACOSX/README.OSX | 9 + putty/MINIBIDI.C | 12 +- putty/MISC.C | 189 +- putty/MISC.H | 5 + putty/MKAUTO.SH | 40 +- putty/MKFILES.PL | 201 +- putty/MKUNXARC.SH | 19 +- putty/NETWORK.H | 15 +- putty/NOTIMING.C | 2 +- putty/PINGER.C | 17 +- putty/PORTFWD.C | 205 +- putty/PPROXY.C | 2 +- putty/PROXY.C | 205 +- putty/PROXY.H | 5 +- putty/PSCP.C | 434 ++-- putty/PSFTP.C | 550 +++-- putty/PSFTP.H | 8 +- putty/PUTTY.H | 633 +++-- putty/PUTTYMEM.H | 12 +- putty/PuTTY.vc90.vcproj | 4 + putty/PuTTY.vcproj | 4 + putty/RAW.C | 87 +- putty/README | 52 +- putty/RECIPE | 48 +- putty/RLOGIN.C | 68 +- putty/Release/PuTTY.dll | Bin 411136 -> 422400 bytes putty/SERCFG.C | 33 +- putty/SETTINGS.C | 1036 ++++---- putty/SFTP.C | 55 +- putty/SFTP.H | 19 +- putty/SSH.C | 2582 +++++++++++--------- putty/SSH.H | 28 +- putty/SSHAES.C | 4 +- putty/SSHARCF.C | 2 +- putty/SSHBN.C | 183 +- putty/SSHDES.C | 8 +- putty/SSHDSS.C | 119 +- putty/SSHDSSG.C | 6 +- putty/SSHGSS.H | 2 +- putty/SSHMD5.C | 8 +- putty/SSHNOGSS.C | 2 +- putty/SSHPRIME.C | 1804 ++++++-------- putty/SSHPUBK.C | 68 +- putty/SSHRAND.C | 94 +- putty/SSHRSA.C | 56 +- putty/SSHRSAG.C | 10 +- putty/SSHSH256.C | 110 + putty/SSHSHA.C | 42 +- putty/SSHZLIB.C | 1 + putty/STORAGE.H | 25 +- putty/TELNET.C | 175 +- putty/TERMINAL.C | 902 +++---- putty/TERMINAL.H | 62 +- putty/TESTBACK.C | 14 +- putty/TESTDATA/BIGNUM.PY | 10 + putty/TIMING.C | 98 +- putty/TREE234.C | 13 +- putty/UNIX/CONFIGUR.AC | 127 +- putty/UNIX/GTKCFG.C | 31 +- putty/UNIX/GTKCOLS.H | 2 +- putty/UNIX/GTKDLG.C | 95 +- putty/UNIX/GTKFONT.C | 697 ++++-- putty/UNIX/GTKFONT.H | 30 +- putty/UNIX/GTKWIN.C | 921 ++++--- putty/UNIX/MAKEFILE.AM | 338 +++ putty/UNIX/MAKEFILE.GTK | 138 +- putty/UNIX/MAKEFILE.IN | 877 ------- putty/UNIX/MAKEFILE.UX | 84 +- putty/UNIX/UNIX.H | 33 +- putty/UNIX/UXAGENTC.C | 6 +- putty/UNIX/UXCFG.C | 19 +- putty/UNIX/UXCONS.C | 104 +- putty/UNIX/UXGEN.C | 1 + putty/UNIX/UXGSS.C | 11 +- putty/UNIX/UXMISC.C | 179 +- putty/UNIX/UXNET.C | 144 +- putty/UNIX/UXPLINK.C | 318 +-- putty/UNIX/UXPROXY.C | 45 +- putty/UNIX/UXPTERM.C | 23 +- putty/UNIX/UXPTY.C | 345 +-- putty/UNIX/UXPUTTY.C | 30 +- putty/UNIX/UXSER.C | 41 +- putty/UNIX/UXSFTP.C | 71 +- putty/UNIX/UXSTORE.C | 151 +- putty/UNIX/UXUCS.C | 32 +- putty/UNIX/UX_X11.C | 2 +- putty/VERSION.C | 16 + putty/WCWIDTH.C | 90 +- putty/WILDCARD.C | 3 +- putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV | 80 +- putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV | 158 +- putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV | 152 +- putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV | 152 +- putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV | 192 +- putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV | 102 +- putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV | 134 +- putty/WINDOWS/MAKEFILE.BOR | 259 +- putty/WINDOWS/MAKEFILE.CYG | 192 +- putty/WINDOWS/MAKEFILE.LCC | 213 +- putty/WINDOWS/MAKEFILE.VC | 277 ++- putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP | 4 + putty/WINDOWS/MSVC/PLINK/PLINK.DSP | 4 + putty/WINDOWS/MSVC/PSCP/PSCP.DSP | 4 + putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP | 4 + putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP | 4 + putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP | 4 + putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP | 4 + putty/WINDOWS/PAGEANT.RC | 4 +- putty/WINDOWS/PUTTY.ISS | 10 +- putty/WINDOWS/PUTTYGEN.RC | 4 +- putty/WINDOWS/VERSION.RC2 | 4 +- putty/WINDOWS/WINCFG.C | 71 +- putty/WINDOWS/WINCONS.C | 49 +- putty/WINDOWS/WINCTRLS.C | 97 +- putty/WINDOWS/WINDEFS.C | 24 +- putty/WINDOWS/WINDLG.C | 26 +- putty/WINDOWS/WINDOW.C | 974 +++++--- putty/WINDOWS/WINGSS.C | 10 +- putty/WINDOWS/WINHANDL.C | 24 + putty/WINDOWS/WINHELP.C | 2 +- putty/WINDOWS/WINHELP.H | 1 + putty/WINDOWS/WINJUMP.C | 18 +- putty/WINDOWS/WINMISC.C | 168 +- putty/WINDOWS/WINNET.C | 130 +- putty/WINDOWS/WINNOISE.C | 33 +- putty/WINDOWS/WINPGEN.C | 134 +- putty/WINDOWS/WINPGNT.C | 181 +- putty/WINDOWS/WINPGNTC.C | 8 +- putty/WINDOWS/WINPLINK.C | 188 +- putty/WINDOWS/WINPRINT.C | 25 +- putty/WINDOWS/WINPROXY.C | 18 +- putty/WINDOWS/WINSER.C | 41 +- putty/WINDOWS/WINSFTP.C | 37 +- putty/WINDOWS/WINSTORE.C | 110 +- putty/WINDOWS/WINSTUFF.H | 29 +- putty/WINDOWS/WINUCS.C | 161 +- putty/WINDOWS/WINUTILS.C | 43 +- putty/WINDOWS/WINX11.C | 7 +- putty/WINDOWS/WIN_RES.RC2 | 4 +- putty/X11FWD.C | 52 +- putty/x64/Release/PuTTY.dll | Bin 519680 -> 542208 bytes 366 files changed, 28418 insertions(+), 19770 deletions(-) create mode 100644 contrib/putty/BUILDSCR.CV create mode 100644 contrib/putty/CONF.C create mode 100644 contrib/putty/CONFIGUR create mode 100644 contrib/putty/CONTRIB/LOGPARSE.PL create mode 100644 contrib/putty/EMPTY.H create mode 100644 contrib/putty/UNIX/MAKEFILE.AM delete mode 100644 contrib/putty/UNIX/MAKEFILE.IN create mode 100644 putty/BUILDSCR.CV create mode 100644 putty/CONF.C create mode 100644 putty/CONFIGUR create mode 100644 putty/CONTRIB/LOGPARSE.PL create mode 100644 putty/EMPTY.H create mode 100644 putty/UNIX/MAKEFILE.AM delete mode 100644 putty/UNIX/MAKEFILE.IN diff --git a/FFFTP_Eng_Release/FFFTP.exe b/FFFTP_Eng_Release/FFFTP.exe index abf9b95d8789d1b3f32f83ff59e43bc603993f40..c5441f53ea6ebfa0002d2f5b2335ece3ad3e20be 100644 GIT binary patch delta 44 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 Ul4a(`RcGH&{?uLaJd))90`Qu+WB>pF diff --git a/Release/FFFTP.exe b/Release/FFFTP.exe index 314ded5d306e6ea1b1086da206632da47f1f830f..33156e6f4269bc61f8e5619ad3802190024f01fc 100644 GIT binary patch delta 44 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 delta 186148 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 diff --git a/contrib/putty/BUILDSCR.CV b/contrib/putty/BUILDSCR.CV new file mode 100644 index 0000000..71fb5c4 --- /dev/null +++ b/contrib/putty/BUILDSCR.CV @@ -0,0 +1,39 @@ +# -*- sh -*- + +# Build script to scan PuTTY with the downloadable Coverity scanner +# and generate a tar file to upload to their open-source scanning +# service. + +module putty + +# Preparations. +in putty do ./mkfiles.pl +in putty do ./mkauto.sh +in putty/doc do make + +# Scan the Unix build, on a 64-bit system to differentiate as much as +# possible from the other scan of the cross-platform files. +delegate covscan64 + in putty do ./configure + in putty do cov-build --dir cov-int make + in putty do tar czvf cov-int.tar.gz cov-int + return putty/cov-int.tar.gz +enddelegate + +# Scan the Windows build, by means of building with Winelib (since as +# of 2013-07-22, the Coverity Scan website doesn't offer a 32-bit +# Windows scanner for download). +delegate covscan32wine + in putty do tar xzvf cov-int.tar.gz + in putty/windows do cov-build --dir ../cov-int make -f Makefile.cyg CC=winegcc RC=wrc + in putty do tar czvf cov-int.tar.gz cov-int + return putty/cov-int.tar.gz +enddelegate + +# Provide the revision number as one of the build outputs, to make it +# easy to construct a curl upload command which will annotate it +# appropriately when uploaded. +in putty do echo $(revision) > revision.txt + +deliver putty/revision.txt $@ +deliver putty/cov-int.tar.gz $@ diff --git a/contrib/putty/CHARSET/CHARSET.H b/contrib/putty/CHARSET/CHARSET.H index 3f7eb34..85572c6 100644 --- a/contrib/putty/CHARSET/CHARSET.H +++ b/contrib/putty/CHARSET/CHARSET.H @@ -32,6 +32,7 @@ typedef enum { CS_ISO8859_16, CS_CP437, CS_CP850, + CS_CP852, CS_CP866, CS_CP1250, CS_CP1251, @@ -98,7 +99,8 @@ typedef struct { * U+FFFD (REPLACEMENT CHARACTER). */ -int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, +int charset_to_unicode(const char **input, int *inlen, + wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen); @@ -121,7 +123,8 @@ int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, * output charset). */ -int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen, +int charset_from_unicode(const wchar_t **input, int *inlen, + char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen); diff --git a/contrib/putty/CHARSET/FROMUCS.C b/contrib/putty/CHARSET/FROMUCS.C index ce69cd7..cdd1b6b 100644 --- a/contrib/putty/CHARSET/FROMUCS.C +++ b/contrib/putty/CHARSET/FROMUCS.C @@ -40,7 +40,8 @@ static void charset_emit(void *ctx, long int output) } } -int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen, +int charset_from_unicode(const wchar_t **input, int *inlen, + char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen) { diff --git a/contrib/putty/CHARSET/LOCALENC.C b/contrib/putty/CHARSET/LOCALENC.C index 9e51f72..99c93d6 100644 --- a/contrib/putty/CHARSET/LOCALENC.C +++ b/contrib/putty/CHARSET/LOCALENC.C @@ -21,6 +21,7 @@ static const struct { int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "", CS_NONE, 0 }, + { "UTF-8", CS_UTF8, 1 }, { "ISO-8859-1", CS_ISO8859_1, 1 }, { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 }, { "ISO-8859-2", CS_ISO8859_2, 1 }, @@ -39,6 +40,7 @@ static const struct { { "ISO-8859-16", CS_ISO8859_16, 1 }, { "CP437", CS_CP437, 1 }, { "CP850", CS_CP850, 1 }, + { "CP852", CS_CP852, 1 }, { "CP866", CS_CP866, 1 }, { "CP1250", CS_CP1250, 1 }, { "CP1251", CS_CP1251, 1 }, @@ -74,7 +76,6 @@ static const struct { { "VISCII", CS_VISCII, 1 }, { "HP ROMAN8", CS_HP_ROMAN8, 1 }, { "DEC MCS", CS_DEC_MCS, 1 }, - { "UTF-8", CS_UTF8, 1 }, }; const char *charset_to_localenc(int charset) diff --git a/contrib/putty/CHARSET/MIMEENC.C b/contrib/putty/CHARSET/MIMEENC.C index 27b860a..788d8ee 100644 --- a/contrib/putty/CHARSET/MIMEENC.C +++ b/contrib/putty/CHARSET/MIMEENC.C @@ -135,6 +135,11 @@ static const struct { { "850", CS_CP850 }, { "csPC850Multilingual", CS_CP850 }, + { "IBM852", CS_CP852 }, + { "cp852", CS_CP852 }, + { "852", CS_CP852 }, + { "csIBM852", CS_CP852 }, + { "IBM866", CS_CP866 }, { "cp866", CS_CP866 }, { "866", CS_CP866 }, diff --git a/contrib/putty/CHARSET/SBCS.DAT b/contrib/putty/CHARSET/SBCS.DAT index 2b919a4..4bf300f 100644 --- a/contrib/putty/CHARSET/SBCS.DAT +++ b/contrib/putty/CHARSET/SBCS.DAT @@ -394,6 +394,28 @@ charset CS_CP866 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0 + Another old DOS code page, submitted by a user and checked against + the translation table at + http://msdn.microsoft.com/en-us/goglobal/cc305161.aspx . + +charset CS_CP852 +0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f +0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f +0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f +0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f +0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f +0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f +0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f +0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f +00c7 00fc 00e9 00e2 00e4 016f 0107 00e7 0142 00eb 0150 0151 00ee 0179 00c4 0106 +00c9 0139 013a 00f4 00f6 013d 013e 015a 015b 00d6 00dc 0164 0165 0141 00d7 010d +00e1 00ed 00f3 00fa 0104 0105 017d 017e 0118 0119 00ac 017a 010c 015f 00ab 00bb +2591 2592 2593 2502 2524 00c1 00c2 011a 015e 2563 2551 2557 255d 017b 017c 2510 +2514 2534 252c 251c 2500 253c 0102 0103 255a 2554 2569 2566 2560 2550 256c 00a4 +0111 0110 010e 00cb 010f 0147 00cd 00ce 011b 2518 250c 2588 2584 0162 016e 2580 +00d3 00df 00d4 0143 0144 0148 0160 0161 0154 00da 0155 0170 00fd 00dd 0163 00b4 +00ad 02dd 02db 02c7 02d8 00a7 00f7 00b8 00b0 00a8 02d9 0171 0158 0159 25a0 00a0 + Here are some Windows code pages, generated by this piece of Bourne shell: diff --git a/contrib/putty/CHARSET/SBCSDAT.C b/contrib/putty/CHARSET/SBCSDAT.C index 664bcd5..d1510de 100644 --- a/contrib/putty/CHARSET/SBCSDAT.C +++ b/contrib/putty/CHARSET/SBCSDAT.C @@ -1425,6 +1425,81 @@ const charset_spec charset_CS_CP866 = { CS_CP866, read_sbcs, write_sbcs, &data_CS_CP866 }; +static const sbcs_data data_CS_CP852 = { + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x016f, 0x0107, 0x00e7, + 0x0142, 0x00eb, 0x0150, 0x0151, 0x00ee, 0x0179, 0x00c4, 0x0106, + 0x00c9, 0x0139, 0x013a, 0x00f4, 0x00f6, 0x013d, 0x013e, 0x015a, + 0x015b, 0x00d6, 0x00dc, 0x0164, 0x0165, 0x0141, 0x00d7, 0x010d, + 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x0104, 0x0105, 0x017d, 0x017e, + 0x0118, 0x0119, 0x00ac, 0x017a, 0x010c, 0x015f, 0x00ab, 0x00bb, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x011a, + 0x015e, 0x2563, 0x2551, 0x2557, 0x255d, 0x017b, 0x017c, 0x2510, + 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x0102, 0x0103, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, + 0x0111, 0x0110, 0x010e, 0x00cb, 0x010f, 0x0147, 0x00cd, 0x00ce, + 0x011b, 0x2518, 0x250c, 0x2588, 0x2584, 0x0162, 0x016e, 0x2580, + 0x00d3, 0x00df, 0x00d4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00da, 0x0155, 0x0170, 0x00fd, 0x00dd, 0x0163, 0x00b4, + 0x00ad, 0x02dd, 0x02db, 0x02c7, 0x02d8, 0x00a7, 0x00f7, 0x00b8, + 0x00b0, 0x00a8, 0x02d9, 0x0171, 0x0158, 0x0159, 0x25a0, 0x00a0 + }, + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0xff, 0xcf, 0xf5, 0xf9, 0xae, 0xaa, 0xf0, 0xf8, + 0xef, 0xf7, 0xaf, 0xb5, 0xb6, 0x8e, 0x80, 0x90, + 0xd3, 0xd6, 0xd7, 0xe0, 0xe2, 0x99, 0x9e, 0xe9, + 0x9a, 0xed, 0xe1, 0xa0, 0x83, 0x84, 0x87, 0x82, + 0x89, 0xa1, 0x8c, 0xa2, 0x93, 0x94, 0xf6, 0xa3, + 0x81, 0xec, 0xc6, 0xc7, 0xa4, 0xa5, 0x8f, 0x86, + 0xac, 0x9f, 0xd2, 0xd4, 0xd1, 0xd0, 0xa8, 0xa9, + 0xb7, 0xd8, 0x91, 0x92, 0x95, 0x96, 0x9d, 0x88, + 0xe3, 0xe4, 0xd5, 0xe5, 0x8a, 0x8b, 0xe8, 0xea, + 0xfc, 0xfd, 0x97, 0x98, 0xb8, 0xad, 0xe6, 0xe7, + 0xdd, 0xee, 0x9b, 0x9c, 0xde, 0x85, 0xeb, 0xfb, + 0x8d, 0xab, 0xbd, 0xbe, 0xa6, 0xa7, 0xf3, 0xf4, + 0xfa, 0xf2, 0xf1, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, + 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, + 0xc9, 0xbb, 0xc8, 0xbc, 0xcc, 0xb9, 0xcb, 0xca, + 0xce, 0xdf, 0xdc, 0xdb, 0xb0, 0xb1, 0xb2, 0xfe + }, + 256 +}; +const charset_spec charset_CS_CP852 = { + CS_CP852, read_sbcs, write_sbcs, &data_CS_CP852 +}; + static const sbcs_data data_CS_CP1250 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, @@ -3980,6 +4055,7 @@ ENUM_CHARSET(CS_ISO8859_1_X11) ENUM_CHARSET(CS_CP437) ENUM_CHARSET(CS_CP850) ENUM_CHARSET(CS_CP866) +ENUM_CHARSET(CS_CP852) ENUM_CHARSET(CS_CP1250) ENUM_CHARSET(CS_CP1251) ENUM_CHARSET(CS_CP1252) diff --git a/contrib/putty/CHARSET/TOUCS.C b/contrib/putty/CHARSET/TOUCS.C index 658eb73..e3608c0 100644 --- a/contrib/putty/CHARSET/TOUCS.C +++ b/contrib/putty/CHARSET/TOUCS.C @@ -46,7 +46,8 @@ static void unicode_emit(void *ctx, long int output) } } -int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, +int charset_to_unicode(const char **input, int *inlen, + wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen) { diff --git a/contrib/putty/CHARSET/XENC.C b/contrib/putty/CHARSET/XENC.C index 2832f31..53e8a5e 100644 --- a/contrib/putty/CHARSET/XENC.C +++ b/contrib/putty/CHARSET/XENC.C @@ -46,6 +46,7 @@ static const struct { { "koi8-u", CS_KOI8_U }, { "ibm-cp437", CS_CP437 }, { "ibm-cp850", CS_CP850 }, + { "ibm-cp852", CS_CP852 }, { "ibm-cp866", CS_CP866 }, { "microsoft-cp1250", CS_CP1250 }, { "microsoft-cp1251", CS_CP1251 }, diff --git a/contrib/putty/CMDGEN.C b/contrib/putty/CMDGEN.C index 5d9efcf..e254c03 100644 --- a/contrib/putty/CMDGEN.C +++ b/contrib/putty/CMDGEN.C @@ -102,6 +102,16 @@ void modalfatalbox(char *p, ...) cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); +} + /* * Stubs to let everything else link sensibly. */ @@ -118,10 +128,7 @@ void sk_cleanup(void) void showversion(void) { - char *verstr = dupstr(ver); - verstr[0] = tolower((unsigned char)verstr[0]); - printf("PuTTYgen %s\n", verstr); - sfree(verstr); + printf("puttygen: %s\n", ver); } void usage(int standalone) @@ -257,12 +264,11 @@ static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen) int main(int argc, char **argv) { char *infile = NULL; - Filename infilename; + Filename *infilename = NULL, *outfilename = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; - Filename outfilename; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE; - int bits = 1024; + int bits = 2048; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; @@ -536,7 +542,7 @@ int main(int argc, char **argv) if (infile) { infilename = filename_from_str(infile); - intype = key_type(&infilename); + intype = key_type(infilename); switch (intype) { /* @@ -668,7 +674,7 @@ int main(int argc, char **argv) return 1; } random_add_heavynoise(entropy, bits / 8); - memset(entropy, 0, bits/8); + smemclr(entropy, bits/8); sfree(entropy); if (keytype == DSA) { @@ -707,11 +713,11 @@ int main(int argc, char **argv) * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) - encrypted = rsakey_encrypted(&infilename, &origcomment); + encrypted = rsakey_encrypted(infilename, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) - encrypted = ssh2_userkey_encrypted(&infilename, &origcomment); + encrypted = ssh2_userkey_encrypted(infilename, &origcomment); else - encrypted = import_encrypted(&infilename, intype, &origcomment); + encrypted = import_encrypted(infilename, intype, &origcomment); /* * If so, ask for a passphrase. @@ -721,7 +727,7 @@ int main(int argc, char **argv) int ret; p->to_server = FALSE; p->name = dupstr("SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { @@ -746,7 +752,7 @@ int main(int argc, char **argv) unsigned char *blob; int n, l, bloblen; - ret = rsakey_pubblob(&infilename, &vblob, &bloblen, + ret = rsakey_pubblob(infilename, &vblob, &bloblen, &origcomment, &error); blob = (unsigned char *)vblob; @@ -767,8 +773,11 @@ int main(int argc, char **argv) } ssh1key->comment = dupstr(origcomment); ssh1key->private_exponent = NULL; + ssh1key->p = NULL; + ssh1key->q = NULL; + ssh1key->iqmp = NULL; } else { - ret = loadrsakey(&infilename, ssh1key, passphrase, &error); + ret = loadrsakey(infilename, ssh1key, passphrase, &error); } if (ret > 0) error = NULL; @@ -778,15 +787,17 @@ int main(int argc, char **argv) case SSH_KEYTYPE_SSH2: if (!load_encrypted) { - ssh2blob = ssh2_userkey_loadpub(&infilename, &ssh2alg, + ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg, &ssh2bloblen, NULL, &error); - ssh2algf = find_pubkey_alg(ssh2alg); - if (ssh2algf) - bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); - else - bits = -1; + if (ssh2blob) { + ssh2algf = find_pubkey_alg(ssh2alg); + if (ssh2algf) + bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); + else + bits = -1; + } } else { - ssh2key = ssh2_load_userkey(&infilename, passphrase, &error); + ssh2key = ssh2_load_userkey(infilename, passphrase, &error); } if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; @@ -800,7 +811,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: - ssh2key = import_ssh2(&infilename, intype, passphrase, &error); + ssh2key = import_ssh2(infilename, intype, passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; @@ -846,8 +857,8 @@ int main(int argc, char **argv) p->to_server = FALSE; p->name = dupstr("New SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE, 512); - add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE); + add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { @@ -861,7 +872,7 @@ int main(int argc, char **argv) return 1; } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } passphrase = dupstr(p->prompts[0]->result); @@ -892,14 +903,14 @@ int main(int argc, char **argv) case PRIVATE: if (sshver == 1) { assert(ssh1key); - ret = saversakey(&outfilename, ssh1key, passphrase); + ret = saversakey(outfilename, ssh1key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-1 private key\n"); return 1; } } else { assert(ssh2key); - ret = ssh2_save_userkey(&outfilename, ssh2key, passphrase); + ret = ssh2_save_userkey(outfilename, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); return 1; @@ -1023,7 +1034,7 @@ int main(int argc, char **argv) case SSHCOM: assert(sshver == 2); assert(ssh2key); - ret = export_ssh2(&outfilename, outtype, ssh2key, passphrase); + ret = export_ssh2(outfilename, outtype, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); return 1; @@ -1036,7 +1047,7 @@ int main(int argc, char **argv) } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } diff --git a/contrib/putty/CMDLINE.C b/contrib/putty/CMDLINE.C index aa376a0..4a33500 100644 --- a/contrib/putty/CMDLINE.C +++ b/contrib/putty/CMDLINE.C @@ -63,7 +63,7 @@ void cmdline_cleanup(void) int pri; if (cmdline_password) { - memset(cmdline_password, 0, strlen(cmdline_password)); + smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; } @@ -105,15 +105,12 @@ int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) { if (tried_once) return 0; - strncpy(p->prompts[0]->result, cmdline_password, - p->prompts[0]->result_len); - p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0'; - memset(cmdline_password, 0, strlen(cmdline_password)); + prompt_set_result(p->prompts[0], cmdline_password); + smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; tried_once = 1; return 1; - } /* @@ -162,7 +159,7 @@ static int cmdline_check_unavailable(int flag, char *p) if (need_save < 0) return x; \ } while (0) -int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) +int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) { int ret = 0; @@ -170,7 +167,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(2); /* This parameter must be processed immediately rather than being * saved. */ - do_defaults(value, cfg); + do_defaults(value, conf); loaded_session = TRUE; cmdline_session_name = dupstr(value); return 2; @@ -179,41 +176,49 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_SSH; - default_port = cfg->port = 22; + default_protocol = PROT_SSH; + default_port = 22; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-telnet")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_TELNET; - default_port = cfg->port = 23; + default_protocol = PROT_TELNET; + default_port = 23; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-rlogin")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_RLOGIN; - default_port = cfg->port = 513; + default_protocol = PROT_RLOGIN; + default_port = 513; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-raw")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_RAW; + default_protocol = PROT_RAW; + conf_set_int(conf, CONF_protocol, default_protocol); } if (!strcmp(p, "-serial")) { RETURN(1); /* Serial is not NONNETWORK in an odd sense of the word */ UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_SERIAL; - /* The host parameter will already be loaded into cfg->host, so copy it across */ - strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1); - cfg->serline[sizeof(cfg->serline) - 1] = '\0'; + default_protocol = PROT_SERIAL; + conf_set_int(conf, CONF_protocol, default_protocol); + /* The host parameter will already be loaded into CONF_host, + * so copy it across */ + conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host)); } if (!strcmp(p, "-v")) { RETURN(1); @@ -223,41 +228,23 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - strncpy(cfg->username, value, sizeof(cfg->username)); - cfg->username[sizeof(cfg->username) - 1] = '\0'; + conf_set_str(conf, CONF_username, value); } if (!strcmp(p, "-loghost")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - strncpy(cfg->loghost, value, sizeof(cfg->loghost)); - cfg->loghost[sizeof(cfg->loghost) - 1] = '\0'; + conf_set_str(conf, CONF_loghost, value); } if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { - char *fwd, *ptr, *q, *qq; - int dynamic, i=0; + char type, *q, *qq, *key, *val; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - dynamic = !strcmp(p, "-D"); - fwd = value; - ptr = cfg->portfwd; - /* if existing forwards, find end of list */ - while (*ptr) { - while (*ptr) - ptr++; - ptr++; - } - i = ptr - cfg->portfwd; - ptr[0] = p[1]; /* insert a 'L', 'R' or 'D' at the start */ - ptr++; - if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) { - cmdline_error("out of space for port forwardings"); - return ret; - } - strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2); - if (!dynamic) { + if (strcmp(p, "-D")) { /* + * For -L or -R forwarding types: + * * We expect _at least_ two colons in this string. The * possible formats are `sourceport:desthost:destport', * or `sourceip:sourceport:desthost:destport' if you're @@ -265,19 +252,47 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) * replace the one between source and dest with a \t; * this means we must find the second-to-last colon in * the string. + * + * (This looks like a foolish way of doing it given the + * existence of strrchr, but it's more efficient than + * two strrchrs - not to mention that the second strrchr + * would require us to modify the input string!) */ - q = qq = strchr(ptr, ':'); + + type = p[1]; /* 'L' or 'R' */ + + q = qq = strchr(value, ':'); while (qq) { char *qqq = strchr(qq+1, ':'); if (qqq) q = qq; qq = qqq; } - if (q) *q = '\t'; /* replace second-last colon with \t */ + + if (!q) { + cmdline_error("-%c expects at least two colons in its" + " argument", type); + return ret; + } + + key = dupprintf("%c%.*s", type, q - value, value); + val = dupstr(q+1); + } else { + /* + * Dynamic port forwardings are entered under the same key + * as if they were local (because they occupy the same + * port space - a local and a dynamic forwarding on the + * same local port are mutually exclusive), with the + * special value "D" (which can be distinguished from + * anything in the ordinary -L case by containing no + * colon). + */ + key = dupprintf("L%s", value); + val = dupstr("D"); } - cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0'; - cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0'; - ptr[strlen(ptr)+1] = '\000'; /* append 2nd '\000' */ + conf_set_str_str(conf, CONF_portfwd, key, val); + sfree(key); + sfree(val); } if ((!strcmp(p, "-nc"))) { char *host, *portp; @@ -286,20 +301,16 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - host = portp = value; - while (*portp && *portp != ':') - portp++; - if (*portp) { - unsigned len = portp - host; - if (len >= sizeof(cfg->ssh_nc_host)) - len = sizeof(cfg->ssh_nc_host) - 1; - memcpy(cfg->ssh_nc_host, value, len); - cfg->ssh_nc_host[len] = '\0'; - cfg->ssh_nc_port = atoi(portp+1); - } else { + portp = strchr(value, ':'); + if (!portp) { cmdline_error("-nc expects argument of form 'host:port'"); return ret; } + + host = dupprintf("%.*s", portp - value, value); + conf_set_str(conf, CONF_ssh_nc_host, host); + conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1)); + sfree(host); } if (!strcmp(p, "-m")) { char *filename, *command; @@ -317,8 +328,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) command = NULL; fp = fopen(filename, "r"); if (!fp) { - cmdline_error("unable to open command " - "file \"%s\"", filename); + cmdline_error("unable to open command file \"%s\"", filename); return ret; } do { @@ -332,16 +342,17 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) } command[cmdlen++] = d; } while (c != EOF); - cfg->remote_cmd_ptr = command; - cfg->remote_cmd_ptr2 = NULL; - cfg->nopty = TRUE; /* command => no terminal */ fclose(fp); + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no terminal */ + sfree(command); } if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -ssh,-telnet */ - cfg->port = atoi(value); + conf_set_int(conf, CONF_port, atoi(value)); } if (!strcmp(p, "-pw")) { RETURN(2); @@ -349,7 +360,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) 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) + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) cmdline_error("the -pw option can only be used with the " "SSH protocol"); else { @@ -357,7 +368,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) /* 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)); + smemclr(value, strlen(value)); } } @@ -366,105 +377,108 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->tryagent = TRUE; + conf_set_int(conf, CONF_tryagent, TRUE); } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->tryagent = FALSE; + conf_set_int(conf, CONF_tryagent, FALSE); } if (!strcmp(p, "-A")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->agentfwd = 1; + conf_set_int(conf, CONF_agentfwd, 1); } if (!strcmp(p, "-a")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->agentfwd = 0; + conf_set_int(conf, CONF_agentfwd, 0); } if (!strcmp(p, "-X")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->x11_forward = 1; + conf_set_int(conf, CONF_x11_forward, 1); } if (!strcmp(p, "-x")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->x11_forward = 0; + conf_set_int(conf, CONF_x11_forward, 0); } if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -m */ - cfg->nopty = 0; + conf_set_int(conf, CONF_nopty, 0); } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); - cfg->nopty = 1; + conf_set_int(conf, CONF_nopty, 1); } if (!strcmp(p, "-N")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->ssh_no_shell = 1; + conf_set_int(conf, CONF_ssh_no_shell, 1); } if (!strcmp(p, "-C")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->compression = 1; + conf_set_int(conf, CONF_compression, 1); } if (!strcmp(p, "-1")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->sshprot = 0; /* ssh protocol 1 only */ + conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */ } if (!strcmp(p, "-2")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->sshprot = 3; /* ssh protocol 2 only */ + conf_set_int(conf, CONF_sshprot, 3); /* ssh protocol 2 only */ } if (!strcmp(p, "-i")) { + Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->keyfile = filename_from_str(value); + fn = filename_from_str(value); + conf_set_filename(conf, CONF_keyfile, fn); + filename_free(fn); } if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) { RETURN(1); SAVEABLE(1); - cfg->addressfamily = ADDRTYPE_IPV4; + conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4); } if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) { RETURN(1); SAVEABLE(1); - cfg->addressfamily = ADDRTYPE_IPV6; + conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6); } if (!strcmp(p, "-sercfg")) { char* nextitem; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); - if (cfg->protocol != PROT_SERIAL) + if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL) cmdline_error("the -sercfg option can only be used with the " "serial protocol"); /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */ @@ -483,55 +497,45 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) if (length == 1) { switch (*nextitem) { case '1': - cfg->serstopbits = 2; - break; case '2': - cfg->serstopbits = 4; + conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0')); break; case '5': - cfg->serdatabits = 5; - break; case '6': - cfg->serdatabits = 6; - break; case '7': - cfg->serdatabits = 7; - break; case '8': - cfg->serdatabits = 8; - break; case '9': - cfg->serdatabits = 9; + conf_set_int(conf, CONF_serdatabits, *nextitem-'0'); break; case 'n': - cfg->serparity = SER_PAR_NONE; + conf_set_int(conf, CONF_serparity, SER_PAR_NONE); break; case 'o': - cfg->serparity = SER_PAR_ODD; + conf_set_int(conf, CONF_serparity, SER_PAR_ODD); break; case 'e': - cfg->serparity = SER_PAR_EVEN; + conf_set_int(conf, CONF_serparity, SER_PAR_EVEN); break; case 'm': - cfg->serparity = SER_PAR_MARK; + conf_set_int(conf, CONF_serparity, SER_PAR_MARK); break; case 's': - cfg->serparity = SER_PAR_SPACE; + conf_set_int(conf, CONF_serparity, SER_PAR_SPACE); break; case 'N': - cfg->serflow = SER_FLOW_NONE; + conf_set_int(conf, CONF_serflow, SER_FLOW_NONE); break; case 'X': - cfg->serflow = SER_FLOW_XONXOFF; + conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF); break; case 'R': - cfg->serflow = SER_FLOW_RTSCTS; + conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS); break; case 'D': - cfg->serflow = SER_FLOW_DSRDTR; + conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR); break; default: @@ -540,11 +544,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) } } else if (length == 3 && !strncmp(nextitem,"1.5",3)) { /* Messy special case */ - cfg->serstopbits = 3; + conf_set_int(conf, CONF_serstopbits, 3); } else { int serspeed = atoi(nextitem); if (serspeed != 0) { - cfg->serspeed = serspeed; + conf_set_int(conf, CONF_serspeed, serspeed); } else { cmdline_error("Unrecognised suboption \"-sercfg %s\"", nextitem); @@ -556,11 +560,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) return ret; /* unrecognised */ } -void cmdline_run_saved(Config *cfg) +void cmdline_run_saved(Conf *conf) { int pri, i; for (pri = 0; pri < NPRIORITIES; pri++) for (i = 0; i < saves[pri].nsaved; i++) cmdline_process_param(saves[pri].params[i].p, - saves[pri].params[i].value, 0, cfg); + saves[pri].params[i].value, 0, conf); } diff --git a/contrib/putty/CONF.C b/contrib/putty/CONF.C new file mode 100644 index 0000000..3f27205 --- /dev/null +++ b/contrib/putty/CONF.C @@ -0,0 +1,613 @@ +/* + * conf.c: implementation of the internal storage format used for + * the configuration of a PuTTY session. + */ + +#include +#include +#include + +#include "tree234.h" +#include "putty.h" + +/* + * Enumeration of types used in keys and values. + */ +typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type; + +/* + * Arrays which allow us to look up the subkey and value types for a + * given primary key id. + */ +#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype, +static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) }; +#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype, +static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) }; + +/* + * Configuration keys are primarily integers (big enum of all the + * different configurable options); some keys have string-designated + * subkeys, such as the list of environment variables (subkeys + * defined by the variable names); some have integer-designated + * subkeys (wordness, colours, preference lists). + */ +struct key { + int primary; + union { + int i; + char *s; + } secondary; +}; + +struct value { + union { + int intval; + char *stringval; + Filename *fileval; + FontSpec *fontval; + } u; +}; + +struct conf_entry { + struct key key; + struct value value; +}; + +struct conf_tag { + tree234 *tree; +}; + +/* + * Because 'struct key' is the first element in 'struct conf_entry', + * it's safe (guaranteed by the C standard) to cast arbitrarily back + * and forth between the two types. Therefore, we only need one + * comparison function, which can double as a main sort function for + * the tree (comparing two conf_entry structures with each other) + * and a search function (looking up an externally supplied key). + */ +static int conf_cmp(void *av, void *bv) +{ + struct key *a = (struct key *)av; + struct key *b = (struct key *)bv; + + if (a->primary < b->primary) + return -1; + else if (a->primary > b->primary) + return +1; + switch (subkeytypes[a->primary]) { + case TYPE_INT: + if (a->secondary.i < b->secondary.i) + return -1; + else if (a->secondary.i > b->secondary.i) + return +1; + return 0; + case TYPE_STR: + return strcmp(a->secondary.s, b->secondary.s); + default: + return 0; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct key'. We + * don't free the structure itself, since it's probably part of a + * larger allocated block. + */ +static void free_key(struct key *key) +{ + if (subkeytypes[key->primary] == TYPE_STR) + sfree(key->secondary.s); +} + +/* + * Copy a 'struct key' into another one, copying its dynamic data + * if necessary. + */ +static void copy_key(struct key *to, struct key *from) +{ + to->primary = from->primary; + switch (subkeytypes[to->primary]) { + case TYPE_INT: + to->secondary.i = from->secondary.i; + break; + case TYPE_STR: + to->secondary.s = dupstr(from->secondary.s); + break; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct value'. We + * don't free the value itself, since it's probably part of a larger + * allocated block. + */ +static void free_value(struct value *val, int type) +{ + if (type == TYPE_STR) + sfree(val->u.stringval); + else if (type == TYPE_FILENAME) + filename_free(val->u.fileval); + else if (type == TYPE_FONT) + fontspec_free(val->u.fontval); +} + +/* + * Copy a 'struct value' into another one, copying its dynamic data + * if necessary. + */ +static void copy_value(struct value *to, struct value *from, int type) +{ + switch (type) { + case TYPE_INT: + to->u.intval = from->u.intval; + break; + case TYPE_STR: + to->u.stringval = dupstr(from->u.stringval); + break; + case TYPE_FILENAME: + to->u.fileval = filename_copy(from->u.fileval); + break; + case TYPE_FONT: + to->u.fontval = fontspec_copy(from->u.fontval); + break; + } +} + +/* + * Free an entire 'struct conf_entry' and its dynamic data. + */ +static void free_entry(struct conf_entry *entry) +{ + free_key(&entry->key); + free_value(&entry->value, valuetypes[entry->key.primary]); + sfree(entry); +} + +Conf *conf_new(void) +{ + Conf *conf = snew(struct conf_tag); + + conf->tree = newtree234(conf_cmp); + + return conf; +} + +static void conf_clear(Conf *conf) +{ + struct conf_entry *entry; + + while ((entry = delpos234(conf->tree, 0)) != NULL) + free_entry(entry); +} + +void conf_free(Conf *conf) +{ + conf_clear(conf); + freetree234(conf->tree); + sfree(conf); +} + +static void conf_insert(Conf *conf, struct conf_entry *entry) +{ + struct conf_entry *oldentry = add234(conf->tree, entry); + if (oldentry && oldentry != entry) { + del234(conf->tree, oldentry); + free_entry(oldentry); + oldentry = add234(conf->tree, entry); + assert(oldentry == entry); + } +} + +void conf_copy_into(Conf *newconf, Conf *oldconf) +{ + struct conf_entry *entry, *entry2; + int i; + + conf_clear(newconf); + + for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) { + entry2 = snew(struct conf_entry); + copy_key(&entry2->key, &entry->key); + copy_value(&entry2->value, &entry->value, + valuetypes[entry->key.primary]); + add234(newconf->tree, entry2); + } +} + +Conf *conf_copy(Conf *oldconf) +{ + Conf *newconf = conf_new(); + + conf_copy_into(newconf, oldconf); + + return newconf; +} + +int conf_get_int(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +int conf_get_int_int(Conf *conf, int primary, int secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + key.secondary.i = secondary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +char *conf_get_str(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.stringval; +} + +char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + return entry ? entry->value.u.stringval : NULL; +} + +char *conf_get_str_str(Conf *conf, int primary, const char *secondary) +{ + char *ret = conf_get_str_str_opt(conf, primary, secondary); + assert(ret); + return ret; +} + +char *conf_get_str_strs(Conf *conf, int primary, + char *subkeyin, char **subkeyout) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + if (subkeyin) { + key.secondary.s = subkeyin; + entry = findrel234(conf->tree, &key, NULL, REL234_GT); + } else { + key.secondary.s = ""; + entry = findrel234(conf->tree, &key, NULL, REL234_GE); + } + if (!entry || entry->key.primary != primary) + return NULL; + *subkeyout = entry->key.secondary.s; + return entry->value.u.stringval; +} + +char *conf_get_str_nthstrkey(Conf *conf, int primary, int n) +{ + struct key key; + struct conf_entry *entry; + int index; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = ""; + entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index); + if (!entry || entry->key.primary != primary) + return NULL; + entry = index234(conf->tree, index + n); + if (!entry || entry->key.primary != primary) + return NULL; + return entry->key.secondary.s; +} + +Filename *conf_get_filename(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.fileval; +} + +FontSpec *conf_get_fontspec(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.fontval; +} + +void conf_set_int(Conf *conf, int primary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_int_int(Conf *conf, int primary, int secondary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->key.secondary.i = secondary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_str(Conf *conf, int primary, const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_set_str_str(Conf *conf, int primary, const char *secondary, + const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->key.secondary.s = dupstr(secondary); + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_del_str_str(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + if (entry) { + del234(conf->tree, entry); + free_entry(entry); + } + } + +void conf_set_filename(Conf *conf, int primary, const Filename *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + entry->key.primary = primary; + entry->value.u.fileval = filename_copy(value); + conf_insert(conf, entry); +} + +void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + entry->key.primary = primary; + entry->value.u.fontval = fontspec_copy(value); + conf_insert(conf, entry); +} + +int conf_serialised_size(Conf *conf) +{ + int i; + struct conf_entry *entry; + int size = 0; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + size += 4; /* primary key */ + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->key.secondary.s); + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->value.u.stringval); + break; + case TYPE_FILENAME: + size += filename_serialise(entry->value.u.fileval, NULL); + break; + case TYPE_FONT: + size += fontspec_serialise(entry->value.u.fontval, NULL); + break; + } + } + + size += 4; /* terminator value */ + + return size; +} + +void conf_serialise(Conf *conf, void *vdata) +{ + unsigned char *data = (unsigned char *)vdata; + int i, len; + struct conf_entry *entry; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + PUT_32BIT_MSB_FIRST(data, entry->key.primary); + data += 4; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->key.secondary.s); + memcpy(data, entry->key.secondary.s, len); + data += len; + *data++ = 0; + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->value.u.intval); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->value.u.stringval); + memcpy(data, entry->value.u.stringval, len); + data += len; + *data++ = 0; + break; + case TYPE_FILENAME: + data += filename_serialise(entry->value.u.fileval, data); + break; + case TYPE_FONT: + data += fontspec_serialise(entry->value.u.fontval, data); + break; + } + } + + PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU); +} + +int conf_deserialise(Conf *conf, void *vdata, int maxsize) +{ + unsigned char *data = (unsigned char *)vdata; + unsigned char *start = data; + struct conf_entry *entry; + unsigned primary; + int used; + unsigned char *zero; + + while (maxsize >= 4) { + primary = GET_32BIT_MSB_FIRST(data); + data += 4, maxsize -= 4; + + if (primary >= N_CONFIG_OPTIONS) + break; + + entry = snew(struct conf_entry); + entry->key.primary = primary; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + sfree(entry); + goto done; + } + entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data)); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + sfree(entry); + goto done; + } + entry->key.secondary.s = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + } + + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data)); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.stringval = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + case TYPE_FILENAME: + entry->value.u.fileval = + filename_deserialise(data, maxsize, &used); + if (!entry->value.u.fileval) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + data += used; + maxsize -= used; + break; + case TYPE_FONT: + entry->value.u.fontval = + fontspec_deserialise(data, maxsize, &used); + if (!entry->value.u.fontval) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + data += used; + maxsize -= used; + break; + } + conf_insert(conf, entry); + } + + done: + return (int)(data - start); +} diff --git a/contrib/putty/CONFIG.C b/contrib/putty/CONFIG.C index 38e47b6..e822eec 100644 --- a/contrib/putty/CONFIG.C +++ b/contrib/putty/CONFIG.C @@ -15,10 +15,149 @@ #define HOST_BOX_TITLE "Host Name (or IP address)" #define PORT_BOX_TITLE "Port" +void conf_radiobutton_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int button; + Conf *conf = (Conf *)data; + + /* + * For a standard radio button set, the context parameter gives + * the primary key (CONF_foo), and the extra data per button + * gives the value the target field should take if that button + * is the one selected. + */ + if (event == EVENT_REFRESH) { + int val = conf_get_int(conf, ctrl->radio.context.i); + for (button = 0; button < ctrl->radio.nbuttons; button++) + if (val == ctrl->radio.buttondata[button].i) + break; + /* We expected that `break' to happen, in all circumstances. */ + assert(button < ctrl->radio.nbuttons); + dlg_radiobutton_set(ctrl, dlg, button); + } else if (event == EVENT_VALCHANGE) { + button = dlg_radiobutton_get(ctrl, dlg); + assert(button >= 0 && button < ctrl->radio.nbuttons); + conf_set_int(conf, ctrl->radio.context.i, + ctrl->radio.buttondata[button].i); + } +} + +#define CHECKBOX_INVERT (1<<30) +void conf_checkbox_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key, invert; + Conf *conf = (Conf *)data; + + /* + * For a standard checkbox, the context parameter gives the + * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT. + */ + key = ctrl->checkbox.context.i; + if (key & CHECKBOX_INVERT) { + key &= ~CHECKBOX_INVERT; + invert = 1; + } else + invert = 0; + + /* + * C lacks a logical XOR, so the following code uses the idiom + * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1 + * iff exactly one of a and b is nonzero, otherwise 0.) + */ + + if (event == EVENT_REFRESH) { + int val = conf_get_int(conf, key); + dlg_checkbox_set(ctrl, dlg, (!val ^ !invert)); + } else if (event == EVENT_VALCHANGE) { + conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert); + } +} + +void conf_editbox_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + /* + * The standard edit-box handler expects the main `context' + * field to contain the primary key. The secondary `context2' + * field indicates the type of this field: + * + * - if context2 > 0, the field is a string. + * - if context2 == -1, the field is an int and the edit box + * is numeric. + * - if context2 < -1, the field is an int and the edit box is + * _floating_, and (-context2) gives the scale. (E.g. if + * context2 == -1000, then typing 1.2 into the box will set + * the field to 1200.) + */ + int key = ctrl->editbox.context.i; + int length = ctrl->editbox.context2.i; + Conf *conf = (Conf *)data; + + if (length > 0) { + if (event == EVENT_REFRESH) { + char *field = conf_get_str(conf, key); + dlg_editbox_set(ctrl, dlg, field); + } else if (event == EVENT_VALCHANGE) { + char *field = dlg_editbox_get(ctrl, dlg); + conf_set_str(conf, key, field); + sfree(field); + } + } else if (length < 0) { + if (event == EVENT_REFRESH) { + char str[80]; + int value = conf_get_int(conf, key); + if (length == -1) + sprintf(str, "%d", value); + else + sprintf(str, "%g", (double)value / (double)(-length)); + dlg_editbox_set(ctrl, dlg, str); + } else if (event == EVENT_VALCHANGE) { + char *str = dlg_editbox_get(ctrl, dlg); + if (length == -1) + conf_set_int(conf, key, atoi(str)); + else + conf_set_int(conf, key, (int)((-length) * atof(str))); + sfree(str); + } + } +} + +void conf_filesel_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key = ctrl->fileselect.context.i; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key)); + } else if (event == EVENT_VALCHANGE) { + Filename *filename = dlg_filesel_get(ctrl, dlg); + conf_set_filename(conf, key, filename); + filename_free(filename); + } +} + +void conf_fontsel_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key = ctrl->fontselect.context.i; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key)); + } else if (event == EVENT_VALCHANGE) { + FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg); + conf_set_fontspec(conf, key, fontspec); + fontspec_free(fontspec); + } +} + static void config_host_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* * This function works just like the standard edit box handler, @@ -26,29 +165,31 @@ static void config_host_handler(union control *ctrl, void *dlg, * different places depending on the protocol. */ if (event == EVENT_REFRESH) { - if (cfg->protocol == PROT_SERIAL) { + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain an n, * since that's the shortcut for the host name control. */ dlg_label_change(ctrl, dlg, "Serial line"); - dlg_editbox_set(ctrl, dlg, cfg->serline); + dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline)); } else { dlg_label_change(ctrl, dlg, HOST_BOX_TITLE); - dlg_editbox_set(ctrl, dlg, cfg->host); + dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host)); } } else if (event == EVENT_VALCHANGE) { - if (cfg->protocol == PROT_SERIAL) - dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline)); + char *s = dlg_editbox_get(ctrl, dlg); + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + conf_set_str(conf, CONF_serline, s); else - dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host)); + conf_set_str(conf, CONF_host, s); + sfree(s); } } static void config_port_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; char buf[80]; /* @@ -57,28 +198,31 @@ static void config_port_handler(union control *ctrl, void *dlg, * different places depending on the protocol. */ if (event == EVENT_REFRESH) { - if (cfg->protocol == PROT_SERIAL) { + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain a p, * since that's the shortcut for the port control. */ dlg_label_change(ctrl, dlg, "Speed"); - sprintf(buf, "%d", cfg->serspeed); + sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed)); } else { dlg_label_change(ctrl, dlg, PORT_BOX_TITLE); - if (cfg->port != 0) - sprintf(buf, "%d", cfg->port); + if (conf_get_int(conf, CONF_port) != 0) + sprintf(buf, "%d", conf_get_int(conf, CONF_port)); else /* Display an (invalid) port of 0 as blank */ buf[0] = '\0'; } dlg_editbox_set(ctrl, dlg, buf); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, buf, lenof(buf)); - if (cfg->protocol == PROT_SERIAL) - cfg->serspeed = atoi(buf); + char *s = dlg_editbox_get(ctrl, dlg); + int i = atoi(s); + sfree(s); + + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + conf_set_int(conf, CONF_serspeed, i); else - cfg->port = atoi(buf); + conf_set_int(conf, CONF_port, i); } } @@ -95,7 +239,7 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct hostport *hp = (struct hostport *)ctrl->radio.context.p; /* @@ -106,20 +250,25 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, * structure giving the `union control's for both. */ if (event == EVENT_REFRESH) { + int protocol = conf_get_int(conf, CONF_protocol); for (button = 0; button < ctrl->radio.nbuttons; button++) - if (cfg->protocol == ctrl->radio.buttondata[button].i) + if (protocol == ctrl->radio.buttondata[button].i) break; /* We expected that `break' to happen, in all circumstances. */ assert(button < ctrl->radio.nbuttons); dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { - int oldproto = cfg->protocol; + int oldproto = conf_get_int(conf, CONF_protocol); + int newproto, port; + button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - cfg->protocol = ctrl->radio.buttondata[button].i; - if (oldproto != cfg->protocol) { + newproto = ctrl->radio.buttondata[button].i; + conf_set_int(conf, CONF_protocol, newproto); + + if (oldproto != newproto) { Backend *ob = backend_from_proto(oldproto); - Backend *nb = backend_from_proto(cfg->protocol); + Backend *nb = backend_from_proto(newproto); assert(ob); assert(nb); /* Iff the user hasn't changed the port from the old protocol's @@ -131,8 +280,9 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, * controls in order and setting a non-default port before * getting to the protocol; we want that non-default port * to be preserved. */ - if (cfg->port == ob->default_port) - cfg->port = nb->default_port; + port = conf_get_int(conf, CONF_port); + if (port == ob->default_port) + conf_set_int(conf, CONF_port, nb->default_port); } dlg_refresh(hp->host, dlg); dlg_refresh(hp->port, dlg); @@ -143,26 +293,28 @@ static void loggingbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* This function works just like the standard radio-button handler, * but it has to fall back to "no logging" in situations where the * configured logging type isn't applicable. */ if (event == EVENT_REFRESH) { + int logtype = conf_get_int(conf, CONF_logtype); + for (button = 0; button < ctrl->radio.nbuttons; button++) - if (cfg->logtype == ctrl->radio.buttondata[button].i) + if (logtype == ctrl->radio.buttondata[button].i) break; - - /* We fell off the end, so we lack the configured logging type */ - if (button == ctrl->radio.nbuttons) { - button=0; - cfg->logtype=LGTYP_NONE; - } - dlg_radiobutton_set(ctrl, dlg, button); + + /* We fell off the end, so we lack the configured logging type */ + if (button == ctrl->radio.nbuttons) { + button = 0; + conf_set_int(conf, CONF_logtype, LGTYP_NONE); + } + dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - cfg->logtype = ctrl->radio.buttondata[button].i; + conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i); } } @@ -170,15 +322,15 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* * This function works much like the standard radio button - * handler, but it has to handle two fields in Config. + * handler, but it has to handle two fields in Conf. */ if (event == EVENT_REFRESH) { - if (cfg->nethack_keypad) + if (conf_get_int(conf, CONF_nethack_keypad)) button = 2; - else if (cfg->app_keypad) + else if (conf_get_int(conf, CONF_app_keypad)) button = 1; else button = 0; @@ -188,11 +340,11 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); if (button == 2) { - cfg->app_keypad = FALSE; - cfg->nethack_keypad = TRUE; + conf_set_int(conf, CONF_app_keypad, FALSE); + conf_set_int(conf, CONF_nethack_keypad, TRUE); } else { - cfg->app_keypad = (button != 0); - cfg->nethack_keypad = FALSE; + conf_set_int(conf, CONF_app_keypad, (button != 0)); + conf_set_int(conf, CONF_nethack_keypad, FALSE); } } } @@ -200,7 +352,7 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, static void cipherlist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; @@ -218,7 +370,7 @@ static void cipherlist_handler(union control *ctrl, void *dlg, dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < CIPHER_MAX; i++) { - int c = cfg->ssh_cipherlist[i]; + int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i); int j; char *cstr = NULL; for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) { @@ -236,8 +388,8 @@ static void cipherlist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < CIPHER_MAX; i++) - cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i); - + conf_set_int_int(conf, CONF_ssh_cipherlist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } @@ -245,14 +397,14 @@ static void cipherlist_handler(union control *ctrl, void *dlg, static void gsslist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < ngsslibs; i++) { - int id = cfg->ssh_gsslist[i]; + int id = conf_get_int_int(conf, CONF_ssh_gsslist, i); assert(id >= 0 && id < ngsslibs); dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id); } @@ -263,7 +415,8 @@ static void gsslist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < ngsslibs; i++) - cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int_int(conf, CONF_ssh_gsslist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } #endif @@ -271,7 +424,7 @@ static void gsslist_handler(union control *ctrl, void *dlg, static void kexlist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; @@ -288,7 +441,7 @@ static void kexlist_handler(union control *ctrl, void *dlg, dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < KEX_MAX; i++) { - int k = cfg->ssh_kexlist[i]; + int k = conf_get_int_int(conf, CONF_ssh_kexlist, i); int j; char *kstr = NULL; for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) { @@ -306,18 +459,19 @@ static void kexlist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < KEX_MAX; i++) - cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i); - + conf_set_int_int(conf, CONF_ssh_kexlist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } static void printerbox_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int nprinters, i; printer_enum *pe; + char *printer; dlg_update_start(ctrl, dlg); /* @@ -332,50 +486,62 @@ static void printerbox_handler(union control *ctrl, void *dlg, dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i)); printer_finish_enum(pe); } - dlg_editbox_set(ctrl, dlg, - (*cfg->printer ? cfg->printer : - PRINTER_DISABLED_STRING)); + printer = conf_get_str(conf, CONF_printer); + if (!printer) + printer = PRINTER_DISABLED_STRING; + dlg_editbox_set(ctrl, dlg, printer); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer)); - if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING)) - *cfg->printer = '\0'; + char *printer = dlg_editbox_get(ctrl, dlg); + if (!strcmp(printer, PRINTER_DISABLED_STRING)) + printer[0] = '\0'; + conf_set_str(conf, CONF_printer, printer); + sfree(printer); } } static void codepage_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; const char *cp, *thiscp; dlg_update_start(ctrl, dlg); - thiscp = cp_name(decode_codepage(cfg->line_codepage)); + thiscp = cp_name(decode_codepage(conf_get_str(conf, + CONF_line_codepage))); dlg_listbox_clear(ctrl, dlg); for (i = 0; (cp = cp_enumerate(i)) != NULL; i++) dlg_listbox_add(ctrl, dlg, cp); dlg_editbox_set(ctrl, dlg, thiscp); - strcpy(cfg->line_codepage, thiscp); + conf_set_str(conf, CONF_line_codepage, thiscp); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, cfg->line_codepage, - sizeof(cfg->line_codepage)); - strcpy(cfg->line_codepage, - cp_name(decode_codepage(cfg->line_codepage))); + char *codepage = dlg_editbox_get(ctrl, dlg); + conf_set_str(conf, CONF_line_codepage, + cp_name(decode_codepage(codepage))); + sfree(codepage); } } static void sshbug_handler(union control *ctrl, void *dlg, void *data, int event) { + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { + /* + * We must fetch the previously configured value from the Conf + * before we start modifying the drop-down list, otherwise the + * spurious SELCHANGE we trigger in the process will overwrite + * the value we wanted to keep. + */ + int oldconf = conf_get_int(conf, ctrl->listbox.context.i); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO); dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF); dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON); - switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) { + switch (oldconf) { case AUTO: dlg_listbox_select(ctrl, dlg, 0); break; case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break; case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break; @@ -387,27 +553,32 @@ static void sshbug_handler(union control *ctrl, void *dlg, i = AUTO; else i = dlg_listbox_getid(ctrl, dlg, i); - *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i; + conf_set_int(conf, ctrl->listbox.context.i, i); } } -#define SAVEDSESSION_LEN 2048 - struct sessionsaver_data { union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton; union control *okbutton, *cancelbutton; struct sesslist sesslist; int midsession; + char *savedsession; /* the current contents of ssd->editbox */ }; +static void sessionsaver_data_free(void *ssdv) +{ + struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv; + sfree(ssd->savedsession); + sfree(ssd); +} + /* * Helper function to load the session selected in the list box, if * any, as this is done in more than one place below. Returns 0 for * failure. */ static int load_selected_session(struct sessionsaver_data *ssd, - char *savedsession, - void *dlg, Config *cfg, int *maybe_launch) + void *dlg, Conf *conf, int *maybe_launch) { int i = dlg_listbox_index(ssd->listbox, dlg); int isdef; @@ -416,18 +587,11 @@ 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], 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; - } + load_settings(ssd->sesslist.sessions[i], conf); + sfree(ssd->savedsession); + ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]); + if (maybe_launch) + *maybe_launch = !isdef; dlg_refresh(NULL, dlg); /* Restore the selection, which might have been clobbered by * changing the value of the edit box. */ @@ -438,30 +602,13 @@ static int load_selected_session(struct sessionsaver_data *ssd, static void sessionsaver_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct sessionsaver_data *ssd = (struct sessionsaver_data *)ctrl->generic.context.p; - char *savedsession; - - /* - * The first time we're called in a new dialog, we must - * allocate space to store the current contents of the saved - * session edit box (since it must persist even when we switch - * panels, but is not part of the Config). - */ - if (!ssd->editbox) { - savedsession = NULL; - } else if (!dlg_get_privdata(ssd->editbox, dlg)) { - savedsession = (char *) - dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN); - savedsession[0] = '\0'; - } else { - savedsession = dlg_get_privdata(ssd->editbox, dlg); - } if (event == EVENT_REFRESH) { if (ctrl == ssd->editbox) { - dlg_editbox_set(ctrl, dlg, savedsession); + dlg_editbox_set(ctrl, dlg, ssd->savedsession); } else if (ctrl == ssd->listbox) { int i; dlg_update_start(ctrl, dlg); @@ -473,13 +620,13 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, } else if (event == EVENT_VALCHANGE) { int top, bottom, halfway, i; if (ctrl == ssd->editbox) { - dlg_editbox_get(ctrl, dlg, savedsession, - SAVEDSESSION_LEN); + sfree(ssd->savedsession); + ssd->savedsession = dlg_editbox_get(ctrl, dlg); top = ssd->sesslist.nsessions; bottom = -1; while (top-bottom > 1) { halfway = (top+bottom)/2; - i = strcmp(savedsession, ssd->sesslist.sessions[halfway]); + i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]); if (i <= 0 ) { top = halfway; } else { @@ -503,29 +650,25 @@ 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, &mbl) && - (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) { + if (load_selected_session(ssd, dlg, conf, &mbl) && + (mbl && ctrl == ssd->listbox && conf_launchable(conf))) { dlg_end(dlg, 1); /* it's all over, and succeeded */ } } else if (ctrl == ssd->savebutton) { - int isdef = !strcmp(savedsession, "Default Settings"); - if (!savedsession[0]) { + int isdef = !strcmp(ssd->savedsession, "Default Settings"); + if (!ssd->savedsession[0]) { int i = dlg_listbox_index(ssd->listbox, dlg); if (i < 0) { dlg_beep(dlg); return; } isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings"); - if (!isdef) { - strncpy(savedsession, ssd->sesslist.sessions[i], - SAVEDSESSION_LEN); - savedsession[SAVEDSESSION_LEN-1] = '\0'; - } else { - savedsession[0] = '\0'; - } + sfree(ssd->savedsession); + ssd->savedsession = dupstr(isdef ? "" : + ssd->sesslist.sessions[i]); } { - char *errmsg = save_settings(savedsession, cfg); + char *errmsg = save_settings(ssd->savedsession, conf); if (errmsg) { dlg_error_msg(dlg, errmsg); sfree(errmsg); @@ -560,21 +703,22 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, * valid host name in it, then load it and go. */ if (dlg_last_focused(ctrl, dlg) == ssd->listbox && - !cfg_launchable(cfg)) { - Config cfg2; + !conf_launchable(conf)) { + Conf *conf2 = conf_new(); int mbl = FALSE; - if (!load_selected_session(ssd, savedsession, dlg, - &cfg2, &mbl)) { + if (!load_selected_session(ssd, dlg, conf2, &mbl)) { dlg_beep(dlg); + conf_free(conf2); return; } /* If at this point we have a valid session, go! */ - if (mbl && cfg_launchable(&cfg2)) { - *cfg = cfg2; /* structure copy */ - cfg->remote_cmd_ptr = NULL; + if (mbl && conf_launchable(conf2)) { + conf_copy_into(conf, conf2); dlg_end(dlg, 1); } else dlg_beep(dlg); + + conf_free(conf2); return; } @@ -582,7 +726,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, * Otherwise, do the normal thing: if we have a valid * session, get going. */ - if (cfg_launchable(cfg)) { + if (conf_launchable(conf)) { dlg_end(dlg, 1); } else dlg_beep(dlg); @@ -599,7 +743,7 @@ struct charclass_data { static void charclass_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct charclass_data *ccd = (struct charclass_data *)ctrl->generic.context.p; @@ -611,20 +755,22 @@ static void charclass_handler(union control *ctrl, void *dlg, for (i = 0; i < 128; i++) { char str[100]; sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, - (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]); + (i >= 0x21 && i != 0x7F) ? i : ' ', + conf_get_int_int(conf, CONF_wordness, i)); dlg_listbox_add(ctrl, dlg, str); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ccd->button) { - char str[100]; + char *str; int i, n; - dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str)); + str = dlg_editbox_get(ccd->editbox, dlg); n = atoi(str); + sfree(str); for (i = 0; i < 128; i++) { if (dlg_listbox_issel(ccd->listbox, dlg, i)) - cfg->wordness[i] = n; + conf_set_int_int(conf, CONF_wordness, i, n); } dlg_refresh(ccd->listbox, dlg); } @@ -652,7 +798,7 @@ static const char *const colours[] = { static void colour_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct colour_data *cd = (struct colour_data *)ctrl->generic.context.p; int update = FALSE, clear = FALSE, r, g, b; @@ -676,31 +822,32 @@ static void colour_handler(union control *ctrl, void *dlg, clear = TRUE; } else { clear = FALSE; - r = cfg->colours[i][0]; - g = cfg->colours[i][1]; - b = cfg->colours[i][2]; + r = conf_get_int_int(conf, CONF_colours, i*3+0); + g = conf_get_int_int(conf, CONF_colours, i*3+1); + b = conf_get_int_int(conf, CONF_colours, i*3+2); } update = TRUE; } } else if (event == EVENT_VALCHANGE) { if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) { /* The user has changed the colour using the edit boxes. */ - char buf[80]; + char *str; int i, cval; - dlg_editbox_get(ctrl, dlg, buf, lenof(buf)); - cval = atoi(buf); + str = dlg_editbox_get(ctrl, dlg); + cval = atoi(str); + sfree(str); if (cval > 255) cval = 255; if (cval < 0) cval = 0; i = dlg_listbox_index(cd->listbox, dlg); if (i >= 0) { if (ctrl == cd->redit) - cfg->colours[i][0] = cval; + conf_set_int_int(conf, CONF_colours, i*3+0, cval); else if (ctrl == cd->gedit) - cfg->colours[i][1] = cval; + conf_set_int_int(conf, CONF_colours, i*3+1, cval); else if (ctrl == cd->bedit) - cfg->colours[i][2] = cval; + conf_set_int_int(conf, CONF_colours, i*3+2, cval); } } } else if (event == EVENT_ACTION) { @@ -716,9 +863,9 @@ static void colour_handler(union control *ctrl, void *dlg, * pick up the results. */ dlg_coloursel_start(ctrl, dlg, - cfg->colours[i][0], - cfg->colours[i][1], - cfg->colours[i][2]); + conf_get_int_int(conf, CONF_colours, i*3+0), + conf_get_int_int(conf, CONF_colours, i*3+1), + conf_get_int_int(conf, CONF_colours, i*3+2)); } } else if (event == EVENT_CALLBACK) { if (ctrl == cd->button) { @@ -729,9 +876,9 @@ static void colour_handler(union control *ctrl, void *dlg, * selector did nothing (user hit Cancel, for example). */ if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) { - cfg->colours[i][0] = r; - cfg->colours[i][1] = g; - cfg->colours[i][2] = b; + conf_set_int_int(conf, CONF_colours, i*3+0, r); + conf_set_int_int(conf, CONF_colours, i*3+1, g); + conf_set_int_int(conf, CONF_colours, i*3+2, b); clear = FALSE; update = TRUE; } @@ -760,22 +907,21 @@ struct ttymodes_data { static void ttymodes_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct ttymodes_data *td = (struct ttymodes_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == td->listbox) { - char *p = cfg->ttymodes; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { - int tabpos = strchr(p, '\t') - p; - char *disp = dupprintf("%.*s\t%s", tabpos, p, - (p[tabpos+1] == 'A') ? "(auto)" : - p+tabpos+2); + for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { + char *disp = dupprintf("%s\t%s", key, + (val[0] == 'A') ? "(auto)" : val+1); dlg_listbox_add(ctrl, dlg, disp); - p += strlen(p) + 1; sfree(disp); } dlg_update_done(ctrl, dlg); @@ -795,73 +941,47 @@ static void ttymodes_handler(union control *ctrl, void *dlg, int ind = dlg_listbox_index(td->modelist, dlg); if (ind >= 0) { char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A'; - int slen, left; - char *p, str[lenof(cfg->ttymodes)]; + const char *key; + char *str, *val; /* Construct new entry */ - memset(str, 0, lenof(str)); - strncpy(str, ttymodes[ind], lenof(str)-3); - slen = strlen(str); - str[slen] = '\t'; - str[slen+1] = type; - slen += 2; - if (type == 'V') { - dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen); - } - /* Find end of list, deleting any existing instance */ - p = cfg->ttymodes; - left = lenof(cfg->ttymodes); - while (*p) { - int t = strchr(p, '\t') - p; - if (t == strlen(ttymodes[ind]) && - strncmp(p, ttymodes[ind], t) == 0) { - memmove(p, p+strlen(p)+1, left - (strlen(p)+1)); - continue; - } - left -= strlen(p) + 1; - p += strlen(p) + 1; - } - /* Append new entry */ - memset(p, 0, left); - strncpy(p, str, left - 2); + key = ttymodes[ind]; + str = dlg_editbox_get(td->valbox, dlg); + val = dupprintf("%c%s", type, str); + sfree(str); + conf_set_str_str(conf, CONF_ttymodes, key, val); + sfree(val); dlg_refresh(td->listbox, dlg); } else dlg_beep(dlg); } else if (ctrl == td->rembutton) { - char *p = cfg->ttymodes; - int i = 0, len = lenof(cfg->ttymodes); - while (*p) { - int multisel = dlg_listbox_index(td->listbox, dlg) < 0; + int i = 0; + char *key, *val; + int multisel = dlg_listbox_index(td->listbox, dlg) < 0; + for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { 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); + int ind = 0; + val++; + while (ttymodes[ind]) { + if (!strcmp(ttymodes[ind], key)) + 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; + conf_del_str_str(conf, CONF_ttymodes, key); } - len -= strlen(p) + 1; - p += strlen(p) + 1; i++; } - memset(p, 0, lenof(cfg->ttymodes) - len); dlg_refresh(td->listbox, dlg); } } @@ -874,96 +994,67 @@ struct environ_data { static void environ_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct environ_data *ed = (struct environ_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == ed->listbox) { - char *p = cfg->environmt; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { + for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { + char *p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); - p += strlen(p) + 1; + sfree(p); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ed->addbutton) { - char str[sizeof(cfg->environmt)]; - char *p; - dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1); - if (!*str) { + char *key, *val, *str; + key = dlg_editbox_get(ed->varbox, dlg); + if (!*key) { + sfree(key); dlg_beep(dlg); return; } - p = str + strlen(str); - *p++ = '\t'; - dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str)); - if (!*p) { + val = dlg_editbox_get(ed->valbox, dlg); + if (!*val) { + sfree(key); + sfree(val); dlg_beep(dlg); return; } - p = cfg->environmt; - while (*p) { - while (*p) - p++; - p++; - } - if ((p - cfg->environmt) + strlen(str) + 2 < - sizeof(cfg->environmt)) { - strcpy(p, str); - p[strlen(str) + 1] = '\0'; - dlg_listbox_add(ed->listbox, dlg, str); - dlg_editbox_set(ed->varbox, dlg, ""); - dlg_editbox_set(ed->valbox, dlg, ""); - } else { - dlg_error_msg(dlg, "Environment too big"); - } + conf_set_str_str(conf, CONF_environmt, key, val); + str = dupcat(key, "\t", val, NULL); + dlg_editbox_set(ed->varbox, dlg, ""); + dlg_editbox_set(ed->valbox, dlg, ""); + sfree(str); + sfree(key); + sfree(val); + dlg_refresh(ed->listbox, dlg); } else if (ctrl == ed->rembutton) { int i = dlg_listbox_index(ed->listbox, dlg); if (i < 0) { dlg_beep(dlg); } else { - char *p, *q, *str; - - dlg_listbox_del(ed->listbox, dlg, i); - p = cfg->environmt; - while (i > 0) { - if (!*p) - goto disaster; - while (*p) - p++; - p++; - i--; - } - q = p; - if (!*p) - goto disaster; - /* 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) - *q++ = *p++; - *q++ = *p++; + char *key, *val; + + key = conf_get_str_nthstrkey(conf, CONF_environmt, i); + if (key) { + /* Populate controls with the entry we're about to delete + * for ease of editing */ + val = conf_get_str_str(conf, CONF_environmt, key); + dlg_editbox_set(ed->varbox, dlg, key); + dlg_editbox_set(ed->valbox, dlg, val); + /* And delete it */ + conf_del_str_str(conf, CONF_environmt, key); } - *q = '\0'; - disaster:; } + dlg_refresh(ed->listbox, dlg); } } } @@ -979,18 +1070,25 @@ struct portfwd_data { static void portfwd_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct portfwd_data *pfd = (struct portfwd_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == pfd->listbox) { - char *p = cfg->portfwd; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { + for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { + char *p; + if (!strcmp(val, "D")) + p = dupprintf("D%s\t", key+1); + else + p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); - p += strlen(p) + 1; + sfree(p); } dlg_update_done(ctrl, dlg); } else if (ctrl == pfd->direction) { @@ -1005,137 +1103,110 @@ static void portfwd_handler(union control *ctrl, void *dlg, } } else if (event == EVENT_ACTION) { if (ctrl == pfd->addbutton) { - char str[sizeof(cfg->portfwd)]; - char *p; - int i, type; + char *family, *type, *src, *key, *val; int whichbutton; - i = 0; #ifndef NO_IPV6 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg); if (whichbutton == 1) - str[i++] = '4'; + family = "4"; else if (whichbutton == 2) - str[i++] = '6'; + family = "6"; + else + family = ""; #endif whichbutton = dlg_radiobutton_get(pfd->direction, dlg); if (whichbutton == 0) - type = 'L'; + type = "L"; else if (whichbutton == 1) - type = 'R'; + type = "R"; else - type = 'D'; - str[i++] = type; + type = "D"; - dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i); - if (!str[i]) { + src = dlg_editbox_get(pfd->sourcebox, dlg); + if (!*src) { dlg_error_msg(dlg, "You need to specify a source port number"); + sfree(src); return; } - p = str + strlen(str); - if (type != 'D') { - *p++ = '\t'; - dlg_editbox_get(pfd->destbox, dlg, p, - sizeof(str) - (p - str)); - if (!*p || !strchr(p, ':')) { + if (*type != 'D') { + val = dlg_editbox_get(pfd->destbox, dlg); + if (!*val || !strchr(val, ':')) { dlg_error_msg(dlg, "You need to specify a destination address\n" "in the form \"host.name:port\""); + sfree(src); + sfree(val); return; } - } else - *p = '\0'; - p = cfg->portfwd; - while (*p) { - if (strcmp(p,str) == 0) { - dlg_error_msg(dlg, "Specified forwarding already exists"); - break; - } - while (*p) - p++; - p++; - } - if (!*p) { - if ((p - cfg->portfwd) + strlen(str) + 2 <= - sizeof(cfg->portfwd)) { - strcpy(p, str); - p[strlen(str) + 1] = '\0'; - dlg_listbox_add(pfd->listbox, dlg, str); - dlg_editbox_set(pfd->sourcebox, dlg, ""); - dlg_editbox_set(pfd->destbox, dlg, ""); - } else { - dlg_error_msg(dlg, "Too many forwardings"); - } + } else { + type = "L"; + val = dupstr("D"); /* special case */ + } + + key = dupcat(family, type, src, NULL); + sfree(src); + + if (conf_get_str_str_opt(conf, CONF_portfwd, key)) { + dlg_error_msg(dlg, "Specified forwarding already exists"); + } else { + conf_set_str_str(conf, CONF_portfwd, key, val); } + + sfree(key); + sfree(val); + dlg_refresh(pfd->listbox, dlg); } else if (ctrl == pfd->rembutton) { int i = dlg_listbox_index(pfd->listbox, dlg); - if (i < 0) + if (i < 0) { dlg_beep(dlg); - else { - char *p, *q, *src, *dst; - char dir; - - dlg_listbox_del(pfd->listbox, dlg, i); - p = cfg->portfwd; - while (i > 0) { - if (!*p) - goto disaster2; - while (*p) - p++; - p++; - i--; - } - q = p; - if (!*p) - goto disaster2; - /* Populate the controls with the entry we're about to - * delete, for ease of editing. */ - { + } else { + char *key, *val, *p; + + key = conf_get_str_nthstrkey(conf, CONF_portfwd, i); + if (key) { static const char *const afs = "A46"; - char *afp = strchr(afs, *p); + static const char *const dirs = "LRD"; + char *afp; + int dir; +#ifndef NO_IPV6 + int idx; +#endif + + /* Populate controls with the entry we're about to delete + * for ease of editing */ + p = key; + + afp = strchr(afs, *p); #ifndef NO_IPV6 - int idx = afp ? afp-afs : 0; + 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; + + val = conf_get_str_str(conf, CONF_portfwd, key); + if (!strcmp(val, "D")) { + dir = 'D'; + val = ""; + } + 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) - *q++ = *p++; - *q++ = *p++; + + dlg_editbox_set(pfd->sourcebox, dlg, p); + dlg_editbox_set(pfd->destbox, dlg, val); + /* And delete it */ + conf_del_str_str(conf, CONF_portfwd, key); } - *q = '\0'; - disaster2:; } + dlg_refresh(pfd->listbox, dlg); } } } @@ -1154,8 +1225,10 @@ void setup_config_box(struct controlbox *b, int midsession, char *str; ssd = (struct sessionsaver_data *) - ctrl_alloc(b, sizeof(struct sessionsaver_data)); + ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data), + sessionsaver_data_free); memset(ssd, 0, sizeof(*ssd)); + ssd->savedsession = dupstr(""); ssd->midsession = midsession; /* @@ -1274,13 +1347,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 1, 100); s = ctrl_getset(b, "Session", "otheropts", NULL); - c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4, - HELPCTX(session_coe), - dlg_stdradiobutton_handler, - I(offsetof(Config, close_on_exit)), - "Always", I(FORCE_ON), - "Never", I(FORCE_OFF), - "Only on clean exit", I(AUTO), NULL); + ctrl_radiobuttons(s, "Close window on exit:", 'x', 4, + HELPCTX(session_coe), + conf_radiobutton_handler, + I(CONF_close_on_exit), + "Always", I(FORCE_ON), + "Never", I(FORCE_OFF), + "Only on clean exit", I(AUTO), NULL); /* * The Session/Logging panel. @@ -1305,7 +1378,7 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2, HELPCTX(logging_main), loggingbuttons_handler, - I(offsetof(Config, logtype)), + I(CONF_logtype), "None", 't', I(LGTYP_NONE), "Printable output", 'p', I(LGTYP_ASCII), "All session output", 'l', I(LGTYP_DEBUG), @@ -1316,19 +1389,19 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_filesel(s, "Log file name:", 'f', NULL, TRUE, "Select session log file name", HELPCTX(logging_filename), - dlg_stdfilesel_handler, I(offsetof(Config, logfilename))); + conf_filesel_handler, I(CONF_logfilename)); ctrl_text(s, "(Log file name can contain &Y, &M, &D for date," " &T for time, and &H for host name)", HELPCTX(logging_filename)); ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1, HELPCTX(logging_exists), - dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)), + conf_radiobutton_handler, I(CONF_logxfovr), "Always overwrite it", I(LGXF_OVR), "Always append to the end of it", I(LGXF_APN), "Ask the user every time", I(LGXF_ASK), NULL); ctrl_checkbox(s, "Flush log file frequently", 'u', HELPCTX(logging_flush), - dlg_stdcheckbox_handler, I(offsetof(Config,logflush))); + conf_checkbox_handler, I(CONF_logflush)); if ((midsession && protocol == PROT_SSH) || (!midsession && backend_from_proto(PROT_SSH))) { @@ -1336,10 +1409,10 @@ void setup_config_box(struct controlbox *b, int midsession, "Options specific to SSH packet logging"); ctrl_checkbox(s, "Omit known password fields", 'k', HELPCTX(logging_ssh_omit_password), - dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass))); + conf_checkbox_handler, I(CONF_logomitpass)); ctrl_checkbox(s, "Omit session data", 'd', HELPCTX(logging_ssh_omit_data), - dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata))); + conf_checkbox_handler, I(CONF_logomitdata)); } /* @@ -1350,37 +1423,36 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal", "general", "Set various terminal options"); ctrl_checkbox(s, "Auto wrap mode initially on", 'w', HELPCTX(terminal_autowrap), - dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode))); + conf_checkbox_handler, I(CONF_wrap_mode)); ctrl_checkbox(s, "DEC Origin Mode initially on", 'd', HELPCTX(terminal_decom), - dlg_stdcheckbox_handler, I(offsetof(Config,dec_om))); + conf_checkbox_handler, I(CONF_dec_om)); ctrl_checkbox(s, "Implicit CR in every LF", 'r', HELPCTX(terminal_lfhascr), - dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr))); + conf_checkbox_handler, I(CONF_lfhascr)); ctrl_checkbox(s, "Implicit LF in every CR", 'f', HELPCTX(terminal_crhaslf), - dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf))); + conf_checkbox_handler, I(CONF_crhaslf)); ctrl_checkbox(s, "Use background colour to erase screen", 'e', HELPCTX(terminal_bce), - dlg_stdcheckbox_handler, I(offsetof(Config,bce))); + conf_checkbox_handler, I(CONF_bce)); ctrl_checkbox(s, "Enable blinking text", 'n', HELPCTX(terminal_blink), - dlg_stdcheckbox_handler, I(offsetof(Config,blinktext))); + conf_checkbox_handler, I(CONF_blinktext)); ctrl_editbox(s, "Answerback to ^E:", 's', 100, HELPCTX(terminal_answerback), - dlg_stdeditbox_handler, I(offsetof(Config,answerback)), - I(sizeof(((Config *)0)->answerback))); + conf_editbox_handler, I(CONF_answerback), I(1)); s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options"); ctrl_radiobuttons(s, "Local echo:", 'l', 3, HELPCTX(terminal_localecho), - dlg_stdradiobutton_handler,I(offsetof(Config,localecho)), + conf_radiobutton_handler,I(CONF_localecho), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); ctrl_radiobuttons(s, "Local line editing:", 't', 3, HELPCTX(terminal_localedit), - dlg_stdradiobutton_handler,I(offsetof(Config,localedit)), + conf_radiobutton_handler,I(CONF_localedit), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); @@ -1400,18 +1472,18 @@ void setup_config_box(struct controlbox *b, int midsession, "Change the sequences sent by:"); ctrl_radiobuttons(s, "The Backspace key", 'b', 2, HELPCTX(keyboard_backspace), - dlg_stdradiobutton_handler, - I(offsetof(Config, bksp_is_delete)), + conf_radiobutton_handler, + I(CONF_bksp_is_delete), "Control-H", I(0), "Control-? (127)", I(1), NULL); ctrl_radiobuttons(s, "The Home and End keys", 'e', 2, HELPCTX(keyboard_homeend), - dlg_stdradiobutton_handler, - I(offsetof(Config, rxvt_homeend)), + conf_radiobutton_handler, + I(CONF_rxvt_homeend), "Standard", I(0), "rxvt", I(1), NULL); ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3, HELPCTX(keyboard_funkeys), - dlg_stdradiobutton_handler, - I(offsetof(Config, funky_type)), + conf_radiobutton_handler, + I(CONF_funky_type), "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2), "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL); @@ -1419,8 +1491,8 @@ void setup_config_box(struct controlbox *b, int midsession, "Application keypad settings:"); ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3, HELPCTX(keyboard_appcursor), - dlg_stdradiobutton_handler, - I(offsetof(Config, app_cursor)), + conf_radiobutton_handler, + I(CONF_app_cursor), "Normal", I(0), "Application", I(1), NULL); ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3, HELPCTX(keyboard_appkeypad), @@ -1437,7 +1509,7 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1, HELPCTX(bell_style), - dlg_stdradiobutton_handler, I(offsetof(Config, beep)), + conf_radiobutton_handler, I(CONF_beep), "None (bell disabled)", I(BELL_DISABLED), "Make default system alert sound", I(BELL_DEFAULT), "Visual bell (flash window)", I(BELL_VISUAL), NULL); @@ -1446,19 +1518,19 @@ void setup_config_box(struct controlbox *b, int midsession, "Control the bell overload behaviour"); ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd', HELPCTX(bell_overload), - dlg_stdcheckbox_handler, I(offsetof(Config,bellovl))); + conf_checkbox_handler, I(CONF_bellovl)); ctrl_editbox(s, "Over-use means this many bells...", 'm', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1)); + conf_editbox_handler, I(CONF_bellovl_n), I(-1)); ctrl_editbox(s, "... in this many seconds", 't', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)), + conf_editbox_handler, I(CONF_bellovl_t), I(-TICKSPERSEC)); ctrl_text(s, "The bell is re-enabled after a few seconds of silence.", HELPCTX(bell_overload)); ctrl_editbox(s, "Seconds of silence required", 's', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)), + conf_editbox_handler, I(CONF_bellovl_s), I(-TICKSPERSEC)); /* @@ -1470,43 +1542,43 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal/Features", "main", NULL); ctrl_checkbox(s, "Disable application cursor keys mode", 'u', HELPCTX(features_application), - dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c))); + conf_checkbox_handler, I(CONF_no_applic_c)); ctrl_checkbox(s, "Disable application keypad mode", 'k', HELPCTX(features_application), - dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k))); + conf_checkbox_handler, I(CONF_no_applic_k)); ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x', HELPCTX(features_mouse), - dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep))); + conf_checkbox_handler, I(CONF_no_mouse_rep)); ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's', HELPCTX(features_resize), - dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_resize))); + conf_checkbox_handler, + I(CONF_no_remote_resize)); ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w', HELPCTX(features_altscreen), - dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen))); + conf_checkbox_handler, I(CONF_no_alt_screen)); ctrl_checkbox(s, "Disable remote-controlled window title changing", 't', HELPCTX(features_retitle), - dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_wintitle))); + conf_checkbox_handler, + I(CONF_no_remote_wintitle)); ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3, HELPCTX(features_qtitle), - dlg_stdradiobutton_handler, - I(offsetof(Config,remote_qtitle_action)), + conf_radiobutton_handler, + I(CONF_remote_qtitle_action), "None", I(TITLE_NONE), "Empty string", I(TITLE_EMPTY), "Window title", I(TITLE_REAL), NULL); ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b', HELPCTX(features_dbackspace), - dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace))); + conf_checkbox_handler, I(CONF_no_dbackspace)); ctrl_checkbox(s, "Disable remote-controlled character set configuration", - 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_charset))); + 'r', HELPCTX(features_charset), conf_checkbox_handler, + I(CONF_no_remote_charset)); ctrl_checkbox(s, "Disable Arabic text shaping", - 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler, - I(offsetof(Config, arabicshaping))); + 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler, + I(CONF_arabicshaping)); ctrl_checkbox(s, "Disable bidirectional text display", - 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler, - I(offsetof(Config, bidi))); + 'd', HELPCTX(features_bidi), conf_checkbox_handler, + I(CONF_bidi)); /* * The Window panel. @@ -1519,11 +1591,11 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 2, 50, 50); c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), - dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1)); + conf_editbox_handler, I(CONF_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)); + conf_editbox_handler, I(CONF_height),I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); @@ -1531,20 +1603,20 @@ void setup_config_box(struct controlbox *b, int midsession, "Control the scrollback in the window"); ctrl_editbox(s, "Lines of scrollback", 's', 50, HELPCTX(window_scrollback), - dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1)); + conf_editbox_handler, I(CONF_savelines), I(-1)); ctrl_checkbox(s, "Display scrollbar", 'd', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar))); + conf_checkbox_handler, I(CONF_scrollbar)); ctrl_checkbox(s, "Reset scrollback on keypress", 'k', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key))); + conf_checkbox_handler, I(CONF_scroll_on_key)); ctrl_checkbox(s, "Reset scrollback on display activity", 'p', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp))); + conf_checkbox_handler, I(CONF_scroll_on_disp)); ctrl_checkbox(s, "Push erased text into scrollback", 'e', HELPCTX(window_erased), - dlg_stdcheckbox_handler, - I(offsetof(Config,erase_to_scrollback))); + conf_checkbox_handler, + I(CONF_erase_to_scrollback)); /* * The Window/Appearance panel. @@ -1557,33 +1629,33 @@ void setup_config_box(struct controlbox *b, int midsession, "Adjust the use of the cursor"); ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3, HELPCTX(appearance_cursor), - dlg_stdradiobutton_handler, - I(offsetof(Config, cursor_type)), + conf_radiobutton_handler, + I(CONF_cursor_type), "Block", 'l', I(0), "Underline", 'u', I(1), "Vertical line", 'v', I(2), NULL); ctrl_checkbox(s, "Cursor blinks", 'b', HELPCTX(appearance_cursor), - dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur))); + conf_checkbox_handler, I(CONF_blink_cur)); s = ctrl_getset(b, "Window/Appearance", "font", "Font settings"); ctrl_fontsel(s, "Font used in the terminal window", 'n', HELPCTX(appearance_font), - dlg_stdfontsel_handler, I(offsetof(Config, font))); + conf_fontsel_handler, I(CONF_font)); s = ctrl_getset(b, "Window/Appearance", "mouse", "Adjust the use of the mouse pointer"); ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p', HELPCTX(appearance_hidemouse), - dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr))); + conf_checkbox_handler, I(CONF_hide_mouseptr)); s = ctrl_getset(b, "Window/Appearance", "border", "Adjust the window border"); ctrl_editbox(s, "Gap between text and window edge:", 'e', 20, HELPCTX(appearance_border), - dlg_stdeditbox_handler, - I(offsetof(Config,window_border)), I(-1)); + conf_editbox_handler, + I(CONF_window_border), I(-1)); /* * The Window/Behaviour panel. @@ -1596,17 +1668,16 @@ void setup_config_box(struct controlbox *b, int midsession, "Adjust the behaviour of the window title"); ctrl_editbox(s, "Window title:", 't', 100, HELPCTX(appearance_title), - dlg_stdeditbox_handler, I(offsetof(Config,wintitle)), - I(sizeof(((Config *)0)->wintitle))); + conf_editbox_handler, I(CONF_wintitle), I(1)); ctrl_checkbox(s, "Separate window and icon titles", 'i', HELPCTX(appearance_title), - dlg_stdcheckbox_handler, - I(CHECKBOX_INVERT | offsetof(Config,win_name_always))); + conf_checkbox_handler, + I(CHECKBOX_INVERT | CONF_win_name_always)); s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Warn before closing window", 'w', HELPCTX(behaviour_closewarn), - dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close))); + conf_checkbox_handler, I(CONF_warn_on_close)); /* * The Window/Translation panel. @@ -1623,21 +1694,21 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w', HELPCTX(translation_cjk_ambig_wide), - dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide))); + conf_checkbox_handler, I(CONF_cjk_ambig_wide)); str = dupprintf("Adjust how %s handles line drawing characters", appname); s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1, HELPCTX(translation_linedraw), - dlg_stdradiobutton_handler, - I(offsetof(Config, vtmode)), + conf_radiobutton_handler, + I(CONF_vtmode), "Use Unicode line drawing code points",'u',I(VT_UNICODE), "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN), NULL); ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d', HELPCTX(selection_linedraw), - dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp))); + conf_checkbox_handler, I(CONF_rawcnp)); /* * The Window/Selection panel. @@ -1648,13 +1719,13 @@ void setup_config_box(struct controlbox *b, int midsession, "Control use of mouse"); ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p', HELPCTX(selection_shiftdrag), - dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override))); + conf_checkbox_handler, I(CONF_mouse_override)); ctrl_radiobuttons(s, "Default selection mode (Alt+drag does the other one):", NO_SHORTCUT, 2, HELPCTX(selection_rect), - dlg_stdradiobutton_handler, - I(offsetof(Config, rect_select)), + conf_radiobutton_handler, + I(CONF_rect_select), "Normal", 'n', I(0), "Rectangular block", 'r', I(1), NULL); @@ -1692,13 +1763,17 @@ void setup_config_box(struct controlbox *b, int midsession, "General options for colour usage"); ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i', HELPCTX(colours_ansi), - dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour))); + conf_checkbox_handler, I(CONF_ansi_colour)); ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2', - HELPCTX(colours_xterm256), dlg_stdcheckbox_handler, - I(offsetof(Config,xterm_256_colour))); - ctrl_checkbox(s, "Bolded text is a different colour", 'b', - HELPCTX(colours_bold), - dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour))); + HELPCTX(colours_xterm256), conf_checkbox_handler, + I(CONF_xterm_256_colour)); + ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3, + HELPCTX(colours_bold), + conf_radiobutton_handler, I(CONF_bold_style), + "The font", I(1), + "The colour", I(2), + "Both", I(3), + NULL); str = dupprintf("Adjust the precise colours %s displays", appname); s = ctrl_getset(b, "Window/Colours", "adjust", str); @@ -1740,7 +1815,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Sending of null packets to keep session active"); ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20, HELPCTX(connection_keepalive), - dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)), + conf_editbox_handler, I(CONF_ping_interval), I(-1)); if (!midsession) { @@ -1748,19 +1823,19 @@ void setup_config_box(struct controlbox *b, int midsession, "Low-level TCP connection options"); ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n', HELPCTX(connection_nodelay), - dlg_stdcheckbox_handler, - I(offsetof(Config,tcp_nodelay))); + conf_checkbox_handler, + I(CONF_tcp_nodelay)); ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)", 'p', HELPCTX(connection_tcpkeepalive), - dlg_stdcheckbox_handler, - I(offsetof(Config,tcp_keepalives))); + conf_checkbox_handler, + I(CONF_tcp_keepalives)); #ifndef NO_IPV6 s = ctrl_getset(b, "Connection", "ipversion", "Internet protocol version"); ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(connection_ipversion), - dlg_stdradiobutton_handler, - I(offsetof(Config, addressfamily)), + conf_radiobutton_handler, + I(CONF_addressfamily), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), "IPv6", '6', I(ADDRTYPE_IPV6), @@ -1775,8 +1850,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Logical name of remote host"); ctrl_editbox(s, label, 'm', 100, HELPCTX(connection_loghost), - dlg_stdeditbox_handler, I(offsetof(Config,loghost)), - I(sizeof(((Config *)0)->loghost))); + conf_editbox_handler, I(CONF_loghost), I(1)); } } @@ -1791,8 +1865,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Login details"); ctrl_editbox(s, "Auto-login username", 'u', 50, HELPCTX(connection_username), - dlg_stdeditbox_handler, I(offsetof(Config,username)), - I(sizeof(((Config *)0)->username))); + conf_editbox_handler, I(CONF_username), I(1)); { /* We assume the local username is sufficiently stable * to include on the dialog box. */ @@ -1802,8 +1875,8 @@ void setup_config_box(struct controlbox *b, int midsession, sfree(user); ctrl_radiobuttons(s, "When username is not specified:", 'n', 4, HELPCTX(connection_username_from_env), - dlg_stdradiobutton_handler, - I(offsetof(Config, username_from_env)), + conf_radiobutton_handler, + I(CONF_username_from_env), "Prompt", I(FALSE), userlabel, I(TRUE), NULL); @@ -1814,12 +1887,10 @@ void setup_config_box(struct controlbox *b, int midsession, "Terminal details"); ctrl_editbox(s, "Terminal-type string", 't', 50, HELPCTX(connection_termtype), - dlg_stdeditbox_handler, I(offsetof(Config,termtype)), - I(sizeof(((Config *)0)->termtype))); + conf_editbox_handler, I(CONF_termtype), I(1)); ctrl_editbox(s, "Terminal speeds", 's', 50, HELPCTX(connection_termspeed), - dlg_stdeditbox_handler, I(offsetof(Config,termspeed)), - I(sizeof(((Config *)0)->termspeed))); + conf_editbox_handler, I(CONF_termspeed), I(1)); s = ctrl_getset(b, "Connection/Data", "env", "Environment variables"); @@ -1865,8 +1936,8 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); ctrl_radiobuttons(s, "Proxy type:", 't', 3, HELPCTX(proxy_type), - dlg_stdradiobutton_handler, - I(offsetof(Config, proxy_type)), + conf_radiobutton_handler, + I(CONF_proxy_type), "None", I(PROXY_NONE), "SOCKS 4", I(PROXY_SOCKS4), "SOCKS 5", I(PROXY_SOCKS5), @@ -1876,49 +1947,44 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_host)), - I(sizeof(((Config *)0)->proxy_host))); + conf_editbox_handler, + I(CONF_proxy_host), I(1)); c->generic.column = 0; c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(proxy_main), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_port)), + conf_editbox_handler, + I(CONF_proxy_port), I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100, HELPCTX(proxy_exclude), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_exclude_list)), - I(sizeof(((Config *)0)->proxy_exclude_list))); + conf_editbox_handler, + I(CONF_proxy_exclude_list), I(1)); ctrl_checkbox(s, "Consider proxying local host connections", 'x', HELPCTX(proxy_exclude), - dlg_stdcheckbox_handler, - I(offsetof(Config,even_proxy_localhost))); + conf_checkbox_handler, + I(CONF_even_proxy_localhost)); ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3, HELPCTX(proxy_dns), - dlg_stdradiobutton_handler, - I(offsetof(Config, proxy_dns)), + conf_radiobutton_handler, + I(CONF_proxy_dns), "No", I(FORCE_OFF), "Auto", I(AUTO), "Yes", I(FORCE_ON), NULL); ctrl_editbox(s, "Username", 'u', 60, HELPCTX(proxy_auth), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_username)), - I(sizeof(((Config *)0)->proxy_username))); + conf_editbox_handler, + I(CONF_proxy_username), I(1)); c = ctrl_editbox(s, "Password", 'w', 60, HELPCTX(proxy_auth), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_password)), - I(sizeof(((Config *)0)->proxy_password))); + conf_editbox_handler, + I(CONF_proxy_password), I(1)); c->editbox.password = 1; ctrl_editbox(s, "Telnet command", 'm', 100, HELPCTX(proxy_command), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_telnet_command)), - I(sizeof(((Config *)0)->proxy_telnet_command))); + conf_editbox_handler, + I(CONF_proxy_telnet_command), I(1)); } /* @@ -1939,24 +2005,24 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:", NO_SHORTCUT, 2, HELPCTX(telnet_oldenviron), - dlg_stdradiobutton_handler, - I(offsetof(Config, rfc_environ)), + conf_radiobutton_handler, + I(CONF_rfc_environ), "BSD (commonplace)", 'b', I(0), "RFC 1408 (unusual)", 'f', I(1), NULL); ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, HELPCTX(telnet_passive), - dlg_stdradiobutton_handler, - I(offsetof(Config, passive_telnet)), + conf_radiobutton_handler, + I(CONF_passive_telnet), "Passive", I(1), "Active", I(0), NULL); } ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', HELPCTX(telnet_specialkeys), - dlg_stdcheckbox_handler, - I(offsetof(Config,telnet_keyboard))); + conf_checkbox_handler, + I(CONF_telnet_keyboard)); ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M", 'm', HELPCTX(telnet_newline), - dlg_stdcheckbox_handler, - I(offsetof(Config,telnet_newline))); + conf_checkbox_handler, + I(CONF_telnet_newline)); } if (!midsession) { @@ -1971,8 +2037,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Data to send to the server"); ctrl_editbox(s, "Local username:", 'l', 50, HELPCTX(rlogin_localuser), - dlg_stdeditbox_handler, I(offsetof(Config,localusername)), - I(sizeof(((Config *)0)->localusername))); + conf_editbox_handler, I(CONF_localusername), I(1)); } @@ -2002,14 +2067,13 @@ void setup_config_box(struct controlbox *b, int midsession, "Data to send to the server"); ctrl_editbox(s, "Remote command:", 'r', 100, HELPCTX(ssh_command), - dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)), - I(sizeof(((Config *)0)->remote_cmd))); + conf_editbox_handler, I(CONF_remote_cmd), I(1)); s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_checkbox(s, "Don't start a shell or command at all", 'n', HELPCTX(ssh_noshell), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_no_shell))); + conf_checkbox_handler, + I(CONF_ssh_no_shell)); } if (!midsession || protcfginfo != 1) { @@ -2017,8 +2081,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Enable compression", 'e', HELPCTX(ssh_compress), - dlg_stdcheckbox_handler, - I(offsetof(Config,compression))); + conf_checkbox_handler, + I(CONF_compression)); } if (!midsession) { @@ -2026,8 +2090,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4, HELPCTX(ssh_protocol), - dlg_stdradiobutton_handler, - I(offsetof(Config, sshprot)), + conf_radiobutton_handler, + I(CONF_sshprot), "1 only", 'l', I(0), "1", '1', I(1), "2", '2', I(2), @@ -2043,8 +2107,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i', HELPCTX(ssh_ciphers), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh2_des_cbc))); + conf_checkbox_handler, + I(CONF_ssh2_des_cbc)); } /* @@ -2068,13 +2132,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20, HELPCTX(ssh_kex_repeat), - dlg_stdeditbox_handler, - I(offsetof(Config,ssh_rekey_time)), + conf_editbox_handler, + I(CONF_ssh_rekey_time), I(-1)); ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20, HELPCTX(ssh_kex_repeat), - dlg_stdeditbox_handler, - I(offsetof(Config,ssh_rekey_data)), + conf_editbox_handler, + I(CONF_ssh_rekey_data), I(16)); ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)", HELPCTX(ssh_kex_repeat)); @@ -2091,41 +2155,41 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL); ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b', HELPCTX(ssh_auth_bypass), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_no_userauth))); + conf_checkbox_handler, + I(CONF_ssh_no_userauth)); ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)", 'd', HELPCTX(ssh_auth_banner), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_show_banner))); + conf_checkbox_handler, + I(CONF_ssh_show_banner)); s = ctrl_getset(b, "Connection/SSH/Auth", "methods", "Authentication methods"); ctrl_checkbox(s, "Attempt authentication using Pageant", 'p', HELPCTX(ssh_auth_pageant), - dlg_stdcheckbox_handler, - I(offsetof(Config,tryagent))); + conf_checkbox_handler, + I(CONF_tryagent)); ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm', HELPCTX(ssh_auth_tis), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_tis_auth))); + conf_checkbox_handler, + I(CONF_try_tis_auth)); ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)", 'i', HELPCTX(ssh_auth_ki), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_ki_auth))); + conf_checkbox_handler, + I(CONF_try_ki_auth)); s = ctrl_getset(b, "Connection/SSH/Auth", "params", "Authentication parameters"); ctrl_checkbox(s, "Allow agent forwarding", 'f', HELPCTX(ssh_auth_agentfwd), - dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd))); + conf_checkbox_handler, I(CONF_agentfwd)); ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT, HELPCTX(ssh_auth_changeuser), - dlg_stdcheckbox_handler, - I(offsetof(Config,change_username))); + conf_checkbox_handler, + I(CONF_change_username)); ctrl_filesel(s, "Private key file for authentication:", 'k', FILTER_KEY_FILES, FALSE, "Select private key file", HELPCTX(ssh_auth_privkey), - dlg_stdfilesel_handler, I(offsetof(Config, keyfile))); + conf_filesel_handler, I(CONF_keyfile)); #ifndef NO_GSSAPI /* @@ -2138,13 +2202,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)", 't', HELPCTX(ssh_gssapi), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_gssapi_auth))); + conf_checkbox_handler, + I(CONF_try_gssapi_auth)); ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l', HELPCTX(ssh_gssapi_delegation), - dlg_stdcheckbox_handler, - I(offsetof(Config,gssapifwd))); + conf_checkbox_handler, + I(CONF_gssapifwd)); /* * GSSAPI library selection. @@ -2177,8 +2241,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_filesel(s, "User-supplied GSSAPI library path:", 's', FILTER_DYNLIB_FILES, FALSE, "Select library file", HELPCTX(ssh_gssapi_libraries), - dlg_stdfilesel_handler, - I(offsetof(Config, ssh_gss_custom))); + conf_filesel_handler, + I(CONF_ssh_gss_custom)); } #endif } @@ -2192,8 +2256,8 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL); ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p', HELPCTX(ssh_nopty), - dlg_stdcheckbox_handler, - I(offsetof(Config,nopty))); + conf_checkbox_handler, + I(CONF_nopty)); s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes", "Terminal modes"); @@ -2259,15 +2323,14 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_checkbox(s, "Enable X11 forwarding", 'e', HELPCTX(ssh_tunnels_x11), - dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward))); + conf_checkbox_handler,I(CONF_x11_forward)); ctrl_editbox(s, "X display location", 'x', 50, HELPCTX(ssh_tunnels_x11), - dlg_stdeditbox_handler, I(offsetof(Config,x11_display)), - I(sizeof(((Config *)0)->x11_display))); + conf_editbox_handler, I(CONF_x11_display), I(1)); ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2, HELPCTX(ssh_tunnels_x11auth), - dlg_stdradiobutton_handler, - I(offsetof(Config, x11_auth)), + conf_radiobutton_handler, + I(CONF_x11_auth), "MIT-Magic-Cookie-1", I(X11_MIT), "XDM-Authorization-1", I(X11_XDM), NULL); } @@ -2282,12 +2345,12 @@ void setup_config_box(struct controlbox *b, int midsession, "Port forwarding"); ctrl_checkbox(s, "Local ports accept connections from other hosts",'t', HELPCTX(ssh_tunnels_portfwd_localhost), - dlg_stdcheckbox_handler, - I(offsetof(Config,lport_acceptall))); + conf_checkbox_handler, + I(CONF_lport_acceptall)); ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p', HELPCTX(ssh_tunnels_portfwd_localhost), - dlg_stdcheckbox_handler, - I(offsetof(Config,rport_acceptall))); + conf_checkbox_handler, + I(CONF_rport_acceptall)); ctrl_columns(s, 3, 55, 20, 25); c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd)); @@ -2355,34 +2418,37 @@ void setup_config_box(struct controlbox *b, int midsession, "Detection of known bugs in SSH servers"); ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20, HELPCTX(ssh_bugs_ignore1), - sshbug_handler, I(offsetof(Config,sshbug_ignore1))); + sshbug_handler, I(CONF_sshbug_ignore1)); ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20, HELPCTX(ssh_bugs_plainpw1), - sshbug_handler, I(offsetof(Config,sshbug_plainpw1))); + sshbug_handler, I(CONF_sshbug_plainpw1)); ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20, HELPCTX(ssh_bugs_rsa1), - sshbug_handler, I(offsetof(Config,sshbug_rsa1))); + sshbug_handler, I(CONF_sshbug_rsa1)); ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20, HELPCTX(ssh_bugs_ignore2), - sshbug_handler, I(offsetof(Config,sshbug_ignore2))); + sshbug_handler, I(CONF_sshbug_ignore2)); + ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j', + 20, HELPCTX(ssh_bugs_winadj), + sshbug_handler, I(CONF_sshbug_winadj)); ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20, HELPCTX(ssh_bugs_hmac2), - sshbug_handler, I(offsetof(Config,sshbug_hmac2))); + sshbug_handler, I(CONF_sshbug_hmac2)); ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20, HELPCTX(ssh_bugs_derivekey2), - sshbug_handler, I(offsetof(Config,sshbug_derivekey2))); + sshbug_handler, I(CONF_sshbug_derivekey2)); ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20, HELPCTX(ssh_bugs_rsapad2), - sshbug_handler, I(offsetof(Config,sshbug_rsapad2))); + sshbug_handler, I(CONF_sshbug_rsapad2)); ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20, HELPCTX(ssh_bugs_pksessid2), - sshbug_handler, I(offsetof(Config,sshbug_pksessid2))); + sshbug_handler, I(CONF_sshbug_pksessid2)); ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20, HELPCTX(ssh_bugs_rekey2), - sshbug_handler, I(offsetof(Config,sshbug_rekey2))); + sshbug_handler, I(CONF_sshbug_rekey2)); ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, HELPCTX(ssh_bugs_maxpkt2), - sshbug_handler, I(offsetof(Config,sshbug_maxpkt2))); + sshbug_handler, I(CONF_sshbug_maxpkt2)); } } } diff --git a/contrib/putty/CONFIGUR b/contrib/putty/CONFIGUR new file mode 100644 index 0000000..fb789fb --- /dev/null +++ b/contrib/putty/CONFIGUR @@ -0,0 +1,3 @@ +#!/bin/sh + +$(echo "$0" | sed '$s!configure$!unix/configure!') "$@" diff --git a/contrib/putty/CONTRIB/CYGTERMD/PTY.C b/contrib/putty/CONTRIB/CYGTERMD/PTY.C index dd4d1a4..19168de 100644 --- a/contrib/putty/CONTRIB/CYGTERMD/PTY.C +++ b/contrib/putty/CONTRIB/CYGTERMD/PTY.C @@ -122,9 +122,23 @@ int run_program_in_pty(const struct shell_data *shdata, close(fd); } #endif + /* + * Make the new pty our controlling terminal. On some OSes + * this is done with TIOCSCTTY; Cygwin doesn't have that, so + * instead it's done by simply opening the pty without + * O_NOCTTY. This code is primarily intended for Cygwin, but + * it's useful to have it work in other contexts for testing + * purposes, so I leave the TIOCSCTTY here anyway. + */ + if ((fd = open(ptyname, O_RDWR)) >= 0) { #ifdef TIOCSCTTY - ioctl(0, TIOCSCTTY, &i); + ioctl(fd, TIOCSCTTY, &i); #endif + close(fd); + } else { + perror("slave pty: open"); + exit(127); + } tcsetpgrp(0, getpgrp()); for (i = 0; i < shdata->nenvvars; i++) diff --git a/contrib/putty/CONTRIB/CYGTERMD/TELNET.C b/contrib/putty/CONTRIB/CYGTERMD/TELNET.C index 9602fd7..800c04b 100644 --- a/contrib/putty/CONTRIB/CYGTERMD/TELNET.C +++ b/contrib/putty/CONTRIB/CYGTERMD/TELNET.C @@ -424,7 +424,10 @@ void telnet_from_net(Telnet telnet, char *buf, int len) char cc = c; sel_write(telnet->pty, &cc, 1); - telnet->state = SEENCR; + if (c == CR) + telnet->state = SEENCR; + else + telnet->state = TOP_LEVEL; } break; case SEENIAC: diff --git a/contrib/putty/CONTRIB/LOGPARSE.PL b/contrib/putty/CONTRIB/LOGPARSE.PL new file mode 100644 index 0000000..bfddce0 --- /dev/null +++ b/contrib/putty/CONTRIB/LOGPARSE.PL @@ -0,0 +1,907 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use FileHandle; + +my $dumpchannels = 0; +my $dumpdata = 0; +while ($ARGV[0] =~ /^-/) { + my $opt = shift @ARGV; + if ($opt eq "--") { + last; # stop processing options + } elsif ($opt eq "-c") { + $dumpchannels = 1; + } elsif ($opt eq "-d") { + $dumpdata = 1; + } else { + die "unrecognised option '$opt'\n"; + } +} + +my @channels = (); # ultimate channel ids are indices in this array +my %chan_by_id = (); # indexed by 'c%d' or 's%d' for client and server ids +my %globalreq = (); # indexed by 'i' or 'o' + +my %packets = ( +#define SSH2_MSG_DISCONNECT 1 /* 0x1 */ + 'SSH2_MSG_DISCONNECT' => sub { + my ($direction, $seq, $data) = @_; + my ($reason, $description, $lang) = &parse("uss", $data); + printf "%s\n", &str($description); + }, +#define SSH2_MSG_IGNORE 2 /* 0x2 */ + 'SSH2_MSG_IGNORE' => sub { + my ($direction, $seq, $data) = @_; + my ($str) = &parse("s", $data); + printf "(%d bytes)\n", length $str; + }, +#define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */ + 'SSH2_MSG_UNIMPLEMENTED' => sub { + my ($direction, $seq, $data) = @_; + my ($rseq) = &parse("u", $data); + printf "i%d\n", $rseq; + }, +#define SSH2_MSG_DEBUG 4 /* 0x4 */ + 'SSH2_MSG_DEBUG' => sub { + my ($direction, $seq, $data) = @_; + my ($disp, $message, $lang) = &parse("bss", $data); + printf "%s\n", &str($message); + }, +#define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */ + 'SSH2_MSG_SERVICE_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($service) = &parse("s", $data); + printf "%s\n", &str($service); + }, +#define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */ + 'SSH2_MSG_SERVICE_ACCEPT' => sub { + my ($direction, $seq, $data) = @_; + my ($service) = &parse("s", $data); + printf "%s\n", &str($service); + }, +#define SSH2_MSG_KEXINIT 20 /* 0x14 */ + 'SSH2_MSG_KEXINIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_NEWKEYS 21 /* 0x15 */ + 'SSH2_MSG_NEWKEYS' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ + 'SSH2_MSG_KEXDH_INIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ + 'SSH2_MSG_KEXDH_REPLY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ + 'SSH2_MSG_KEX_DH_GEX_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ + 'SSH2_MSG_KEX_DH_GEX_GROUP' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ + 'SSH2_MSG_KEX_DH_GEX_INIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ + 'SSH2_MSG_KEX_DH_GEX_REPLY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ + 'SSH2_MSG_KEXRSA_PUBKEY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ + 'SSH2_MSG_KEXRSA_SECRET' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ + 'SSH2_MSG_KEXRSA_DONE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ + 'SSH2_MSG_USERAUTH_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($user, $service, $method) = &parse("sss", $data); + my $out = sprintf "%s %s %s", + &str($user), &str($service), &str($method); + if ($method eq "publickey") { + my ($real) = &parse("b", $data); + $out .= " real=$real"; + } elsif ($method eq "password") { + my ($change) = &parse("b", $data); + $out .= " change=$change"; + } + print "$out\n"; + }, +#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ + 'SSH2_MSG_USERAUTH_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($options) = &parse("s", $data); + printf "%s\n", &str($options); + }, +#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ + 'SSH2_MSG_USERAUTH_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */ + 'SSH2_MSG_USERAUTH_BANNER' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_PK_OK' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_INFO_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* 0x3d */ + 'SSH2_MSG_USERAUTH_INFO_RESPONSE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */ + 'SSH2_MSG_GLOBAL_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($type, $wantreply) = &parse("sb", $data); + printf "%s (%s)", $type, $wantreply eq "yes" ? "reply" : "noreply"; + my $request = [$seq, $type]; + push @{$globalreq{$direction}}, $request if $wantreply eq "yes"; + if ($type eq "tcpip-forward" or $type eq "cancel-tcpip-forward") { + my ($addr, $port) = &parse("su", $data); + printf " %s:%s", $addr, $port; + push @$request, $port; + } + print "\n"; + }, +#define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */ + 'SSH2_MSG_REQUEST_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$globalreq{$otherdir}}; + if (defined $request) { + printf "to %s", $request->[0]; + if ($request->[1] eq "tcpip-forward" and $request->[2] == 0) { + my ($port) = &parse("u", $data); + printf " port=%s", $port; + } + } else { + print "(spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */ + 'SSH2_MSG_REQUEST_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$globalreq{$otherdir}}; + if (defined $request) { + printf "to %s", $request->[0]; + } else { + print "(spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */ + 'SSH2_MSG_CHANNEL_OPEN' => sub { + my ($direction, $seq, $data) = @_; + my ($type, $sid, $winsize, $packet) = &parse("suuu", $data); + # CHANNEL_OPEN tells the other side the _sender's_ id for the + # channel, so this choice between "s" and "c" prefixes is + # opposite to every other message in the protocol, which all + # quote the _recipient's_ id of the channel. + $sid = ($direction eq "i" ? "s" : "c") . $sid; + my $chan = {'id'=>$sid, 'state'=>'halfopen', + 'i'=>{'win'=>0, 'seq'=>0}, + 'o'=>{'win'=>0, 'seq'=>0}}; + $chan->{$direction}{'win'} = $winsize; + push @channels, $chan; + my $index = $#channels; + $chan_by_id{$sid} = $index; + printf "ch%d (%s) %s (--%d)", $index, $chan->{'id'}, $type, + $chan->{$direction}{'win'}; + if ($type eq "x11") { + my ($addr, $port) = &parse("su", $data); + printf " from %s:%s", $addr, $port; + } elsif ($type eq "forwarded-tcpip") { + my ($saddr, $sport, $paddr, $pport) = &parse("susu", $data); + printf " to %s:%s from %s:%s", $saddr, $sport, $paddr, $pport; + } elsif ($type eq "direct-tcpip") { + my ($daddr, $dport, $saddr, $sport) = &parse("susu", $data); + printf " to %s:%s from %s:%s", $daddr, $dport, $saddr, $sport; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */ + 'SSH2_MSG_CHANNEL_OPEN_CONFIRMATION' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $sid, $winsize, $packet) = &parse("uuuu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + $sid = ($direction eq "i" ? "s" : "c") . $sid; + $chan_by_id{$sid} = $index; + my $chan = $channels[$index]; + $chan->{'id'} = ($direction eq "i" ? "$rid/$sid" : "$sid/$rid"); + $chan->{'state'} = 'open'; + $chan->{$direction}{'win'} = $winsize; + printf "ch%d (%s) (--%d)\n", $index, $chan->{'id'}, + $chan->{$direction}{'win'}; + }, +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */ + 'SSH2_MSG_CHANNEL_OPEN_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $reason, $desc, $lang) = &parse("uuss", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{'state'} = 'rejected'; + printf "ch%d (%s) %s\n", $index, $chan->{'id'}, &str($reason); + }, +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */ + 'SSH2_MSG_CHANNEL_WINDOW_ADJUST' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{$direction}{'win'} += $bytes; + printf "ch%d (%s) +%d (--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$direction}{'win'}; + }, +#define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */ + 'SSH2_MSG_CHANNEL_DATA' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{$direction}{'seq'} += $bytes; + printf "ch%d (%s), %s bytes (%d--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$direction}{'seq'}-$bytes, $chan->{$direction}{'seq'}; + my @realdata = splice @$data, 0, $bytes; + if ($dumpdata) { + my $filekey = $direction . "file"; + if (!defined $chan->{$filekey}) { + my $filename = sprintf "ch%d.%s", $index, $direction; + $chan->{$filekey} = FileHandle->new(">$filename"); + if (!defined $chan->{$filekey}) { + die "$filename: $!\n"; + } + } + die "channel data not present in $seq\n" if @realdata < $bytes; + my $rawdata = pack "C*", @realdata; + my $fh = $chan->{$filekey}; + print $fh $rawdata; + } + if (@realdata == $bytes and defined $chan->{$direction."data"}) { + my $rawdata = pack "C*", @realdata; + $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); + } + }, +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */ + 'SSH2_MSG_CHANNEL_EXTENDED_DATA' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + my $dir = $direction eq "i" ? 'sc' : 'cs'; + $chan->{$dir}{'seq'} += $bytes; + printf "ch%d (%s), %s bytes (%d--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$dir}{$seq}-$bytes, $chan->{$dir}{$seq}; + printf "ch%d (%s), %s bytes\n", $index, $chan->{'id'}, $bytes; + my @realdata = splice @$data, 0, $bytes; + if ($dumpdata) { + # We treat EXTENDED_DATA as equivalent to DATA, for the + # moment. It's not clear what else would be a better thing + # to do with it, and this at least is the Right Answer if + # the data is going to a terminal and the aim is to debug + # the terminal emulator. + my $filekey = $direction . "file"; + if (!defined $chan->{$filekey}) { + my $filename = sprintf "ch%d.%s", $index, $direction; + $chan->{$filekey} = FileHandle->new; + if (!$chan->{$filekey}->open(">", $filename)) { + die "$filename: $!\n"; + } + } + die "channel data not present in $seq\n" if @realdata < $bytes; + my $rawdata = pack "C*", @realdata; + my $fh = $chan->{$filekey}; + print $fh $rawdata; + } + if (@realdata == $bytes and defined $chan->{$direction."data"}) { + my $rawdata = pack "C*", @realdata; + $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); + } + }, +#define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */ + 'SSH2_MSG_CHANNEL_EOF' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)\n", $index, $chan->{'id'}; + }, +#define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */ + 'SSH2_MSG_CHANNEL_CLOSE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{'state'} = ($chan->{'state'} eq "open" ? "halfclosed" : + $chan->{'state'} eq "halfclosed" ? "closed" : + "confused"); + if ($chan->{'state'} eq "closed") { + $chan->{'ifile'}->close if defined $chan->{'ifile'}; + $chan->{'ofile'}->close if defined $chan->{'ofile'}; + } + printf "ch%d (%s)\n", $index, $chan->{'id'}; + }, +#define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ + 'SSH2_MSG_CHANNEL_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $type, $wantreply) = &parse("usb", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s) %s (%s)", + $index, $chan->{'id'}, $type, $wantreply eq "yes" ? "reply" : "noreply"; + push @{$chan->{'requests_'.$direction}}, [$seq, $type] + if $wantreply eq "yes"; + if ($type eq "pty-req") { + my ($term, $w, $h, $pw, $ph, $modes) = &parse("suuuus", $data); + printf " %s %sx%s", &str($term), $w, $h; + } elsif ($type eq "x11-req") { + my ($single, $xprot, $xcookie, $xscreen) = &parse("bssu", $data); + print " one-off" if $single eq "yes"; + printf " %s :%s", $xprot, $xscreen; + } elsif ($type eq "exec") { + my ($command) = &parse("s", $data); + printf " %s", &str($command); + } elsif ($type eq "subsystem") { + my ($subsys) = &parse("s", $data); + printf " %s", &str($subsys); + if ($subsys eq "sftp") { + &sftp_setup($index); + } + } elsif ($type eq "window-change") { + my ($w, $h, $pw, $ph) = &parse("uuuu", $data); + printf " %sx%s", $w, $h; + } elsif ($type eq "xon-xoff") { + my ($can) = &parse("b", $data); + printf " %s", $can; + } elsif ($type eq "signal") { + my ($sig) = &parse("s", $data); + printf " %s", &str($sig); + } elsif ($type eq "exit-status") { + my ($status) = &parse("u", $data); + printf " %s", $status; + } elsif ($type eq "exit-signal") { + my ($sig, $core, $error, $lang) = &parse("sbss", $data); + printf " %s", &str($sig); + print " (core dumped)" if $core eq "yes"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ + 'SSH2_MSG_CHANNEL_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)", $index, $chan->{'id'}; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$chan->{'requests_' . $otherdir}}; + if (defined $request) { + printf " to %s", $request->[0]; + } else { + print " (spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ + 'SSH2_MSG_CHANNEL_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)", $index, $chan->{'id'}; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$chan->{'requests_' . $otherdir}}; + if (defined $request) { + printf " to %s", $request->[0]; + } else { + print " (spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 + 'SSH2_MSG_USERAUTH_GSSAPI_RESPONSE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 + 'SSH2_MSG_USERAUTH_GSSAPI_TOKEN' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 + 'SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 + 'SSH2_MSG_USERAUTH_GSSAPI_ERROR' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 + 'SSH2_MSG_USERAUTH_GSSAPI_ERRTOK' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 + 'SSH2_MSG_USERAUTH_GSSAPI_MIC' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +); + +my %sftp_packets = ( +#define SSH_FXP_INIT 1 /* 0x1 */ + 0x1 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($ver) = &parse("u", $data); + printf "SSH_FXP_INIT %d\n", $ver; + }, +#define SSH_FXP_VERSION 2 /* 0x2 */ + 0x2 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($ver) = &parse("u", $data); + printf "SSH_FXP_VERSION %d\n", $ver; + }, +#define SSH_FXP_OPEN 3 /* 0x3 */ + 0x3 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path, $pflags) = &parse("usu", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPEN"); + printf " \"%s\" ", $path; + if ($pflags eq 0) { + print "0"; + } else { + my $sep = ""; + if ($pflags & 1) { $pflags ^= 1; print "${sep}READ"; $sep = "|"; } + if ($pflags & 2) { $pflags ^= 2; print "${sep}WRITE"; $sep = "|"; } + if ($pflags & 4) { $pflags ^= 4; print "${sep}APPEND"; $sep = "|"; } + if ($pflags & 8) { $pflags ^= 8; print "${sep}CREAT"; $sep = "|"; } + if ($pflags & 16) { $pflags ^= 16; print "${sep}TRUNC"; $sep = "|"; } + if ($pflags & 32) { $pflags ^= 32; print "${sep}EXCL"; $sep = "|"; } + if ($pflags) { print "${sep}${pflags}"; } + } + print "\n"; + }, +#define SSH_FXP_CLOSE 4 /* 0x4 */ + 0x4 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_CLOSE"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_READ 5 /* 0x5 */ + 0x5 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle, $offset, $len) = &parse("usUu", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READ"); + printf " \"%s\" %d %d", &stringescape($handle), $offset, $len; + print "\n"; + }, +#define SSH_FXP_WRITE 6 /* 0x6 */ + 0x6 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle, $offset, $wdata) = &parse("usUs", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_WRITE"); + printf " \"%s\" %d [%d bytes]", &stringescape($handle), $offset, length $wdata; + print "\n"; + }, +#define SSH_FXP_LSTAT 7 /* 0x7 */ + 0x7 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_LSTAT"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_FSTAT 8 /* 0x8 */ + 0x8 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSTAT"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_SETSTAT 9 /* 0x9 */ + 0x9 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_SETSTAT"); + my $attrs = &sftp_parse_attrs($data); + printf " \"%s\" %s", $path, $attrs; + print "\n"; + }, +#define SSH_FXP_FSETSTAT 10 /* 0xa */ + 0xa => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSETSTAT"); + my $attrs = &sftp_parse_attrs($data); + printf " \"%s\" %s", &stringescape($handle), $attrs; + print "\n"; + }, +#define SSH_FXP_OPENDIR 11 /* 0xb */ + 0xb => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPENDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_READDIR 12 /* 0xc */ + 0xc => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READDIR"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_REMOVE 13 /* 0xd */ + 0xd => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REMOVE"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_MKDIR 14 /* 0xe */ + 0xe => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_MKDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_RMDIR 15 /* 0xf */ + 0xf => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RMDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_REALPATH 16 /* 0x10 */ + 0x10 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REALPATH"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_STAT 17 /* 0x11 */ + 0x11 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_STAT"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_RENAME 18 /* 0x12 */ + 0x12 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $srcpath, $dstpath) = &parse("uss", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RENAME"); + printf " \"%s\" \"%s\"", $srcpath, $dstpath; + print "\n"; + }, +#define SSH_FXP_STATUS 101 /* 0x65 */ + 0x65 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $status) = &parse("uu", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_STATUS"); + print " "; + if ($status eq "0") { print "SSH_FX_OK"; } + elsif ($status eq "1") { print "SSH_FX_EOF"; } + elsif ($status eq "2") { print "SSH_FX_NO_SUCH_FILE"; } + elsif ($status eq "3") { print "SSH_FX_PERMISSION_DENIED"; } + elsif ($status eq "4") { print "SSH_FX_FAILURE"; } + elsif ($status eq "5") { print "SSH_FX_BAD_MESSAGE"; } + elsif ($status eq "6") { print "SSH_FX_NO_CONNECTION"; } + elsif ($status eq "7") { print "SSH_FX_CONNECTION_LOST"; } + elsif ($status eq "8") { print "SSH_FX_OP_UNSUPPORTED"; } + else { printf "[unknown status %d]", $status; } + print "\n"; + }, +#define SSH_FXP_HANDLE 102 /* 0x66 */ + 0x66 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_HANDLE"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_DATA 103 /* 0x67 */ + 0x67 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $retdata) = &parse("us", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_DATA"); + printf " [%d bytes]", length $retdata; + print "\n"; + }, +#define SSH_FXP_NAME 104 /* 0x68 */ + 0x68 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $count) = &parse("uu", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_NAME"); + for my $i (1..$count) { + my ($name, $longname) = &parse("ss", $data); + my $attrs = &sftp_parse_attrs($data); + print " [name=\"$name\", longname=\"$longname\", attrs=$attrs]"; + } + print "\n"; + }, +#define SSH_FXP_ATTRS 105 /* 0x69 */ + 0x69 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid) = &parse("u", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_ATTRS"); + my $attrs = &sftp_parse_attrs($data); + printf " %s", $attrs; + print "\n"; + }, +#define SSH_FXP_EXTENDED 200 /* 0xc8 */ + 0xc8 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $type) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_EXTENDED"); + printf " \"%s\"", $type; + print "\n"; + }, +#define SSH_FXP_EXTENDED_REPLY 201 /* 0xc9 */ + 0xc9 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid) = &parse("u", $data); + print "\n"; + &sftp_logreply($chan, $direction, $reqid,$id,"SSH_FXP_EXTENDED_REPLY"); + }, +); + +my ($direction, $seq, $ourseq, $type, $data, $recording); +my %ourseqs = ('i'=>0, 'o'=>0); + +$recording = 0; +while (<>) { + if ($recording) { + if (/^ [0-9a-fA-F]{8} ((?:[0-9a-fA-F]{2} )*[0-9a-fA-F]{2})/) { + push @$data, map { $_ eq "XX" ? -1 : hex $_ } split / /, $1; + } else { + $recording = 0; + my $fullseq = "$direction$ourseq"; + print "$fullseq: $type "; + if (defined $packets{$type}) { + $packets{$type}->($direction, $fullseq, $data); + } else { + printf "raw %s\n", join "", map { sprintf "%02x", $_ } @$data; + } + } + } + if (/^(Incoming|Outgoing) packet #0x([0-9a-fA-F]+), type \d+ \/ 0x[0-9a-fA-F]+ \((.*)\)/) { + $direction = ($1 eq "Incoming" ? 'i' : 'o'); + # $seq is the sequence number quoted in the log file. $ourseq + # is our own count of the sequence number, which differs in + # that it shouldn't wrap at 2^32, should anyone manage to run + # this script over such a huge log file. + $seq = hex $2; + $ourseq = $ourseqs{$direction}++; + $type = $3; + $data = []; + $recording = 1; + } +} + +if ($dumpchannels) { + my %stateorder = ('closed'=>0, 'rejected'=>1, + 'halfclosed'=>2, 'open'=>3, 'halfopen'=>4); + for my $index (0..$#channels) { + my $chan = $channels[$index]; + my $so = $stateorder{$chan->{'state'}}; + $so = 1000 unless defined $so; # any state I've missed above comes last + $chan->{'index'} = sprintf "ch%d", $index; + $chan->{'order'} = sprintf "%08d %08d", $so, $index; + } + my @sortedchannels = sort { $a->{'order'} cmp $b->{'order'} } @channels; + for my $chan (@sortedchannels) { + printf "%s (%s): %s\n", $chan->{'index'}, $chan->{'id'}, $chan->{'state'}; + } +} + +sub parseone { + my ($type, $data) = @_; + if ($type eq "u") { # uint32 + my @bytes = splice @$data, 0, 4; + return "" if @bytes < 4 or grep { $_<0 } @bytes; + return unpack "N", pack "C*", @bytes; + } elsif ($type eq "U") { # uint64 + my @bytes = splice @$data, 0, 8; + return "" if @bytes < 8 or grep { $_<0 } @bytes; + my @words = unpack "NN", pack "C*", @bytes; + return ($words[0] << 32) + $words[1]; + } elsif ($type eq "b") { # boolean + my $byte = shift @$data; + return "" if !defined $byte or $byte < 0; + return $byte ? "yes" : "no"; + } elsif ($type eq "B") { # byte + my $byte = shift @$data; + return "" if !defined $byte or $byte < 0; + return $byte; + } elsif ($type eq "s" or $type eq "m") { # string, mpint + my @bytes = splice @$data, 0, 4; + return "" if @bytes < 4 or grep { $_<0 } @bytes; + my $len = unpack "N", pack "C*", @bytes; + @bytes = splice @$data, 0, $len; + return "" if @bytes < $len or grep { $_<0 } @bytes; + if ($type eq "mpint") { + my $str = ""; + if ($bytes[0] >= 128) { + # Take two's complement. + @bytes = map { 0xFF ^ $_ } @bytes; + for my $i (reverse 0..$#bytes) { + if ($bytes[$i] < 0xFF) { + $bytes[$i]++; + last; + } else { + $bytes[$i] = 0; + } + } + $str = "-"; + } + $str .= "0x" . join "", map { sprintf "%02x", $_ } @bytes; + return $str; + } else { + return pack "C*", @bytes; + } + } +} + +sub parse { + my ($template, $data) = @_; + return map { &parseone($_, $data) } split //, $template; +} + +sub str { + # Quote as a string. If I get enthusiastic I might arrange for + # strange characters inside the string to be quoted. + my $str = shift @_; + return "'$str'"; +} + +sub sftp_setup { + my $index = shift @_; + my $chan = $channels[$index]; + $chan->{'obuf'} = $chan->{'ibuf'} = ''; + $chan->{'ocnt'} = $chan->{'icnt'} = 0; + $chan->{'odata'} = $chan->{'idata'} = \&sftp_data; + $chan->{'sftpreqs'} = {}; +} + +sub sftp_data { + my ($chan, $index, $direction, $data) = @_; + my $buf = \$chan->{$direction."buf"}; + my $cnt = \$chan->{$direction."cnt"}; + $$buf .= $data; + while (length $$buf >= 4) { + my $msglen = unpack "N", $$buf; + last if length $$buf < 4 + $msglen; + my $msg = substr $$buf, 4, $msglen; + $$buf = substr $$buf, 4 + $msglen; + $msg = [unpack "C*", $msg]; + my $type = shift @$msg; + my $id = sprintf "ch%d_sftp_%s%d", $index, $direction, ${$cnt}++; + print "$id: "; + if (defined $sftp_packets{$type}) { + $sftp_packets{$type}->($chan, $index, $direction, $id, $msg); + } else { + printf "unknown SFTP packet type %d\n", $type; + } + } +} + +sub sftp_logreq { + my ($chan, $direction, $reqid, $id, $name) = @_; + print "$name"; + if ($direction eq "o") { # requests coming _in_ are too weird to track + $chan->{'sftpreqs'}->{$reqid} = $id; + } +} + +sub sftp_logreply { + my ($chan, $direction, $reqid, $id, $name) = @_; + print "$name"; + if ($direction eq "i") { # replies going _out_ are too weird to track + if (defined $chan->{'sftpreqs'}->{$reqid}) { + print " to ", $chan->{'sftpreqs'}->{$reqid}; + $chan->{'sftpreqs'}->{$reqid} = undef; + } + } +} + +sub sftp_parse_attrs { + my ($data) = @_; + my ($flags) = &parse("u", $data); + return $flags if $flags eq ""; + my $out = "{"; + my $sep = ""; + if ($flags & 0x00000001) { # SSH_FILEXFER_ATTR_SIZE + $out .= $sep . sprintf "size=%d", &parse("U", $data); + $sep = ", "; + } + if ($flags & 0x00000002) { # SSH_FILEXFER_ATTR_UIDGID + $out .= $sep . sprintf "uid=%d", &parse("u", $data); + $out .= $sep . sprintf "gid=%d", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x00000004) { # SSH_FILEXFER_ATTR_PERMISSIONS + $out .= $sep . sprintf "perms=%#o", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x00000008) { # SSH_FILEXFER_ATTR_ACMODTIME + $out .= $sep . sprintf "atime=%d", &parse("u", $data); + $out .= $sep . sprintf "mtime=%d", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x80000000) { # SSH_FILEXFER_ATTR_EXTENDED + my $extcount = &parse("u", $data); + while ($extcount-- > 0) { + $out .= $sep . sprintf "\"%s\"=\"%s\"", &parse("ss", $data); + $sep = ", "; + } + } + $out .= "}"; + return $out; +} + +sub stringescape { + my ($str) = @_; + $str =~ s!\\!\\\\!g; + $str =~ s![^ -~]!sprintf "\\x%02X", ord $&!eg; + return $str; +} diff --git a/contrib/putty/CPROXY.C b/contrib/putty/CPROXY.C index 5537fca..d5049af 100644 --- a/contrib/putty/CPROXY.C +++ b/contrib/putty/CPROXY.C @@ -130,7 +130,8 @@ int proxy_socks5_handlechap (Proxy_Socket p) outbuf[2] = 0x04; /* Response */ outbuf[3] = 0x10; /* Length */ hmacmd5_chap(data, p->chap_current_datalen, - p->cfg.proxy_password, &outbuf[4]); + conf_get_str(p->conf, CONF_proxy_password), + &outbuf[4]); sk_write(p->sub_socket, (char *)outbuf, 20); break; case 0x11: @@ -159,7 +160,9 @@ int proxy_socks5_handlechap (Proxy_Socket p) int proxy_socks5_selectchap(Proxy_Socket p) { - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { + char *username = conf_get_str(p->conf, CONF_proxy_username); + char *password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { char chapbuf[514]; int ulen; chapbuf[0] = '\x01'; /* Version */ @@ -169,11 +172,11 @@ int proxy_socks5_selectchap(Proxy_Socket p) chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */ chapbuf[5] = '\x02'; /* Second attribute - username */ - ulen = strlen(p->cfg.proxy_username); + ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; chapbuf[6] = ulen; - memcpy(chapbuf+7, p->cfg.proxy_username, ulen); + memcpy(chapbuf+7, username, ulen); sk_write(p->sub_socket, chapbuf, ulen + 7); p->chap_num_attributes = 0; diff --git a/contrib/putty/DIALOG.C b/contrib/putty/DIALOG.C index 55bece8..63bcb1e 100644 --- a/contrib/putty/DIALOG.C +++ b/contrib/putty/DIALOG.C @@ -47,6 +47,7 @@ struct controlbox *ctrl_new_box(void) ret->ctrlsets = NULL; ret->nfrees = ret->freesize = 0; ret->frees = NULL; + ret->freefuncs = NULL; return ret; } @@ -59,9 +60,10 @@ void ctrl_free_box(struct controlbox *b) ctrl_free_set(b->ctrlsets[i]); } for (i = 0; i < b->nfrees; i++) - sfree(b->frees[i]); + b->freefuncs[i](b->frees[i]); sfree(b->ctrlsets); sfree(b->frees); + sfree(b->freefuncs); sfree(b); } @@ -181,7 +183,8 @@ struct controlset *ctrl_getset(struct controlbox *b, } /* Allocate some private data in a controlbox. */ -void *ctrl_alloc(struct controlbox *b, size_t size) +void *ctrl_alloc_with_free(struct controlbox *b, size_t size, + ctrl_freefn_t freefunc) { void *p; /* @@ -192,11 +195,24 @@ void *ctrl_alloc(struct controlbox *b, size_t size) if (b->nfrees >= b->freesize) { b->freesize = b->nfrees + 32; b->frees = sresize(b->frees, b->freesize, void *); + b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t); } - b->frees[b->nfrees++] = p; + b->frees[b->nfrees] = p; + b->freefuncs[b->nfrees] = freefunc; + b->nfrees++; return p; } +static void ctrl_default_free(void *p) +{ + sfree(p); +} + +void *ctrl_alloc(struct controlbox *b, size_t size) +{ + return ctrl_alloc_with_free(b, size, ctrl_default_free); +} + static union control *ctrl_new(struct controlset *s, int type, intorptr helpctx, handler_fn handler, intorptr context) @@ -456,140 +472,3 @@ void ctrl_free(union control *ctrl) } sfree(ctrl); } - -void dlg_stdradiobutton_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - int button; - /* - * For a standard radio button set, the context parameter gives - * offsetof(targetfield, Config), and the extra data per button - * gives the value the target field should take if that button - * is the one selected. - */ - if (event == EVENT_REFRESH) { - for (button = 0; button < ctrl->radio.nbuttons; button++) - if (*(int *)ATOFFSET(data, ctrl->radio.context.i) == - ctrl->radio.buttondata[button].i) - break; - /* We expected that `break' to happen, in all circumstances. */ - assert(button < ctrl->radio.nbuttons); - dlg_radiobutton_set(ctrl, dlg, button); - } else if (event == EVENT_VALCHANGE) { - button = dlg_radiobutton_get(ctrl, dlg); - assert(button >= 0 && button < ctrl->radio.nbuttons); - *(int *)ATOFFSET(data, ctrl->radio.context.i) = - ctrl->radio.buttondata[button].i; - } -} - -void dlg_stdcheckbox_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - int offset, invert; - - /* - * For a standard checkbox, the context parameter gives - * offsetof(targetfield, Config), optionally ORed with - * CHECKBOX_INVERT. - */ - offset = ctrl->checkbox.context.i; - if (offset & CHECKBOX_INVERT) { - offset &= ~CHECKBOX_INVERT; - invert = 1; - } else - invert = 0; - - /* - * C lacks a logical XOR, so the following code uses the idiom - * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1 - * iff exactly one of a and b is nonzero, otherwise 0.) - */ - - if (event == EVENT_REFRESH) { - dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert)); - } else if (event == EVENT_VALCHANGE) { - *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert; - } -} - -void dlg_stdeditbox_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard edit-box handler expects the main `context' - * field to contain the `offsetof' a field in the structure - * pointed to by `data'. The secondary `context2' field - * indicates the type of this field: - * - * - if context2 > 0, the field is a char array and context2 - * gives its size. - * - if context2 == -1, the field is an int and the edit box - * is numeric. - * - if context2 < -1, the field is an int and the edit box is - * _floating_, and (-context2) gives the scale. (E.g. if - * context2 == -1000, then typing 1.2 into the box will set - * the field to 1200.) - */ - int offset = ctrl->editbox.context.i; - int length = ctrl->editbox.context2.i; - - if (length > 0) { - char *field = (char *)ATOFFSET(data, offset); - if (event == EVENT_REFRESH) { - dlg_editbox_set(ctrl, dlg, field); - } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, field, length); - } - } else if (length < 0) { - int *field = (int *)ATOFFSET(data, offset); - char data[80]; - if (event == EVENT_REFRESH) { - if (length == -1) - sprintf(data, "%d", *field); - else - sprintf(data, "%g", (double)*field / (double)(-length)); - dlg_editbox_set(ctrl, dlg, data); - } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, data, lenof(data)); - if (length == -1) - *field = atoi(data); - else - *field = (int)((-length) * atof(data)); - } - } -} - -void dlg_stdfilesel_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard file-selector handler expects the `context' - * field to contain the `offsetof' a Filename field in the - * structure pointed to by `data'. - */ - int offset = ctrl->fileselect.context.i; - - if (event == EVENT_REFRESH) { - dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset)); - } else if (event == EVENT_VALCHANGE) { - dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset)); - } -} - -void dlg_stdfontsel_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard file-selector handler expects the `context' - * field to contain the `offsetof' a FontSpec field in the - * structure pointed to by `data'. - */ - int offset = ctrl->fontselect.context.i; - - if (event == EVENT_REFRESH) { - dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset)); - } else if (event == EVENT_VALCHANGE) { - dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset)); - } -} diff --git a/contrib/putty/DIALOG.H b/contrib/putty/DIALOG.H index 67f79ed..db28da3 100644 --- a/contrib/putty/DIALOG.H +++ b/contrib/putty/DIALOG.H @@ -162,7 +162,7 @@ union control { * * The `data' parameter points to the writable data being * modified as a result of the configuration activity; for - * example, the PuTTY `Config' structure, although not + * example, the PuTTY `Conf' structure, although not * necessarily. * * The `dlg' parameter is passed back to the platform- @@ -427,6 +427,8 @@ struct controlset { union control **ctrls; /* actual array */ }; +typedef void (*ctrl_freefn_t)(void *); /* used by ctrl_alloc_with_free */ + /* * This is the container structure which holds a complete set of * controls. @@ -438,6 +440,7 @@ struct controlbox { int nfrees; int freesize; void **frees; /* array of aux data areas to free */ + ctrl_freefn_t *freefuncs; /* parallel array of free functions */ }; struct controlbox *ctrl_new_box(void); @@ -464,8 +467,14 @@ void ctrl_free(union control *); * and so data allocated through this function is better not used * to hold modifiable per-instance things. It's mostly here for * allocating structures to be passed as control handler params. + * + * ctrl_alloc_with_free also allows you to provide a function to free + * the structure, in case there are other dynamically allocated bits + * and pieces dangling off it. */ void *ctrl_alloc(struct controlbox *b, size_t size); +void *ctrl_alloc_with_free(struct controlbox *b, size_t size, + ctrl_freefn_t freefunc); /* * Individual routines to create `union control' structures in a controlset. @@ -522,60 +531,6 @@ union control *ctrl_checkbox(struct controlset *, char *label, char shortcut, union control *ctrl_tabdelay(struct controlset *, union control *); /* - * Standard handler routines to cover most of the common cases in - * the config box. - */ -/* - * The standard radio-button handler expects the main `context' - * field to contain the `offsetof' of an int field in the structure - * pointed to by `data', and expects each of the individual button - * data to give a value for that int field. - */ -void dlg_stdradiobutton_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard checkbox handler expects the main `context' field - * to contain the `offsetof' an int field in the structure pointed - * to by `data', optionally ORed with CHECKBOX_INVERT to indicate - * that the sense of the datum is opposite to the sense of the - * checkbox. - */ -#define CHECKBOX_INVERT (1<<30) -void dlg_stdcheckbox_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard edit-box handler expects the main `context' field - * to contain the `offsetof' a field in the structure pointed to by - * `data'. The secondary `context2' field indicates the type of - * this field: - * - * - if context2 > 0, the field is a char array and context2 gives - * its size. - * - if context2 == -1, the field is an int and the edit box is - * numeric. - * - if context2 < -1, the field is an int and the edit box is - * _floating_, and (-context2) gives the scale. (E.g. if - * context2 == -1000, then typing 1.2 into the box will set the - * field to 1200.) - */ -void dlg_stdeditbox_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard file-selector handler expects the main `context' - * field to contain the `offsetof' a Filename field in the - * structure pointed to by `data'. - */ -void dlg_stdfilesel_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard font-selector handler expects the main `context' - * field to contain the `offsetof' a Font field in the structure - * pointed to by `data'. - */ -void dlg_stdfontsel_handler(union control *ctrl, void *dlg, - void *data, int event); - -/* * Routines the platform-independent dialog code can call to read * and write the values of controls. */ @@ -584,7 +539,7 @@ int dlg_radiobutton_get(union control *ctrl, void *dlg); void dlg_checkbox_set(union control *ctrl, void *dlg, int checked); int dlg_checkbox_get(union control *ctrl, void *dlg); void dlg_editbox_set(union control *ctrl, void *dlg, char const *text); -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length); +char *dlg_editbox_get(union control *ctrl, void *dlg); /* result must be freed by caller */ /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, void *dlg); void dlg_listbox_del(union control *ctrl, void *dlg, int index); @@ -604,10 +559,10 @@ int dlg_listbox_index(union control *ctrl, void *dlg); int dlg_listbox_issel(union control *ctrl, void *dlg, int index); void dlg_listbox_select(union control *ctrl, void *dlg, int index); void dlg_text_set(union control *ctrl, void *dlg, char const *text); -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn); -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn); -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fn); -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fn); +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn); +Filename *dlg_filesel_get(union control *ctrl, void *dlg); +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn); +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg); /* * Bracketing a large set of updates in these two functions will * cause the front end (if possible) to delay updating the screen diff --git a/contrib/putty/DOC/BLURB.BUT b/contrib/putty/DOC/BLURB.BUT index f03341d..4a99646 100644 --- a/contrib/putty/DOC/BLURB.BUT +++ b/contrib/putty/DOC/BLURB.BUT @@ -1,4 +1,6 @@ -\define{versionidblurb} \versionid $Id: blurb.but 9072 2011-01-05 12:01:00Z jacob $ +\define{versionidblurb} \versionid $Id: blurb.but 9993 2013-08-05 15:15:17Z jacob $ + +\define{dash} \u2013{-} \title PuTTY User Manual @@ -31,6 +33,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-2011 Simon Tatham. All +\copyright This manual is copyright 2001-2013 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. diff --git a/contrib/putty/DOC/CONFIG.BUT b/contrib/putty/DOC/CONFIG.BUT index 2a87f3a..9840aed 100644 --- a/contrib/putty/DOC/CONFIG.BUT +++ b/contrib/putty/DOC/CONFIG.BUT @@ -1,4 +1,4 @@ -\define{versionidconfig} \versionid $Id: config.but 9063 2010-12-29 14:11:25Z simon $ +\define{versionidconfig} \versionid $Id: config.but 9846 2013-05-28 23:46:44Z jacob $ \C{config} Configuring PuTTY @@ -1254,12 +1254,16 @@ mechanism for PuTTY and the server to communicate this information, so it must usually be manually configured. There are a lot of character sets to choose from. The \q{Remote -character set} option lets you select one. By default PuTTY will -attempt to choose a character set that is right for your \i{locale} as -reported by Windows; if it gets it wrong, you can select a different -one using this control. +character set} option lets you select one. -A few notable character sets are: +By default PuTTY will use the \i{UTF-8} encoding of \i{Unicode}, which +can represent pretty much any character; data coming from the server +is interpreted as UTF-8, and keystrokes are sent UTF-8 encoded. This +is what most modern distributions of Linux will expect by default. +However, if this is wrong for your server, you can select a different +character set using this control. + +A few other notable character sets are: \b The \i{ISO-8859} series are all standard character sets that include various accented characters appropriate for different sets of @@ -1273,12 +1277,6 @@ Euro symbol. \b If you want the old IBM PC character set with block graphics and line-drawing characters, you can select \q{\i{CP437}}. -\b PuTTY also supports \i{Unicode} mode, in which the data coming from -the server is interpreted as being in the \i{UTF-8} encoding of Unicode, -and keystrokes are sent UTF-8 encoded. If you select \q{UTF-8} as a -character set you can use this mode. Not all server-side applications -will support it. - If you need support for a numeric \i{code page} which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (\c{\i{CP866}} for example) in the list box. If the @@ -1541,20 +1539,22 @@ If you do not see \cq{colors#256} in the output, you may need to change your terminal setting. On modern Linux machines, you could try \cq{xterm-256color}. -\S{config-boldcolour} \q{Bolded text is a different colour} +\S{config-boldcolour} \q{Indicate bolded text by changing} \cfg{winhelp-topic}{colours.bold} When the server sends a \i{control sequence} indicating that some text -should be displayed in \i{bold}, PuTTY can handle this two ways. It can -either change the \i{font} for a bold version, or use the same font in a -brighter colour. This control lets you choose which. - -By default the box is checked, so non-bold text is displayed in -light grey and bold text is displayed in bright white (and similarly -in other colours). If you uncheck the box, bold and non-bold text -will be displayed in the same colour, and instead the font will -change to indicate the difference. +should be displayed in \i{bold}, PuTTY can handle this in several +ways. It can either change the \i{font} for a bold version, or use the +same font in a brighter colour, or it can do both (brighten the colour +\e{and} embolden the font). This control lets you choose which. + +By default bold is indicated by colour, so non-bold text is displayed +in light grey and bold text is displayed in bright white (and +similarly in other colours). If you change the setting to \q{The font} +box, bold and non-bold text will be displayed in the same colour, and +instead the font will change to indicate the difference. If you select +\q{Both}, the font and the colour will both change. \S{config-logpalette} \q{Attempt to use \i{logical palettes}} @@ -3206,6 +3206,29 @@ 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. +\S{config-ssh-bug-winadj} \q{Chokes on PuTTY's SSH-2 \cq{winadj} requests} + +\cfg{winhelp-topic}{ssh.bugs.winadj} + +PuTTY sometimes sends a special request to SSH servers in the middle +of channel data, with the name \cw{winadj@putty.projects.tartarus.org} +(see \k{sshnames-channel}). The purpose of this request is to measure +the round-trip time to the server, which PuTTY uses to tune its flow +control. The server does not actually have to \e{understand} the +message; it is expected to send back a \cw{SSH_MSG_CHANNEL_FAILURE} +message indicating that it didn't understand it. (All PuTTY needs for +its timing calculations is \e{some} kind of response.) + +It has been known for some SSH servers to get confused by this message +in one way or another \dash because it has a long name, or because +they can't cope with unrecognised request names even to the extent of +sending back the correct failure response, or because they handle it +sensibly but fill up the server's log file with pointless spam, or +whatever. PuTTY therefore supports this bug-compatibility flag: if it +believes the server has this bug, it will never send its +\cq{winadj@putty.projects.tartarus.org} request, and will make do +without its timing data. + \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply diff --git a/contrib/putty/DOC/ERRORS.BUT b/contrib/putty/DOC/ERRORS.BUT index 4de6713..ca1e59c 100644 --- a/contrib/putty/DOC/ERRORS.BUT +++ b/contrib/putty/DOC/ERRORS.BUT @@ -1,4 +1,4 @@ -\define{versioniderrors} \versionid $Id: errors.but 8897 2010-03-13 14:47:14Z jacob $ +\define{versioniderrors} \versionid $Id: errors.but 9627 2012-08-26 09:50:57Z jacob $ \C{errors} Common \i{error messages} @@ -58,20 +58,6 @@ in the same way as you would if it was new. See \k{gs-hostkey} for more information on host keys. -\H{errors-portfwd-space} \q{Out of space for port forwardings} - -PuTTY has a fixed-size buffer which it uses to store the details of -all \i{port forwardings} you have set up in an SSH session. If you -specify too many port forwardings on the PuTTY or Plink command line -and this buffer becomes full, you will see this error message. - -We need to fix this (fixed-size buffers are almost always a mistake) -but we haven't got round to it. If you actually have trouble with -this, let us know and we'll move it up our priority list. - -If you're running into this limit, you may want to consider using -dynamic port forwarding instead; see \k{using-port-forwarding}. - \H{errors-cipher-warning} \q{The first cipher supported by the server is ... below the configured warning threshold} diff --git a/contrib/putty/DOC/FAQ.BUT b/contrib/putty/DOC/FAQ.BUT index 913c254..d363280 100644 --- a/contrib/putty/DOC/FAQ.BUT +++ b/contrib/putty/DOC/FAQ.BUT @@ -1,4 +1,4 @@ -\define{versionidfaq} \versionid $Id: faq.but 8733 2009-11-01 22:06:05Z jacob $ +\define{versionidfaq} \versionid $Id: faq.but 9391 2012-01-30 00:29:32Z jacob $ \A{faq} PuTTY \i{FAQ} @@ -1043,6 +1043,23 @@ is triggered by PuTTY 0.58. This was fixed in 0.59. The \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}} entry in PuTTY's wishlist has more details. +\S{faq-system32}{Question} When I put PuTTY in +\cw{C:\\WINDOWS\\\i{SYSTEM32}} on my \i{64-bit Windows} system, +\i{\q{Duplicate Session}} doesn't work. + +The short answer is not to put the PuTTY executables in that location. + +On 64-bit systems, \cw{C:\\WINDOWS\\SYSTEM32} is intended to contain +only 64-bit binaries; Windows' 32-bit binaries live in +\cw{C:\\WINDOWS\\SYSWOW64}. When a 32-bit program such as PuTTY runs +on a 64-bit system, it cannot by default see the \q{real} +\cw{C:\\WINDOWS\\SYSTEM32} at all, because the +\W{http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx}{File +System Redirector} arranges that the running program sees the +appropriate kind of binaries in \cw{SYSTEM32}. Thus, operations in +the PuTTY suite that involve it accessing its own executables, such as +\i{\q{New Session}} and \q{Duplicate Session}, will not work. + \H{faq-secure} Security questions \S{faq-publicpc}{Question} Is it safe for me to download PuTTY and diff --git a/contrib/putty/DOC/INDEX.BUT b/contrib/putty/DOC/INDEX.BUT index aded2e9..a42f42e 100644 --- a/contrib/putty/DOC/INDEX.BUT +++ b/contrib/putty/DOC/INDEX.BUT @@ -1,4 +1,4 @@ -\define{versionidindex} \versionid $Id: index.but 9009 2010-09-25 16:18:02Z jacob $ +\define{versionidindex} \versionid $Id: index.but 9391 2012-01-30 00:29:32Z jacob $ \IM{Unix version} Unix version of PuTTY tools \IM{Unix version} Linux version of PuTTY tools @@ -849,3 +849,8 @@ saved sessions from \IM{GSSAPI credential delegation} GSSAPI credential delegation \IM{GSSAPI credential delegation} credential delegation, GSSAPI \IM{GSSAPI credential delegation} delegation, of GSSAPI credentials + +\IM{SYSTEM32} \cw{SYSTEM32} directory, on Windows + +\IM{64-bit Windows} 64-bit Windows +\IM{64-bit Windows} Windows, 64-bit diff --git a/contrib/putty/DOC/LICENCE.BUT b/contrib/putty/DOC/LICENCE.BUT index 62d8d74..986b38a 100644 --- a/contrib/putty/DOC/LICENCE.BUT +++ b/contrib/putty/DOC/LICENCE.BUT @@ -1,8 +1,8 @@ -\define{versionidlicence} \versionid $Id: licence.but 9072 2011-01-05 12:01:00Z jacob $ +\define{versionidlicence} \versionid $Id: licence.but 9993 2013-08-05 15:15:17Z jacob $ \A{licence} PuTTY \ii{Licence} -PuTTY is \i{copyright} 1997-2011 Simon Tatham. +PuTTY is \i{copyright} 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/contrib/putty/DOC/MAN-PTEL.BUT b/contrib/putty/DOC/MAN-PTEL.BUT index 1bdd334..f85eacb 100644 --- a/contrib/putty/DOC/MAN-PTEL.BUT +++ b/contrib/putty/DOC/MAN-PTEL.BUT @@ -37,7 +37,7 @@ Sorry.) \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, -so this option will be ignored. If \cw{BoldAsColour} is set to 0 +so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{puttytel} will overprint the normal font to make it look bolder. @@ -50,7 +50,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -74,12 +74,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if -the \cw{BoldAsColour} resource is set to 1 (the default). (This +the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) diff --git a/contrib/putty/DOC/MAN-PTER.BUT b/contrib/putty/DOC/MAN-PTER.BUT index 9e96f77..5ac3f7e 100644 --- a/contrib/putty/DOC/MAN-PTER.BUT +++ b/contrib/putty/DOC/MAN-PTER.BUT @@ -57,7 +57,7 @@ sets of defaults and choose between them. \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, -so this option will be ignored. If \cw{BoldAsColour} is set to 0 +so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{pterm} will overprint the normal font to make it look bolder. @@ -70,7 +70,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -94,12 +94,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if -the \cw{BoldAsColour} resource is set to 1 (the default). (This +the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) @@ -498,7 +498,7 @@ controls the font used to display normal text. The default is \dd This resource is the same as the \cw{\-fb} command-line option: it controls the font used to display bold text when \cw{BoldAsColour} -is turned off. The default is unset (the font will be bolded by +is set to 0 or 2. The default is unset (the font will be bolded by printing it twice at a one-pixel offset). \dt \cw{pterm.WideFont} @@ -511,7 +511,7 @@ default is unset (double-width characters cannot be displayed). \dd This resource is the same as the \cw{\-fwb} command-line option: it controls the font used to display double-width characters in bold, -when \cw{BoldAsColour} is turned off. The default is unset +when \cw{BoldAsColour} is set to 0 or 2. The default is unset (double-width characters are displayed in bold by printing them twice at a one-pixel offset). @@ -529,10 +529,11 @@ than 1 (in one direction or the other). \dt \cw{pterm.BoldAsColour} -\dd This option should be set to either 0 or 1; the default is 1. It -specifies the default state of auto wrap mode. When set to 1, bold +\dd This option should be set to either 0, 1, or 2; the default is 1. +It specifies how bold text should be displayed. When set to 1, bold text is shown by displaying it in a brighter colour; when set to 0, -bold text is shown by displaying it in a heavier font. +bold text is shown by displaying it in a heavier font; when set to 2, +both effects happen at once (a heavy font \e{and} a brighter colour). \dt \cw{pterm.Colour0}, \cw{pterm.Colour1}, ..., \cw{pterm.Colour21} diff --git a/contrib/putty/DOC/MAN-PUTT.BUT b/contrib/putty/DOC/MAN-PUTT.BUT index 004c88e..27b831a 100644 --- a/contrib/putty/DOC/MAN-PUTT.BUT +++ b/contrib/putty/DOC/MAN-PUTT.BUT @@ -37,7 +37,7 @@ Sorry.) If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to -0 and you do not specify a bold font, \cw{putty} will overprint the +0 or 2 and you do not specify a bold font, \cw{putty} will overprint the normal font to make it look bolder. \dt \cw{\-fw} \e{font-name} @@ -49,7 +49,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -73,12 +73,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video -text, if the \cw{BoldAsColour} resource is set to 1 (the default). +text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) diff --git a/contrib/putty/DOC/PLINK.BUT b/contrib/putty/DOC/PLINK.BUT index bd0104b..82fad08 100644 --- a/contrib/putty/DOC/PLINK.BUT +++ b/contrib/putty/DOC/PLINK.BUT @@ -1,4 +1,4 @@ -\define{versionidplink} \versionid $Id: plink.but 9366 2011-12-10 12:08:09Z simon $ +\define{versionidplink} \versionid $Id: plink.but 9998 2013-08-06 17:09:07Z simon $ \C{plink} Using the command-line connection tool \i{Plink} @@ -43,7 +43,7 @@ use Plink: \c Z:\sysosd>plink \c PuTTY Link: command-line connection utility -\c Release 0.62 +\c Release 0.63 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: diff --git a/contrib/putty/DOC/PSCP.BUT b/contrib/putty/DOC/PSCP.BUT index 9bf0089..d44c552 100644 --- a/contrib/putty/DOC/PSCP.BUT +++ b/contrib/putty/DOC/PSCP.BUT @@ -1,4 +1,4 @@ -\define{versionidpscp} \versionid $Id: pscp.but 9366 2011-12-10 12:08:09Z simon $ +\define{versionidpscp} \versionid $Id: pscp.but 9998 2013-08-06 17:09:07Z simon $ \#FIXME: Need examples @@ -41,7 +41,7 @@ use PSCP: \c Z:\owendadmin>pscp \c PuTTY Secure Copy client -\c Release 0.62 +\c Release 0.63 \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/contrib/putty/DOC/PUBKEY.BUT b/contrib/putty/DOC/PUBKEY.BUT index 580c6ee..4b55564 100644 --- a/contrib/putty/DOC/PUBKEY.BUT +++ b/contrib/putty/DOC/PUBKEY.BUT @@ -1,4 +1,4 @@ -\define{versionidpubkey} \versionid $Id: pubkey.but 8607 2009-07-12 12:02:58Z simon $ +\define{versionidpubkey} \versionid $Id: pubkey.but 9422 2012-03-04 01:01:11Z jacob $ \C{pubkey} Using public keys for SSH authentication @@ -151,18 +151,6 @@ of the key PuTTYgen will generate. Currently 1024 bits should be sufficient for most purposes. -Note that an RSA key is generated by finding two primes of half the -length requested, and then multiplying them together. For example, -if you ask PuTTYgen for a 1024-bit RSA key, it will create two -512-bit primes and multiply them. The result of this multiplication -might be 1024 bits long, or it might be only 1023; so you may not -get the exact length of key you asked for. This is perfectly normal, -and you do not need to worry. The lengths should only ever differ by -one, and there is no perceptible drop in security as a result. - -DSA keys are not created by multiplying primes together, so they -should always be exactly the length you asked for. - \S{puttygen-generate} The \q{Generate} button \cfg{winhelp-topic}{puttygen.generate} diff --git a/contrib/putty/EMPTY.H b/contrib/putty/EMPTY.H new file mode 100644 index 0000000..e607ce8 --- /dev/null +++ b/contrib/putty/EMPTY.H @@ -0,0 +1 @@ +/* Empty file touched by automake makefile to force rebuild of version.o */ diff --git a/contrib/putty/IMPORT.C b/contrib/putty/IMPORT.C index 1490c64..36aa765 100644 --- a/contrib/putty/IMPORT.C +++ b/contrib/putty/IMPORT.C @@ -289,8 +289,8 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) if (len < 4) goto error; - bytes = GET_32BIT(d); - if (len < 4+bytes) + bytes = toint(GET_32BIT(d)); + if (bytes < 0 || len-4 < bytes) goto error; ret->start = d + 4; @@ -321,7 +321,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, const char **errmsg_p) { struct openssh_key *ret; - FILE *fp; + FILE *fp = NULL; char *line = NULL; char *errmsg, *p; int headers_done; @@ -334,7 +334,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, ret->encrypted = 0; memset(ret->iv, 0, sizeof(ret->iv)); - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; @@ -358,7 +358,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, errmsg = "unrecognised key type"; goto error; } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; @@ -370,8 +370,11 @@ static struct openssh_key *load_openssh_key(const Filename *filename, } strip_crlf(line); if (0 == strncmp(line, "-----END ", 9) && - 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) + 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { + sfree(line); + line = NULL; break; /* done */ + } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; @@ -442,17 +445,20 @@ static struct openssh_key *load_openssh_key(const Filename *filename, memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; - memset(out, 0, sizeof(out)); + smemclr(out, sizeof(out)); } p++; } } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } + fclose(fp); + fp = NULL; + if (ret->keyblob_len == 0 || !ret->keyblob) { errmsg = "key body not present"; goto error; @@ -463,26 +469,27 @@ static struct openssh_key *load_openssh_key(const Filename *filename, goto error; } - memset(base64_bit, 0, sizeof(base64_bit)); + smemclr(base64_bit, sizeof(base64_bit)); if (errmsg_p) *errmsg_p = NULL; return ret; error: if (line) { - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } - memset(base64_bit, 0, sizeof(base64_bit)); + smemclr(base64_bit, sizeof(base64_bit)); if (ret) { if (ret->keyblob) { - memset(ret->keyblob, 0, ret->keyblob_size); + smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } - memset(ret, 0, sizeof(*ret)); + smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; + if (fp) fclose(fp); return NULL; } @@ -494,9 +501,9 @@ int openssh_encrypted(const Filename *filename) if (!key) return 0; ret = key->encrypted; - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); return ret; } @@ -564,8 +571,8 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, aes_free_context(ctx); } - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* @@ -588,12 +595,13 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, p = key->keyblob; - /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ + /* Expect the SEQUENCE header. Take its absence as a failure to + * decrypt, if the key was encrypted. */ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); p += ret; if (ret < 0 || id != 16) { errmsg = "ASN.1 decoding failure"; - retval = SSH2_WRONG_PASSPHRASE; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } @@ -625,7 +633,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; - retval = SSH2_WRONG_PASSPHRASE; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } @@ -698,12 +706,12 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, error: if (blob) { - memset(blob, 0, blobsize); + smemclr(blob, blobsize); sfree(blob); } - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return retval; @@ -740,6 +748,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; Bignum bd, bp, bq, bdmp1, bdmq1; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); @@ -793,6 +805,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos p, q, g, y, x; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); @@ -911,15 +927,15 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, */ des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb", TRUE); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs(header, fp); @@ -936,19 +952,19 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, error: if (outblob) { - memset(outblob, 0, outlen); + smemclr(outblob, outlen); sfree(outblob); } if (spareblob) { - memset(spareblob, 0, sparelen); + smemclr(spareblob, sparelen); sfree(spareblob); } if (privblob) { - memset(privblob, 0, privlen); + smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { - memset(pubblob, 0, publen); + smemclr(pubblob, publen); sfree(pubblob); } return ret; @@ -1053,7 +1069,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; @@ -1067,7 +1083,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, errmsg = "file does not begin with ssh.com key header"; goto error; } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; @@ -1078,8 +1094,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, goto error; } strip_crlf(line); - if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) + if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) { + sfree(line); + line = NULL; break; /* done */ + } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; @@ -1112,7 +1131,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, len += line2len - 1; assert(!line[len]); - memset(line2, 0, strlen(line2)); + smemclr(line2, strlen(line2)); sfree(line2); line2 = NULL; } @@ -1158,7 +1177,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, p++; } } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } @@ -1168,21 +1187,25 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, goto error; } + fclose(fp); if (errmsg_p) *errmsg_p = NULL; return ret; error: + if (fp) + fclose(fp); + if (line) { - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret) { if (ret->keyblob) { - memset(ret->keyblob, 0, ret->keyblob_size); + smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } - memset(ret, 0, sizeof(*ret)); + smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; @@ -1194,45 +1217,51 @@ int sshcom_encrypted(const Filename *filename, char **comment) struct sshcom_key *key = load_sshcom_key(filename, NULL); int pos, len, answer; + answer = 0; + *comment = NULL; if (!key) - return 0; + goto done; /* * Check magic number. */ - if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) - return 0; /* key is invalid */ + if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) { + goto done; /* key is invalid */ + } /* * Find the cipher-type string. */ - answer = 0; pos = 8; if (key->keyblob_len < pos+4) goto done; /* key is far too short */ - pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */ - if (key->keyblob_len < pos+4) + len = toint(GET_32BIT(key->keyblob + pos)); + if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* key is far too short */ - len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */ - if (key->keyblob_len < pos+4+len) + pos += 4 + len; /* skip key type */ + len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */ + if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* cipher type string is incomplete */ if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) answer = 1; done: - *comment = dupstr(key->comment); - memset(key->keyblob, 0, key->keyblob_size); - sfree(key->keyblob); - memset(key, 0, sizeof(*key)); - sfree(key); + if (key) { + *comment = dupstr(key->comment); + smemclr(key->keyblob, key->keyblob_size); + sfree(key->keyblob); + smemclr(key, sizeof(*key)); + sfree(key); + } else { + *comment = dupstr(""); + } return answer; } static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) { - int bits; - int bytes; + unsigned bits, bytes; unsigned char *d = (unsigned char *) data; if (len < 4) @@ -1304,7 +1333,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, */ pos = 8; if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a key type string"; goto error; } @@ -1324,7 +1354,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, * Determine the cipher type. */ if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a cipher type string"; goto error; } @@ -1342,7 +1373,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, * Get hold of the encrypted part of the key. */ if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain actual key data"; goto error; } @@ -1390,8 +1422,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); /* * Hereafter we return WRONG_PASSPHRASE for any parsing @@ -1406,7 +1438,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, /* * Strip away the containing string to get to the real meat. */ - len = GET_32BIT(ciphertext); + len = toint(GET_32BIT(ciphertext)); if (len < 0 || len > cipherlen-4) { errmsg = "containing string was ill-formed"; goto error; @@ -1447,9 +1479,12 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, u.start, u.bytes); privlen = pos - publen; - } else if (type == DSA) { + } else { struct mpint_pos p, q, g, x, y; int pos = 4; + + assert(type == DSA); /* the only other option from the if above */ + if (GET_32BIT(ciphertext) != 0) { errmsg = "predefined DSA parameters not supported"; goto error; @@ -1474,8 +1509,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, publen = pos; pos += put_mp(blob+pos, x.start, x.bytes); privlen = pos - publen; - } else - return NULL; + } assert(privlen > 0); /* should have bombed by now if not */ @@ -1494,12 +1528,12 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, error: if (blob) { - memset(blob, 0, blobsize); + smemclr(blob, blobsize); sfree(blob); } - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return ret; @@ -1535,6 +1569,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos n, e, d, p, q, iqmp; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); @@ -1560,6 +1598,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos p, q, g, y, x; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); @@ -1664,15 +1706,15 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb", TRUE); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); @@ -1700,15 +1742,15 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, error: if (outblob) { - memset(outblob, 0, outlen); + smemclr(outblob, outlen); sfree(outblob); } if (privblob) { - memset(privblob, 0, privlen); + smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { - memset(pubblob, 0, publen); + smemclr(pubblob, publen); sfree(pubblob); } return ret; diff --git a/contrib/putty/LDISC.C b/contrib/putty/LDISC.C index 119a02a..7ed42a3 100644 --- a/contrib/putty/LDISC.C +++ b/contrib/putty/LDISC.C @@ -12,12 +12,12 @@ #include "terminal.h" #include "ldisc.h" -#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \ - (ldisc->cfg->localecho == AUTO && \ +#define ECHOING (ldisc->localecho == FORCE_ON || \ + (ldisc->localecho == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \ term_ldisc(ldisc->term, LD_ECHO)))) -#define EDITING (ldisc->cfg->localedit == FORCE_ON || \ - (ldisc->cfg->localedit == AUTO && \ +#define EDITING (ldisc->localedit == FORCE_ON || \ + (ldisc->localedit == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \ term_ldisc(ldisc->term, LD_EDIT)))) @@ -76,7 +76,7 @@ static void bsb(Ldisc ldisc, int n) #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) -void *ldisc_create(Config *mycfg, Terminal *term, +void *ldisc_create(Conf *conf, Terminal *term, Backend *back, void *backhandle, void *frontend) { @@ -87,12 +87,13 @@ void *ldisc_create(Config *mycfg, Terminal *term, ldisc->bufsiz = 0; ldisc->quotenext = 0; - ldisc->cfg = mycfg; ldisc->back = back; ldisc->backhandle = backhandle; ldisc->term = term; ldisc->frontend = frontend; + ldisc_configure(ldisc, conf); + /* Link ourselves into the backend and the terminal */ if (term) term->ldisc = ldisc; @@ -102,6 +103,17 @@ void *ldisc_create(Config *mycfg, Terminal *term, return ldisc; } +void ldisc_configure(void *handle, Conf *conf) +{ + Ldisc ldisc = (Ldisc) handle; + + ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard); + ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline); + ldisc->protocol = conf_get_int(conf, CONF_protocol); + ldisc->localecho = conf_get_int(conf, CONF_localecho); + ldisc->localedit = conf_get_int(conf, CONF_localedit); +} + void ldisc_free(void *handle) { Ldisc ldisc = (Ldisc) handle; @@ -203,7 +215,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) * configured telnet specials off! This breaks * talkers otherwise. */ - if (!ldisc->cfg->telnet_keyboard) + if (!ldisc->telnet_keyboard) goto default_case; if (c == CTRL('C')) ldisc->back->special(ldisc->backhandle, TS_IP); @@ -255,7 +267,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) * default clause because of the break. */ case CTRL('J'): - if (ldisc->cfg->protocol == PROT_RAW && + if (ldisc->protocol == PROT_RAW && ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); @@ -264,9 +276,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) case KCTRL('M'): /* send with newline */ if (ldisc->buflen > 0) ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen); - if (ldisc->cfg->protocol == PROT_RAW) + if (ldisc->protocol == PROT_RAW) ldisc->back->send(ldisc->backhandle, "\r\n", 2); - else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline) + else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); @@ -300,27 +312,27 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) if (len > 0) { if (ECHOING) c_write(ldisc, buf, len); - if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) { + if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) { switch (buf[0]) { case CTRL('M'): - if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline) + if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); break; case CTRL('?'): case CTRL('H'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_EC); break; } case CTRL('C'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_IP); break; } case CTRL('Z'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_SUSP); break; } diff --git a/contrib/putty/LDISC.H b/contrib/putty/LDISC.H index ef84f6d..030c0ce 100644 --- a/contrib/putty/LDISC.H +++ b/contrib/putty/LDISC.H @@ -11,10 +11,14 @@ typedef struct ldisc_tag { Terminal *term; Backend *back; - Config *cfg; void *backhandle; void *frontend; + /* + * Values cached out of conf. + */ + int telnet_keyboard, telnet_newline, protocol, localecho, localedit; + char *buf; int buflen, bufsiz, quotenext; } *Ldisc; diff --git a/contrib/putty/LDISCUCS.C b/contrib/putty/LDISCUCS.C index eda383b..5cece3e 100644 --- a/contrib/putty/LDISCUCS.C +++ b/contrib/putty/LDISCUCS.C @@ -51,13 +51,12 @@ void luni_send(void *handle, wchar_t * widebuf, int len, int interactive) for (p = linebuffer, i = 0; i < len; i++) { unsigned long ch = widebuf[i]; - if ((ch & 0xF800) == 0xD800) { + if (IS_SURROGATE(ch)) { #ifdef PLATFORM_IS_UTF16 if (i+1 < len) { unsigned long ch2 = widebuf[i+1]; - if ((ch & 0xFC00) == 0xD800 && - (ch2 & 0xFC00) == 0xDC00) { - ch = 0x10000 + ((ch & 0x3FF) << 10) + (ch2 & 0x3FF); + if (IS_SURROGATE_PAIR(ch, ch2)) { + ch = FROM_SURROGATES(ch, ch2); i++; } } else diff --git a/contrib/putty/LICENCE b/contrib/putty/LICENCE index d404fee..dece9d2 100644 --- a/contrib/putty/LICENCE +++ b/contrib/putty/LICENCE @@ -1,4 +1,4 @@ -PuTTY is copyright 1997-2011 Simon Tatham. +PuTTY is copyright 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/contrib/putty/LOGGING.C b/contrib/putty/LOGGING.C index 8fc5819..153ba67 100644 --- a/contrib/putty/LOGGING.C +++ b/contrib/putty/LOGGING.C @@ -16,12 +16,13 @@ struct LogContext { FILE *lgfp; enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state; bufchain queue; - Filename currlogfilename; + Filename *currlogfilename; void *frontend; - Config cfg; + Conf *conf; + int logtype; /* cached out of conf */ }; -static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm); +static Filename *xlatlognam(Filename *s, char *hostname, struct tm *tm); /* * Internal wrapper function which must be called for _all_ output @@ -75,7 +76,7 @@ static void logprintf(struct LogContext *ctx, const char *fmt, ...) */ void logflush(void *handle) { struct LogContext *ctx = (struct LogContext *)handle; - if (ctx->cfg.logtype > 0) + if (ctx->logtype > 0) if (ctx->state == L_OPEN) fflush(ctx->lgfp); } @@ -110,12 +111,12 @@ static void logfopen_callback(void *handle, int mode) ctx->state == L_ERROR ? (mode == 0 ? "Disabled writing" : "Error writing") : (mode == 1 ? "Appending" : "Writing new"), - (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" : - ctx->cfg.logtype == LGTYP_DEBUG ? "raw" : - ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" : - ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" : + (ctx->logtype == LGTYP_ASCII ? "ASCII" : + ctx->logtype == LGTYP_DEBUG ? "raw" : + ctx->logtype == LGTYP_PACKETS ? "SSH packets" : + ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" : "unknown"), - filename_to_str(&ctx->currlogfilename)); + filename_to_str(ctx->currlogfilename)); logevent(ctx->frontend, event); sfree(event); @@ -148,19 +149,24 @@ void logfopen(void *handle) if (ctx->state != L_CLOSED) return; - if (!ctx->cfg.logtype) + if (!ctx->logtype) return; tm = ltime(); /* substitute special codes in file name */ - xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm); + if (ctx->currlogfilename) + filename_free(ctx->currlogfilename); + ctx->currlogfilename = + xlatlognam(conf_get_filename(ctx->conf, CONF_logfilename), + conf_get_str(ctx->conf, CONF_host), &tm); ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE); /* file already present? */ if (ctx->lgfp) { + int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr); fclose(ctx->lgfp); - if (ctx->cfg.logxfovr != LGXF_ASK) { - mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1); + if (logxfovr != LGXF_ASK) { + mode = ((logxfovr == LGXF_OVR) ? 2 : 1); } else mode = askappend(ctx->frontend, ctx->currlogfilename, logfopen_callback, ctx); @@ -189,8 +195,8 @@ void logfclose(void *handle) void logtraffic(void *handle, unsigned char c, int logmode) { struct LogContext *ctx = (struct LogContext *)handle; - if (ctx->cfg.logtype > 0) { - if (ctx->cfg.logtype == logmode) + if (ctx->logtype > 0) { + if (ctx->logtype == logmode) logwrite(ctx, &c, 1); } } @@ -214,8 +220,8 @@ void log_eventlog(void *handle, const char *event) /* If we don't have a context yet (eg winnet.c init) then skip entirely */ if (!ctx) return; - if (ctx->cfg.logtype != LGTYP_PACKETS && - ctx->cfg.logtype != LGTYP_SSHRAW) + if (ctx->logtype != LGTYP_PACKETS && + ctx->logtype != LGTYP_SSHRAW) return; logprintf(ctx, "Event Log: %s\r\n", event); logflush(ctx); @@ -236,8 +242,8 @@ void log_packet(void *handle, int direction, int type, int p = 0, b = 0, omitted = 0; int output_pos = 0; /* NZ if pending output in dumpdata */ - if (!(ctx->cfg.logtype == LGTYP_SSHRAW || - (ctx->cfg.logtype == LGTYP_PACKETS && texttype))) + if (!(ctx->logtype == LGTYP_SSHRAW || + (ctx->logtype == LGTYP_PACKETS && texttype))) return; /* Packet header. */ @@ -252,8 +258,21 @@ void log_packet(void *handle, int direction, int type, type, type, texttype); } } else { - logprintf(ctx, "%s raw data\r\n", - direction == PKT_INCOMING ? "Incoming" : "Outgoing"); + /* + * Raw data is logged with a timestamp, so that it's possible + * to determine whether a mysterious delay occurred at the + * client or server end. (Timestamping the raw data avoids + * cluttering the normal case of only logging decrypted SSH + * messages, and also adds conceptual rigour in the case where + * an SSH message arrives in several pieces.) + */ + char buf[256]; + struct tm tm; + tm = ltime(); + strftime(buf, 24, "%Y-%m-%d %H:%M:%S", &tm); + logprintf(ctx, "%s raw data at %s\r\n", + direction == PKT_INCOMING ? "Incoming" : "Outgoing", + buf); } /* @@ -326,13 +345,15 @@ void log_packet(void *handle, int direction, int type, logflush(ctx); } -void *log_init(void *frontend, Config *cfg) +void *log_init(void *frontend, Conf *conf) { struct LogContext *ctx = snew(struct LogContext); ctx->lgfp = NULL; ctx->state = L_CLOSED; ctx->frontend = frontend; - ctx->cfg = *cfg; /* STRUCTURE COPY */ + ctx->conf = conf_copy(conf); + ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); + ctx->currlogfilename = NULL; bufchain_init(&ctx->queue); return ctx; } @@ -343,16 +364,20 @@ void log_free(void *handle) logfclose(ctx); bufchain_clear(&ctx->queue); + if (ctx->currlogfilename) + filename_free(ctx->currlogfilename); sfree(ctx); } -void log_reconfig(void *handle, Config *cfg) +void log_reconfig(void *handle, Conf *conf) { struct LogContext *ctx = (struct LogContext *)handle; int reset_logging; - if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) || - ctx->cfg.logtype != cfg->logtype) + if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename), + conf_get_filename(conf, CONF_logfilename)) || + conf_get_int(ctx->conf, CONF_logtype) != + conf_get_int(conf, CONF_logtype)) reset_logging = TRUE; else reset_logging = FALSE; @@ -360,7 +385,10 @@ void log_reconfig(void *handle, Config *cfg) if (reset_logging) logfclose(ctx); - ctx->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(ctx->conf); + ctx->conf = conf_copy(conf); + + ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); if (reset_logging) logfopen(ctx); @@ -372,17 +400,19 @@ void log_reconfig(void *handle, Config *cfg) * * "&Y":YYYY "&m":MM "&d":DD "&T":hhmmss "&h": "&&":& */ -static void xlatlognam(Filename *dest, Filename src, - char *hostname, struct tm *tm) { +static Filename *xlatlognam(Filename *src, char *hostname, struct tm *tm) +{ char buf[10], *bufp; int size; - char buffer[FILENAME_MAX]; - int len = sizeof(buffer)-1; - char *d; + char *buffer; + int buflen, bufsize; const char *s; + Filename *ret; - d = buffer; - s = filename_to_str(&src); + bufsize = FILENAME_MAX; + buffer = snewn(bufsize, char); + buflen = 0; + s = filename_to_str(src); while (*s) { /* Let (bufp, len) be the string to append. */ @@ -418,13 +448,16 @@ static void xlatlognam(Filename *dest, Filename src, buf[0] = *s++; size = 1; } - if (size > len) - size = len; - memcpy(d, bufp, size); - d += size; - len -= size; + if (bufsize <= buflen + size) { + bufsize = (buflen + size) * 5 / 4 + 512; + buffer = sresize(buffer, bufsize, char); + } + memcpy(buffer + buflen, bufp, size); + buflen += size; } - *d = '\0'; + buffer[buflen] = '\0'; - *dest = filename_from_str(buffer); + ret = filename_from_str(buffer); + sfree(buffer); + return ret; } diff --git a/contrib/putty/MACOSX/MAKEFILE b/contrib/putty/MACOSX/MAKEFILE index 1771e0b..d654584 100644 --- a/contrib/putty/MACOSX/MAKEFILE +++ b/contrib/putty/MACOSX/MAKEFILE @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -128,20 +133,21 @@ PuTTY: PuTTY.app/Contents/MacOS/PuTTY \ PuTTY.app/Contents/Resources/PuTTY.icns \ PuTTY.app/Contents/Info.plist $(PuTTY_extra) -PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o config.o \ - cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o osxctrls.o \ - osxdlg.o osxmain.o osxsel.o osxwin.o pgssapi.o pinger.o \ - portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \ - settings.o slookup.o ssh.o sshaes.o ssharcf.o sshblowf.o \ - sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ - sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ - sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o testback.o \ - time.o timing.o toucs.o tree234.o utf8.o ux_x11.o uxagentc.o \ - uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxprint.o \ - uxproxy.o uxpty.o uxsel.o uxser.o uxsignal.o uxstore.o \ - uxucs.o version.o wcwidth.o wildcard.o x11fwd.o xenc.o - $(CC) $(MLDFLAGS) -o $@ be_all_s.o config.o cproxy.o dialog.o \ +PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o conf.o \ + config.o cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o \ + localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ + osxctrls.o osxdlg.o osxmain.o osxsel.o osxwin.o pgssapi.o \ + pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o \ + sercfg.o settings.o slookup.o ssh.o sshaes.o ssharcf.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o \ + sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o \ + testback.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ + uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxprint.o uxproxy.o uxpty.o uxsel.o uxser.o uxsignal.o \ + uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ + xenc.o + $(CC) $(MLDFLAGS) -o $@ be_all_s.o conf.o config.o cproxy.o dialog.o \ fromucs.o ldisc.o ldiscucs.o localenc.o logging.o macenc.o \ mimeenc.o minibidi.o misc.o osxctrls.o osxdlg.o osxmain.o \ osxsel.o osxwin.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ @@ -155,16 +161,16 @@ PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o config.o \ uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ wcwidth.o wildcard.o x11fwd.o xenc.o -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) $(ULDFLAGS) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o settings.o ssh.o sshaes.o ssharcf.o sshblowf.o \ sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ @@ -174,7 +180,7 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ uxnet.o uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o \ uxsignal.o uxstore.o version.o wildcard.o x11fwd.o -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -182,7 +188,7 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o cproxy.o int64.o \ + $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ @@ -192,7 +198,7 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ x11fwd.o -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -200,7 +206,7 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o cproxy.o int64.o \ + $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o \ psftp.o settings.o sftp.o ssh.o sshaes.o ssharcf.o \ sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ @@ -210,16 +216,16 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ uxnoise.o uxproxy.o uxsel.o uxsftp.o uxstore.o version.o \ wildcard.o x11fwd.o -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) $(ULDFLAGS) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o \ - sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ - sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ - sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ - uxnoise.o uxstore.o version.o +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ + sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ + sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ + uxstore.o version.o + $(CC) $(ULDFLAGS) -o $@ cmdgen.o conf.o import.o misc.o notiming.o \ + sshaes.o sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o \ + sshprime.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o \ + sshsh512.o sshsha.o time.o tree234.o uxcons.o uxgen.o \ + uxmisc.o uxnoise.o uxstore.o version.o be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -251,6 +257,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -529,7 +540,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< diff --git a/contrib/putty/MACOSX/OSXMAIN.M b/contrib/putty/MACOSX/OSXMAIN.M index 1c7599c..6a9521a 100644 --- a/contrib/putty/MACOSX/OSXMAIN.M +++ b/contrib/putty/MACOSX/OSXMAIN.M @@ -85,6 +85,24 @@ static void commonfatalbox(char *p, va_list ap) exit(1); } +void nonfatal(void *frontend, char *p, ...) +{ + char *errorbuf; + NSAlert *alert; + va_list ap; + + va_start(ap, p); + errorbuf = dupvprintf(p, ap); + va_end(ap); + + alert = [[[NSAlert alloc] init] autorelease]; + [alert addButtonWithTitle:@"Error"]; + [alert setInformativeText:[NSString stringWithCString:errorbuf]]; + [alert runModal]; + + sfree(errorbuf); +} + void fatalbox(char *p, ...) { va_list ap; diff --git a/contrib/putty/MACOSX/OSXWIN.M b/contrib/putty/MACOSX/OSXWIN.M index 9de1e08..ff07080 100644 --- a/contrib/putty/MACOSX/OSXWIN.M +++ b/contrib/putty/MACOSX/OSXWIN.M @@ -109,11 +109,11 @@ nfg = nbg; nbg = t; } - if (cfg.bold_colour && (attr & ATTR_BOLD)) { + if ((cfg.bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (cfg.bold_colour && (attr & ATTR_BLINK)) { + if ((cfg.bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -129,7 +129,7 @@ widefactor = 1; } - /* FIXME: ATTR_BOLD without cfg.bold_colour */ + /* FIXME: ATTR_BOLD if cfg.bold_style & 1 */ if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; @@ -957,7 +957,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0]; diff --git a/contrib/putty/MACOSX/README.OSX b/contrib/putty/MACOSX/README.OSX index e9243a0..c059846 100644 --- a/contrib/putty/MACOSX/README.OSX +++ b/contrib/putty/MACOSX/README.OSX @@ -20,6 +20,15 @@ say you weren't warned! Other ways in which the port is currently unfinished include: +Bit rot +------- + + - the conversion of the old fixed-size 'Config' structure to the + new dynamic 'Conf' was never applied to this directory + + - probably other things are out of date too; it would need some + work to make it compile again + Missing terminal window features -------------------------------- diff --git a/contrib/putty/MINIBIDI.C b/contrib/putty/MINIBIDI.C index 4c2eea5..1af6c05 100644 --- a/contrib/putty/MINIBIDI.C +++ b/contrib/putty/MINIBIDI.C @@ -1,5 +1,5 @@ /************************************************************************ - * $Id: minibidi.c 9169 2011-05-07 10:57:19Z simon $ + * $Id: minibidi.c 9426 2012-03-05 18:34:40Z simon $ * * ------------ * Description: @@ -14,9 +14,9 @@ * ----------------- * Revision Details: (Updated by Revision Control System) * ----------------- - * $Date: 2011-05-07 11:57:19 +0100 (Sat, 07 May 2011) $ + * $Date: 2012-03-05 18:34:40 +0000 (Mon, 05 Mar 2012) $ * $Author: simon $ - * $Revision: 9169 $ + * $Revision: 9426 $ * * (www.arabeyes.org - under MIT license) * @@ -58,7 +58,7 @@ shapetypes[(xh)-SHAPE_FIRST].type : SU) /*))*/ #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) typedef struct bidi_char { - wchar_t origwc, wc; + unsigned int origwc, wc; unsigned short index; } bidi_char; @@ -70,7 +70,7 @@ unsigned char setOverrideBits(unsigned char level, unsigned char override); int getPreviousLevel(unsigned char* level, int from); int do_shape(bidi_char *line, bidi_char *to, int count); int do_bidi(bidi_char *line, int count); -void doMirror(wchar_t* ch); +void doMirror(unsigned int *ch); /* character types */ enum { @@ -1636,7 +1636,7 @@ int do_bidi(bidi_char *line, int count) * takes a pointer to a character that is checked for * having a mirror glyph. */ -void doMirror(wchar_t* ch) +void doMirror(unsigned int *ch) { if ((*ch & 0xFF00) == 0) { switch (*ch) { diff --git a/contrib/putty/MISC.C b/contrib/putty/MISC.C index 4aeab50..d652b39 100644 --- a/contrib/putty/MISC.C +++ b/contrib/putty/MISC.C @@ -99,24 +99,48 @@ prompts_t *new_prompts(void *frontend) p->name_reqd = p->instr_reqd = FALSE; return p; } -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len) +void add_prompt(prompts_t *p, char *promptstr, int echo) { prompt_t *pr = snew(prompt_t); - char *result = snewn(len, char); pr->prompt = promptstr; pr->echo = echo; - pr->result = result; - pr->result_len = len; + pr->result = NULL; + pr->resultsize = 0; p->n_prompts++; p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); p->prompts[p->n_prompts-1] = pr; } +void prompt_ensure_result_size(prompt_t *pr, int newlen) +{ + if ((int)pr->resultsize < newlen) { + char *newbuf; + newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ + + /* + * We don't use sresize / realloc here, because we will be + * storing sensitive stuff like passwords in here, and we want + * to make sure that the data doesn't get copied around in + * memory without the old copy being destroyed. + */ + newbuf = snewn(newlen, char); + memcpy(newbuf, pr->result, pr->resultsize); + smemclr(pr->result, pr->resultsize); + sfree(pr->result); + pr->result = newbuf; + pr->resultsize = newlen; + } +} +void prompt_set_result(prompt_t *pr, const char *newstr) +{ + prompt_ensure_result_size(pr, strlen(newstr) + 1); + strcpy(pr->result, newstr); +} void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; - memset(pr->result, 0, pr->result_len); /* burn the evidence */ + smemclr(pr->result, pr->resultsize); /* burn the evidence */ sfree(pr->result); sfree(pr->prompt); sfree(pr); @@ -176,6 +200,37 @@ char *dupcat(const char *s1, ...) return p; } +void burnstr(char *string) /* sfree(str), only clear it first */ +{ + if (string) { + smemclr(string, strlen(string)); + sfree(string); + } +} + +int toint(unsigned u) +{ + /* + * Convert an unsigned to an int, without running into the + * undefined behaviour which happens by the strict C standard if + * the value overflows. You'd hope that sensible compilers would + * do the sensible thing in response to a cast, but actually I + * don't trust modern compilers not to do silly things like + * assuming that _obviously_ you wouldn't have caused an overflow + * and so they can elide an 'if (i < 0)' test immediately after + * the cast. + * + * Sensible compilers ought of course to optimise this entire + * function into 'just return the input value'! + */ + if (u <= (unsigned)INT_MAX) + return (int)u; + else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ + return INT_MIN + (int)(u - (unsigned)INT_MIN); + else + return INT_MIN; /* fallback; should never occur on binary machines */ +} + /* * Do an sprintf(), but into a custom-allocated buffer. * @@ -332,12 +387,11 @@ void base64_encode_atom(unsigned char *data, int n, char *out) * - return the current size of the buffer chain in bytes */ -#define BUFFER_GRANULE 512 +#define BUFFER_MIN_GRANULE 512 struct bufchain_granule { struct bufchain_granule *next; - int buflen, bufpos; - char buf[BUFFER_GRANULE]; + char *bufpos, *bufend, *bufmax; }; void bufchain_init(bufchain *ch) @@ -371,28 +425,29 @@ void bufchain_add(bufchain *ch, const void *data, int len) ch->buffersize += len; - if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) { - int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen); - memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen); - buf += copylen; - len -= copylen; - ch->tail->buflen += copylen; - } while (len > 0) { - int grainlen = min(len, BUFFER_GRANULE); - struct bufchain_granule *newbuf; - newbuf = snew(struct bufchain_granule); - newbuf->bufpos = 0; - newbuf->buflen = grainlen; - memcpy(newbuf->buf, buf, grainlen); - buf += grainlen; - len -= grainlen; - if (ch->tail) - ch->tail->next = newbuf; - else - ch->head = ch->tail = newbuf; - newbuf->next = NULL; - ch->tail = newbuf; + if (ch->tail && ch->tail->bufend < ch->tail->bufmax) { + int copylen = min(len, ch->tail->bufmax - ch->tail->bufend); + memcpy(ch->tail->bufend, buf, copylen); + buf += copylen; + len -= copylen; + ch->tail->bufend += copylen; + } + if (len > 0) { + int grainlen = + max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE); + struct bufchain_granule *newbuf; + newbuf = smalloc(grainlen); + newbuf->bufpos = newbuf->bufend = + (char *)newbuf + sizeof(struct bufchain_granule); + newbuf->bufmax = (char *)newbuf + grainlen; + newbuf->next = NULL; + if (ch->tail) + ch->tail->next = newbuf; + else + ch->head = newbuf; + ch->tail = newbuf; + } } } @@ -404,13 +459,13 @@ void bufchain_consume(bufchain *ch, int len) while (len > 0) { int remlen = len; assert(ch->head != NULL); - if (remlen >= ch->head->buflen - ch->head->bufpos) { - remlen = ch->head->buflen - ch->head->bufpos; + if (remlen >= ch->head->bufend - ch->head->bufpos) { + remlen = ch->head->bufend - ch->head->bufpos; tmp = ch->head; ch->head = tmp->next; - sfree(tmp); if (!ch->head) ch->tail = NULL; + sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; @@ -420,8 +475,8 @@ void bufchain_consume(bufchain *ch, int len) void bufchain_prefix(bufchain *ch, void **data, int *len) { - *len = ch->head->buflen - ch->head->bufpos; - *data = ch->head->buf + ch->head->bufpos; + *len = ch->head->bufend - ch->head->bufpos; + *data = ch->head->bufpos; } void bufchain_fetch(bufchain *ch, void *data, int len) @@ -436,9 +491,9 @@ void bufchain_fetch(bufchain *ch, void *data, int len) int remlen = len; assert(tmp != NULL); - if (remlen >= tmp->buflen - tmp->bufpos) - remlen = tmp->buflen - tmp->bufpos; - memcpy(data_c, tmp->buf + tmp->bufpos, remlen); + if (remlen >= tmp->bufend - tmp->bufpos) + remlen = tmp->bufend - tmp->bufpos; + memcpy(data_c, tmp->bufpos, remlen); tmp = tmp->next; len -= remlen; @@ -635,21 +690,61 @@ void debug_memdump(void *buf, int len, int L) #endif /* def DEBUG */ /* - * Determine whether or not a Config structure represents a session - * which can sensibly be launched right now. + * Determine whether or not a Conf represents a session which can + * sensibly be launched right now. */ -int cfg_launchable(const Config *cfg) +int conf_launchable(Conf *conf) { - if (cfg->protocol == PROT_SERIAL) - return cfg->serline[0] != 0; + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + return conf_get_str(conf, CONF_serline)[0] != 0; else - return cfg->host[0] != 0; + return conf_get_str(conf, CONF_host)[0] != 0; } -char const *cfg_dest(const Config *cfg) +char const *conf_dest(Conf *conf) { - if (cfg->protocol == PROT_SERIAL) - return cfg->serline; + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + return conf_get_str(conf, CONF_serline); else - return cfg->host; + return conf_get_str(conf, CONF_host); } + +#ifndef PLATFORM_HAS_SMEMCLR +/* + * Securely wipe memory. + * + * The actual wiping is no different from what memset would do: the + * point of 'securely' is to try to be sure over-clever compilers + * won't optimise away memsets on variables that are about to be freed + * or go out of scope. See + * https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html + * + * Some platforms (e.g. Windows) may provide their own version of this + * function. + */ +void smemclr(void *b, size_t n) { + volatile char *vp; + + if (b && n > 0) { + /* + * Zero out the memory. + */ + memset(b, 0, n); + + /* + * Perform a volatile access to the object, forcing the + * compiler to admit that the previous memset was important. + * + * This while loop should in practice run for zero iterations + * (since we know we just zeroed the object out), but in + * theory (as far as the compiler knows) it might range over + * the whole object. (If we had just written, say, '*vp = + * *vp;', a compiler could in principle have 'helpfully' + * optimised the memset into only zeroing out the first byte. + * This should be robust.) + */ + vp = b; + while (*vp) vp++; + } +} +#endif diff --git a/contrib/putty/MISC.H b/contrib/putty/MISC.H index 1123314..b44e233 100644 --- a/contrib/putty/MISC.H +++ b/contrib/putty/MISC.H @@ -28,6 +28,9 @@ char *dupstr(const char *s); char *dupcat(const char *s1, ...); char *dupprintf(const char *fmt, ...); char *dupvprintf(const char *fmt, va_list ap); +void burnstr(char *string); + +int toint(unsigned); char *fgetline(FILE *fp); @@ -49,6 +52,8 @@ void bufchain_fetch(bufchain *ch, void *data, int len); struct tm ltime(void); +void smemclr(void *b, size_t len); + /* * Debugging functions. * diff --git a/contrib/putty/MKAUTO.SH b/contrib/putty/MKAUTO.SH index 6548951..17e98bb 100644 --- a/contrib/putty/MKAUTO.SH +++ b/contrib/putty/MKAUTO.SH @@ -7,41 +7,5 @@ # well as from outside. test -f unix.h && cd .. -# Persuade automake to give us a copy of its install-sh. This is a -# pain because I don't actually want to have to _use_ automake. -# Instead, I construct a trivial unrelated automake project in a -# temporary subdirectory, run automake so that it'll copy -# install-sh into that directory, then copy it back out again. -# Hideous, but it should work. - -mkdir automake-grievous-hack -cat > automake-grievous-hack/hello.c << EOF -#include -int main(int argc, char **argv) -{ - printf("hello, world\n"); - return 0; -} -EOF -cat > automake-grievous-hack/Makefile.am << EOF -bin_PROGRAMS = hello -hello_SOURCES = hello.c -EOF -cat > automake-grievous-hack/configure.ac << EOF -AC_INIT -AM_INIT_AUTOMAKE(hello, 1.0) -AC_CONFIG_FILES([Makefile]) -AC_PROG_CC -AC_OUTPUT -EOF -echo Some news > automake-grievous-hack/NEWS -echo Some text > automake-grievous-hack/README -echo Some people > automake-grievous-hack/AUTHORS -echo Some changes > automake-grievous-hack/ChangeLog -rm -f install-sh # this won't work if we accidentally have one _here_ -(cd automake-grievous-hack && autoreconf -i && \ - cp install-sh ../unix/install-sh) -rm -rf automake-grievous-hack - -# That was the hard bit. Now run autoconf on our real configure.in. -(cd unix && autoreconf && rm -rf aclocal.m4 autom4te.cache) +# Run autoconf on our real configure.in. +(cd unix && autoreconf -i && rm -rf autom4te.cache) diff --git a/contrib/putty/MKFILES.PL b/contrib/putty/MKFILES.PL index 8075ddb..24379fc 100644 --- a/contrib/putty/MKFILES.PL +++ b/contrib/putty/MKFILES.PL @@ -17,8 +17,24 @@ use warnings; use FileHandle; +use File::Basename; use Cwd; +if ($#ARGV >= 0 and ($ARGV[0] eq "-u" or $ARGV[0] eq "-U")) { + # Convenience for Unix users: -u means that after we finish what + # we're doing here, we also run mkauto.sh and then 'configure' in + # the Unix subdirectory. So it's a one-stop shop for regenerating + # the actual end-product Unix makefile. + # + # Arguments supplied after -u go to configure. + # + # -U is identical, but runs 'configure' at the _top_ level, for + # people who habitually do that. + $do_unix = ($ARGV[0] eq "-U" ? 2 : 1); + shift @ARGV; + @confargs = @ARGV; +} + open IN, "Recipe" or do { # We want to deal correctly with being run from one of the # subdirs in the source tree. So if we can't find Recipe here, @@ -64,9 +80,17 @@ while () { if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;} + if ($_[0] eq "!cflags" and &mfval($_[1])) { + ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line + $rest = 1 if $rest eq ""; + $cflags{$_[1]}->{$_[2]} = $rest; + next; + } if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; } if ($_[0] eq "!begin") { - if (&mfval($_[1])) { + if ($_[1] =~ /^>(.*)/) { + $divert = \$auxfiles{$1}; + } elsif (&mfval($_[1])) { $sect = $_[2] ? $_[2] : "end"; $divert = \($makefile_extra{$_[1]}->{$sect}); } else { @@ -123,6 +147,12 @@ while () { close IN; +foreach $aux (sort keys %auxfiles) { + open AUX, ">$aux"; + print AUX $auxfiles{$aux}; + close AUX; +} + # Now retrieve the complete list of objects and resource files, and # construct dependency data for them. While we're here, expand the # object list for each program, and complain if its type isn't set. @@ -177,11 +207,13 @@ foreach $i (@prognames) { # file name into a listref containing further source file names. %further = (); +%allsourcefiles = (); # this is wanted by some makefiles while (scalar @scanlist > 0) { $file = shift @scanlist; next if defined $further{$file}; # skip if we've already done it $further{$file} = []; $dirfile = &findfile($file); + $allsourcefiles{$dirfile} = 1; open IN, "$dirfile" or die "unable to open source file $file\n"; while () { chomp; @@ -226,7 +258,7 @@ sub mfval($) { # prints a warning and returns false; if (grep { $type eq $_ } ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix", - "ac","osx",)) { + "am","osx",)) { return 1; } warn "$.:unknown makefile type '$type'\n"; @@ -391,6 +423,8 @@ sub manpages { return (); } +$orig_dir = cwd; + # Now we're ready to output the actual Makefiles. if (defined $makefiles{'cygwin'}) { @@ -399,7 +433,7 @@ if (defined $makefiles{'cygwin'}) { ##-- CygWin makefile open OUT, ">$makefiles{'cygwin'}"; select OUT; print - "# Makefile for $project_name under cygwin.\n". + "# Makefile for $project_name under Cygwin, MinGW, or Winelib.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; # gcc command line option is -D not /D @@ -419,7 +453,7 @@ if (defined $makefiles{'cygwin'}) { "# RCINC = --include-dir c:\\cygwin\\include\\\n". "\n". &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT". - " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " . + " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -DNO_SECUREZEROMEMORY " . (join " ", map {"-I$dirpfx$_"} @srcdirs)) . "\n". "LDFLAGS = -mno-cygwin -s\n". @@ -450,7 +484,7 @@ if (defined $makefiles{'cygwin'}) { join " ", @{$d->{deps}})), "\n"; } if ($d->{obj} =~ /\.res\.o$/) { - print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." ".$d->{obj}."\n\n"; + print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n"; } else { print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n"; } @@ -458,7 +492,7 @@ if (defined $makefiles{'cygwin'}) { print "\n"; print $makefile_extra{'cygwin'}->{'end'}; print "\nclean:\n". - "\trm -f *.o *.exe *.res.o *.map\n". + "\trm -f *.o *.exe *.res.o *.so *.map\n". "\n". "FORCE:\n"; select STDOUT; close OUT; @@ -669,8 +703,6 @@ if (defined $makefiles{'vc'}) { if (defined $makefiles{'vcproj'}) { $dirpfx = &dirpfx($makefiles{'vcproj'}, "\\"); - $orig_dir = cwd; - ##-- MSVC 6 Workspace and projects # # Note: All files created in this section are written in binary @@ -1084,71 +1116,92 @@ if (defined $makefiles{'unix'}) { select STDOUT; close OUT; } -if (defined $makefiles{'ac'}) { - $dirpfx = &dirpfx($makefiles{'ac'}, "/"); +if (defined $makefiles{'am'}) { + $dirpfx = "\$(srcdir)/" . &dirpfx($makefiles{'am'}, "/"); - ##-- Unix/autoconf makefile - open OUT, ">$makefiles{'ac'}"; select OUT; + ##-- Unix/autoconf Makefile.am + open OUT, ">$makefiles{'am'}"; select OUT; print - "# Makefile.in for $project_name under Unix with Autoconf.\n". + "# Makefile.am for $project_name under Unix with Autoconf/Automake.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/([=" ])\/D/$1-D/gs; - print $_; - print - "\n". - "CC = \@CC\@\n". - "\n". - &splitline("CFLAGS = \@CFLAGS\@ \@PUTTYCFLAGS\@ \@CPPFLAGS\@ " . - "\@DEFS\@ \@GTK_CFLAGS\@ " . - (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". - "XLDFLAGS = \@LDFLAGS\@ \@LIBS\@ \@GTK_LIBS\@\n". - "ULDFLAGS = \@LDFLAGS\@ \@LIBS\@\n". - "INSTALL=\@INSTALL\@\n". - "INSTALL_PROGRAM=\$(INSTALL)\n". - "INSTALL_DATA=\$(INSTALL)\n". - "prefix=\@prefix\@\n". - "exec_prefix=\@exec_prefix\@\n". - "bindir=\@bindir\@\n". - "datarootdir=\@datarootdir\@\n". - "mandir=\@mandir\@\n". - "man1dir=\$(mandir)/man1\n". - "\n". - &def($makefile_extra{'gtk'}->{'vars'}) . - "\n". - ".SUFFIXES:\n". - "\n". - "\n". - "all: \@all_targets\@\n". - &splitline("all-cli:" . join "", map { " $_" } &progrealnames("U"))."\n". - &splitline("all-gtk:" . join "", map { " $_" } &progrealnames("X"))."\n"; - print "\n"; - foreach $p (&prognames("X:U")) { + "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n\n"; + + # Complete list of source and header files. Not used by the + # auto-generated parts of this makefile, but Recipe might like to + # have it available as a variable so that mandatory-rebuild things + # (version.o) can conveniently be made to depend on it. + @sources = ("allsources", "=", + map {"${dirpfx}$_"} sort keys %allsourcefiles); + print &splitline(join " ", @sources), "\n\n"; + + @cliprogs = ("bin_PROGRAMS", "="); + foreach $p (&prognames("U")) { ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", undef, undef); - print &splitline($prog . ": " . $objstr), "\n"; - $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC) -o \$@ " . - $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n"; + push @cliprogs, $prog; } - foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) { - if ($forceobj{$d->{obj_orig}}) { - printf("%s: FORCE\n", $d->{obj}); - } else { - print &splitline(sprintf("%s: %s", $d->{obj}, - join " ", @{$d->{deps}})), "\n"; + @allprogs = @cliprogs; + foreach $p (&prognames("X")) { + ($prog, $type) = split ",", $p; + push @allprogs, $prog; + } + print "if HAVE_GTK\n"; + print &splitline(join " ", @allprogs), "\n"; + print "else\n"; + print &splitline(join " ", @cliprogs), "\n"; + print "endif\n\n"; + + %objtosrc = (); + foreach $d (&deps("X", undef, $dirpfx, "/", "am")) { + $objtosrc{$d->{obj}} = $d->{deps}->[0]; + } + + print &splitline(join " ", "AM_CPPFLAGS", "=", + map {"-I$dirpfx$_"} @srcdirs), "\n"; + + @amcflags = ("\$(COMPAT)", "\$(XFLAGS)", "\$(WARNINGOPTS)"); + print "if HAVE_GTK\n"; + print &splitline(join " ", "AM_CFLAGS", "=", + "\$(GTK_CFLAGS)", @amcflags), "\n"; + print "else\n"; + print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n"; + print "endif\n\n"; + + %amspeciallibs = (); + foreach $obj (sort { $a cmp $b } keys %{$cflags{'am'}}) { + print "lib${obj}_a_SOURCES = ", $objtosrc{$obj}, "\n"; + print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", @amcflags, + $cflags{'am'}->{$obj}), "\n"; + $amspeciallibs{$obj} = "lib${obj}.a"; + } + print &splitline(join " ", "noinst_LIBRARIES", "=", + sort { $a cmp $b } values %amspeciallibs), "\n\n"; + + foreach $p (&prognames("X:U")) { + ($prog, $type) = split ",", $p; + print "if HAVE_GTK\n" if $type eq "X"; + @progsources = ("${prog}_SOURCES", "="); + %sourcefiles = (); + @ldadd = (); + $objstr = &objects($p, "X", undef, undef); + foreach $obj (split / /,$objstr) { + if ($amspeciallibs{$obj}) { + push @ldadd, $amspeciallibs{$obj}; + } else { + $sourcefiles{$objtosrc{$obj}} = 1; + } } - print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); + push @progsources, sort { $a cmp $b } keys %sourcefiles; + print &splitline(join " ", @progsources), "\n"; + if ($type eq "X") { + push @ldadd, "\$(GTK_LIBS)"; + } + if (@ldadd) { + print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n"; + } + print "endif\n" if $type eq "X"; + print "\n"; } - print "\n"; - print $makefile_extra{'gtk'}->{'end'}; - print "\nclean:\n". - "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n"; - print "\ndistclean: clean\n". - "\t". &splitline("rm -f config.status config.cache config.log ". - "configure.lineno config.status.lineno Makefile") . "\n"; - print "\nFORCE:\n"; + print $makefile_extra{'am'}->{'end'}; select STDOUT; close OUT; } @@ -1336,6 +1389,8 @@ if (defined $makefiles{'devcppproj'}) { create_devcpp_project(\%all_object_deps, $progname); } + chdir $orig_dir; + sub create_devcpp_project { my ($all_object_deps, $progname) = @_; # Construct program's dependency info (Taken from 'vcproj', seems to work right here, too.) @@ -1491,3 +1546,17 @@ if (defined $makefiles{'devcppproj'}) { chdir ".."; } } + +# All done, so do the Unix postprocessing if asked to. + +if ($do_unix) { + chdir $orig_dir; + system "./mkauto.sh"; + die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0; + if ($do_unix == 1) { + chdir ($targetdir = dirname($makefiles{"am"})) + or die "$targetdir: chdir: $!\n"; + } + system "./configure", @confargs; + die "mkfiles.pl: configure returned $?\n" if $? > 0; +} diff --git a/contrib/putty/MKUNXARC.SH b/contrib/putty/MKUNXARC.SH index aed6728..5631832 100644 --- a/contrib/putty/MKUNXARC.SH +++ b/contrib/putty/MKUNXARC.SH @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Build a Unix source distribution from the PuTTY CVS area. # @@ -10,16 +10,19 @@ case "$1" in ????-??-??) case "$1" in *[!-0-9]*) echo "Malformed snapshot ID '$1'" >&2;exit 1;;esac - arcsuffix="-`cat LATEST.VER`-$1" + autoconfver="`cat LATEST.VER`-$1" + arcsuffix="-$autoconfver" ver="-DSNAPSHOT=$1" docver= ;; r*) - arcsuffix="-$1" - ver="-DSVN_REV=$1" + autoconfver="$1" + arcsuffix="-$autoconfver" + ver="-DSVN_REV=${1#r}" docver= ;; '') + autoconfver="X.XX" # got to put something in here! arcsuffix= ver= docver= @@ -35,7 +38,8 @@ case "$1" in ;; *) case "$1" in *[!.0-9a-z~]*) echo "Malformed release ID '$1'">&2;exit 1;;esac - arcsuffix="-$1" + autoconfver="$1" + arcsuffix="-$autoconfver" ver="-DRELEASE=$1" docver="VERSION=\"PuTTY release $1\"" ;; @@ -43,7 +47,6 @@ esac perl mkfiles.pl (cd doc && make -s ${docver:+"$docver"}) -sh mkauto.sh 2>/dev/null relver=`cat LATEST.VER` arcname="putty$arcsuffix" @@ -58,6 +61,7 @@ find . -name uxarc -prune -o \ -name CVS -prune -o \ -name .cvsignore -prune -o \ -name .svn -prune -o \ + -name configure.ac -prune -o \ -name '*.zip' -prune -o \ -name '*.tar.gz' -prune -o \ -type f -exec ln -s $PWD/{} uxarc/$arcname/{} \; @@ -66,5 +70,8 @@ if test "x$ver" != "x"; then md5sum `find . -name '*.[ch]' -print` > manifest; echo "$ver" > version.def) fi +sed "s/^AC_INIT(putty,.*/AC_INIT(putty, $autoconfver)/" unix/configure.ac > uxarc/$arcname/unix/configure.ac +(cd uxarc/$arcname && sh mkauto.sh) 2>errors || { cat errors >&2; exit 1; } + tar -C uxarc -chzof $arcname.tar.gz $arcname rm -rf uxarc diff --git a/contrib/putty/NETWORK.H b/contrib/putty/NETWORK.H index b1b5590..4c9fef0 100644 --- a/contrib/putty/NETWORK.H +++ b/contrib/putty/NETWORK.H @@ -15,7 +15,7 @@ #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -37,6 +37,7 @@ struct socket_function_table { void (*close) (Socket s); int (*write) (Socket s, const char *data, int len); int (*write_oob) (Socket s, const char *data, int len); + void (*write_eof) (Socket s); void (*flush) (Socket s); void (*set_private_ptr) (Socket s, void *ptr); void *(*get_private_ptr) (Socket s); @@ -94,18 +95,18 @@ struct plug_function_table { Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg); + Plug plug, Conf *conf); Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, - const Config *cfg, int addressfamily); + Conf *conf, int addressfamily); SockAddr name_lookup(char *host, int port, char **canonicalname, - const Config *cfg, int addressfamily); + Conf *conf, int addressfamily); /* platform-dependent callback from new_connection() */ /* (same caveat about addr as new_connection()) */ Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg); + Plug plug, Conf *conf); /* socket functions */ @@ -115,8 +116,9 @@ void sk_cleanup(void); /* called just before program exit */ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family); SockAddr sk_nonamelookup(const char *host); void sk_getaddr(SockAddr addr, char *buf, int buflen); -int sk_hostname_is_local(char *name); +int sk_hostname_is_local(const char *name); int sk_address_is_local(SockAddr addr); +int sk_address_is_special_local(SockAddr addr); int sk_addrtype(SockAddr addr); void sk_addrcopy(SockAddr addr, char *buf); void sk_addr_free(SockAddr addr); @@ -140,6 +142,7 @@ Socket sk_register(OSSocket sock, Plug plug); #define sk_close(s) (((*s)->close) (s)) #define sk_write(s,buf,len) (((*s)->write) (s, buf, len)) #define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len)) +#define sk_write_eof(s) (((*s)->write_eof) (s)) #define sk_flush(s) (((*s)->flush) (s)) #ifdef DEFINE_PLUG_METHOD_MACROS diff --git a/contrib/putty/NOTIMING.C b/contrib/putty/NOTIMING.C index aed3e45..75cc214 100644 --- a/contrib/putty/NOTIMING.C +++ b/contrib/putty/NOTIMING.C @@ -11,7 +11,7 @@ #include "putty.h" -long schedule_timer(int ticks, timer_fn_t fn, void *ctx) +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { return 0; } diff --git a/contrib/putty/PINGER.C b/contrib/putty/PINGER.C index b6fde24..071d02c 100644 --- a/contrib/putty/PINGER.C +++ b/contrib/putty/PINGER.C @@ -8,18 +8,18 @@ struct pinger_tag { int interval; int pending; - long next; + unsigned long next; Backend *back; void *backhandle; }; static void pinger_schedule(Pinger pinger); -static void pinger_timer(void *ctx, long now) +static void pinger_timer(void *ctx, unsigned long now) { Pinger pinger = (Pinger)ctx; - if (pinger->pending && now - pinger->next >= 0) { + if (pinger->pending && now == pinger->next) { pinger->back->special(pinger->backhandle, TS_PING); pinger->pending = FALSE; pinger_schedule(pinger); @@ -43,11 +43,11 @@ static void pinger_schedule(Pinger pinger) } } -Pinger pinger_new(Config *cfg, Backend *back, void *backhandle) +Pinger pinger_new(Conf *conf, Backend *back, void *backhandle) { Pinger pinger = snew(struct pinger_tag); - pinger->interval = cfg->ping_interval; + pinger->interval = conf_get_int(conf, CONF_ping_interval); pinger->pending = FALSE; pinger->back = back; pinger->backhandle = backhandle; @@ -56,10 +56,11 @@ Pinger pinger_new(Config *cfg, Backend *back, void *backhandle) return pinger; } -void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg) +void pinger_reconfig(Pinger pinger, Conf *oldconf, Conf *newconf) { - if (oldcfg->ping_interval != newcfg->ping_interval) { - pinger->interval = newcfg->ping_interval; + int newinterval = conf_get_int(newconf, CONF_ping_interval); + if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) { + pinger->interval = newinterval; pinger_schedule(pinger); } } diff --git a/contrib/putty/PORTFWD.C b/contrib/putty/PORTFWD.C index e5874a6..bb9abe6 100644 --- a/contrib/putty/PORTFWD.C +++ b/contrib/putty/PORTFWD.C @@ -33,14 +33,16 @@ struct PFwdPrivate { int dynamic; /* * `hostname' and `port' are the real hostname and port, once - * we know what we're connecting to; they're unused for this - * purpose while conducting a local SOCKS exchange, which means - * we can also use them as a buffer and pointer for reading - * data from the SOCKS client. + * we know what we're connecting to. */ - char hostname[256+8]; + char *hostname; int port; /* + * `socksbuf' is the buffer we use to accumulate a SOCKS request. + */ + char *socksbuf; + int sockslen, sockssize; + /* * When doing dynamic port forwarding, we can receive * connection data before we are actually able to send it; so * we may have to temporarily hold some in a dynamically @@ -50,6 +52,26 @@ struct PFwdPrivate { int buflen; }; +static struct PFwdPrivate *new_portfwd_private(void) +{ + struct PFwdPrivate *pr = snew(struct PFwdPrivate); + pr->hostname = NULL; + pr->socksbuf = NULL; + pr->sockslen = pr->sockssize = 0; + pr->buffer = NULL; + return pr; +} + +static void free_portfwd_private(struct PFwdPrivate *pr) +{ + if (!pr) + return; + sfree(pr->hostname); + sfree(pr->socksbuf); + sfree(pr->buffer); + sfree(pr); +} + static void pfd_log(Plug plug, int type, SockAddr addr, int port, const char *error_msg, int error_code) { @@ -61,14 +83,20 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code, { struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; - /* - * We have no way to communicate down the forwarded connection, - * so if an error occurred on the socket, we just ignore it - * and treat it like a proper close. - */ - if (pr->c) - sshfwd_close(pr->c); - pfd_close(pr->s); + if (error_msg) { + /* + * Socket error. Slam the connection instantly shut. + */ + sshfwd_unclean_close(pr->c); + } else { + /* + * Ordinary EOF received on socket. Send an EOF on the SSH + * channel. + */ + if (pr->c) + sshfwd_write_eof(pr->c); + } + return 1; } @@ -77,37 +105,26 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; if (pr->dynamic) { while (len--) { - /* - * Throughout SOCKS negotiation, "hostname" is re-used as a - * random protocol buffer with "port" storing the length. - */ - if (pr->port >= lenof(pr->hostname)) { - /* Request too long. */ - if ((pr->dynamic >> 12) == 4) { - /* Send back a SOCKS 4 error before closing. */ - char data[8]; - memset(data, 0, sizeof(data)); - data[1] = 91; /* generic `request rejected' */ - sk_write(pr->s, data, 8); - } - pfd_close(pr->s); - return 1; + if (pr->sockslen >= pr->sockssize) { + pr->sockssize = pr->sockslen * 5 / 4 + 256; + pr->socksbuf = sresize(pr->socksbuf, pr->sockssize, char); } - pr->hostname[pr->port++] = *data++; + pr->socksbuf[pr->sockslen++] = *data++; /* * Now check what's in the buffer to see if it's a * valid and complete message in the SOCKS exchange. */ if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) && - pr->hostname[0] == 4) { + pr->socksbuf[0] == 4) { /* * SOCKS 4. */ if (pr->dynamic == 1) pr->dynamic = 0x4000; - if (pr->port < 2) continue;/* don't have command code yet */ - if (pr->hostname[1] != 1) { + if (pr->sockslen < 2) + continue; /* don't have command code yet */ + if (pr->socksbuf[1] != 1) { /* Not CONNECT. */ /* Send back a SOCKS 4 error before closing. */ char data[8]; @@ -117,15 +134,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) pfd_close(pr->s); return 1; } - if (pr->port <= 8) continue; /* haven't started user/hostname */ - if (pr->hostname[pr->port-1] != 0) + if (pr->sockslen <= 8) + continue; /* haven't started user/hostname */ + if (pr->socksbuf[pr->sockslen-1] != 0) continue; /* haven't _finished_ user/hostname */ /* * Now we have a full SOCKS 4 request. Check it to * see if it's a SOCKS 4A request. */ - if (pr->hostname[4] == 0 && pr->hostname[5] == 0 && - pr->hostname[6] == 0 && pr->hostname[7] != 0) { + if (pr->socksbuf[4] == 0 && pr->socksbuf[5] == 0 && + pr->socksbuf[6] == 0 && pr->socksbuf[7] != 0) { /* * It's SOCKS 4A. So if we haven't yet * collected the host name, we should continue @@ -135,15 +153,17 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) int len; if (pr->dynamic == 0x4000) { pr->dynamic = 0x4001; - pr->port = 8; /* reset buffer to overwrite name */ + pr->sockslen = 8; /* reset buffer to overwrite name */ continue; } - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - len= pr->port - 8; - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - memmove(pr->hostname, pr->hostname + 8, len); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + len = pr->sockslen - 8; + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = snewn(len+1, char); + pr->hostname[len] = '\0'; + memcpy(pr->hostname, pr->socksbuf + 8, len); goto connect; } else { /* @@ -151,21 +171,21 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * the IP address into the hostname string and * then just go. */ - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } } if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) && - pr->hostname[0] == 5) { + pr->socksbuf[0] == 5) { /* * SOCKS 5. */ @@ -178,12 +198,13 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) /* * We're receiving a set of method identifiers. */ - if (pr->port < 2) continue;/* no method count yet */ - if (pr->port < 2 + (unsigned char)pr->hostname[1]) + if (pr->sockslen < 2) + continue; /* no method count yet */ + if (pr->sockslen < 2 + (unsigned char)pr->socksbuf[1]) continue; /* no methods yet */ method = 0xFF; /* invalid */ - for (i = 0; i < (unsigned char)pr->hostname[1]; i++) - if (pr->hostname[2+i] == 0) { + for (i = 0; i < (unsigned char)pr->socksbuf[1]; i++) + if (pr->socksbuf[2+i] == 0) { method = 0;/* no auth */ break; } @@ -191,7 +212,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) data[1] = method; sk_write(pr->s, data, 2); pr->dynamic = 0x5001; - pr->port = 0; /* re-empty the buffer */ + pr->sockslen = 0; /* re-empty the buffer */ continue; } @@ -213,16 +234,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) reply[0] = 5; /* VER */ reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */ - if (pr->port < 6) continue; - atype = (unsigned char)pr->hostname[3]; + if (pr->sockslen < 6) continue; + atype = (unsigned char)pr->socksbuf[3]; if (atype == 1) /* IPv4 address */ alen = 4; if (atype == 4) /* IPv6 address */ alen = 16; if (atype == 3) /* domain name has leading length */ - alen = 1 + (unsigned char)pr->hostname[4]; - if (pr->port < 6 + alen) continue; - if (pr->hostname[1] != 1 || pr->hostname[2] != 0) { + alen = 1 + (unsigned char)pr->socksbuf[4]; + if (pr->sockslen < 6 + alen) continue; + if (pr->socksbuf[1] != 1 || pr->socksbuf[2] != 0) { /* Not CONNECT or reserved field nonzero - error */ reply[1] = 1; /* generic failure */ sk_write(pr->s, (char *) reply, lenof(reply)); @@ -233,21 +254,22 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * Now we have a viable connect request. Switch * on atype. */ - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen); + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+4+alen); if (atype == 1) { /* REP=0 (success) already */ sk_write(pr->s, (char *) reply, lenof(reply)); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } else if (atype == 3) { /* REP=0 (success) already */ sk_write(pr->s, (char *) reply, lenof(reply)); - memmove(pr->hostname, pr->hostname + 5, alen-1); + pr->hostname = snewn(alen, char); pr->hostname[alen-1] = '\0'; + memcpy(pr->hostname, pr->socksbuf + 5, alen-1); goto connect; } else { /* @@ -277,6 +299,8 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * connection. */ connect: + sfree(pr->socksbuf); + pr->socksbuf = NULL; /* * Freeze the socket until the SSH server confirms the @@ -325,7 +349,7 @@ static void pfd_sent(Plug plug, int bufsize) * Called when receiving a PORT OPEN from the server */ const char *pfd_newconnect(Socket *s, char *hostname, int port, - void *c, const Config *cfg, int addressfamily) + void *c, Conf *conf, int addressfamily) { static const struct plug_function_table fn_table = { pfd_log, @@ -343,17 +367,17 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port, /* * Try to find host. */ - addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily); + addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); + sfree(dummy_realhost); return err; } /* * Open socket. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->throttled = pr->throttle_override = 0; pr->ready = 1; @@ -362,9 +386,10 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port, pr->dynamic = 0; pr->s = *s = new_connection(addr, dummy_realhost, port, - 0, 1, 0, 0, (Plug) pr, cfg); + 0, 1, 0, 0, (Plug) pr, conf); + sfree(dummy_realhost); if ((err = sk_socket_error(*s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -390,8 +415,7 @@ static int pfd_accepting(Plug p, OSSocket sock) const char *err; org = (struct PFwdPrivate *)p; - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; @@ -399,7 +423,7 @@ static int pfd_accepting(Plug p, OSSocket sock) pr->s = s = sk_register(sock, (Plug) pr); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err != NULL; } @@ -414,12 +438,12 @@ static int pfd_accepting(Plug p, OSSocket sock) sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ } else { pr->dynamic = 0; - strcpy(pr->hostname, org->hostname); + pr->hostname = dupstr(org->hostname); pr->port = org->port; pr->c = new_sock_channel(org->backhandle, s); if (pr->c == NULL) { - sfree(pr); + free_portfwd_private(pr); return 1; } else { /* asks to forward to the specified host/port for this */ @@ -435,7 +459,7 @@ static int pfd_accepting(Plug p, OSSocket sock) sets up a listener on the local machine on (srcaddr:)port */ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, - int port, void *backhandle, const Config *cfg, + int port, void *backhandle, Conf *conf, void **sockdata, int address_family) { static const struct plug_function_table fn_table = { @@ -453,12 +477,11 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, /* * Open socket. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; if (desthost) { - strcpy(pr->hostname, desthost); + pr->hostname = dupstr(desthost); pr->port = destport; pr->dynamic = 0; } else @@ -468,9 +491,10 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, pr->backhandle = backhandle; pr->s = s = new_listener(srcaddr, port, (Plug) pr, - !cfg->lport_acceptall, cfg, address_family); + !conf_get_int(conf, CONF_lport_acceptall), + conf, address_family); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -490,8 +514,7 @@ void pfd_close(Socket s) pr = (struct PFwdPrivate *) sk_get_private_ptr(s); - sfree(pr->buffer); - sfree(pr); + free_portfwd_private(pr); sk_close(s); } @@ -536,6 +559,10 @@ int pfd_send(Socket s, char *data, int len) return sk_write(s, data, len); } +void pfd_send_eof(Socket s) +{ + sk_write_eof(s); +} void pfd_confirm(Socket s) { diff --git a/contrib/putty/PPROXY.C b/contrib/putty/PPROXY.C index 5ab31b2..e2954a2 100644 --- a/contrib/putty/PPROXY.C +++ b/contrib/putty/PPROXY.C @@ -11,7 +11,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { return NULL; } diff --git a/contrib/putty/PROXY.C b/contrib/putty/PROXY.C index 1f42999..a48a73b 100644 --- a/contrib/putty/PROXY.C +++ b/contrib/putty/PROXY.C @@ -14,9 +14,10 @@ #include "network.h" #include "proxy.h" -#define do_proxy_dns(cfg) \ - (cfg->proxy_dns == FORCE_ON || \ - (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4)) +#define do_proxy_dns(conf) \ + (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \ + (conf_get_int(conf, CONF_proxy_dns) == AUTO && \ + conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4)) /* * Call this when proxy negotiation is complete, so that this @@ -64,6 +65,9 @@ void proxy_activate (Proxy_Socket p) */ if (p->pending_flush) sk_flush(p->sub_socket); + /* if we have a pending EOF to send, send it */ + if (p->pending_eof) sk_write_eof(p->sub_socket); + /* if the backend wanted the socket unfrozen, try to unfreeze. * our set_frozen handler will flush buffered receive data before * unfreezing the actual underlying socket. @@ -116,6 +120,17 @@ static int sk_proxy_write_oob (Socket s, const char *data, int len) return sk_write_oob(ps->sub_socket, data, len); } +static void sk_proxy_write_eof (Socket s) +{ + Proxy_Socket ps = (Proxy_Socket) s; + + if (ps->state != PROXY_STATE_ACTIVE) { + ps->pending_eof = 1; + return; + } + sk_write_eof(ps->sub_socket); +} + static void sk_proxy_flush (Socket s) { Proxy_Socket ps = (Proxy_Socket) s; @@ -262,8 +277,8 @@ static int plug_proxy_accepting (Plug p, OSSocket sock) * This function can accept a NULL pointer as `addr', in which case * it will only check the host name. */ -static int proxy_for_destination (SockAddr addr, char *hostname, int port, - const Config *cfg) +static int proxy_for_destination (SockAddr addr, const char *hostname, + int port, Conf *conf) { int s = 0, e = 0; char hostip[64]; @@ -271,10 +286,19 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, const char *exclude_list; /* + * Special local connections such as Unix-domain sockets + * unconditionally cannot be proxied, even in proxy-localhost + * mode. There just isn't any way to ask any known proxy type for + * them. + */ + if (addr && sk_address_is_special_local(addr)) + return 0; /* do not proxy */ + + /* * Check the host name and IP against the hard-coded * representations of `localhost'. */ - if (!cfg->even_proxy_localhost && + if (!conf_get_int(conf, CONF_even_proxy_localhost) && (sk_hostname_is_local(hostname) || (addr && sk_address_is_local(addr)))) return 0; /* do not proxy */ @@ -288,7 +312,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, hostname_len = strlen(hostname); - exclude_list = cfg->proxy_exclude_list; + exclude_list = conf_get_str(conf, CONF_proxy_exclude_list); /* now parse the exclude list, and see if either our IP * or hostname matches anything in it. @@ -349,11 +373,11 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, } SockAddr name_lookup(char *host, int port, char **canonicalname, - const Config *cfg, int addressfamily) + Conf *conf, int addressfamily) { - if (cfg->proxy_type != PROXY_NONE && - do_proxy_dns(cfg) && - proxy_for_destination(NULL, host, port, cfg)) { + if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && + do_proxy_dns(conf) && + proxy_for_destination(NULL, host, port, conf)) { *canonicalname = dupstr(host); return sk_nonamelookup(host); } @@ -364,13 +388,14 @@ SockAddr name_lookup(char *host, int port, char **canonicalname, Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { static const struct socket_function_table socket_fn_table = { sk_proxy_plug, sk_proxy_close, sk_proxy_write, sk_proxy_write_oob, + sk_proxy_write_eof, sk_proxy_flush, sk_proxy_set_private_ptr, sk_proxy_get_private_ptr, @@ -386,30 +411,32 @@ Socket new_connection(SockAddr addr, char *hostname, plug_proxy_accepting }; - if (cfg->proxy_type != PROXY_NONE && - proxy_for_destination(addr, hostname, port, cfg)) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && + proxy_for_destination(addr, hostname, port, conf)) { Proxy_Socket ret; Proxy_Plug pplug; SockAddr proxy_addr; char *proxy_canonical_name; Socket sret; + int type; if ((sret = platform_new_connection(addr, hostname, port, privport, oobinline, nodelay, keepalive, - plug, cfg)) != + plug, conf)) != NULL) return sret; ret = snew(struct Socket_proxy_tag); ret->fn = &socket_fn_table; - ret->cfg = *cfg; /* STRUCTURE COPY */ + ret->conf = conf_copy(conf); ret->plug = plug; ret->remote_addr = addr; /* will need to be freed on close */ ret->remote_port = port; ret->error = NULL; ret->pending_flush = 0; + ret->pending_eof = 0; ret->freeze = 0; bufchain_init(&ret->pending_input_data); @@ -419,14 +446,15 @@ Socket new_connection(SockAddr addr, char *hostname, ret->sub_socket = NULL; ret->state = PROXY_STATE_NEW; ret->negotiate = NULL; - - if (cfg->proxy_type == PROXY_HTTP) { + + type = conf_get_int(conf, CONF_proxy_type); + if (type == PROXY_HTTP) { ret->negotiate = proxy_http_negotiate; - } else if (cfg->proxy_type == PROXY_SOCKS4) { + } else if (type == PROXY_SOCKS4) { ret->negotiate = proxy_socks4_negotiate; - } else if (cfg->proxy_type == PROXY_SOCKS5) { + } else if (type == PROXY_SOCKS5) { ret->negotiate = proxy_socks5_negotiate; - } else if (cfg->proxy_type == PROXY_TELNET) { + } else if (type == PROXY_TELNET) { ret->negotiate = proxy_telnet_negotiate; } else { ret->error = "Proxy error: Unknown proxy method"; @@ -440,10 +468,13 @@ Socket new_connection(SockAddr addr, char *hostname, pplug->proxy_socket = ret; /* look-up proxy */ - proxy_addr = sk_namelookup(cfg->proxy_host, - &proxy_canonical_name, cfg->addressfamily); + proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host), + &proxy_canonical_name, + conf_get_int(conf, CONF_addressfamily)); if (sk_addr_error(proxy_addr) != NULL) { ret->error = "Proxy error: Unable to resolve proxy host name"; + sfree(pplug); + sk_addr_free(proxy_addr); return (Socket)ret; } sfree(proxy_canonical_name); @@ -451,7 +482,8 @@ Socket new_connection(SockAddr addr, char *hostname, /* create the actual socket we will be using, * connected to our proxy server and port. */ - ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port, + ret->sub_socket = sk_new(proxy_addr, + conf_get_int(conf, CONF_proxy_port), privport, oobinline, nodelay, keepalive, (Plug) pplug); if (sk_socket_error(ret->sub_socket) != NULL) @@ -469,7 +501,7 @@ Socket new_connection(SockAddr addr, char *hostname, } Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, - const Config *cfg, int addressfamily) + Conf *conf, int addressfamily) { /* TODO: SOCKS (and potentially others) support inbound * TODO: connections via the proxy. support them. @@ -525,6 +557,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change) * request */ char *buf, dest[512]; + char *username, *password; sk_getaddr(p->remote_addr, dest, lenof(dest)); @@ -533,18 +566,22 @@ int proxy_http_negotiate (Proxy_Socket p, int change) sk_write(p->sub_socket, buf, strlen(buf)); sfree(buf); - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { - char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)]; - char buf2[sizeof(buf)*4/3 + 100]; + username = conf_get_str(p->conf, CONF_proxy_username); + password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { + char *buf, *buf2; int i, j, len; - sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password); + buf = dupprintf("%s:%s", username, password); len = strlen(buf); + buf2 = snewn(len * 4 / 3 + 100, char); sprintf(buf2, "Proxy-Authorization: Basic "); for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4) base64_encode_atom((unsigned char *)(buf+i), (len-i > 3 ? 3 : len-i), buf2+j); strcpy(buf2+j, "\r\n"); sk_write(p->sub_socket, buf2, strlen(buf2)); + sfree(buf); + sfree(buf2); } sk_write(p->sub_socket, "\r\n", 2); @@ -711,11 +748,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) int length, type, namelen; char *command, addr[4], hostname[512]; + char *username; type = sk_addrtype(p->remote_addr); if (type == ADDRTYPE_IPV6) { - plug_closing(p->plug, "Proxy error: SOCKS version 4 does" - " not support IPv6", PROXY_ERROR_GENERAL, 0); + p->error = "Proxy error: SOCKS version 4 does not support IPv6"; return 1; } else if (type == ADDRTYPE_IPV4) { namelen = 0; @@ -728,9 +765,10 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) addr[3] = 1; } - length = strlen(p->cfg.proxy_username) + namelen + 9; + username = conf_get_str(p->conf, CONF_proxy_username); + length = strlen(username) + namelen + 9; command = snewn(length, char); - strcpy(command + 8, p->cfg.proxy_username); + strcpy(command + 8, username); command[0] = 4; /* version 4 */ command[1] = 1; /* CONNECT command */ @@ -743,10 +781,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) memcpy(command + 4, addr, 4); /* hostname */ - memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1, + memcpy(command + 8 + strlen(username) + 1, hostname, namelen); sk_write(p->sub_socket, command, length); + sfree(username); sfree(command); p->state = 1; @@ -868,10 +907,13 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) */ char command[5]; + char *username, *password; int len; command[0] = 5; /* version 5 */ - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { + username = conf_get_str(p->conf, CONF_proxy_username); + password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { command[2] = 0x00; /* no authentication */ len = 3; proxy_socks5_offerencryptedauth (command, &len); @@ -1148,18 +1190,20 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) } if (p->state == 5) { - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { - char userpwbuf[514]; + char *username = conf_get_str(p->conf, CONF_proxy_username); + char *password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { + char userpwbuf[255 + 255 + 3]; int ulen, plen; - ulen = strlen(p->cfg.proxy_username); + ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; - plen = strlen(p->cfg.proxy_password); + plen = strlen(password); if (plen > 255) plen = 255; if (plen < 1) plen = 1; userpwbuf[0] = 1; /* version number of subnegotiation */ userpwbuf[1] = ulen; - memcpy(userpwbuf+2, p->cfg.proxy_username, ulen); + memcpy(userpwbuf+2, username, ulen); userpwbuf[ulen+2] = plen; - memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen); + memcpy(userpwbuf+ulen+3, password, plen); sk_write(p->sub_socket, userpwbuf, ulen + plen + 3); p->state = 7; } else @@ -1192,8 +1236,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) * standardised or at all well-defined.) */ -char *format_telnet_command(SockAddr addr, int port, const Config *cfg) +char *format_telnet_command(SockAddr addr, int port, Conf *conf) { + char *fmt = conf_get_str(conf, CONF_proxy_telnet_command); char *ret = NULL; int retlen = 0, retsize = 0; int so = 0, eo = 0; @@ -1208,22 +1253,21 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) * %%, %host, %port, %user, and %pass */ - while (cfg->proxy_telnet_command[eo] != 0) { + while (fmt[eo] != 0) { /* scan forward until we hit end-of-line, * or an escape character (\ or %) */ - while (cfg->proxy_telnet_command[eo] != 0 && - cfg->proxy_telnet_command[eo] != '%' && - cfg->proxy_telnet_command[eo] != '\\') eo++; + while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\') + eo++; /* if we hit eol, break out of our escaping loop */ - if (cfg->proxy_telnet_command[eo] == 0) break; + if (fmt[eo] == 0) break; /* if there was any unescaped text before the escape * character, send that now */ if (eo != so) { ENSURE(eo - so); - memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } @@ -1231,15 +1275,15 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) /* if the escape character was the last character of * the line, we'll just stop and send it. */ - if (cfg->proxy_telnet_command[eo] == 0) break; + if (fmt[eo] == 0) break; - if (cfg->proxy_telnet_command[so] == '\\') { + if (fmt[so] == '\\') { /* we recognize \\, \%, \r, \n, \t, \x??. * anything else, we just send unescaped (including the \). */ - switch (cfg->proxy_telnet_command[eo]) { + switch (fmt[eo]) { case '\\': ENSURE(1); @@ -1280,15 +1324,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) for (;;) { eo++; - if (cfg->proxy_telnet_command[eo] >= '0' && - cfg->proxy_telnet_command[eo] <= '9') - v += cfg->proxy_telnet_command[eo] - '0'; - else if (cfg->proxy_telnet_command[eo] >= 'a' && - cfg->proxy_telnet_command[eo] <= 'f') - v += cfg->proxy_telnet_command[eo] - 'a' + 10; - else if (cfg->proxy_telnet_command[eo] >= 'A' && - cfg->proxy_telnet_command[eo] <= 'F') - v += cfg->proxy_telnet_command[eo] - 'A' + 10; + if (fmt[eo] >= '0' && fmt[eo] <= '9') + v += fmt[eo] - '0'; + else if (fmt[eo] >= 'a' && fmt[eo] <= 'f') + v += fmt[eo] - 'a' + 10; + else if (fmt[eo] >= 'A' && fmt[eo] <= 'F') + v += fmt[eo] - 'A' + 10; else { /* non hex character, so we abort and just * send the whole thing unescaped (including \x) @@ -1315,7 +1356,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) default: ENSURE(2); - memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2); + memcpy(ret+retlen, fmt + so, 2); retlen += 2; eo++; break; @@ -1327,13 +1368,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) * unescaped (including the %). */ - if (cfg->proxy_telnet_command[eo] == '%') { + if (fmt[eo] == '%') { ENSURE(1); ret[retlen++] = '%'; eo++; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "host", 4) == 0) { + else if (strnicmp(fmt + eo, "host", 4) == 0) { char dest[512]; int destlen; sk_getaddr(addr, dest, lenof(dest)); @@ -1343,8 +1383,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) retlen += destlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "port", 4) == 0) { + else if (strnicmp(fmt + eo, "port", 4) == 0) { char portstr[8], portlen; portlen = sprintf(portstr, "%i", port); ENSURE(portlen); @@ -1352,35 +1391,35 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) retlen += portlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "user", 4) == 0) { - int userlen = strlen(cfg->proxy_username); + else if (strnicmp(fmt + eo, "user", 4) == 0) { + char *username = conf_get_str(conf, CONF_proxy_username); + int userlen = strlen(username); ENSURE(userlen); - memcpy(ret+retlen, cfg->proxy_username, userlen); + memcpy(ret+retlen, username, userlen); retlen += userlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "pass", 4) == 0) { - int passlen = strlen(cfg->proxy_password); + else if (strnicmp(fmt + eo, "pass", 4) == 0) { + char *password = conf_get_str(conf, CONF_proxy_password); + int passlen = strlen(password); ENSURE(passlen); - memcpy(ret+retlen, cfg->proxy_password, passlen); + memcpy(ret+retlen, password, passlen); retlen += passlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "proxyhost", 9) == 0) { - int phlen = strlen(cfg->proxy_host); + else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) { + char *host = conf_get_str(conf, CONF_proxy_host); + int phlen = strlen(host); ENSURE(phlen); - memcpy(ret+retlen, cfg->proxy_host, phlen); + memcpy(ret+retlen, host, phlen); retlen += phlen; eo += 9; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "proxyport", 9) == 0) { + else if (strnicmp(fmt + eo, "proxyport", 9) == 0) { + int port = conf_get_int(conf, CONF_proxy_port); char pport[50]; int pplen; - sprintf(pport, "%d", cfg->proxy_port); + sprintf(pport, "%d", port); pplen = strlen(pport); ENSURE(pplen); memcpy(ret+retlen, pport, pplen); @@ -1404,7 +1443,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) /* if there is any unescaped text at the end of the line, send it */ if (eo != so) { ENSURE(eo - so); - memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } @@ -1421,7 +1460,7 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change) char *formatted_cmd; formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port, - &p->cfg); + p->conf); sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd)); sfree(formatted_cmd); diff --git a/contrib/putty/PROXY.H b/contrib/putty/PROXY.H index 683b260..9e64aad 100644 --- a/contrib/putty/PROXY.H +++ b/contrib/putty/PROXY.H @@ -30,6 +30,7 @@ struct Socket_proxy_tag { bufchain pending_oob_output_data; int pending_flush; bufchain pending_input_data; + int pending_eof; #define PROXY_STATE_NEW -1 #define PROXY_STATE_ACTIVE 0 @@ -80,7 +81,7 @@ struct Socket_proxy_tag { OSSocket accepting_sock; /* configuration, used to look up proxy settings */ - Config cfg; + Conf *conf; /* CHAP transient data */ int chap_num_attributes; @@ -110,7 +111,7 @@ extern int proxy_socks5_negotiate (Proxy_Socket, int); * This may be reused by local-command proxies on individual * platforms. */ -char *format_telnet_command(SockAddr addr, int port, const Config *cfg); +char *format_telnet_command(SockAddr addr, int port, Conf *conf); /* * These are implemented in cproxy.c or nocproxy.c, depending on diff --git a/contrib/putty/PSCP.C b/contrib/putty/PSCP.C index 14fff5c..66275c7 100644 --- a/contrib/putty/PSCP.C +++ b/contrib/putty/PSCP.C @@ -44,7 +44,8 @@ static int using_sftp = 0; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; +int sent_eof = FALSE; static void source(char *src); static void rsource(char *src); @@ -128,6 +129,19 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +void nonfatal(char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + tell_str(stderr, str2); + sfree(str2); + errs++; +} void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; @@ -214,6 +228,19 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. + */ + if (!sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from server"); + } + return FALSE; +} static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; @@ -298,6 +325,7 @@ static void bump(char *fmt, ...) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } @@ -305,6 +333,29 @@ static void bump(char *fmt, ...) } /* + * Wait for the reply to a single SFTP request. Parallels the same + * function in psftp.c (but isn't centralised into sftp.c because the + * latter module handles SFTP only and shouldn't assume that SFTP is + * the only thing going on by calling connection_fatal). + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} + +/* * Open an SSH connection to user@host and execute cmd. */ static void do_cmd(char *host, char *user, char *cmd) @@ -333,88 +384,90 @@ static void do_cmd(char *host, char *user, char *cmd) */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ - Config cfg2; - cfg2.host[0] = '\0'; - do_defaults(host, &cfg2); - if (cfg2.host[0] != '\0') { + Conf *conf2 = conf_new(); + conf_set_str(conf2, CONF_host, ""); + do_defaults(host, conf2); + if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ - do_defaults(host, &cfg); + do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } } else { /* Patch in hostname `host' to session details. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ - if (cfg.protocol != PROT_SSH) { - cfg.protocol = PROT_SSH; - cfg.port = 22; + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); } /* * Enact command-line overrides. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; + + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } - } - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; } - cfg.host[p1] = '\0'; + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; - } else if (cfg.username[0] == '\0') { + conf_set_str(conf, CONF_username, user); + } else if (conf_get_str(conf, CONF_username)[0] == '\0') { user = get_username(); if (!user) bump("Empty user name"); else { if (verbose) tell_user(stderr, "Guessing user name: %s", user); - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); sfree(user); } } @@ -424,10 +477,14 @@ static void do_cmd(char *host, char *user, char *cmd) * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ - cfg.x11_forward = 0; - cfg.agentfwd = 0; - cfg.portfwd[0] = cfg.portfwd[1] = '\0'; - cfg.ssh_simple = TRUE; + conf_set_int(conf, CONF_x11_forward, 0); + conf_set_int(conf, CONF_agentfwd, 0); + conf_set_int(conf, CONF_ssh_simple, TRUE); + { + char *key; + while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) + conf_del_str_str(conf, CONF_portfwd, key); + } /* * Set up main and possibly fallback command depending on @@ -435,49 +492,54 @@ static void do_cmd(char *host, char *user, char *cmd) * Attempt to start the SFTP subsystem as a first choice, * falling back to the provided scp command if that fails. */ - cfg.remote_cmd_ptr2 = NULL; + conf_set_str(conf, CONF_remote_cmd2, ""); if (try_sftp) { /* First choice is SFTP subsystem. */ main_cmd_is_sftp = 1; - strcpy(cfg.remote_cmd, "sftp"); - cfg.ssh_subsys = TRUE; + conf_set_str(conf, CONF_remote_cmd, "sftp"); + conf_set_int(conf, CONF_ssh_subsys, TRUE); if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; - cfg.remote_cmd_ptr2 = cmd; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, cmd); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); } else { /* Since we're not going to try SCP, we may as well try * harder to find an SFTP server, since in the current * implementation we have a spare slot. */ fallback_cmd_is_sftp = 1; /* see psftp.c for full explanation of this kludge */ - cfg.remote_cmd_ptr2 = - "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" - "exec sftp-server"; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, + "test -x /usr/lib/sftp-server &&" + " exec /usr/lib/sftp-server\n" + "test -x /usr/local/lib/sftp-server &&" + " exec /usr/local/lib/sftp-server\n" + "exec sftp-server"); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); } } else { /* Don't try SFTP at all; just try the scp command. */ main_cmd_is_sftp = 0; - cfg.remote_cmd_ptr = cmd; - cfg.ssh_subsys = FALSE; + conf_set_str(conf, CONF_remote_cmd, cmd); + conf_set_int(conf, CONF_ssh_subsys, FALSE); } - cfg.nopty = TRUE; + conf_set_int(conf, CONF_nopty, TRUE); back = &ssh_backend; - err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, - 0, cfg.tcp_keepalives); + err = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, 0, + conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); ssh_scp_init(); if (verbose && realhost != NULL && errs == 0) - tell_user(stderr, "Connected to %s\n", realhost); + tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } @@ -626,7 +688,7 @@ static int response(void) } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) - tell_user(stderr, "%s\n", rbuf); + tell_user(stderr, "%s", rbuf); else bump("%s", rbuf); errs++; @@ -659,7 +721,7 @@ void scp_sftp_listdir(char *dirname) struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int nnames, namesize; int i; @@ -671,10 +733,9 @@ void scp_sftp_listdir(char *dirname) printf("Listing directory %s\n", dirname); - sftp_register(req = fxp_opendir_send(dirname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dirname); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dirname, fxp_error()); @@ -684,10 +745,9 @@ void scp_sftp_listdir(char *dirname) while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -710,22 +770,24 @@ void scp_sftp_listdir(char *dirname) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); + + sfree(ournames); } } @@ -760,7 +822,7 @@ int scp_source_setup(char *target, int shouldbedir) * directory. */ struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int ret; @@ -770,10 +832,9 @@ int scp_source_setup(char *target, int shouldbedir) return 1; } - sftp_register(req = fxp_stat_send(target)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(target); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; @@ -819,12 +880,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime) } } -int scp_send_filename(char *name, uint64 size, int modes) +int scp_send_filename(char *name, uint64 size, int permissions) { if (using_sftp) { char *fullname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; + struct fxp_attrs attrs; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); @@ -832,15 +894,19 @@ int scp_send_filename(char *name, uint64 size, int modes) fullname = dupstr(scp_sftp_remotepath); } - sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); + + req = fxp_open_send(fullname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); + sfree(fullname); errs++; return 1; } @@ -853,7 +919,9 @@ int scp_send_filename(char *name, uint64 size, int modes) char buf[40]; char sizestr[40]; uint64_decimal(size, sizestr); - sprintf(buf, "C%04o %s ", modes, sizestr); + if (permissions < 0) + permissions = 0644; + sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); @@ -874,8 +942,10 @@ int scp_send_filedata(char *data, int len) while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); - if (!ret) { - tell_user(stderr, "error while writing: %s\n", fxp_error()); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return 1; } @@ -909,12 +979,19 @@ int scp_send_finish(void) if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); - xfer_upload_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return 1; + } } xfer_cleanup(scp_sftp_xfer); @@ -925,19 +1002,17 @@ int scp_send_finish(void) attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; - sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fsetstat_recv(pktin, rreq); + req = fxp_fsetstat_send(scp_sftp_filehandle, attrs); + pktin = sftp_wait_for_reply(req); + ret = fxp_fsetstat_recv(pktin, req); if (!ret) { - tell_user(stderr, "unable to set file times: %s\n", fxp_error()); + tell_user(stderr, "unable to set file times: %s", fxp_error()); errs++; } } - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); scp_has_times = 0; return 0; } else { @@ -967,7 +1042,7 @@ int scp_send_dirname(char *name, int modes) char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (scp_sftp_targetisdir) { @@ -983,25 +1058,24 @@ int scp_send_dirname(char *name, int modes) * exists and is a directory we will assume we were either * successful or it didn't matter. */ - sftp_register(req = fxp_mkdir_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_mkdir_recv(pktin, req); if (!ret) err = fxp_error(); else err = "server reported no error"; - sftp_register(req = fxp_stat_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); + sfree(fullname); errs++; return 1; } @@ -1057,6 +1131,9 @@ int scp_sink_setup(char *source, int preserve, int recursive) if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; + + sfree(newsource); + dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); @@ -1133,7 +1210,7 @@ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ - int mode; /* access mode (not ENDDIR) */ + long permissions; /* access permissions (not ENDDIR) */ uint64 size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */ @@ -1146,7 +1223,7 @@ int scp_get_sink_action(struct scp_sink_action *act) int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (!scp_sftp_dirstack_head) { @@ -1215,14 +1292,14 @@ int scp_get_sink_action(struct scp_sink_action *act) * Now we have a filename. Stat it, and see if it's a file * or a directory. */ - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { tell_user(stderr, "unable to identify %s: %s", fname, ret ? "file type not supplied" : fxp_error()); + if (must_free_fname) sfree(fname); errs++; return 1; } @@ -1271,13 +1348,12 @@ int scp_get_sink_action(struct scp_sink_action *act) * list), we must push the other (target,namelist) pair * on a stack. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { - tell_user(stderr, "scp: unable to open directory %s: %s", + tell_user(stderr, "pscp: unable to open directory %s: %s", fname, fxp_error()); if (must_free_fname) sfree(fname); errs++; @@ -1288,16 +1364,20 @@ int scp_get_sink_action(struct scp_sink_action *act) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; - tell_user(stderr, "scp: reading directory %s: %s\n", + tell_user(stderr, "pscp: reading directory %s: %s", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + if (must_free_fname) sfree(fname); sfree(ournames); errs++; @@ -1321,7 +1401,7 @@ int scp_get_sink_action(struct scp_sink_action *act) */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" - "supplied filename '%s'\n", + "supplied filename '%s'", names->names[i].filename); } else ournames[nnames++] = names->names[i]; @@ -1329,10 +1409,9 @@ int scp_get_sink_action(struct scp_sink_action *act) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); newitem = snew(struct scp_sftp_dirstack); newitem->next = scp_sftp_dirstack_head; @@ -1359,7 +1438,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1381,7 +1460,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1425,7 +1504,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ - tell_user(stderr, "%s\n", act->buf); + tell_user(stderr, "%s", act->buf); errs++; continue; /* go round again */ case '\02': /* fatal error */ @@ -1463,7 +1542,8 @@ int scp_get_sink_action(struct scp_sink_action *act) { char sizestr[40]; - if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2) + if (sscanf(act->buf, "%lo %s %n", &act->permissions, + sizestr, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->size = uint64_from_decimal(sizestr); act->name = act->buf + i; @@ -1476,12 +1556,11 @@ int scp_accept_filexfer(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", @@ -1510,9 +1589,10 @@ int scp_recv_filedata(char *data, int len) xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return -1; } @@ -1542,7 +1622,7 @@ int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Ensure that xfer_done() will work correctly, so we can @@ -1552,19 +1632,25 @@ int scp_finish_filerecv(void) xfer_set_error(scp_sftp_xfer); while (!xfer_done(scp_sftp_xfer)) { void *vbuf; - int len; + int ret, len; pktin = sftp_recv(); - xfer_download_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return -1; + } if (xfer_download_data(scp_sftp_xfer, &vbuf, &len)) sfree(vbuf); } xfer_cleanup(scp_sftp_xfer); - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } else { back->send(backhandle, "", 1); @@ -1583,7 +1669,7 @@ static void run_err(const char *fmt, ...) va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); - str2 = dupcat("scp: ", str, "\n", NULL); + str2 = dupcat("pscp: ", str, "\n", NULL); sfree(str); scp_send_errmsg(str2); tell_user(stderr, "%s", str2); @@ -1598,6 +1684,7 @@ static void source(char *src) { uint64 size; unsigned long mtime, atime; + long permissions; char *last; RFile *f; int attr; @@ -1645,14 +1732,16 @@ static void source(char *src) if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; - f = open_existing_file(src, &size, &mtime, &atime); + f = open_existing_file(src, &size, &mtime, &atime, &permissions); if (f == NULL) { run_err("%s: Cannot open file", src); return; } if (preserve) { - if (scp_send_filetimes(mtime, atime)) + if (scp_send_filetimes(mtime, atime)) { + close_rfile(f); return; + } } if (verbose) { @@ -1660,8 +1749,10 @@ static void source(char *src) uint64_decimal(size, sizestr); tell_user(stderr, "Sending file %s, size=%s", last, sizestr); } - if (scp_send_filename(last, size, 0644)) + if (scp_send_filename(last, size, permissions)) { + close_rfile(f); return; + } stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); @@ -1864,27 +1955,34 @@ static void sink(char *targ, char *src) if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); + sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); + sfree(destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ + sfree(destfname); continue; } - f = open_new_file(destfname); + f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); + sfree(destfname); continue; } - if (scp_accept_filexfer()) + if (scp_accept_filexfer()) { + sfree(destfname); + close_wfile(f); return; + } stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); @@ -1931,6 +2029,7 @@ static void sink(char *targ, char *src) close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); + sfree(destfname); continue; } (void) scp_finish_filerecv(); @@ -2221,14 +2320,15 @@ int psftp_main(int argc, char *argv[]) sk_init(); /* Load Default Settings before doing anything else. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; - ret = cmdline_process_param(argv[i], i+1connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed(); diff --git a/contrib/putty/PSFTP.C b/contrib/putty/PSFTP.C index 3583fd7..d8b87af 100644 --- a/contrib/putty/PSFTP.C +++ b/contrib/putty/PSFTP.C @@ -36,7 +36,28 @@ void do_sftp_cleanup(); char *pwd, *homedir; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; +int sent_eof = FALSE; + +/* ---------------------------------------------------------------------- + * Manage sending requests and waiting for replies. + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} /* ---------------------------------------------------------------------- * Higher-level helper functions used in commands. @@ -51,7 +72,7 @@ char *canonify(char *name) { char *fullname, *canonname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; if (name[0] == '/') { fullname = dupstr(name); @@ -64,10 +85,9 @@ char *canonify(char *name) fullname = dupcat(pwd, slash, name, NULL); } - sftp_register(req = fxp_realpath_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - canonname = fxp_realpath_recv(pktin, rreq); + req = fxp_realpath_send(fullname); + pktin = sftp_wait_for_reply(req); + canonname = fxp_realpath_recv(pktin, req); if (canonname) { sfree(fullname); @@ -122,13 +142,12 @@ char *canonify(char *name) */ fullname[i] = '\0'; /* separate the string */ if (i == 0) { - sftp_register(req = fxp_realpath_send("/")); + req = fxp_realpath_send("/"); } else { - sftp_register(req = fxp_realpath_send(fullname)); + req = fxp_realpath_send(fullname); } - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - canonname = fxp_realpath_recv(pktin, rreq); + pktin = sftp_wait_for_reply(req); + canonname = fxp_realpath_recv(pktin, req); if (!canonname) { /* Even that failed. Restore our best guess at the @@ -207,11 +226,12 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) { struct fxp_handle *fh; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_xfer *xfer; uint64 offset; WFile *file; int ret, shown_err = FALSE; + struct fxp_attrs attrs; /* * In recursive mode, see if we're dealing with a directory. @@ -219,13 +239,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * subsequent FXP_OPEN will return a usable error message.) */ if (recurse) { - struct fxp_attrs attrs; int result; - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && @@ -251,10 +269,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * Now get the list of filenames in the remote * directory. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { printf("%s: unable to open directory: %s\n", @@ -266,15 +283,19 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("%s: reading directory: %s\n", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + sfree(ournames); return 0; } @@ -300,10 +321,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) } fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Sort the names into a clear order. This ought to @@ -312,7 +332,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * readdirs on the same remote directory return a * different order. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * If we're in restart mode, find the last filename on @@ -327,11 +348,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) while (i < nnames) { char *nextoutfname; int ret; - if (outfname) - nextoutfname = dir_file_cat(outfname, - ournames[i]->filename); - else - nextoutfname = dupstr(ournames[i]->filename); + nextoutfname = dir_file_cat(outfname, + ournames[i]->filename); ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); sfree(nextoutfname); if (ret) @@ -353,11 +371,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) int ret; nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); - if (outfname) - nextoutfname = dir_file_cat(outfname, - ournames[i]->filename); - else - nextoutfname = dupstr(ournames[i]->filename); + nextoutfname = dir_file_cat(outfname, ournames[i]->filename); ret = sftp_get_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); @@ -383,10 +397,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) } } - sftp_register(req = fxp_open_send(fname, SSH_FXF_READ)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fh = fxp_open_recv(pktin, rreq); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + if (!fxp_stat_recv(pktin, req, &attrs)) + attrs.flags = 0; + + req = fxp_open_send(fname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + fh = fxp_open_recv(pktin, req); if (!fh) { printf("%s: open for read: %s\n", fname, fxp_error()); @@ -396,16 +414,15 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) if (restart) { file = open_existing_wfile(outfname, NULL); } else { - file = open_new_file(outfname); + file = open_new_file(outfname, GET_PERMISSIONS(attrs)); } if (!file) { printf("local: unable to open %s\n", outfname); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } @@ -416,10 +433,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) close_wfile(file); printf("reget: cannot restart %s - file too large\n", outfname); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } @@ -447,12 +463,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) xfer_download_queue(xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { if (!shown_err) { printf("error while reading: %s\n", fxp_error()); shown_err = TRUE; } + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); ret = 0; } @@ -483,10 +500,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) close_wfile(file); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return ret; } @@ -496,10 +512,12 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) struct fxp_handle *fh; struct fxp_xfer *xfer; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; uint64 offset; RFile *file; int ret, err, eof; + struct fxp_attrs attrs; + long permissions; /* * In recursive mode, see if we're dealing with a directory. @@ -507,7 +525,6 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * subsequent fopen will return an error message.) */ if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) { - struct fxp_attrs attrs; int result; int nnames, namesize; char *name, **ournames; @@ -518,17 +535,15 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * First, attempt to create the destination directory, * unless it already exists. */ - sftp_register(req = fxp_stat_send(outfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(outfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { - sftp_register(req = fxp_mkdir_send(outfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(outfname); + pktin = sftp_wait_for_reply(req); + result = fxp_mkdir_recv(pktin, req); if (!result) { printf("%s: create directory: %s\n", @@ -563,7 +578,8 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * same directory, just in case two readdirs on the same * local directory return a different order. */ - qsort(ournames, nnames, sizeof(*ournames), bare_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), bare_name_compare); /* * If we're in restart mode, find the last filename on this @@ -578,10 +594,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int 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); + req = fxp_stat_send(nextoutfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); sfree(nextoutfname); if (!result) break; @@ -601,10 +616,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) char *nextfname, *nextoutfname; int ret; - if (fname) - nextfname = dir_file_cat(fname, ournames[i]); - else - nextfname = dupstr(ournames[i]); + nextfname = dir_file_cat(fname, ournames[i]); nextoutfname = dupcat(outfname, "/", ournames[i], NULL); ret = sftp_put_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ @@ -630,20 +642,22 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) return 1; } - file = open_existing_file(fname, NULL, NULL, NULL); + file = open_existing_file(fname, NULL, NULL, NULL, &permissions); if (!file) { printf("local: unable to open %s\n", fname); return 0; } + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); if (restart) { - sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE)); + req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs); } else { - sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); + req = fxp_open_send(outfname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); } - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fh = fxp_open_recv(pktin, rreq); + pktin = sftp_wait_for_reply(req); + fh = fxp_open_recv(pktin, req); if (!fh) { close_rfile(file); @@ -656,10 +670,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) struct fxp_attrs attrs; int ret; - sftp_register(req = fxp_fstat_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fstat_recv(pktin, rreq, &attrs); + req = fxp_fstat_send(fh); + pktin = sftp_wait_for_reply(req); + ret = fxp_fstat_recv(pktin, req, &attrs); if (!ret) { close_rfile(file); @@ -709,19 +722,22 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) if (!xfer_done(xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(xfer, pktin); - if (ret <= 0 && !err) { - printf("error while writing: %s\n", fxp_error()); - err = 1; + if (ret <= 0) { + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + if (!err) { + printf("error while writing: %s\n", fxp_error()); + err = 1; + } } } } xfer_cleanup(xfer); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); close_rfile(file); @@ -743,7 +759,7 @@ typedef struct SftpWildcardMatcher { SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; char *wildcard; char *unwcdir, *tmpdir, *cdir; int len, check; @@ -774,10 +790,9 @@ SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) cdir = canonify(unwcdir); - sftp_register(req = fxp_opendir_send(cdir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(cdir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh) { swcm = snew(SftpWildcardMatcher); @@ -800,7 +815,7 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) { struct fxp_name *name; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; while (1) { if (swcm->names && swcm->namepos >= swcm->names->nnames) { @@ -809,17 +824,26 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) } if (!swcm->names) { - sftp_register(req = fxp_readdir_send(swcm->dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - swcm->names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(swcm->dirh); + pktin = sftp_wait_for_reply(req); + swcm->names = fxp_readdir_recv(pktin, req); if (!swcm->names) { if (fxp_error_type() != SSH_FX_EOF) printf("%s: reading directory: %s\n", swcm->prefix, fxp_error()); return NULL; - } + } else if (swcm->names->nnames == 0) { + /* + * Another failure mode which we treat as EOF is if + * the server reports success from FXP_READDIR but + * returns no actual names. This is unusual, since + * from most servers you'd expect at least "." and + * "..", but there's nothing forbidding a server from + * omitting those if it wants to. + */ + return NULL; + } swcm->namepos = 0; } @@ -854,12 +878,11 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_close_send(swcm->dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(swcm->dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); if (swcm->names) fxp_free_names(swcm->names); @@ -899,6 +922,7 @@ int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx) printf("%s: canonify: %s\n", newname, fxp_error()); ret = 0; } + sfree(newname); matched = TRUE; ret &= func(ctx, cname); sfree(cname); @@ -970,6 +994,7 @@ int sftp_cmd_close(struct sftp_command *cmd) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); @@ -989,7 +1014,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) int nnames, namesize; char *dir, *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int i; if (back == NULL) { @@ -1010,6 +1035,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) char *tmpdir; int len, check; + sfree(unwcdir); wildcard = stripslashes(dir, 0); unwcdir = dupstr(dir); len = wildcard - dir; @@ -1036,10 +1062,9 @@ int sftp_cmd_ls(struct sftp_command *cmd) printf("Listing directory %s\n", cdir); - sftp_register(req = fxp_opendir_send(cdir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(cdir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dir, fxp_error()); @@ -1049,10 +1074,9 @@ int sftp_cmd_ls(struct sftp_command *cmd) while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -1076,16 +1100,16 @@ int sftp_cmd_ls(struct sftp_command *cmd) fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * And print them. @@ -1111,7 +1135,7 @@ int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; char *dir; if (back == NULL) { @@ -1129,10 +1153,9 @@ int sftp_cmd_cd(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_opendir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (!dirh) { printf("Directory %s: %s\n", dir, fxp_error()); @@ -1140,10 +1163,9 @@ int sftp_cmd_cd(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); sfree(pwd); pwd = dir; @@ -1236,7 +1258,9 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple) fname = canonify(origwfname); if (!fname) { + sftp_finish_wildcard_matching(swcm); printf("%s: canonify: %s\n", origwfname, fxp_error()); + sfree(origwfname); sfree(unwcfname); return 0; } @@ -1392,7 +1416,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; int i, ret; @@ -1414,10 +1438,9 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_mkdir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(dir); + pktin = sftp_wait_for_reply(req); + result = fxp_mkdir_recv(pktin, req); if (!result) { printf("mkdir %s: %s\n", dir, fxp_error()); @@ -1434,13 +1457,12 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) static int sftp_action_rmdir(void *vctx, char *dir) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; - sftp_register(req = fxp_rmdir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_rmdir_recv(pktin, rreq); + req = fxp_rmdir_send(dir); + pktin = sftp_wait_for_reply(req); + result = fxp_rmdir_recv(pktin, req); if (!result) { printf("rmdir %s: %s\n", dir, fxp_error()); @@ -1476,13 +1498,12 @@ int sftp_cmd_rmdir(struct sftp_command *cmd) static int sftp_action_rm(void *vctx, char *fname) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; - sftp_register(req = fxp_remove_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_remove_recv(pktin, rreq); + req = fxp_remove_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_remove_recv(pktin, req); if (!result) { printf("rm %s: %s\n", fname, fxp_error()); @@ -1518,14 +1539,13 @@ int sftp_cmd_rm(struct sftp_command *cmd) static int check_is_dir(char *dstfname) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int result; - sftp_register(req = fxp_stat_send(dstfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(dstfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && @@ -1544,7 +1564,7 @@ static int sftp_action_mv(void *vctx, char *srcfname) { struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; const char *error; char *finalfname, *newcanon = NULL; int ret, result; @@ -1569,10 +1589,9 @@ static int sftp_action_mv(void *vctx, char *srcfname) finalfname = ctx->dstfname; } - sftp_register(req = fxp_rename_send(srcfname, finalfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_rename_recv(pktin, rreq); + req = fxp_rename_send(srcfname, finalfname); + pktin = sftp_wait_for_reply(req); + result = fxp_rename_recv(pktin, req); error = result ? NULL : fxp_error(); @@ -1641,15 +1660,14 @@ static int sftp_action_chmod(void *vctx, char *fname) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; unsigned oldperms, newperms; struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx; - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { printf("get attrs for %s: %s\n", fname, @@ -1666,10 +1684,9 @@ static int sftp_action_chmod(void *vctx, char *fname) if (oldperms == newperms) return 1; /* no need to do anything! */ - sftp_register(req = fxp_setstat_send(fname, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_setstat_recv(pktin, rreq); + req = fxp_setstat_send(fname, attrs); + pktin = sftp_wait_for_reply(req); + result = fxp_setstat_recv(pktin, req); if (!result) { printf("set attrs for %s: %s\n", fname, fxp_error()); @@ -2219,6 +2236,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) cmd->obey = sftp_cmd_quit; if ((mode == 0) || (modeflags & 1)) printf("quit\n"); + sfree(line); return cmd; /* eof */ } @@ -2265,10 +2283,13 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) * >this has "quotes" in< * >and"this"< */ - while (*p) { + while (1) { /* skip whitespace */ while (*p && (*p == ' ' || *p == '\t')) p++; + /* terminate loop */ + if (!*p) + break; /* mark start of word */ q = r = p; /* q sits at start, r writes word */ quoting = 0; @@ -2316,7 +2337,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) static int do_sftp_init(void) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Do protocol initialisation. @@ -2330,10 +2351,9 @@ static int do_sftp_init(void) /* * Find out where our home directory is. */ - sftp_register(req = fxp_realpath_send(".")); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - homedir = fxp_realpath_recv(pktin, rreq); + req = fxp_realpath_send("."); + pktin = sftp_wait_for_reply(req); + homedir = fxp_realpath_recv(pktin, req); if (!homedir) { fprintf(stderr, @@ -2352,6 +2372,7 @@ void do_sftp_cleanup() char ch; if (back) { back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); back->free(backhandle); sftp_cleanup_request(); @@ -2458,6 +2479,18 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +void nonfatal(char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + fputs(str2, stderr); + sfree(str2); +} void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; @@ -2560,6 +2593,19 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. + */ + if (!sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from SFTP server"); + } + return FALSE; +} int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; @@ -2664,32 +2710,31 @@ static int psftp_connect(char *userhost, char *user, int portnumber) */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ - Config cfg2; - cfg2.host[0] = '\0'; - do_defaults(host, &cfg2); - if (cfg2.host[0] != '\0') { + Conf *conf2 = conf_new(); + conf_set_str(conf2, CONF_host, ""); + do_defaults(host, conf2); + if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ - do_defaults(host, &cfg); + do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } + conf_free(conf2); } else { /* Patch in hostname `host' to session details. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ - if (cfg.protocol != PROT_SSH) { - cfg.protocol = PROT_SSH; - cfg.port = 22; + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); } /* @@ -2698,78 +2743,82 @@ static int psftp_connect(char *userhost, char *user, int portnumber) * work for SFTP. (Can be overridden with `-1' option.) * But if it says `2 only' or `2', respect which. */ - if (cfg.sshprot != 2 && cfg.sshprot != 3) - cfg.sshprot = 2; + if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2) /* is it 2 or 3? */ + conf_set_int(conf, CONF_sshprot, 2); /* * Enact command-line overrides. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } - - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; - } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); - } - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - p2++; } - cfg.host[p1] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } if (portnumber) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ - cfg.x11_forward = 0; - cfg.agentfwd = 0; - cfg.portfwd[0] = cfg.portfwd[1] = '\0'; - cfg.ssh_simple = TRUE; + conf_set_int(conf, CONF_x11_forward, 0); + conf_set_int(conf, CONF_agentfwd, 0); + conf_set_int(conf, CONF_ssh_simple, TRUE); + { + char *key; + while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) + conf_del_str_str(conf, CONF_portfwd, key); + } /* Set up subsystem name. */ - strcpy(cfg.remote_cmd, "sftp"); - cfg.ssh_subsys = TRUE; - cfg.nopty = TRUE; + conf_set_str(conf, CONF_remote_cmd, "sftp"); + conf_set_int(conf, CONF_ssh_subsys, TRUE); + conf_set_int(conf, CONF_nopty, TRUE); /* * Set up fallback option, for SSH-1 servers or servers with the @@ -2788,21 +2837,26 @@ static int psftp_connect(char *userhost, char *user, int portnumber) * obvious pathnames and then give up, and when it does give up * it will print the preferred pathname in the error messages. */ - cfg.remote_cmd_ptr2 = - "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" - "exec sftp-server"; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, + "test -x /usr/lib/sftp-server &&" + " exec /usr/lib/sftp-server\n" + "test -x /usr/local/lib/sftp-server &&" + " exec /usr/local/lib/sftp-server\n" + "exec sftp-server"); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); back = &ssh_backend; - err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, - 0, cfg.tcp_keepalives); + err = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, 0, + conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) { fprintf(stderr, "ssh_init: %s\n", err); return 1; } - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); while (!back->sendok(backhandle)) { @@ -2854,7 +2908,8 @@ int psftp_main(int argc, char *argv[]) userhost = user = NULL; /* Load Default Settings before doing anything else. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { @@ -2866,7 +2921,7 @@ int psftp_main(int argc, char *argv[]) userhost = dupstr(argv[i]); continue; } - ret = cmdline_process_param(argv[i], i+1connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); diff --git a/contrib/putty/PSFTP.H b/contrib/putty/PSFTP.H index 3e13887..e7e8ecc 100644 --- a/contrib/putty/PSFTP.H +++ b/contrib/putty/PSFTP.H @@ -85,15 +85,17 @@ void gui_enable(char *arg); */ typedef struct RFile RFile; typedef struct WFile WFile; -/* Output params size, mtime and atime can all be NULL if desired */ +/* Output params size, perms, mtime and atime can all be NULL if + * desired. perms will be -1 if the OS does not support POSIX permissions. */ RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime); + unsigned long *mtime, unsigned long *atime, + long *perms); WFile *open_existing_wfile(char *name, uint64 *size); /* Returns <0 on error, 0 on eof, or number of bytes read, as usual */ int read_from_file(RFile *f, void *buffer, int length); /* Closes and frees the RFile */ void close_rfile(RFile *f); -WFile *open_new_file(char *name); +WFile *open_new_file(char *name, long perms); /* Returns <0 on error, 0 on eof, or number of bytes written, as usual */ int write_to_file(WFile *f, void *buffer, int length); void set_file_times(WFile *f, unsigned long mtime, unsigned long atime); diff --git a/contrib/putty/PUTTY.H b/contrib/putty/PUTTY.H index e2baee8..47b20a1 100644 --- a/contrib/putty/PUTTY.H +++ b/contrib/putty/PUTTY.H @@ -18,7 +18,7 @@ #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -304,7 +304,7 @@ enum { }; enum { - /* Protocol back ends. (cfg.protocol) */ + /* Protocol back ends. (CONF_protocol) */ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ @@ -312,22 +312,22 @@ enum { }; enum { - /* Bell settings (cfg.beep) */ + /* Bell settings (CONF_beep) */ BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER }; enum { - /* Taskbar flashing indication on bell (cfg.beep_ind) */ + /* Taskbar flashing indication on bell (CONF_beep_ind) */ B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY }; enum { - /* Resize actions (cfg.resize_action) */ + /* Resize actions (CONF_resize_action) */ RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER }; enum { - /* Function key types (cfg.funky_type) */ + /* Function key types (CONF_funky_type) */ FUNKY_TILDE, FUNKY_LINUX, FUNKY_XTERM, @@ -415,12 +415,11 @@ enum { struct backend_tag { const char *(*init) (void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive); + Conf *conf, char *host, int port, char **realhost, + int nodelay, int keepalive); void (*free) (void *handle); /* back->reconfig() passes in a replacement configuration. */ - void (*reconfig) (void *handle, Config *cfg); + void (*reconfig) (void *handle, Conf *conf); /* back->send() returns the current amount of buffered data. */ int (*send) (void *handle, char *buf, int len); /* back->sendbuffer() does the same thing but without attempting a send */ @@ -462,214 +461,6 @@ extern const int be_default_protocol; extern const char *const appname; /* - * IMPORTANT POLICY POINT: everything in this structure which wants - * to be treated like an integer must be an actual, honest-to- - * goodness `int'. No enum-typed variables. This is because parts - * of the code will want to pass around `int *' pointers to them - * and we can't run the risk of porting to some system on which the - * enum comes out as a different size from int. - */ -struct config_tag { - /* Basic options */ - char host[512]; - int port; - int protocol; - int addressfamily; - int close_on_exit; - int warn_on_close; - int ping_interval; /* in seconds */ - int tcp_nodelay; - int tcp_keepalives; - char loghost[512]; /* logical host being contacted, for host key check */ - /* Proxy options */ - char proxy_exclude_list[512]; - int proxy_dns; - int even_proxy_localhost; - int proxy_type; - char proxy_host[512]; - int proxy_port; - char proxy_username[128]; - char proxy_password[128]; - char proxy_telnet_command[512]; - /* SSH options */ - char remote_cmd[512]; - char *remote_cmd_ptr; /* might point to a larger command - * but never for loading/saving */ - char *remote_cmd_ptr2; /* might point to a larger command - * but never for loading/saving */ - int nopty; - int compression; - int ssh_kexlist[KEX_MAX]; - int ssh_rekey_time; /* in minutes */ - char ssh_rekey_data[16]; - int tryagent; - int agentfwd; - int change_username; /* allow username switching in SSH-2 */ - int ssh_cipherlist[CIPHER_MAX]; - Filename keyfile; - int sshprot; /* use v1 or v2 when both available */ - int ssh2_des_cbc; /* "des-cbc" unrecommended SSH-2 cipher */ - int ssh_no_userauth; /* bypass "ssh-userauth" (SSH-2 only) */ - int ssh_show_banner; /* show USERAUTH_BANNERs (SSH-2 only) */ - int try_tis_auth; - int try_ki_auth; - int try_gssapi_auth; /* attempt gssapi auth */ - int gssapifwd; /* forward tgt via gss */ - int ssh_gsslist[4]; /* preference order for local GSS libs */ - Filename ssh_gss_custom; - int ssh_subsys; /* run a subsystem rather than a command */ - int ssh_subsys2; /* fallback to go with remote_cmd_ptr2 */ - int ssh_no_shell; /* avoid running a shell */ - char ssh_nc_host[512]; /* host to connect to in `nc' mode */ - int ssh_nc_port; /* port to connect to in `nc' mode */ - /* Telnet options */ - char termtype[32]; - char termspeed[32]; - char ttymodes[768]; /* MODE\tVvalue\0MODE\tA\0\0 */ - char environmt[1024]; /* VAR\tvalue\0VAR\tvalue\0\0 */ - char username[100]; - int username_from_env; - char localusername[100]; - int rfc_environ; - int passive_telnet; - /* Serial port options */ - char serline[256]; - int serspeed; - int serdatabits, serstopbits; - int serparity; - int serflow; - /* Keyboard options */ - int bksp_is_delete; - int rxvt_homeend; - int funky_type; - int no_applic_c; /* totally disable app cursor keys */ - int no_applic_k; /* totally disable app keypad */ - int no_mouse_rep; /* totally disable mouse reporting */ - int no_remote_resize; /* disable remote resizing */ - int no_alt_screen; /* disable alternate screen */ - int no_remote_wintitle; /* disable remote retitling */ - int no_dbackspace; /* disable destructive backspace */ - int no_remote_charset; /* disable remote charset config */ - int remote_qtitle_action; /* remote win title query action */ - int app_cursor; - int app_keypad; - int nethack_keypad; - int telnet_keyboard; - int telnet_newline; - int alt_f4; /* is it special? */ - int alt_space; /* is it special? */ - int alt_only; /* is it special? */ - int localecho; - int localedit; - int alwaysontop; - int fullscreenonaltenter; - int scroll_on_key; - int scroll_on_disp; - int erase_to_scrollback; - int compose_key; - int ctrlaltkeys; - char wintitle[256]; /* initial window title */ - /* Terminal options */ - int savelines; - int dec_om; - int wrap_mode; - int lfhascr; - int cursor_type; /* 0=block 1=underline 2=vertical */ - int blink_cur; - int beep; - int beep_ind; - int bellovl; /* bell overload protection active? */ - int bellovl_n; /* number of bells to cause overload */ - int bellovl_t; /* time interval for overload (seconds) */ - int bellovl_s; /* period of silence to re-enable bell (s) */ - Filename bell_wavefile; - int scrollbar; - int scrollbar_in_fullscreen; - int resize_action; - int bce; - int blinktext; - int win_name_always; - int width, height; - FontSpec font; - int font_quality; - Filename logfilename; - int logtype; - int logxfovr; - int logflush; - int logomitpass; - int logomitdata; - int hide_mouseptr; - int sunken_edge; - int window_border; - char answerback[256]; - char printer[128]; - int arabicshaping; - int bidi; - /* Colour options */ - int ansi_colour; - int xterm_256_colour; - int system_colour; - int try_palette; - int bold_colour; - unsigned char colours[22][3]; - /* Selection options */ - int mouse_is_xterm; - int rect_select; - int rawcnp; - int rtf_paste; - int mouse_override; - short wordness[256]; - /* translations */ - int vtmode; - char line_codepage[128]; - int cjk_ambig_wide; - int utf8_override; - int xlat_capslockcyr; - /* X11 forwarding */ - int x11_forward; - char x11_display[128]; - int x11_auth; - Filename xauthfile; - /* port forwarding */ - int lport_acceptall; /* accept conns from hosts other than localhost */ - int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */ - /* - * The port forwarding string contains a number of - * NUL-terminated substrings, terminated in turn by an empty - * string (i.e. a second NUL immediately after the previous - * one). Each string can be of one of the following forms: - * - * [LR]localport\thost:port - * [LR]localaddr:localport\thost:port - * Dlocalport - * Dlocaladdr:localport - */ - char portfwd[1024]; - /* SSH bug compatibility modes */ - int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1, - sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2, - sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2, - sshbug_ignore2; - /* - * 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; - int scrollbar_on_left; - int shadowbold; - FontSpec boldfont; - FontSpec widefont; - FontSpec wideboldfont; - int shadowboldoffset; - int crhaslf; - char winclass[256]; -}; - -/* * Some global flags denoting the type of application. * * FLAG_VERBOSE is set when the user requests verbose details. @@ -734,8 +525,19 @@ struct RSAKey; /* be a little careful of scope */ typedef struct { char *prompt; int echo; - char *result; /* allocated/freed by caller */ - size_t result_len; + /* + * 'result' must be a dynamically allocated array of exactly + * 'resultsize' chars. The code for actually reading input may + * realloc it bigger (and adjust resultsize accordingly) if it has + * to. The caller should free it again when finished with it. + * + * If resultsize==0, then result may be NULL. When setting up a + * prompt_t, it's therefore easiest to initialise them this way, + * which means all actual allocation is done by the callee. This + * is what add_prompt does. + */ + char *result; + size_t resultsize; } prompt_t; typedef struct { /* @@ -758,7 +560,9 @@ typedef struct { * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len); +void add_prompt(prompts_t *p, char *promptstr, int echo); +void prompt_set_result(prompt_t *pr, const char *newstr); +void prompt_ensure_result_size(prompt_t *pr, int len); /* Burn the evidence. (Assumes _all_ strings want free()ing.) */ void free_prompts(prompts_t *p); @@ -785,6 +589,7 @@ void get_clip(void *frontend, wchar_t **, int *); void optimised_move(void *frontend, int, int, int); void set_raw_mouse_mode(void *frontend, int); void connection_fatal(void *frontend, char *, ...); +void nonfatal(char *, ...); void fatalbox(char *, ...); void modalfatalbox(char *, ...); #ifdef macintosh @@ -804,6 +609,11 @@ void ldisc_update(void *frontend, int echo, int edit); void update_specials_menu(void *frontend); int from_backend(void *frontend, int is_stderr, const char *data, int len); int from_backend_untrusted(void *frontend, const char *data, int len); +/* Called when the back end wants to indicate that EOF has arrived on + * the server-to-client stream. Returns FALSE to indicate that we + * intend to keep the session open in the other direction, or TRUE to + * indicate that if they're closing so are we. */ +int from_backend_eof(void *frontend); void notify_remote_exit(void *frontend); /* Get a sensible value for a tty mode. NULL return = don't set. * Otherwise, returned value should be freed by caller. */ @@ -839,6 +649,270 @@ void set_busy_status(void *frontend, int status); void cleanup_exit(int); /* + * Exports from conf.c, and a big enum (via parametric macro) of + * configuration option keys. + */ +#define CONFIG_OPTIONS(X) \ + /* X(value-type, subkey-type, keyword) */ \ + X(STR, NONE, host) \ + X(INT, NONE, port) \ + X(INT, NONE, protocol) \ + X(INT, NONE, addressfamily) \ + X(INT, NONE, close_on_exit) \ + X(INT, NONE, warn_on_close) \ + X(INT, NONE, ping_interval) /* in seconds */ \ + X(INT, NONE, tcp_nodelay) \ + X(INT, NONE, tcp_keepalives) \ + X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \ + /* Proxy options */ \ + X(STR, NONE, proxy_exclude_list) \ + X(INT, NONE, proxy_dns) \ + X(INT, NONE, even_proxy_localhost) \ + X(INT, NONE, proxy_type) \ + X(STR, NONE, proxy_host) \ + X(INT, NONE, proxy_port) \ + X(STR, NONE, proxy_username) \ + X(STR, NONE, proxy_password) \ + X(STR, NONE, proxy_telnet_command) \ + /* SSH options */ \ + X(STR, NONE, remote_cmd) \ + X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \ + X(INT, NONE, nopty) \ + X(INT, NONE, compression) \ + X(INT, INT, ssh_kexlist) \ + X(INT, NONE, ssh_rekey_time) /* in minutes */ \ + X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ + X(INT, NONE, tryagent) \ + X(INT, NONE, agentfwd) \ + X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \ + X(INT, INT, ssh_cipherlist) \ + X(FILENAME, NONE, keyfile) \ + X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \ + X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ + X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ + X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ + X(INT, NONE, try_tis_auth) \ + X(INT, NONE, try_ki_auth) \ + X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \ + X(INT, NONE, gssapifwd) /* forward tgt via gss */ \ + X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \ + X(FILENAME, NONE, ssh_gss_custom) \ + X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \ + X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \ + X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \ + X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \ + X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \ + /* Telnet options */ \ + X(STR, NONE, termtype) \ + X(STR, NONE, termspeed) \ + X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \ + X(STR, STR, environmt) \ + X(STR, NONE, username) \ + X(INT, NONE, username_from_env) \ + X(STR, NONE, localusername) \ + X(INT, NONE, rfc_environ) \ + X(INT, NONE, passive_telnet) \ + /* Serial port options */ \ + X(STR, NONE, serline) \ + X(INT, NONE, serspeed) \ + X(INT, NONE, serdatabits) \ + X(INT, NONE, serstopbits) \ + X(INT, NONE, serparity) \ + X(INT, NONE, serflow) \ + /* Keyboard options */ \ + X(INT, NONE, bksp_is_delete) \ + X(INT, NONE, rxvt_homeend) \ + X(INT, NONE, funky_type) \ + X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \ + X(INT, NONE, no_applic_k) /* totally disable app keypad */ \ + X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \ + X(INT, NONE, no_remote_resize) /* disable remote resizing */ \ + X(INT, NONE, no_alt_screen) /* disable alternate screen */ \ + X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \ + X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \ + X(INT, NONE, no_remote_charset) /* disable remote charset config */ \ + X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \ + X(INT, NONE, app_cursor) \ + X(INT, NONE, app_keypad) \ + X(INT, NONE, nethack_keypad) \ + X(INT, NONE, telnet_keyboard) \ + X(INT, NONE, telnet_newline) \ + X(INT, NONE, alt_f4) /* is it special? */ \ + X(INT, NONE, alt_space) /* is it special? */ \ + X(INT, NONE, alt_only) /* is it special? */ \ + X(INT, NONE, localecho) \ + X(INT, NONE, localedit) \ + X(INT, NONE, alwaysontop) \ + X(INT, NONE, fullscreenonaltenter) \ + X(INT, NONE, scroll_on_key) \ + X(INT, NONE, scroll_on_disp) \ + X(INT, NONE, erase_to_scrollback) \ + X(INT, NONE, compose_key) \ + X(INT, NONE, ctrlaltkeys) \ + X(STR, NONE, wintitle) /* initial window title */ \ + /* Terminal options */ \ + X(INT, NONE, savelines) \ + X(INT, NONE, dec_om) \ + X(INT, NONE, wrap_mode) \ + X(INT, NONE, lfhascr) \ + X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \ + X(INT, NONE, blink_cur) \ + X(INT, NONE, beep) \ + X(INT, NONE, beep_ind) \ + X(INT, NONE, bellovl) /* bell overload protection active? */ \ + X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \ + X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \ + X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \ + X(FILENAME, NONE, bell_wavefile) \ + X(INT, NONE, scrollbar) \ + X(INT, NONE, scrollbar_in_fullscreen) \ + X(INT, NONE, resize_action) \ + X(INT, NONE, bce) \ + X(INT, NONE, blinktext) \ + X(INT, NONE, win_name_always) \ + X(INT, NONE, width) \ + X(INT, NONE, height) \ + X(FONT, NONE, font) \ + X(INT, NONE, font_quality) \ + X(FILENAME, NONE, logfilename) \ + X(INT, NONE, logtype) \ + X(INT, NONE, logxfovr) \ + X(INT, NONE, logflush) \ + X(INT, NONE, logomitpass) \ + X(INT, NONE, logomitdata) \ + X(INT, NONE, hide_mouseptr) \ + X(INT, NONE, sunken_edge) \ + X(INT, NONE, window_border) \ + X(STR, NONE, answerback) \ + X(STR, NONE, printer) \ + X(INT, NONE, arabicshaping) \ + X(INT, NONE, bidi) \ + /* Colour options */ \ + X(INT, NONE, ansi_colour) \ + X(INT, NONE, xterm_256_colour) \ + X(INT, NONE, system_colour) \ + X(INT, NONE, try_palette) \ + X(INT, NONE, bold_style) \ + X(INT, INT, colours) \ + /* Selection options */ \ + X(INT, NONE, mouse_is_xterm) \ + X(INT, NONE, rect_select) \ + X(INT, NONE, rawcnp) \ + X(INT, NONE, rtf_paste) \ + X(INT, NONE, mouse_override) \ + X(INT, INT, wordness) \ + /* translations */ \ + X(INT, NONE, vtmode) \ + X(STR, NONE, line_codepage) \ + X(INT, NONE, cjk_ambig_wide) \ + X(INT, NONE, utf8_override) \ + X(INT, NONE, xlat_capslockcyr) \ + /* X11 forwarding */ \ + X(INT, NONE, x11_forward) \ + X(STR, NONE, x11_display) \ + X(INT, NONE, x11_auth) \ + X(FILENAME, NONE, xauthfile) \ + /* port forwarding */ \ + X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \ + X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \ + /* \ + * Subkeys for 'portfwd' can have the following forms: \ + * \ + * [LR]localport \ + * [LR]localaddr:localport \ + * \ + * Dynamic forwardings are indicated by an 'L' key, and the \ + * special value "D". For all other forwardings, the value \ + * should be of the form 'host:port'. \ + */ \ + X(STR, STR, portfwd) \ + /* SSH bug compatibility modes */ \ + X(INT, NONE, sshbug_ignore1) \ + X(INT, NONE, sshbug_plainpw1) \ + X(INT, NONE, sshbug_rsa1) \ + X(INT, NONE, sshbug_hmac2) \ + X(INT, NONE, sshbug_derivekey2) \ + X(INT, NONE, sshbug_rsapad2) \ + X(INT, NONE, sshbug_pksessid2) \ + X(INT, NONE, sshbug_rekey2) \ + X(INT, NONE, sshbug_maxpkt2) \ + X(INT, NONE, sshbug_ignore2) \ + X(INT, NONE, sshbug_winadj) \ + /* \ + * 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. \ + */ \ + X(INT, NONE, ssh_simple) \ + /* Options for pterm. Should split out into platform-dependent part. */ \ + X(INT, NONE, stamp_utmp) \ + X(INT, NONE, login_shell) \ + X(INT, NONE, scrollbar_on_left) \ + X(INT, NONE, shadowbold) \ + X(FONT, NONE, boldfont) \ + X(FONT, NONE, widefont) \ + X(FONT, NONE, wideboldfont) \ + X(INT, NONE, shadowboldoffset) \ + X(INT, NONE, crhaslf) \ + X(STR, NONE, winclass) \ + +/* Now define the actual enum of option keywords using that macro. */ +#define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword, +enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS }; +#undef CONF_ENUM_DEF + +#define NCFGCOLOURS 22 /* number of colours in CONF_colours above */ + +/* Functions handling configuration structures. */ +Conf *conf_new(void); /* create an empty configuration */ +void conf_free(Conf *conf); +Conf *conf_copy(Conf *oldconf); +void conf_copy_into(Conf *dest, Conf *src); +/* Mandatory accessor functions: enforce by assertion that keys exist. */ +int conf_get_int(Conf *conf, int key); +int conf_get_int_int(Conf *conf, int key, int subkey); +char *conf_get_str(Conf *conf, int key); /* result still owned by conf */ +char *conf_get_str_str(Conf *conf, int key, const char *subkey); +Filename *conf_get_filename(Conf *conf, int key); +FontSpec *conf_get_fontspec(Conf *conf, int key); /* still owned by conf */ +/* Optional accessor function: return NULL if key does not exist. */ +char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey); +/* Accessor function to step through a string-subkeyed list. + * Returns the next subkey after the provided one, or the first if NULL. + * Returns NULL if there are none left. + * Both the return value and *subkeyout are still owned by conf. */ +char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout); +/* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */ +char *conf_get_str_nthstrkey(Conf *conf, int key, int n); +/* Functions to set entries in configuration. Always copy their inputs. */ +void conf_set_int(Conf *conf, int key, int value); +void conf_set_int_int(Conf *conf, int key, int subkey, int value); +void conf_set_str(Conf *conf, int key, const char *value); +void conf_set_str_str(Conf *conf, int key, + const char *subkey, const char *val); +void conf_del_str_str(Conf *conf, int key, const char *subkey); +void conf_set_filename(Conf *conf, int key, const Filename *val); +void conf_set_fontspec(Conf *conf, int key, const FontSpec *val); +/* Serialisation functions for Duplicate Session */ +int conf_serialised_size(Conf *conf); +void conf_serialise(Conf *conf, void *data); +int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/ + +/* + * Functions to copy, free, serialise and deserialise FontSpecs. + * Provided per-platform, to go with the platform's idea of a + * FontSpec's contents. + * + * fontspec_serialise returns the number of bytes written, and can + * handle data==NULL without crashing. So you can call it once to find + * out a size, then again once you've allocated a buffer. + */ +FontSpec *fontspec_copy(const FontSpec *f); +void fontspec_free(FontSpec *f); +int fontspec_serialise(FontSpec *f, void *data); +FontSpec *fontspec_deserialise(void *data, int maxsize, int *used); + +/* * Exports from noise.c. */ void noise_get_heavy(void (*func) (void *, int)); @@ -853,13 +927,13 @@ void random_destroy_seed(void); */ Backend *backend_from_name(const char *name); Backend *backend_from_proto(int proto); -int get_remote_username(Config *cfg, char *user, size_t len); -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); +char *get_remote_username(Conf *conf); /* dynamically allocated */ +char *save_settings(char *section, Conf *conf); +void save_open_settings(void *sesskey, Conf *conf); +void load_settings(char *section, Conf *conf); +void load_open_settings(void *sesskey, Conf *conf); void get_sesslist(struct sesslist *, int allocate); -void do_defaults(char *, Config *); +void do_defaults(char *, Conf *); void registry_cleanup(void); /* @@ -872,17 +946,21 @@ void registry_cleanup(void); * function is perfectly all right returning NULL, of course. The * Filename and FontSpec functions are _not allowed_ to fail to * return, since these defaults _must_ be per-platform.) + * + * The 'Filename *' returned by platform_default_filename, and the + * 'FontSpec *' returned by platform_default_fontspec, have ownership + * transferred to the caller, and must be freed. */ char *platform_default_s(const char *name); int platform_default_i(const char *name, int def); -Filename platform_default_filename(const char *name); -FontSpec platform_default_fontspec(const char *name); +Filename *platform_default_filename(const char *name); +FontSpec *platform_default_fontspec(const char *name); /* * Exports from terminal.c. */ -Terminal *term_init(Config *, struct unicode_data *, void *); +Terminal *term_init(Conf *, struct unicode_data *, void *); void term_free(Terminal *); void term_size(Terminal *, int, int, int); void term_paint(Terminal *, Context, int, int, int, int, int); @@ -904,7 +982,7 @@ void term_paste(Terminal *); void term_nopaste(Terminal *); int term_ldisc(Terminal *, int option); void term_copyall(Terminal *); -void term_reconfig(Terminal *, Config *); +void term_reconfig(Terminal *, Conf *); void term_seen_key_event(Terminal *); int term_data(Terminal *, int is_stderr, const char *data, int len); int term_data_untrusted(Terminal *, const char *data, int len); @@ -922,9 +1000,9 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl); /* * Exports from logging.c. */ -void *log_init(void *frontend, Config *cfg); +void *log_init(void *frontend, Conf *conf); void log_free(void *logctx); -void log_reconfig(void *logctx, Config *cfg); +void log_reconfig(void *logctx, Conf *conf); void logfopen(void *logctx); void logfclose(void *logctx); void logtraffic(void *logctx, unsigned char c, int logmode); @@ -975,7 +1053,8 @@ extern Backend ssh_backend; /* * Exports from ldisc.c. */ -void *ldisc_create(Config *, Terminal *, Backend *, void *, void *); +void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *); +void ldisc_configure(void *, Conf *); void ldisc_free(void *); void ldisc_send(void *handle, char *buf, int len, int interactive); @@ -1003,8 +1082,8 @@ void random_unref(void); * Exports from pinger.c. */ typedef struct pinger_tag *Pinger; -Pinger pinger_new(Config *cfg, Backend *back, void *backhandle); -void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg); +Pinger pinger_new(Conf *conf, Backend *back, void *backhandle); +void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf); void pinger_free(Pinger); /* @@ -1012,8 +1091,8 @@ void pinger_free(Pinger); */ #include "misc.h" -int cfg_launchable(const Config *cfg); -char const *cfg_dest(const Config *cfg); +int conf_launchable(Conf *conf); +char const *conf_dest(Conf *conf); /* * Exports from sercfg.c. @@ -1034,9 +1113,9 @@ extern char ver[]; #endif /* void init_ucs(void); -- this is now in platform-specific headers */ int is_dbcs_leadbyte(int codepage, char byte); -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen); -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata); wchar_t xlat_uskbd2cyrllic(int ch); @@ -1049,10 +1128,10 @@ void get_unitab(int codepage, wchar_t * unitab, int ftype); /* * Exports from wcwidth.c */ -int mk_wcwidth(wchar_t ucs); -int mk_wcswidth(const wchar_t *pwcs, size_t n); -int mk_wcwidth_cjk(wchar_t ucs); -int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n); +int mk_wcwidth(unsigned int ucs); +int mk_wcswidth(const unsigned int *pwcs, size_t n); +int mk_wcwidth_cjk(unsigned int ucs); +int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n); /* * Exports from mscrypto.c @@ -1118,7 +1197,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * - 0 means cancel logging for this session * - -1 means please wait. */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); /* @@ -1147,8 +1226,8 @@ void printer_finish_job(printer_job *); * defined differently in various places and required _by_ * cmdline.c). */ -int cmdline_process_param(char *, char *, int, Config *); -void cmdline_run_saved(Config *); +int cmdline_process_param(char *, char *, int, Conf *); +void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen); #define TOOLTYPE_FILETRANSFER 1 @@ -1161,6 +1240,18 @@ void cmdline_error(char *, ...); * Exports from config.c. */ struct controlbox; +union control; +void conf_radiobutton_handler(union control *ctrl, void *dlg, + void *data, int event); +#define CHECKBOX_INVERT (1<<30) +void conf_checkbox_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_editbox_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_filesel_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_fontsel_handler(union control *ctrl, void *dlg, + void *data, int event); void setup_config_box(struct controlbox *b, int midsession, int protocol, int protcfginfo); @@ -1168,7 +1259,7 @@ void setup_config_box(struct controlbox *b, int midsession, * Exports from minibidi.c. */ typedef struct bidi_char { - wchar_t origwc, wc; + unsigned int origwc, wc; unsigned short index; } bidi_char; int do_bidi(bidi_char *line, int count); @@ -1188,11 +1279,18 @@ extern const char *const x11_authnames[]; /* declared in x11fwd.c */ /* * Miscellaneous exports from the platform-specific code. + * + * filename_serialise and filename_deserialise have the same semantics + * as fontspec_serialise and fontspec_deserialise above. */ -Filename filename_from_str(const char *string); +Filename *filename_from_str(const char *string); const char *filename_to_str(const Filename *fn); -int filename_equal(Filename f1, Filename f2); -int filename_is_null(Filename fn); +int filename_equal(const Filename *f1, const Filename *f2); +int filename_is_null(const Filename *fn); +Filename *filename_copy(const Filename *fn); +void filename_free(Filename *fn); +int filename_serialise(const Filename *f, void *data); +Filename *filename_deserialise(void *data, int maxsize, int *used); char *get_username(void); /* return value needs freeing */ char *get_random_data(int bytes); /* used in cmdgen.c */ @@ -1286,11 +1384,11 @@ char *get_random_data(int bytes); /* used in cmdgen.c */ * GETTICKCOUNT() and compare the result with the returned `next' * value to find out how long you have to make your next wait().) */ -typedef void (*timer_fn_t)(void *ctx, long now); -long schedule_timer(int ticks, timer_fn_t fn, void *ctx); +typedef void (*timer_fn_t)(void *ctx, unsigned long now); +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx); void expire_timer_context(void *ctx); -int run_timers(long now, long *next); -void timer_change_notify(long next); +int run_timers(unsigned long now, unsigned long *next); +void timer_change_notify(unsigned long next); /* * Define no-op macros for the jump list functions, on platforms that @@ -1303,4 +1401,29 @@ void timer_change_notify(long next); #define remove_session_from_jumplist(x) ((void)0) #endif +/* SURROGATE PAIR */ +#ifndef IS_HIGH_SURROGATE +#define HIGH_SURROGATE_START 0xd800 +#define HIGH_SURROGATE_END 0xdbff +#define LOW_SURROGATE_START 0xdc00 +#define LOW_SURROGATE_END 0xdfff + +#define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ + ((wch) <= HIGH_SURROGATE_END)) +#define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && \ + ((wch) <= LOW_SURROGATE_END)) +#define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && \ + IS_LOW_SURROGATE(ls)) +#endif + + +#define IS_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ + ((wch) <= LOW_SURROGATE_END)) +#define HIGH_SURROGATE_OF(codept) \ + (HIGH_SURROGATE_START + (((codept) - 0x10000) >> 10)) +#define LOW_SURROGATE_OF(codept) \ + (LOW_SURROGATE_START + (((codept) - 0x10000) & 0x3FF)) +#define FROM_SURROGATES(wch1, wch2) \ + (0x10000 + (((wch1) & 0x3FF) << 10) + ((wch2) & 0x3FF)) + #endif diff --git a/contrib/putty/PUTTYMEM.H b/contrib/putty/PUTTYMEM.H index d478f79..cf1afda 100644 --- a/contrib/putty/PUTTYMEM.H +++ b/contrib/putty/PUTTYMEM.H @@ -34,9 +34,19 @@ void safefree(void *); * possible, in favour of these type-casting macros which ensure * you don't mistakenly allocate enough space for one sort of * structure and assign it to a different sort of pointer. + * + * The nasty trick in sresize with sizeof arranges for the compiler, + * in passing, to type-check the expression ((type *)0 == (ptr)), i.e. + * to type-check that the input pointer is a pointer to the correct + * type. The construction sizeof(stuff) ? (b) : (b) looks like a + * violation of the first principle of safe macros, but in fact it's + * OK - although it _expands_ the macro parameter more than once, it + * only _evaluates_ it once, so it's still side-effect safe. */ #define snew(type) ((type *)snmalloc(1, sizeof(type))) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) -#define sresize(ptr, n, type) ((type *)snrealloc((ptr), (n), sizeof(type))) +#define sresize(ptr, n, type) \ + ((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ + (n), sizeof(type))) #endif diff --git a/contrib/putty/RAW.C b/contrib/putty/RAW.C index ea51d74..480a8f9 100644 --- a/contrib/putty/RAW.C +++ b/contrib/putty/RAW.C @@ -4,6 +4,7 @@ #include #include +#include #include "putty.h" @@ -21,8 +22,10 @@ typedef struct raw_backend_data { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; int bufsize; void *frontend; + int sent_console_eof, sent_socket_eof; } *Raw; static void raw_size(void *handle, int width, int height); @@ -47,6 +50,22 @@ static void raw_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(raw->frontend, msg); + sfree(msg); +} + +static void raw_check_close(Raw raw) +{ + /* + * Called after we send EOF on either the socket or the console. + * Its job is to wind up the session once we have sent EOF on both. + */ + if (raw->sent_console_eof && raw->sent_socket_eof) { + if (raw->s) { + sk_close(raw->s); + raw->s = NULL; + notify_remote_exit(raw->frontend); + } + } } static int raw_closing(Plug plug, const char *error_msg, int error_code, @@ -54,16 +73,32 @@ static int raw_closing(Plug plug, const char *error_msg, int error_code, { Raw raw = (Raw) plug; - if (raw->s) { - sk_close(raw->s); - raw->s = NULL; - notify_remote_exit(raw->frontend); - } if (error_msg) { - /* A socket error has occurred. */ - logevent(raw->frontend, error_msg); - connection_fatal(raw->frontend, "%s", error_msg); - } /* Otherwise, the remote side closed the connection normally. */ + /* A socket error has occurred. */ + if (raw->s) { + sk_close(raw->s); + raw->s = NULL; + raw->closed_on_socket_error = TRUE; + notify_remote_exit(raw->frontend); + } + logevent(raw->frontend, error_msg); + connection_fatal(raw->frontend, "%s", error_msg); + } else { + /* Otherwise, the remote side closed the connection normally. */ + if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) { + /* + * The front end wants us to close the outgoing side of the + * connection as soon as we see EOF from the far end. + */ + if (!raw->sent_socket_eof) { + if (raw->s) + sk_write_eof(raw->s); + raw->sent_socket_eof= TRUE; + } + } + raw->sent_console_eof = TRUE; + raw_check_close(raw); + } return 0; } @@ -89,7 +124,7 @@ static void raw_sent(Plug plug, int bufsize) * freed by the caller. */ static const char *raw_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -102,27 +137,32 @@ static const char *raw_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Raw raw; + int addressfamily; + char *loghost; raw = snew(struct raw_backend_data); raw->fn = &fn_table; raw->s = NULL; + raw->closed_on_socket_error = FALSE; *backend_handle = raw; + raw->sent_console_eof = raw->sent_socket_eof = FALSE; raw->frontend = frontend_handle; + addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(raw->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -135,15 +175,16 @@ static const char *raw_init(void *frontend_handle, void **backend_handle, * Open socket. */ raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive, - (Plug) raw, cfg); + (Plug) raw, conf); if ((err = sk_socket_error(raw->s)) != NULL) return err; - if (*cfg->loghost) { + loghost = conf_get_str(conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(cfg->loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -170,7 +211,7 @@ static void raw_free(void *handle) /* * Stub routine (we don't have any need to reconfigure this backend). */ -static void raw_reconfig(void *handle, Config *cfg) +static void raw_reconfig(void *handle, Conf *conf) { } @@ -208,11 +249,17 @@ static void raw_size(void *handle, int width, int height) } /* - * Send raw special codes. + * Send raw special codes. We only handle outgoing EOF here. */ static void raw_special(void *handle, Telnet_Special code) { - /* Do nothing! */ + Raw raw = (Raw) handle; + if (code == TS_EOF && raw->s) { + sk_write_eof(raw->s); + raw->sent_socket_eof= TRUE; + raw_check_close(raw); + } + return; } @@ -264,6 +311,8 @@ static int raw_exitcode(void *handle) Raw raw = (Raw) handle; if (raw->s != NULL) return -1; /* still connected */ + else if (raw->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* Exit codes are a meaningless concept in the Raw protocol */ return 0; diff --git a/contrib/putty/README b/contrib/putty/README index 9cca5c0..97aed71 100644 --- a/contrib/putty/README +++ b/contrib/putty/README @@ -40,7 +40,7 @@ For building on Windows: Makefile.bor' while in the `windows' subdirectory to build all the PuTTY binaries. - - windows/Makefile.cyg is for Cygwin / mingw32 installations. Type + - windows/Makefile.cyg is for Cygwin / MinGW installations. Type `make -f Makefile.cyg' while in the `windows' subdirectory to build all the PuTTY binaries. @@ -65,8 +65,12 @@ For building on Unix: - unix/configure is for Unix and GTK. If you don't have GTK, you should still be able to build the command-line utilities (PSCP, - PSFTP, Plink, PuTTYgen) using this script. To use it, change - into the `unix' subdirectory, run `./configure' and then `make'. + PSFTP, Plink, PuTTYgen) using this script. To use it, change into + the `unix' subdirectory, run `./configure' and then `make'. Or you + can do the same in the top-level directory (we provide a little + wrapper that invokes configure one level down), which is more like + a normal Unix source archive but doesn't do so well at keeping the + per-platform stuff in each platform's subdirectory; it's up to you. Note that Unix PuTTY has mostly only been tested on Linux so far; portability problems such as BSD-style ptys or different header file @@ -80,22 +84,46 @@ For building on Unix: utilities and has no Gtk dependence. - For the graphical utilities, Gtk+-1.2 and Gtk+-2.0 should both be - supported. - - - Both Unix Makefiles have an `install' target. Note that by default - it tries to install `man' pages, which you may need to have built - using Halibut first -- see below. + supported. If you have both installed, you can manually specify + which one you want by giving the option '--with-gtk=1' or + '--with-gtk=2' to the configure script. (2 is the default, of + course.) In the absence of either, the configure script will + automatically construct a Makefile which builds only the + command-line utilities; you can manually create this condition by + giving configure the option '--without-gtk'. + + - pterm would like to be setuid or setgid, as appropriate, to permit + it to write records of user logins to /var/run/utmp and + /var/log/wtmp. (Of course it will not use this privilege for + anything else, and in particular it will drop all privileges before + starting up complex subsystems like GTK.) By default the makefile + will not attempt to add privileges to the pterm executable at 'make + install' time, but you can ask it to do so by running configure + with the option '--enable-setuid=USER' or '--enable-setgid=GROUP'. + + - The Unix Makefiles have an `install' target. Note that by default + it tries to install `man' pages; if you have fetched the source via + Subversion then you will need to have built these using Halibut + first - see below. + + - It's also possible to build the Windows version of PuTTY to run + on Unix by using Winelib. To do this, change to the `windows' + directory and run `make -f Makefile.cyg CC=winegcc RC=wrc'. All of the Makefiles are generated automatically from the file -`Recipe' by the Perl script `mkfiles.pl'. Additions and corrections -to Recipe and the mkfiles.pl are much more useful than additions and -corrections to the alternative Makefiles themselves. +`Recipe' by the Perl script `mkfiles.pl' (except for the Unix one, +which is generated by the `configure' script; mkfiles.pl only +generates the input to automake). Additions and corrections to Recipe, +mkfiles.pl and/or configure.ac are much more useful than additions and +corrections to the actual Makefiles, Makefile.am or Makefile.in. The Unix `configure' script and its various requirements are generated by the shell script `mkauto.sh', which requires GNU Autoconf, GNU Automake, and Gtk; if you've got the source from Subversion rather than using one of our source snapshots, you'll need to run this -yourself. +yourself. The input file to Automake is generated by mkfiles.pl along +with all the rest of the makefiles, so you will need to run mkfiles.pl +and then mkauto.sh. Documentation (in various formats including Windows Help and Unix `man' pages) is built from the Halibut (`.but') files in the `doc' diff --git a/contrib/putty/RECIPE b/contrib/putty/RECIPE index cfc7ac9..e1d2c18 100644 --- a/contrib/putty/RECIPE +++ b/contrib/putty/RECIPE @@ -19,7 +19,7 @@ !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile unix unix/Makefile.ux -!makefile ac unix/Makefile.in +!makefile am unix/Makefile.am !makefile osx macosx/Makefile !makefile devcppproj windows/DEVCPP # Source directories. @@ -113,6 +113,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -169,6 +174,21 @@ version.o: FORCE fi !end !specialobj gtk version +# In the automake build, we have to do the whole job by supplying +# extra CFLAGS, so we have to put the if statement inside one big +# backtick expression. We also force rebuilding via a -D option that +# makes version.o include empty.h, which we construct ourselves and +# touch whenever any source file is updated. +!cflags am version $(VER) -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; md5sum -c manifest >/dev/null 2>&1); then cat $(srcdir)/../version.def; else echo "$(VER)"; fi` +!begin am +BUILT_SOURCES = empty.h +empty.h: $(allsources) + echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ + +!end +!begin >empty.h +/* Empty file touched by automake makefile to force rebuild of version.o */ +!end # Add VER to Windows resource targets, and force them to be rebuilt every # time, on the assumption that they will contain version information. @@ -177,6 +197,7 @@ CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32 RCFLAGS = $(RCFLAGS) $(VER) !end !begin cygwin vars +CFLAGS += -DSECURITY_WIN32 # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) !end @@ -228,6 +249,21 @@ install-strip: CFLAGS += -DMACOSX !end +# List the man pages for the automake makefile. +!begin am +man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \ + ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1 +!end + +# In automake, chgrp/chmod pterm after installation, if configured to. +!begin am +if HAVE_SETID_CMD +install-exec-local: + @SETID_CMD@ $(bindir)/pterm + chmod @SETID_MODE@ $(bindir)/pterm +endif +!end + # Random symbols. !begin cygwin vars # _WIN32_IE is required to expose identifiers that only make sense on @@ -245,7 +281,7 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 # Terminal emulator and its (platform-independent) dependencies. TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi - + config dialog + + config dialog conf # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint @@ -272,7 +308,7 @@ SFTP = sftp int64 logging # Miscellaneous objects appearing in all the network utilities (not # Pageant or PuTTYgen). -MISC = timing misc version settings tree234 proxy +MISC = timing misc version settings tree234 proxy conf WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy + wintime UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time @@ -315,12 +351,12 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils - + winmisc winhelp pageant.res LIBS + + winmisc winhelp conf pageant.res LIBS puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res - + tree234 notiming winhelp winnojmp LIBS wintime + + tree234 notiming winhelp winnojmp conf LIBS wintime pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg @@ -338,7 +374,7 @@ 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 sshsh256 sshsh512 import puttygen.res time tree234 - + uxgen notiming + + uxgen notiming conf pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC diff --git a/contrib/putty/RLOGIN.C b/contrib/putty/RLOGIN.C index 0abf8cd..ec1b21b 100644 --- a/contrib/putty/RLOGIN.C +++ b/contrib/putty/RLOGIN.C @@ -4,6 +4,7 @@ #include #include +#include #include #include "putty.h" @@ -22,13 +23,14 @@ typedef struct rlogin_tag { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; int bufsize; int firstbyte; int cansize; int term_width, term_height; void *frontend; - Config cfg; + Conf *conf; /* In case we need to read a username from the terminal before starting */ prompts_t *prompt; @@ -56,15 +58,25 @@ static void rlogin_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(rlogin->frontend, msg); + sfree(msg); } static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; + + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; + if (error_msg) + rlogin->closed_on_socket_error = TRUE; notify_remote_exit(rlogin->frontend); } if (error_msg) { @@ -122,18 +134,18 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser) { char z = 0; char *p; + sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, rlogin->cfg.localusername, - strlen(rlogin->cfg.localusername)); + p = conf_get_str(rlogin->conf, CONF_localusername); + sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, ruser, - strlen(ruser)); + sk_write(rlogin->s, ruser, strlen(ruser)); sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, rlogin->cfg.termtype, - strlen(rlogin->cfg.termtype)); + p = conf_get_str(rlogin->conf, CONF_termtype); + sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, "/", 1); - for (p = rlogin->cfg.termspeed; isdigit((unsigned char)*p); p++) continue; - sk_write(rlogin->s, rlogin->cfg.termspeed, p - rlogin->cfg.termspeed); + p = conf_get_str(rlogin->conf, CONF_termspeed); + sk_write(rlogin->s, p, strspn(p, "0123456789")); rlogin->bufsize = sk_write(rlogin->s, &z, 1); rlogin->prompt = NULL; @@ -148,7 +160,7 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser) * freed by the caller. */ static const char *rlogin_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -161,33 +173,37 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Rlogin rlogin; - char ruser[sizeof(cfg->username)]; + char *ruser; + int addressfamily; + char *loghost; rlogin = snew(struct rlogin_tag); rlogin->fn = &fn_table; rlogin->s = NULL; + rlogin->closed_on_socket_error = FALSE; rlogin->frontend = frontend_handle; - rlogin->term_width = cfg->width; - rlogin->term_height = cfg->height; + rlogin->term_width = conf_get_int(conf, CONF_width); + rlogin->term_height = conf_get_int(conf, CONF_height); rlogin->firstbyte = 1; rlogin->cansize = 0; rlogin->prompt = NULL; - rlogin->cfg = *cfg; /* STRUCTURE COPY */ + rlogin->conf = conf_copy(conf); *backend_handle = rlogin; + addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(rlogin->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -200,15 +216,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, * Open socket. */ rlogin->s = new_connection(addr, *realhost, port, 1, 0, - nodelay, keepalive, (Plug) rlogin, cfg); + nodelay, keepalive, (Plug) rlogin, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) return err; - if (*cfg->loghost) { + loghost = conf_get_str(conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(cfg->loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -226,16 +243,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, * in which case we prompt for it and may end up deferring doing * anything else until the local prompt mechanism returns. */ - if (get_remote_username(cfg, ruser, sizeof(ruser))) { + if ((ruser = get_remote_username(conf)) != NULL) { rlogin_startup(rlogin, ruser); + sfree(ruser); } else { int ret; rlogin->prompt = new_prompts(rlogin->frontend); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); - add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE, - sizeof(cfg->username)); + add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); ret = get_userpass_input(rlogin->prompt, NULL, 0); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); @@ -253,13 +270,14 @@ static void rlogin_free(void *handle) free_prompts(rlogin->prompt); if (rlogin->s) sk_close(rlogin->s); + conf_free(rlogin->conf); sfree(rlogin); } /* * Stub routine (we don't have any need to reconfigure this backend). */ -static void rlogin_reconfig(void *handle, Config *cfg) +static void rlogin_reconfig(void *handle, Conf *conf) { } @@ -380,6 +398,8 @@ static int rlogin_exitcode(void *handle) Rlogin rlogin = (Rlogin) handle; if (rlogin->s != NULL) return -1; /* still connected */ + else if (rlogin->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* If we ever implement RSH, we'll probably need to do this properly */ return 0; diff --git a/contrib/putty/SERCFG.C b/contrib/putty/SERCFG.C index 86a8a0b..4b86893 100644 --- a/contrib/putty/SERCFG.C +++ b/contrib/putty/SERCFG.C @@ -31,10 +31,14 @@ static void serial_parity_handler(union control *ctrl, void *dlg, }; int mask = ctrl->listbox.context.i; int i, j; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { - int oldparity = cfg->serparity;/* preserve past reentrant calls */ + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldparity = conf_get_int(conf, CONF_serparity); + dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(parities); i++) { @@ -56,14 +60,14 @@ static void serial_parity_handler(union control *ctrl, void *dlg, oldparity = SER_PAR_NONE; } dlg_update_done(ctrl, dlg); - cfg->serparity = oldparity; /* restore */ + conf_set_int(conf, CONF_serparity, oldparity); /* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_PAR_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); - cfg->serparity = i; + conf_set_int(conf, CONF_serparity, i); } } @@ -81,10 +85,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg, }; int mask = ctrl->listbox.context.i; int i, j; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { - int oldflow = cfg->serflow; /* preserve past reentrant calls */ + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldflow = conf_get_int(conf, CONF_serflow); + dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(flows); i++) { @@ -105,14 +113,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg, oldflow = SER_FLOW_NONE; } dlg_update_done(ctrl, dlg); - cfg->serflow = oldflow; /* restore */ + conf_set_int(conf, CONF_serflow, oldflow);/* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_FLOW_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); - cfg->serflow = i; + conf_set_int(conf, CONF_serflow, i); } } @@ -173,23 +181,22 @@ void ser_setup_config_box(struct controlbox *b, int midsession, "Select a serial line"); ctrl_editbox(s, "Serial line to connect to", 'l', 40, HELPCTX(serial_line), - dlg_stdeditbox_handler, I(offsetof(Config,serline)), - I(sizeof(((Config *)0)->serline))); + conf_editbox_handler, I(CONF_serline), I(1)); } s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); ctrl_editbox(s, "Speed (baud)", 's', 40, HELPCTX(serial_speed), - dlg_stdeditbox_handler, I(offsetof(Config,serspeed)), I(-1)); + conf_editbox_handler, I(CONF_serspeed), I(-1)); ctrl_editbox(s, "Data bits", 'b', 40, HELPCTX(serial_databits), - dlg_stdeditbox_handler,I(offsetof(Config,serdatabits)),I(-1)); + conf_editbox_handler, I(CONF_serdatabits), I(-1)); /* * Stop bits come in units of one half. */ ctrl_editbox(s, "Stop bits", 't', 40, HELPCTX(serial_stopbits), - dlg_stdeditbox_handler,I(offsetof(Config,serstopbits)),I(-2)); + conf_editbox_handler, I(CONF_serstopbits), I(-2)); ctrl_droplist(s, "Parity", 'p', 40, HELPCTX(serial_parity), serial_parity_handler, I(parity_mask)); diff --git a/contrib/putty/SETTINGS.C b/contrib/putty/SETTINGS.C index 9a62f7e..9a55781 100644 --- a/contrib/putty/SETTINGS.C +++ b/contrib/putty/SETTINGS.C @@ -70,67 +70,68 @@ Backend *backend_from_proto(int proto) return NULL; } -int get_remote_username(Config *cfg, char *user, size_t len) +char *get_remote_username(Conf *conf) { - if (*cfg->username) { - strncpy(user, cfg->username, len); - user[len-1] = '\0'; + char *username = conf_get_str(conf, CONF_username); + if (*username) { + return dupstr(username); + } else if (conf_get_int(conf, CONF_username_from_env)) { + /* Use local username. */ + return get_username(); /* might still be NULL */ } else { - if (cfg->username_from_env) { - /* Use local username. */ - char *luser = get_username(); - if (luser) { - strncpy(user, luser, len); - user[len-1] = '\0'; - sfree(luser); - } else { - *user = '\0'; - } - } else { - *user = '\0'; - } + return NULL; } - return (*user != '\0'); } -static void gpps(void *handle, const char *name, const char *def, - char *val, int len) +static char *gpps_raw(void *handle, const char *name, const char *def) { - if (!read_setting_s(handle, name, val, len)) { - char *pdef; - - pdef = platform_default_s(name); - if (pdef) { - strncpy(val, pdef, len); - sfree(pdef); - } else { - strncpy(val, def, len); - } + char *ret = read_setting_s(handle, name); + if (!ret) + ret = platform_default_s(name); + if (!ret) + ret = def ? dupstr(def) : NULL; /* permit NULL as final fallback */ + return ret; +} - val[len - 1] = '\0'; - } +static void gpps(void *handle, const char *name, const char *def, + Conf *conf, int primary) +{ + char *val = gpps_raw(handle, name, def); + conf_set_str(conf, primary, val); + sfree(val); } /* * gppfont and gppfile cannot have local defaults, since the very - * format of a Filename or Font is platform-dependent. So the + * format of a Filename or FontSpec is platform-dependent. So the * platform-dependent functions MUST return some sort of value. */ -static void gppfont(void *handle, const char *name, FontSpec *result) +static void gppfont(void *handle, const char *name, Conf *conf, int primary) { - if (!read_setting_fontspec(handle, name, result)) - *result = platform_default_fontspec(name); + FontSpec *result = read_setting_fontspec(handle, name); + if (!result) + result = platform_default_fontspec(name); + conf_set_fontspec(conf, primary, result); + fontspec_free(result); } -static void gppfile(void *handle, const char *name, Filename *result) +static void gppfile(void *handle, const char *name, Conf *conf, int primary) { - if (!read_setting_filename(handle, name, result)) - *result = platform_default_filename(name); + Filename *result = read_setting_filename(handle, name); + if (!result) + result = platform_default_filename(name); + conf_set_filename(conf, primary, result); + filename_free(result); } -static void gppi(void *handle, char *name, int def, int *i) +static int gppi_raw(void *handle, char *name, int def) { def = platform_default_i(name, def); - *i = read_setting_i(handle, name, def); + return read_setting_i(handle, name, def); +} + +static void gppi(void *handle, char *name, int def, Conf *conf, int primary) +{ + conf_set_int(conf, primary, gppi_raw(handle, name, def)); } /* @@ -139,52 +140,126 @@ static void gppi(void *handle, char *name, int def, int *i) * NAME=VALUE,NAME=VALUE, in storage * `def' is in the storage format. */ -static void gppmap(void *handle, char *name, char *def, char *val, int len) +static int gppmap(void *handle, char *name, Conf *conf, int primary) { - char *buf = snewn(2*len, char), *p, *q; - gpps(handle, name, def, buf, 2*len); + char *buf, *p, *q, *key, *val; + + /* + * Start by clearing any existing subkeys of this key from conf. + */ + while ((key = conf_get_str_nthstrkey(conf, primary, 0)) != NULL) + conf_del_str_str(conf, primary, key); + + /* + * Now read a serialised list from the settings and unmarshal it + * into its components. + */ + buf = gpps_raw(handle, name, NULL); + if (!buf) + return FALSE; + p = buf; - q = val; while (*p) { + q = buf; + val = NULL; while (*p && *p != ',') { int c = *p++; if (c == '=') - c = '\t'; + c = '\0'; if (c == '\\') c = *p++; *q++ = c; + if (!c) + val = q; } if (*p == ',') p++; - *q++ = '\0'; + if (!val) + val = q; + *q = '\0'; + + if (primary == CONF_portfwd && buf[0] == 'D') { + /* + * Backwards-compatibility hack: dynamic forwardings are + * indexed in the data store as a third type letter in the + * key, 'D' alongside 'L' and 'R' - but really, they + * should be filed under 'L' with a special _value_, + * because local and dynamic forwardings both involve + * _listening_ on a local port, and are hence mutually + * exclusive on the same port number. So here we translate + * the legacy storage format into the sensible internal + * form. + */ + char *newkey = dupcat("L", buf+1, NULL); + conf_set_str_str(conf, primary, newkey, "D"); + sfree(newkey); + } else { + conf_set_str_str(conf, primary, buf, val); + } } - *q = '\0'; sfree(buf); + + return TRUE; } /* * Write a set of name/value pairs in the above format. */ -static void wmap(void *handle, char const *key, char const *value, int len) +static void wmap(void *handle, char const *outkey, Conf *conf, int primary) { - char *buf = snewn(2*len, char), *p; - const char *q; + char *buf, *p, *q, *key, *realkey, *val; + int len; + + len = 1; /* allow for NUL */ + + for (val = conf_get_str_strs(conf, primary, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, primary, key, &key)) + len += 2 + 2 * (strlen(key) + strlen(val)); /* allow for escaping */ + + buf = snewn(len, char); p = buf; - q = value; - while (*q) { - while (*q) { - int c = *q++; - if (c == '=' || c == ',' || c == '\\') + + for (val = conf_get_str_strs(conf, primary, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, primary, key, &key)) { + + if (primary == CONF_portfwd && !strcmp(val, "D")) { + /* + * Backwards-compatibility hack, as above: translate from + * the sensible internal representation of dynamic + * forwardings (key "L", value "D") to the + * conceptually incoherent legacy storage format (key + * "D", value empty). + */ + realkey = key; /* restore it at end of loop */ + val = ""; + key = dupcat("D", key+1, NULL); + } else { + realkey = NULL; + } + + if (p != buf) + *p++ = ','; + for (q = key; *q; q++) { + if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; - if (c == '\t') - c = '='; - *p++ = c; + *p++ = *q; } - *p++ = ','; - q++; + *p++ = '='; + for (q = val; *q; q++) { + if (*q == '=' || *q == ',' || *q == '\\') + *p++ = '\\'; + *p++ = *q; + } + + if (realkey) { + free(key); + key = realkey; + } } *p = '\0'; - write_setting_s(handle, key, buf); + write_setting_s(handle, outkey, buf); sfree(buf); } @@ -214,9 +289,9 @@ static const char *val2key(const struct keyvalwhere *mapping, */ static void gprefs(void *sesskey, char *name, char *def, const struct keyvalwhere *mapping, int nvals, - int *array) + Conf *conf, int primary) { - char commalist[256]; + char *commalist; char *p, *q; int i, j, n, v, pos; unsigned long seen = 0; /* bitmap for weeding dups etc */ @@ -224,7 +299,7 @@ static void gprefs(void *sesskey, char *name, char *def, /* * Fetch the string which we'll parse as a comma-separated list. */ - gpps(sesskey, name, def, commalist, sizeof(commalist)); + commalist = gpps_raw(sesskey, name, def); /* * Go through that list and convert it into values. @@ -243,10 +318,13 @@ static void gprefs(void *sesskey, char *name, char *def, v = key2val(mapping, nvals, q); if (v != -1 && !(seen & (1 << v))) { seen |= (1 << v); - array[n++] = v; + conf_set_int_int(conf, primary, n, v); + n++; } } + sfree(commalist); + /* * Now go through 'mapping' and add values that weren't mentioned * in the list we fetched. We may have to loop over it multiple @@ -272,7 +350,8 @@ static void gprefs(void *sesskey, char *name, char *def, pos = (mapping[i].where < 0 ? n : 0); } else { for (j = 0; j < n; j++) - if (array[j] == mapping[i].vrel) + if (conf_get_int_int(conf, primary, j) == + mapping[i].vrel) break; assert(j < n); /* implied by (seen & (1<= pos; j--) - array[j+1] = array[j]; - array[pos] = mapping[i].v; + conf_set_int_int(conf, primary, j+1, + conf_get_int_int(conf, primary, j)); + conf_set_int_int(conf, primary, pos, mapping[i].v); n++; } } @@ -295,13 +375,14 @@ static void gprefs(void *sesskey, char *name, char *def, */ static void wprefs(void *sesskey, char *name, const struct keyvalwhere *mapping, int nvals, - int *array) + Conf *conf, int primary) { char *buf, *p; int i, maxlen; for (maxlen = i = 0; i < nvals; i++) { - const char *s = val2key(mapping, nvals, array[i]); + const char *s = val2key(mapping, nvals, + conf_get_int_int(conf, primary, i)); if (s) { maxlen += (maxlen > 0 ? 1 : 0) + strlen(s); } @@ -311,7 +392,8 @@ static void wprefs(void *sesskey, char *name, p = buf; for (i = 0; i < nvals; i++) { - const char *s = val2key(mapping, nvals, array[i]); + const char *s = val2key(mapping, nvals, + conf_get_int_int(conf, primary, i)); if (s) { p += sprintf(p, "%s%s", (p > buf ? "," : ""), s); } @@ -325,7 +407,7 @@ static void wprefs(void *sesskey, char *name, sfree(buf); } -char *save_settings(char *section, Config * cfg) +char *save_settings(char *section, Conf *conf) { void *sesskey; char *errmsg; @@ -333,169 +415,169 @@ char *save_settings(char *section, Config * cfg) sesskey = open_settings_w(section, &errmsg); if (!sesskey) return errmsg; - save_open_settings(sesskey, cfg); + save_open_settings(sesskey, conf); close_settings_w(sesskey); return NULL; } -void save_open_settings(void *sesskey, Config *cfg) +void save_open_settings(void *sesskey, Conf *conf) { int i; char *p; write_setting_i(sesskey, "Present", 1); - 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); - write_setting_i(sesskey, "LogFlush", cfg->logflush); - write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass); - write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata); + write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host)); + write_setting_filename(sesskey, "LogFileName", conf_get_filename(conf, CONF_logfilename)); + write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype)); + write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr)); + write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush)); + write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass)); + write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata)); p = "raw"; { - const Backend *b = backend_from_proto(cfg->protocol); + const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (b) p = b->name; } write_setting_s(sesskey, "Protocol", p); - write_setting_i(sesskey, "PortNumber", cfg->port); + write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port)); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ - write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3); - write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close); - write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */ - write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60); /* seconds */ - write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay); - write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives); - write_setting_s(sesskey, "TerminalType", cfg->termtype); - write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed); - wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes)); + write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3); + write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close)); + write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */ + write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */ + write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay)); + write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives)); + write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype)); + write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed)); + wmap(sesskey, "TerminalModes", conf, CONF_ttymodes); /* Address family selection */ - write_setting_i(sesskey, "AddressFamily", cfg->addressfamily); + write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily)); /* proxy settings */ - write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list); - write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3); - write_setting_i(sesskey, "ProxyLocalhost", cfg->even_proxy_localhost); - write_setting_i(sesskey, "ProxyMethod", cfg->proxy_type); - write_setting_s(sesskey, "ProxyHost", cfg->proxy_host); - write_setting_i(sesskey, "ProxyPort", cfg->proxy_port); - write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username); - write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password); - write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command); - wmap(sesskey, "Environment", cfg->environmt, lenof(cfg->environmt)); - write_setting_s(sesskey, "UserName", cfg->username); - write_setting_i(sesskey, "UserNameFromEnvironment", cfg->username_from_env); - write_setting_s(sesskey, "LocalUserName", cfg->localusername); - write_setting_i(sesskey, "NoPTY", cfg->nopty); - write_setting_i(sesskey, "Compression", cfg->compression); - write_setting_i(sesskey, "TryAgent", cfg->tryagent); - write_setting_i(sesskey, "AgentFwd", cfg->agentfwd); - write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd); - write_setting_i(sesskey, "ChangeUsername", cfg->change_username); - wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, - cfg->ssh_cipherlist); - wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist); - write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time); - write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data); - write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth); - write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner); - write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth); - write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth); - write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth); + write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list)); + write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3); + write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost)); + write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type)); + write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host)); + write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port)); + write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username)); + write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password)); + write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command)); + wmap(sesskey, "Environment", conf, CONF_environmt); + write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); + write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env)); + write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); + write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty)); + write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression)); + write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent)); + write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd)); + write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd)); + write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username)); + wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); + wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); + write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); + write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); + write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth)); + write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner)); + write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth)); + write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth)); + write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth)); #ifndef NO_GSSAPI - wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, - cfg->ssh_gsslist); - write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom); + wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); + write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom)); #endif - write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell); - write_setting_i(sesskey, "SshProt", cfg->sshprot); - write_setting_s(sesskey, "LogHost", cfg->loghost); - write_setting_i(sesskey, "SSH2DES", cfg->ssh2_des_cbc); - write_setting_filename(sesskey, "PublicKeyFile", cfg->keyfile); - write_setting_s(sesskey, "RemoteCommand", cfg->remote_cmd); - write_setting_i(sesskey, "RFCEnviron", cfg->rfc_environ); - write_setting_i(sesskey, "PassiveTelnet", cfg->passive_telnet); - write_setting_i(sesskey, "BackspaceIsDelete", cfg->bksp_is_delete); - write_setting_i(sesskey, "RXVTHomeEnd", cfg->rxvt_homeend); - write_setting_i(sesskey, "LinuxFunctionKeys", cfg->funky_type); - write_setting_i(sesskey, "NoApplicationKeys", cfg->no_applic_k); - write_setting_i(sesskey, "NoApplicationCursors", cfg->no_applic_c); - write_setting_i(sesskey, "NoMouseReporting", cfg->no_mouse_rep); - write_setting_i(sesskey, "NoRemoteResize", cfg->no_remote_resize); - write_setting_i(sesskey, "NoAltScreen", cfg->no_alt_screen); - write_setting_i(sesskey, "NoRemoteWinTitle", cfg->no_remote_wintitle); - write_setting_i(sesskey, "RemoteQTitleAction", cfg->remote_qtitle_action); - write_setting_i(sesskey, "NoDBackspace", cfg->no_dbackspace); - write_setting_i(sesskey, "NoRemoteCharset", cfg->no_remote_charset); - write_setting_i(sesskey, "ApplicationCursorKeys", cfg->app_cursor); - write_setting_i(sesskey, "ApplicationKeypad", cfg->app_keypad); - write_setting_i(sesskey, "NetHackKeypad", cfg->nethack_keypad); - write_setting_i(sesskey, "AltF4", cfg->alt_f4); - write_setting_i(sesskey, "AltSpace", cfg->alt_space); - write_setting_i(sesskey, "AltOnly", cfg->alt_only); - write_setting_i(sesskey, "ComposeKey", cfg->compose_key); - write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys); - write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard); - write_setting_i(sesskey, "TelnetRet", cfg->telnet_newline); - write_setting_i(sesskey, "LocalEcho", cfg->localecho); - write_setting_i(sesskey, "LocalEdit", cfg->localedit); - write_setting_s(sesskey, "Answerback", cfg->answerback); - write_setting_i(sesskey, "AlwaysOnTop", cfg->alwaysontop); - write_setting_i(sesskey, "FullScreenOnAltEnter", cfg->fullscreenonaltenter); - write_setting_i(sesskey, "HideMousePtr", cfg->hide_mouseptr); - write_setting_i(sesskey, "SunkenEdge", cfg->sunken_edge); - write_setting_i(sesskey, "WindowBorder", cfg->window_border); - write_setting_i(sesskey, "CurType", cfg->cursor_type); - write_setting_i(sesskey, "BlinkCur", cfg->blink_cur); - write_setting_i(sesskey, "Beep", cfg->beep); - write_setting_i(sesskey, "BeepInd", cfg->beep_ind); - write_setting_filename(sesskey, "BellWaveFile", cfg->bell_wavefile); - write_setting_i(sesskey, "BellOverload", cfg->bellovl); - write_setting_i(sesskey, "BellOverloadN", cfg->bellovl_n); - write_setting_i(sesskey, "BellOverloadT", cfg->bellovl_t + write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell)); + write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot)); + write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost)); + write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc)); + write_setting_filename(sesskey, "PublicKeyFile", conf_get_filename(conf, CONF_keyfile)); + write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd)); + write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ)); + write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet)); + write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete)); + write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend)); + write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type)); + write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k)); + write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c)); + write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep)); + write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize)); + write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen)); + write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle)); + write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action)); + write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace)); + write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset)); + write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor)); + write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad)); + write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad)); + write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4)); + write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space)); + write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only)); + write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key)); + write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys)); + write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard)); + write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline)); + write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho)); + write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit)); + write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback)); + write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop)); + write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter)); + write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr)); + write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge)); + write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border)); + write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type)); + write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur)); + write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep)); + write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind)); + write_setting_filename(sesskey, "BellWaveFile", conf_get_filename(conf, CONF_bell_wavefile)); + write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl)); + write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n)); + write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t) #ifdef PUTTY_UNIX_H * 1000 #endif ); - write_setting_i(sesskey, "BellOverloadS", cfg->bellovl_s + write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s) #ifdef PUTTY_UNIX_H * 1000 #endif ); - write_setting_i(sesskey, "ScrollbackLines", cfg->savelines); - 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); - write_setting_s(sesskey, "WinTitle", cfg->wintitle); - write_setting_i(sesskey, "TermWidth", cfg->width); - write_setting_i(sesskey, "TermHeight", cfg->height); - write_setting_fontspec(sesskey, "Font", cfg->font); - write_setting_i(sesskey, "FontQuality", cfg->font_quality); - write_setting_i(sesskey, "FontVTMode", cfg->vtmode); - write_setting_i(sesskey, "UseSystemColours", cfg->system_colour); - write_setting_i(sesskey, "TryPalette", cfg->try_palette); - write_setting_i(sesskey, "ANSIColour", cfg->ansi_colour); - write_setting_i(sesskey, "Xterm256Colour", cfg->xterm_256_colour); - write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour); + write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines)); + write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om)); + write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode)); + write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr)); + write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf)); + write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping)); + write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi)); + write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always)); + write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle)); + write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width)); + write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height)); + write_setting_fontspec(sesskey, "Font", conf_get_fontspec(conf, CONF_font)); + write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality)); + write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode)); + write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour)); + write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette)); + write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour)); + write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour)); + write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_style)-1); for (i = 0; i < 22; i++) { char buf[20], buf2[30]; sprintf(buf, "Colour%d", i); - sprintf(buf2, "%d,%d,%d", cfg->colours[i][0], - cfg->colours[i][1], cfg->colours[i][2]); + sprintf(buf2, "%d,%d,%d", + conf_get_int_int(conf, CONF_colours, i*3+0), + conf_get_int_int(conf, CONF_colours, i*3+1), + conf_get_int_int(conf, CONF_colours, i*3+2)); write_setting_s(sesskey, buf, buf2); } - write_setting_i(sesskey, "RawCNP", cfg->rawcnp); - write_setting_i(sesskey, "PasteRTF", cfg->rtf_paste); - write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm); - write_setting_i(sesskey, "RectSelect", cfg->rect_select); - write_setting_i(sesskey, "MouseOverride", cfg->mouse_override); + write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp)); + write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste)); + write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); + write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select)); + write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override)); for (i = 0; i < 256; i += 32) { char buf[20], buf2[256]; int j; @@ -503,305 +585,288 @@ void save_open_settings(void *sesskey, Config *cfg) *buf2 = '\0'; for (j = i; j < i + 32; j++) { sprintf(buf2 + strlen(buf2), "%s%d", - (*buf2 ? "," : ""), cfg->wordness[j]); + (*buf2 ? "," : ""), + conf_get_int_int(conf, CONF_wordness, j)); } write_setting_s(sesskey, buf, buf2); } - write_setting_s(sesskey, "LineCodePage", cfg->line_codepage); - write_setting_i(sesskey, "CJKAmbigWide", cfg->cjk_ambig_wide); - write_setting_i(sesskey, "UTF8Override", cfg->utf8_override); - write_setting_s(sesskey, "Printer", cfg->printer); - write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr); - write_setting_i(sesskey, "ScrollBar", cfg->scrollbar); - write_setting_i(sesskey, "ScrollBarFullScreen", cfg->scrollbar_in_fullscreen); - write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key); - write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp); - write_setting_i(sesskey, "EraseToScrollback", cfg->erase_to_scrollback); - write_setting_i(sesskey, "LockSize", cfg->resize_action); - write_setting_i(sesskey, "BCE", cfg->bce); - write_setting_i(sesskey, "BlinkText", cfg->blinktext); - write_setting_i(sesskey, "X11Forward", cfg->x11_forward); - write_setting_s(sesskey, "X11Display", cfg->x11_display); - write_setting_i(sesskey, "X11AuthType", cfg->x11_auth); - write_setting_filename(sesskey, "X11AuthFile", cfg->xauthfile); - write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall); - write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall); - wmap(sesskey, "PortForwardings", cfg->portfwd, lenof(cfg->portfwd)); - write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1); - write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1); - write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1); - write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2); - write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2); - write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2); - 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); - write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont); - write_setting_fontspec(sesskey, "WideFont", cfg->widefont); - write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont); - write_setting_i(sesskey, "ShadowBold", cfg->shadowbold); - write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset); - write_setting_s(sesskey, "SerialLine", cfg->serline); - write_setting_i(sesskey, "SerialSpeed", cfg->serspeed); - write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits); - write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits); - write_setting_i(sesskey, "SerialParity", cfg->serparity); - write_setting_i(sesskey, "SerialFlowControl", cfg->serflow); - write_setting_s(sesskey, "WindowClass", cfg->winclass); + write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage)); + write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide)); + write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override)); + write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer)); + write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr)); + write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar)); + write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen)); + write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key)); + write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp)); + write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback)); + write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action)); + write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce)); + write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext)); + write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward)); + write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display)); + write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth)); + write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile)); + write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall)); + write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall)); + wmap(sesskey, "PortForwardings", conf, CONF_portfwd); + write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1)); + write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1)); + write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1)); + write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2)); + write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2)); + write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2)); + write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2)); + write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2)); + write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2)); + write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2)); + write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); + write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp)); + write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell)); + write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left)); + write_setting_fontspec(sesskey, "BoldFont", conf_get_fontspec(conf, CONF_boldfont)); + write_setting_fontspec(sesskey, "WideFont", conf_get_fontspec(conf, CONF_widefont)); + write_setting_fontspec(sesskey, "WideBoldFont", conf_get_fontspec(conf, CONF_wideboldfont)); + write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold)); + write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset)); + write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline)); + write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed)); + write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits)); + write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits)); + write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity)); + write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow)); + write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass)); } -void load_settings(char *section, Config * cfg) +void load_settings(char *section, Conf *conf) { void *sesskey; sesskey = open_settings_r(section); - load_open_settings(sesskey, cfg); + load_open_settings(sesskey, conf); close_settings_r(sesskey); - if (cfg_launchable(cfg)) + if (conf_launchable(conf)) add_session_to_jumplist(section); } -void load_open_settings(void *sesskey, Config *cfg) +void load_open_settings(void *sesskey, Conf *conf) { int i; - char prot[10]; - - cfg->ssh_subsys = 0; /* FIXME: load this properly */ - cfg->remote_cmd_ptr = NULL; - cfg->remote_cmd_ptr2 = NULL; - cfg->ssh_nc_host[0] = '\0'; - - 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); - gppi(sesskey, "LogFlush", 1, &cfg->logflush); - gppi(sesskey, "SSHLogOmitPasswords", 1, &cfg->logomitpass); - gppi(sesskey, "SSHLogOmitData", 0, &cfg->logomitdata); - - gpps(sesskey, "Protocol", "default", prot, 10); - cfg->protocol = default_protocol; - cfg->port = default_port; + char *prot; + + conf_set_int(conf, CONF_ssh_subsys, 0); /* FIXME: load this properly */ + conf_set_str(conf, CONF_remote_cmd, ""); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_str(conf, CONF_ssh_nc_host, ""); + + gpps(sesskey, "HostName", "", conf, CONF_host); + gppfile(sesskey, "LogFileName", conf, CONF_logfilename); + gppi(sesskey, "LogType", 0, conf, CONF_logtype); + gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr); + gppi(sesskey, "LogFlush", 1, conf, CONF_logflush); + gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass); + gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata); + + prot = gpps_raw(sesskey, "Protocol", "default"); + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); { const Backend *b = backend_from_name(prot); if (b) { - cfg->protocol = b->protocol; - gppi(sesskey, "PortNumber", default_port, &cfg->port); + conf_set_int(conf, CONF_protocol, b->protocol); + gppi(sesskey, "PortNumber", default_port, conf, CONF_port); } } + sfree(prot); /* Address family selection */ - gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily); + gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ - gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3; - gppi(sesskey, "WarnOnClose", 1, &cfg->warn_on_close); + i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3); + gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close); { /* This is two values for backward compatibility with 0.50/0.51 */ int pingmin, pingsec; - gppi(sesskey, "PingInterval", 0, &pingmin); - gppi(sesskey, "PingIntervalSecs", 0, &pingsec); - cfg->ping_interval = pingmin * 60 + pingsec; + pingmin = gppi_raw(sesskey, "PingInterval", 0); + pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0); + conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec); } - gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay); - gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives); - gpps(sesskey, "TerminalType", "xterm", cfg->termtype, - sizeof(cfg->termtype)); - gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed, - sizeof(cfg->termspeed)); - { + gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay); + gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives); + gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype); + gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed); + if (!gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) { /* This hardcodes a big set of defaults in any new saved * sessions. Let's hope we don't change our mind. */ - int i; - char *def = dupstr(""); - /* Default: all set to "auto" */ - for (i = 0; ttymodes[i]; i++) { - char *def2 = dupprintf("%s%s=A,", def, ttymodes[i]); - sfree(def); - def = def2; - } - gppmap(sesskey, "TerminalModes", def, - cfg->ttymodes, lenof(cfg->ttymodes)); - sfree(def); + for (i = 0; ttymodes[i]; i++) + conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A"); } /* proxy settings */ - gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list, - sizeof(cfg->proxy_exclude_list)); - gppi(sesskey, "ProxyDNS", 1, &i); cfg->proxy_dns = (i+1)%3; - gppi(sesskey, "ProxyLocalhost", 0, &cfg->even_proxy_localhost); - gppi(sesskey, "ProxyMethod", -1, &cfg->proxy_type); - if (cfg->proxy_type == -1) { + gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list); + i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3); + gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost); + gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type); + if (conf_get_int(conf, CONF_proxy_type) == -1) { int i; - gppi(sesskey, "ProxyType", 0, &i); + i = gppi_raw(sesskey, "ProxyType", 0); if (i == 0) - cfg->proxy_type = PROXY_NONE; + conf_set_int(conf, CONF_proxy_type, PROXY_NONE); else if (i == 1) - cfg->proxy_type = PROXY_HTTP; + conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); else if (i == 3) - cfg->proxy_type = PROXY_TELNET; + conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); else if (i == 4) - cfg->proxy_type = PROXY_CMD; + conf_set_int(conf, CONF_proxy_type, PROXY_CMD); else { - gppi(sesskey, "ProxySOCKSVersion", 5, &i); + i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); if (i == 5) - cfg->proxy_type = PROXY_SOCKS5; + conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5); else - cfg->proxy_type = PROXY_SOCKS4; + conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4); } } - gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host, - sizeof(cfg->proxy_host)); - gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port); - gpps(sesskey, "ProxyUsername", "", cfg->proxy_username, - sizeof(cfg->proxy_username)); - gpps(sesskey, "ProxyPassword", "", cfg->proxy_password, - sizeof(cfg->proxy_password)); + gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host); + gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port); + gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username); + gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password); gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n", - cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command)); - gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt)); - gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username)); - gppi(sesskey, "UserNameFromEnvironment", 0, &cfg->username_from_env); - gpps(sesskey, "LocalUserName", "", cfg->localusername, - sizeof(cfg->localusername)); - gppi(sesskey, "NoPTY", 0, &cfg->nopty); - gppi(sesskey, "Compression", 0, &cfg->compression); - gppi(sesskey, "TryAgent", 1, &cfg->tryagent); - gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd); - gppi(sesskey, "ChangeUsername", 0, &cfg->change_username); - gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd); + conf, CONF_proxy_telnet_command); + gppmap(sesskey, "Environment", conf, CONF_environmt); + gpps(sesskey, "UserName", "", conf, CONF_username); + gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env); + gpps(sesskey, "LocalUserName", "", conf, CONF_localusername); + gppi(sesskey, "NoPTY", 0, conf, CONF_nopty); + gppi(sesskey, "Compression", 0, conf, CONF_compression); + gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent); + gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd); + gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username); + gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd); gprefs(sesskey, "Cipher", "\0", - ciphernames, CIPHER_MAX, cfg->ssh_cipherlist); + ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); { /* Backward-compatibility: we used to have an option to * disable gex under the "bugs" panel after one report of * a server which offered it then choked, but we never got * a server version string or any other reports. */ char *default_kexes; - gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i; + i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0); if (i == FORCE_ON) 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,rsa,WARN"; gprefs(sesskey, "KEX", default_kexes, - kexnames, KEX_MAX, cfg->ssh_kexlist); + kexnames, KEX_MAX, conf, CONF_ssh_kexlist); } - gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time); - gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data, - sizeof(cfg->ssh_rekey_data)); - gppi(sesskey, "SshProt", 2, &cfg->sshprot); - gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost)); - gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc); - gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth); - gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner); - gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth); - gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth); - gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth); + gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); + gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); + gppi(sesskey, "SshProt", 2, conf, CONF_sshprot); + gpps(sesskey, "LogHost", "", conf, CONF_loghost); + gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc); + gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth); + gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner); + gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth); + gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth); + gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth); #ifndef NO_GSSAPI gprefs(sesskey, "GSSLibs", "\0", - gsslibkeywords, ngsslibs, cfg->ssh_gsslist); - gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom); + gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); + gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif - gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell); - gppfile(sesskey, "PublicKeyFile", &cfg->keyfile); - gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd, - sizeof(cfg->remote_cmd)); - gppi(sesskey, "RFCEnviron", 0, &cfg->rfc_environ); - gppi(sesskey, "PassiveTelnet", 0, &cfg->passive_telnet); - gppi(sesskey, "BackspaceIsDelete", 1, &cfg->bksp_is_delete); - gppi(sesskey, "RXVTHomeEnd", 0, &cfg->rxvt_homeend); - gppi(sesskey, "LinuxFunctionKeys", 0, &cfg->funky_type); - gppi(sesskey, "NoApplicationKeys", 0, &cfg->no_applic_k); - gppi(sesskey, "NoApplicationCursors", 0, &cfg->no_applic_c); - gppi(sesskey, "NoMouseReporting", 0, &cfg->no_mouse_rep); - gppi(sesskey, "NoRemoteResize", 0, &cfg->no_remote_resize); - gppi(sesskey, "NoAltScreen", 0, &cfg->no_alt_screen); - gppi(sesskey, "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle); + gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell); + gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile); + gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd); + gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ); + gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet); + gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete); + gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend); + gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type); + gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k); + gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c); + gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep); + gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize); + gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen); + gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle); { /* Backward compatibility */ - int no_remote_qtitle; - gppi(sesskey, "NoRemoteQTitle", 1, &no_remote_qtitle); + int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1); /* We deliberately interpret the old setting of "no response" as * "empty string". This changes the behaviour, but hopefully for * the better; the user can always recover the old behaviour. */ gppi(sesskey, "RemoteQTitleAction", no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL, - &cfg->remote_qtitle_action); + conf, CONF_remote_qtitle_action); } - gppi(sesskey, "NoDBackspace", 0, &cfg->no_dbackspace); - gppi(sesskey, "NoRemoteCharset", 0, &cfg->no_remote_charset); - gppi(sesskey, "ApplicationCursorKeys", 0, &cfg->app_cursor); - gppi(sesskey, "ApplicationKeypad", 0, &cfg->app_keypad); - gppi(sesskey, "NetHackKeypad", 0, &cfg->nethack_keypad); - gppi(sesskey, "AltF4", 1, &cfg->alt_f4); - gppi(sesskey, "AltSpace", 0, &cfg->alt_space); - gppi(sesskey, "AltOnly", 0, &cfg->alt_only); - gppi(sesskey, "ComposeKey", 0, &cfg->compose_key); - gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys); - gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard); - gppi(sesskey, "TelnetRet", 1, &cfg->telnet_newline); - gppi(sesskey, "LocalEcho", AUTO, &cfg->localecho); - gppi(sesskey, "LocalEdit", AUTO, &cfg->localedit); - gpps(sesskey, "Answerback", "PuTTY", cfg->answerback, - sizeof(cfg->answerback)); - gppi(sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop); - gppi(sesskey, "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter); - gppi(sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr); - gppi(sesskey, "SunkenEdge", 0, &cfg->sunken_edge); - gppi(sesskey, "WindowBorder", 1, &cfg->window_border); - gppi(sesskey, "CurType", 0, &cfg->cursor_type); - gppi(sesskey, "BlinkCur", 0, &cfg->blink_cur); - /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */ - gppi(sesskey, "Beep", 1, &cfg->beep); - gppi(sesskey, "BeepInd", 0, &cfg->beep_ind); - gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile); - gppi(sesskey, "BellOverload", 1, &cfg->bellovl); - gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n); - gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC + gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace); + gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset); + gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor); + gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad); + gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad); + gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4); + gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space); + gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only); + gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key); + gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys); + gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard); + gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline); + gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho); + gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit); + gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback); + gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop); + gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter); + gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr); + gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge); + gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); + gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); + gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur); + /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */ + gppi(sesskey, "Beep", 1, conf, CONF_beep); + gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind); + gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile); + gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl); + gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n); + i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif - , &i); - cfg->bellovl_t = i + ); + conf_set_int(conf, CONF_bellovl_t, i #ifdef PUTTY_UNIX_H - / 1000 + / 1000 #endif - ; - gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC + ); + i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif - , &i); - cfg->bellovl_s = i + ); + conf_set_int(conf, CONF_bellovl_s, i #ifdef PUTTY_UNIX_H - / 1000 + / 1000 #endif - ; - gppi(sesskey, "ScrollbackLines", 200, &cfg->savelines); - 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); - gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle)); - gppi(sesskey, "TermWidth", 80, &cfg->width); - gppi(sesskey, "TermHeight", 24, &cfg->height); - gppfont(sesskey, "Font", &cfg->font); - gppi(sesskey, "FontQuality", FQ_DEFAULT, &cfg->font_quality); - gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode); - gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour); - gppi(sesskey, "TryPalette", 0, &cfg->try_palette); - gppi(sesskey, "ANSIColour", 1, &cfg->ansi_colour); - gppi(sesskey, "Xterm256Colour", 1, &cfg->xterm_256_colour); - gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour); + ); + gppi(sesskey, "ScrollbackLines", 2000, conf, CONF_savelines); + gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om); + gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode); + gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr); + gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf); + gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping); + gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi); + gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always); + gpps(sesskey, "WinTitle", "", conf, CONF_wintitle); + gppi(sesskey, "TermWidth", 80, conf, CONF_width); + gppi(sesskey, "TermHeight", 24, conf, CONF_height); + gppfont(sesskey, "Font", conf, CONF_font); + gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality); + gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode); + gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour); + gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette); + gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour); + gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour); + i = gppi_raw(sesskey, "BoldAsColour", 0); conf_set_int(conf, CONF_bold_style, i+1); for (i = 0; i < 22; i++) { static const char *const defaults[] = { @@ -811,21 +876,22 @@ void load_open_settings(void *sesskey, Config *cfg) "85,85,255", "187,0,187", "255,85,255", "0,187,187", "85,255,255", "187,187,187", "255,255,255" }; - char buf[20], buf2[30]; + char buf[20], *buf2; int c0, c1, c2; sprintf(buf, "Colour%d", i); - gpps(sesskey, buf, defaults[i], buf2, sizeof(buf2)); + buf2 = gpps_raw(sesskey, buf, defaults[i]); if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) { - cfg->colours[i][0] = c0; - cfg->colours[i][1] = c1; - cfg->colours[i][2] = c2; + conf_set_int_int(conf, CONF_colours, i*3+0, c0); + conf_set_int_int(conf, CONF_colours, i*3+1, c1); + conf_set_int_int(conf, CONF_colours, i*3+2, c2); } + sfree(buf2); } - gppi(sesskey, "RawCNP", 0, &cfg->rawcnp); - gppi(sesskey, "PasteRTF", 0, &cfg->rtf_paste); - gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm); - gppi(sesskey, "RectSelect", 0, &cfg->rect_select); - gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override); + gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp); + gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste); + gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); + gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select); + gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override); for (i = 0; i < 256; i += 32) { static const char *const defaults[] = { "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", @@ -837,10 +903,10 @@ void load_open_settings(void *sesskey, Config *cfg) "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2" }; - char buf[20], buf2[256], *p; + char buf[20], *buf2, *p; int j; sprintf(buf, "Wordness%d", i); - gpps(sesskey, buf, defaults[i / 32], buf2, sizeof(buf2)); + buf2 = gpps_raw(sesskey, buf, defaults[i / 32]); p = buf2; for (j = i; j < i + 32; j++) { char *q = p; @@ -848,75 +914,75 @@ void load_open_settings(void *sesskey, Config *cfg) p++; if (*p == ',') *p++ = '\0'; - cfg->wordness[j] = atoi(q); + conf_set_int_int(conf, CONF_wordness, j, atoi(q)); } + sfree(buf2); } /* * The empty default for LineCodePage will be converted later * into a plausible default for the locale. */ - gpps(sesskey, "LineCodePage", "", cfg->line_codepage, - sizeof(cfg->line_codepage)); - gppi(sesskey, "CJKAmbigWide", 0, &cfg->cjk_ambig_wide); - gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override); - gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer)); - gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr); - gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar); - gppi(sesskey, "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen); - gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key); - gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp); - gppi(sesskey, "EraseToScrollback", 1, &cfg->erase_to_scrollback); - gppi(sesskey, "LockSize", 0, &cfg->resize_action); - gppi(sesskey, "BCE", 1, &cfg->bce); - gppi(sesskey, "BlinkText", 0, &cfg->blinktext); - gppi(sesskey, "X11Forward", 0, &cfg->x11_forward); - gpps(sesskey, "X11Display", "", cfg->x11_display, - sizeof(cfg->x11_display)); - gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth); - gppfile(sesskey, "X11AuthFile", &cfg->xauthfile); - - gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall); - gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall); - gppmap(sesskey, "PortForwardings", "", cfg->portfwd, lenof(cfg->portfwd)); - gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i; - gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i; - gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i; - gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i; + gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage); + gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide); + gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override); + gpps(sesskey, "Printer", "", conf, CONF_printer); + gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr); + gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar); + gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen); + gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key); + gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp); + gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback); + gppi(sesskey, "LockSize", 0, conf, CONF_resize_action); + gppi(sesskey, "BCE", 1, conf, CONF_bce); + gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext); + gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward); + gpps(sesskey, "X11Display", "", conf, CONF_x11_display); + gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth); + gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile); + + gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall); + gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall); + gppmap(sesskey, "PortForwardings", conf, CONF_portfwd); + i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i); + i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i); + i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i); + i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i); { int i; - gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i; - if (cfg->sshbug_hmac2 == AUTO) { - gppi(sesskey, "BuggyMAC", 0, &i); + i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i); + if (2-i == AUTO) { + i = gppi_raw(sesskey, "BuggyMAC", 0); if (i == 1) - cfg->sshbug_hmac2 = FORCE_ON; + conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON); } } - gppi(sesskey, "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i; - 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); - gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold); - gppfont(sesskey, "BoldFont", &cfg->boldfont); - gppfont(sesskey, "WideFont", &cfg->widefont); - gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont); - gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset); - gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline)); - gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed); - gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits); - gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits); - gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity); - gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow); - gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass)); + i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i); + i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i); + i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i); + i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i); + i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i); + i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); + conf_set_int(conf, CONF_ssh_simple, FALSE); + gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp); + gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell); + gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left); + gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold); + gppfont(sesskey, "BoldFont", conf, CONF_boldfont); + gppfont(sesskey, "WideFont", conf, CONF_widefont); + gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont); + gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset); + gpps(sesskey, "SerialLine", "", conf, CONF_serline); + gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed); + gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits); + gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits); + gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity); + gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow); + gpps(sesskey, "WindowClass", "", conf, CONF_winclass); } -void do_defaults(char *session, Config * cfg) +void do_defaults(char *session, Conf *conf) { - load_settings(session, cfg); + load_settings(session, conf); } static int sessioncmp(const void *av, const void *bv) diff --git a/contrib/putty/SFTP.C b/contrib/putty/SFTP.C index e665dfb..3e6bc6c 100644 --- a/contrib/putty/SFTP.C +++ b/contrib/putty/SFTP.C @@ -45,6 +45,13 @@ static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte) { sftp_pkt_adddata(pkt, &byte, 1); } +static void sftp_pkt_adduint32(struct sftp_packet *pkt, + unsigned long value) +{ + unsigned char x[4]; + PUT_32BIT(x, value); + sftp_pkt_adddata(pkt, x, 4); +} static struct sftp_packet *sftp_pkt_init(int pkt_type) { struct sftp_packet *pkt; @@ -53,6 +60,7 @@ static struct sftp_packet *sftp_pkt_init(int pkt_type) pkt->savedpos = -1; pkt->length = 0; pkt->maxlen = 0; + sftp_pkt_adduint32(pkt, 0); /* length field will be filled in later */ sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); return pkt; } @@ -62,13 +70,6 @@ static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) sftp_pkt_adddata(pkt, &value, 1); } */ -static void sftp_pkt_adduint32(struct sftp_packet *pkt, - unsigned long value) -{ - unsigned char x[4]; - PUT_32BIT(x, value); - sftp_pkt_adddata(pkt, x, 4); -} static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value) { unsigned char x[8]; @@ -149,7 +150,7 @@ static int sftp_pkt_getstring(struct sftp_packet *pkt, *p = NULL; if (pkt->length - pkt->savedpos < 4) return 0; - *length = GET_32BIT(pkt->data + pkt->savedpos); + *length = toint(GET_32BIT(pkt->data + pkt->savedpos)); pkt->savedpos += 4; if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) { *length = 0; @@ -215,9 +216,8 @@ static void sftp_pkt_free(struct sftp_packet *pkt) int sftp_send(struct sftp_packet *pkt) { int ret; - char x[4]; - PUT_32BIT(x, pkt->length); - ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length)); + PUT_32BIT(pkt->data, pkt->length - 4); + ret = sftp_senddata(pkt->data, pkt->length); sftp_pkt_free(pkt); return ret; } @@ -366,7 +366,6 @@ struct sftp_request *sftp_find_request(struct sftp_packet *pktin) if (!req || !req->registered) { fxp_internal_error("request ID mismatch\n"); - sftp_pkt_free(pktin); return NULL; } @@ -548,7 +547,8 @@ char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req) /* * Open a file. */ -struct sftp_request *fxp_open_send(char *path, int type) +struct sftp_request *fxp_open_send(char *path, int type, + struct fxp_attrs *attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; @@ -557,7 +557,10 @@ struct sftp_request *fxp_open_send(char *path, int type) sftp_pkt_adduint32(pktout, req->id); sftp_pkt_addstring(pktout, path); sftp_pkt_adduint32(pktout, type); - sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ + if (attrs) + sftp_pkt_addattrs(pktout, *attrs); + else + sftp_pkt_adduint32(pktout, 0); /* empty ATTRS structure */ sftp_send(pktout); return req; @@ -1193,15 +1196,23 @@ struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset) return xfer; } +/* + * Returns INT_MIN to indicate that it didn't even get as far as + * fxp_read_recv and hence has not freed pktin. + */ int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr; rreq = sftp_find_request(pktin); + if (!rreq) + return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); - if (!rr) - return 0; /* this packet isn't ours */ + if (!rr) { + fxp_internal_error("request ID is not part of the current download"); + return INT_MIN; /* this packet isn't ours */ + } rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len); #ifdef DEBUG_DOWNLOAD printf("read request %p has returned [%d]\n", rr, rr->retlen); @@ -1372,6 +1383,10 @@ void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len) #endif } +/* + * Returns INT_MIN to indicate that it didn't even get as far as + * fxp_write_recv and hence has not freed pktin. + */ int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; @@ -1379,9 +1394,13 @@ int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) int ret; rreq = sftp_find_request(pktin); + if (!rreq) + return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); - if (!rr) - return 0; /* this packet isn't ours */ + if (!rr) { + fxp_internal_error("request ID is not part of the current upload"); + return INT_MIN; /* this packet isn't ours */ + } ret = fxp_write_recv(pktin, rreq); #ifdef DEBUG_UPLOAD printf("write request %p has returned [%d]\n", rr, ret); diff --git a/contrib/putty/SFTP.H b/contrib/putty/SFTP.H index 98368b3..2169603 100644 --- a/contrib/putty/SFTP.H +++ b/contrib/putty/SFTP.H @@ -82,6 +82,19 @@ struct fxp_attrs { unsigned long mtime; }; +/* + * Copy between the possibly-unused permissions field in an fxp_attrs + * and a possibly-negative integer containing the same permissions. + */ +#define PUT_PERMISSIONS(attrs, perms) \ + ((perms) >= 0 ? \ + ((attrs).flags |= SSH_FILEXFER_ATTR_PERMISSIONS, \ + (attrs).permissions = (perms)) : \ + ((attrs).flags &= ~SSH_FILEXFER_ATTR_PERMISSIONS)) +#define GET_PERMISSIONS(attrs) \ + ((attrs).flags & SSH_FILEXFER_ATTR_PERMISSIONS ? \ + (attrs).permissions : -1) + struct fxp_handle { char *hstring; int hlen; @@ -116,9 +129,11 @@ struct sftp_request *fxp_realpath_send(char *path); char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req); /* - * Open a file. + * Open a file. 'attrs' contains attributes to be applied to the file + * if it's being created. */ -struct sftp_request *fxp_open_send(char *path, int type); +struct sftp_request *fxp_open_send(char *path, int type, + struct fxp_attrs *attrs); struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, struct sftp_request *req); diff --git a/contrib/putty/SSH.C b/contrib/putty/SSH.C index 6b7eb8d..bffe277 100644 --- a/contrib/putty/SSH.C +++ b/contrib/putty/SSH.C @@ -196,6 +196,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_PK_SESSIONID 128 #define BUG_SSH2_MAXPKT 256 #define BUG_CHOKES_ON_SSH2_IGNORE 512 +#define BUG_CHOKES_ON_WINADJ 1024 /* * Codes for terminal modes. @@ -430,12 +431,16 @@ enum { * Database for Edit and Continue'. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; -#define crState(t) \ - struct t *s; \ - if (!ssh->t) ssh->t = snew(struct t); \ - s = ssh->t; +#define crBeginState crBegin(s->crLine) +#define crStateP(t, v) \ + struct t *s; \ + if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \ + s = (v); +#define crState(t) crStateP(t, ssh->t) #define crFinish(z) } *crLine = 0; return (z); } #define crFinishV } *crLine = 0; return; } +#define crFinishFree(z) } sfree(s); return (z); } +#define crFinishFreeV } sfree(s); return; } #define crReturn(z) \ do {\ *crLine =__LINE__; return (z); case __LINE__:;\ @@ -455,14 +460,14 @@ struct Packet; static struct Packet *ssh1_pkt_init(int pkt_type); static struct Packet *ssh2_pkt_init(int pkt_type); static void ssh_pkt_ensure(struct Packet *, int length); -static void ssh_pkt_adddata(struct Packet *, void *data, int len); +static void ssh_pkt_adddata(struct Packet *, const void *data, int len); static void ssh_pkt_addbyte(struct Packet *, unsigned char value); static void ssh2_pkt_addbool(struct Packet *, unsigned char value); static void ssh_pkt_adduint32(struct Packet *, unsigned long value); static void ssh_pkt_addstring_start(struct Packet *); -static void ssh_pkt_addstring_str(struct Packet *, char *data); -static void ssh_pkt_addstring_data(struct Packet *, char *data, int len); -static void ssh_pkt_addstring(struct Packet *, char *data); +static void ssh_pkt_addstring_str(struct Packet *, const char *data); +static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len); +static void ssh_pkt_addstring(struct Packet *, const char *data); static unsigned char *ssh2_mpint_fmt(Bignum b, int *len); static void ssh1_pkt_addmp(struct Packet *, Bignum b); static void ssh2_pkt_addmp(struct Packet *, Bignum b); @@ -473,6 +478,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); +static void ssh2_channel_check_close(struct ssh_channel *c); +static void ssh_channel_destroy(struct ssh_channel *c); /* * Buffer management constants. There are several of these for @@ -515,13 +522,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, #define OUR_V2_MAXPKT 0x4000UL #define OUR_V2_PACKETLIMIT 0x9000UL -/* Maximum length of passwords/passphrases (arbitrary) */ -#define SSH_MAX_PASSWORD_LEN 100 - const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { - &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 + &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static struct ssh_mac *buggymacs[] = { &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 @@ -559,15 +563,29 @@ enum { /* channel types */ CHAN_X11, CHAN_AGENT, CHAN_SOCKDATA, - CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */ + CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */ + /* + * CHAN_ZOMBIE is used to indicate a channel for which we've + * already destroyed the local data source: for instance, if a + * forwarded port experiences a socket error on the local side, we + * immediately destroy its local socket and turn the SSH channel + * into CHAN_ZOMBIE. + */ + CHAN_ZOMBIE }; +typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); +typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); +typedef void (*cchandler_fn_t)(struct ssh_channel *, struct Packet *, void *); + /* - * little structure to keep track of outstanding WINDOW_ADJUSTs + * Each channel has a queue of outstanding CHANNEL_REQUESTS and their + * handlers. */ -struct winadj { - struct winadj *next; - unsigned size; +struct outstanding_channel_request { + cchandler_fn_t handler; + void *ctx; + struct outstanding_channel_request *next; }; /* @@ -588,18 +606,35 @@ struct ssh_channel { * 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. * * A channel is completely finished with when all four bits are set. + * + * In SSH-2, the four bits mean: + * + * 1 We have sent SSH2_MSG_CHANNEL_EOF. + * 2 We have sent SSH2_MSG_CHANNEL_CLOSE. + * 4 We have received SSH2_MSG_CHANNEL_EOF. + * 8 We have received SSH2_MSG_CHANNEL_CLOSE. + * + * A channel is completely finished with when we have both sent + * and received CLOSE. + * + * The symbolic constants below use the SSH-2 terminology, which + * is a bit confusing in SSH-1, but we have to use _something_. */ +#define CLOSES_SENT_EOF 1 +#define CLOSES_SENT_CLOSE 2 +#define CLOSES_RCVD_EOF 4 +#define CLOSES_RCVD_CLOSE 8 int closes; /* - * This flag indicates that a close is pending on the outgoing - * side of the channel: that is, wherever we're getting the data - * for this channel has sent us some data followed by EOF. We - * can't actually close the channel until we've finished sending - * the data, so we set this flag instead to remind us to - * initiate the closing process once our buffer is clear. + * This flag indicates that an EOF is pending on the outgoing side + * of the channel: that is, wherever we're getting the data for + * this channel has sent us some data followed by EOF. We can't + * actually send the EOF until we've finished sending the data, so + * we set this flag instead to remind us to do so once our buffer + * is clear. */ - int pending_close; + int pending_eof; /* * True if this channel is causing the underlying connection to be @@ -619,10 +654,10 @@ struct ssh_channel { */ int remlocwin; /* - * These store the list of window adjusts that haven't + * These store the list of channel requests that haven't * been acked. */ - struct winadj *winadj_head, *winadj_tail; + struct outstanding_channel_request *chanreq_head, *chanreq_tail; enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; @@ -631,6 +666,7 @@ struct ssh_channel { unsigned char *message; unsigned char msglen[4]; unsigned lensofar, totallen; + int outstanding_requests; } a; struct ssh_x11_channel { Socket s; @@ -736,9 +772,10 @@ static int ssh_do_close(Ssh ssh, int notify_exit); static unsigned long ssh_pkt_getuint32(struct Packet *pkt); static int ssh2_pkt_getbool(struct Packet *pkt); static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length); -static void ssh2_timer(void *ctx, long now); -static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, - struct Packet *pktin); +static void ssh2_timer(void *ctx, unsigned long now); +static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, + struct Packet *pktin); +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin); struct rdpkt1_state_tag { long len, pad, biglen, to_read; @@ -757,9 +794,6 @@ struct rdpkt2_state_tag { struct Packet *pktin; }; -typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); -typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); - struct queued_handler; struct queued_handler { int msg1, msg2; @@ -830,6 +864,8 @@ struct ssh_tag { } state; int size_needed, eof_needed; + int sent_console_eof; + int got_pty; /* affects EOF behaviour on main channel */ struct Packet **queue; int queuelen, queuesize; @@ -861,12 +897,8 @@ struct ssh_tag { int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; - int do_ssh_init_crstate; int ssh_gotdata_crstate; - int do_ssh1_login_crstate; int do_ssh1_connection_crstate; - int do_ssh2_transport_crstate; - int do_ssh2_authconn_crstate; void *do_ssh_init_state; void *do_ssh1_login_state; @@ -884,12 +916,26 @@ struct ssh_tag { struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen); /* - * We maintain a full _copy_ of a Config structure here, not - * merely a pointer to it. That way, when we're passed a new - * one for reconfiguration, we can check the differences and - * potentially reconfigure port forwardings etc in mid-session. + * We maintain our own copy of a Conf structure here. That way, + * when we're passed a new one for reconfiguration, we can check + * the differences and potentially reconfigure port forwardings + * etc in mid-session. + */ + Conf *conf; + + /* + * Values cached out of conf so as to avoid the tree234 lookup + * cost every time they're used. + */ + int logomitdata; + + /* + * Dynamically allocated username string created during SSH + * login. Stored in here rather than in the coroutine state so + * that it'll be reliably freed if we shut down the SSH session + * at some unexpected moment. */ - Config cfg; + char *username; /* * Used to transfer data back from async callbacks. @@ -921,6 +967,7 @@ struct ssh_tag { * indications from a request. */ struct queued_handler *qhead, *qtail; + handler_fn_t q_saved_handler1, q_saved_handler2; /* * This module deals with sending keepalives. @@ -934,7 +981,7 @@ struct ssh_tag { unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; unsigned long max_data_size; int kex_in_progress; - long next_rekey, last_rekey; + unsigned long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ /* @@ -965,26 +1012,27 @@ static void logeventf(Ssh ssh, const char *fmt, ...) sfree(buf); } -#define bombout(msg) \ - do { \ - char *text = dupprintf msg; \ - ssh_do_close(ssh, FALSE); \ - logevent(text); \ - connection_fatal(ssh->frontend, "%s", text); \ - sfree(text); \ - } while (0) +static void bomb_out(Ssh ssh, char *text) +{ + ssh_do_close(ssh, FALSE); + logevent(text); + connection_fatal(ssh->frontend, "%s", text); + sfree(text); +} + +#define bombout(msg) bomb_out(ssh, dupprintf msg) /* Functions to leave bits out of the SSH packet log file. */ static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype) { - if (ssh->cfg.logomitpass) + if (conf_get_int(ssh->conf, CONF_logomitpass)) pkt->logmode = blanktype; } static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype) { - if (ssh->cfg.logomitdata) + if (ssh->logomitdata) pkt->logmode = blanktype; } @@ -993,26 +1041,29 @@ static void end_log_omission(Ssh ssh, struct Packet *pkt) pkt->logmode = PKTLOG_EMIT; } -/* Helper function for common bits of parsing cfg.ttymodes. */ -static void parse_ttymodes(Ssh ssh, char *modes, +/* Helper function for common bits of parsing ttymodes. */ +static void parse_ttymodes(Ssh ssh, void (*do_mode)(void *data, char *mode, char *val), void *data) { - while (*modes) { - char *t = strchr(modes, '\t'); - char *m = snewn(t-modes+1, char); - char *val; - strncpy(m, modes, t-modes); - m[t-modes] = '\0'; - if (*(t+1) == 'A') - val = get_ttymode(ssh->frontend, m); - else - val = dupstr(t+2); - if (val) - do_mode(data, m, val); - sfree(m); - sfree(val); - modes += strlen(modes) + 1; + char *key, *val; + + for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) { + /* + * val[0] is either 'V', indicating that an explicit value + * follows it, or 'A' indicating that we should pass the + * value through from the local environment via get_ttymode. + */ + if (val[0] == 'A') { + val = get_ttymode(ssh->frontend, key); + if (val) { + do_mode(data, key, val); + sfree(val); + } + } else + do_mode(data, key, val + 1); /* skip the 'V' */ } } @@ -1300,7 +1351,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) if (ssh->logctx) { int nblanks = 0; struct logblank_t blank; - if (ssh->cfg.logomitdata) { + if (ssh->logomitdata) { int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) || @@ -1398,7 +1449,8 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* See if that gives us a valid packet. */ if (ssh->scmac->verresult(ssh->sc_mac_ctx, st->pktin->data + st->packetlen) && - (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen) + ((st->len = toint(GET_32BIT(st->pktin->data))) == + st->packetlen-4)) break; if (st->packetlen >= OUR_V2_PACKETLIMIT) { bombout(("No valid incoming packet found")); @@ -1431,7 +1483,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* * Now get the length figure. */ - st->len = GET_32BIT(st->pktin->data); + st->len = toint(GET_32BIT(st->pktin->data)); /* * _Completely_ silly lengths should be stomped on before they @@ -1533,7 +1585,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) if (ssh->logctx) { int nblanks = 0; struct logblank_t blank; - if (ssh->cfg.logomitdata) { + if (ssh->logomitdata) { int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) { @@ -1777,7 +1829,7 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length) if (body) pkt->body = pkt->data + offset; } } -static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len) +static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len) { if (pkt->logmode != PKTLOG_EMIT) { pkt->nblanks++; @@ -1811,17 +1863,18 @@ static void ssh_pkt_addstring_start(struct Packet *pkt) ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } -static void ssh_pkt_addstring_str(struct Packet *pkt, char *data) +static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data) { ssh_pkt_adddata(pkt, data, strlen(data)); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len) +static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data, + int len) { ssh_pkt_adddata(pkt, data, len); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring(struct Packet *pkt, char *data) +static void ssh_pkt_addstring(struct Packet *pkt, const char *data) { ssh_pkt_addstring_start(pkt); ssh_pkt_addstring_str(pkt, data); @@ -2262,7 +2315,7 @@ static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length) *length = 0; if (pkt->length - pkt->savedpos < 4) return; - len = GET_32BIT(pkt->body + pkt->savedpos); + len = toint(GET_32BIT(pkt->body + pkt->savedpos)); if (len < 0) return; *length = len; @@ -2346,7 +2399,7 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, * See if this is in fact an ssh-rsa signature and a buggy * server; otherwise we can just do this the easy way. */ - if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && + if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 && (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) { int pos, len, siglen; @@ -2355,8 +2408,15 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, */ pos = 4+7; /* skip over "ssh-rsa" */ - pos += 4 + GET_32BIT(pkblob+pos); /* skip over exponent */ - len = GET_32BIT(pkblob+pos); /* find length of modulus */ + len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */ + if (len < 0 || len > pkblob_len - pos - 4) + goto give_up; + pos += 4 + len; /* skip over exponent */ + if (pkblob_len - pos < 4) + goto give_up; + len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */ + if (len < 0 || len > pkblob_len - pos - 4) + goto give_up; pos += 4; /* find modulus itself */ while (len > 0 && pkblob[pos] == 0) len--, pos++; @@ -2366,7 +2426,11 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, * Now find the signature integer. */ pos = 4+7; /* skip over "ssh-rsa" */ - siglen = GET_32BIT(sigblob+pos); + if (sigblob_len < pos+4) + goto give_up; + siglen = toint(GET_32BIT(sigblob+pos)); + if (siglen != sigblob_len - pos - 4) + goto give_up; /* debug(("signature length is %d\n", siglen)); */ if (len != siglen) { @@ -2388,7 +2452,10 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, return; } - /* Otherwise fall through and do it the easy way. */ + /* Otherwise fall through and do it the easy way. We also come + * here as a fallback if we discover above that the key blob + * is misformatted in some way. */ + give_up:; } ssh2_pkt_addstring_start(pkt); @@ -2417,8 +2484,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * with SSH1_MSG_IGNOREs -- but this string never seems to change, * so we can't distinguish them. */ - if (ssh->cfg.sshbug_ignore1 == FORCE_ON || - (ssh->cfg.sshbug_ignore1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") || @@ -2432,8 +2499,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-1 ignore bug"); } - if (ssh->cfg.sshbug_plainpw1 == FORCE_ON || - (ssh->cfg.sshbug_plainpw1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO && (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) { /* * These versions need a plain password sent; they can't @@ -2444,8 +2511,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version needs a plain SSH-1 password"); } - if (ssh->cfg.sshbug_rsa1 == FORCE_ON || - (ssh->cfg.sshbug_rsa1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO && (!strcmp(imp, "Cisco-1.25")))) { /* * These versions apparently have no clue whatever about @@ -2456,8 +2523,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version can't handle SSH-1 RSA authentication"); } - if (ssh->cfg.sshbug_hmac2 == FORCE_ON || - (ssh->cfg.sshbug_hmac2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) || wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) || @@ -2469,8 +2536,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 HMAC bug"); } - if (ssh->cfg.sshbug_derivekey2 == FORCE_ON || - (ssh->cfg.sshbug_derivekey2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) { /* @@ -2482,8 +2549,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 key-derivation bug"); } - if (ssh->cfg.sshbug_rsapad2 == FORCE_ON || - (ssh->cfg.sshbug_rsapad2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO && (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp)))) { /* @@ -2493,8 +2560,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 RSA padding bug"); } - if (ssh->cfg.sshbug_pksessid2 == FORCE_ON || - (ssh->cfg.sshbug_pksessid2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO && wc_match("OpenSSH_2.[0-2]*", imp))) { /* * These versions have the SSH-2 session-ID bug in @@ -2504,8 +2571,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 public-key-session-ID bug"); } - if (ssh->cfg.sshbug_rekey2 == FORCE_ON || - (ssh->cfg.sshbug_rekey2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO && (wc_match("DigiSSH_2.0", imp) || wc_match("OpenSSH_2.[0-4]*", imp) || wc_match("OpenSSH_2.5.[0-3]*", imp) || @@ -2520,8 +2587,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 rekey bug"); } - if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || - (ssh->cfg.sshbug_maxpkt2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO && (wc_match("1.36_sshlib GlobalSCAPE", imp) || wc_match("1.36 sshlib: GlobalScape", imp)))) { /* @@ -2531,7 +2598,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version ignores SSH-2 maximum packet size"); } - if (ssh->cfg.sshbug_ignore2 == FORCE_ON) { + if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) { /* * Servers that don't support SSH2_MSG_IGNORE. Currently, * none detected automatically. @@ -2539,6 +2606,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; logevent("We believe remote version has SSH-2 ignore bug"); } + + if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) { + /* + * Servers that don't support our winadj request for one + * reason or another. Currently, none detected automatically. + */ + ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ; + logevent("We believe remote version has winadj bug"); + } } /* @@ -2608,6 +2684,7 @@ static void ssh_send_verstring(Ssh ssh, char *svers) static int do_ssh_init(Ssh ssh, unsigned char c) { struct do_ssh_init_state { + int crLine; int vslen; char version[10]; char *vstring; @@ -2616,8 +2693,8 @@ static int do_ssh_init(Ssh ssh, unsigned char c) int proto1, proto2; }; crState(do_ssh_init_state); - - crBegin(ssh->do_ssh_init_crstate); + + crBeginState; /* Search for a line beginning with the string "SSH-" in the input. */ for (;;) { @@ -2674,16 +2751,16 @@ static int do_ssh_init(Ssh ssh, unsigned char c) /* Anything greater or equal to "1.99" means protocol 2 is supported. */ s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0; - if (ssh->cfg.sshprot == 0 && !s->proto1) { + if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) { bombout(("SSH protocol version 1 required by user but not provided by server")); crStop(0); } - if (ssh->cfg.sshprot == 3 && !s->proto2) { + if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) { bombout(("SSH protocol version 2 required by user but not provided by server")); crStop(0); } - if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) + if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1)) ssh->version = 2; else ssh->version = 1; @@ -2691,7 +2768,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) logeventf(ssh, "Using SSH protocol version %d", ssh->version); /* Send the version string, if we haven't already */ - if (ssh->cfg.sshprot != 3) + if (conf_get_int(ssh->conf, CONF_sshprot) != 3) ssh_send_verstring(ssh, s->version); if (ssh->version == 2) { @@ -2723,7 +2800,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) update_specials_menu(ssh->frontend); ssh->state = SSH_STATE_BEFORE_SIZE; - ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh); + ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh); sfree(s->vstring); @@ -2976,11 +3053,14 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, SockAddr addr; const char *err; - - if (*ssh->cfg.loghost) { + char *loghost; + int addressfamily, sshprot; + + loghost = conf_get_str(ssh->conf, CONF_loghost); + if (*loghost) { char *colon; - ssh->savedhost = dupstr(ssh->cfg.loghost); + ssh->savedhost = dupstr(loghost); ssh->savedport = 22; /* default ssh port */ /* @@ -3005,11 +3085,11 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, /* * Try to find host. */ + addressfamily = conf_get_int(ssh->conf, CONF_addressfamily); logeventf(ssh, "Looking up host \"%s\"%s", host, - (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); - addr = name_lookup(host, port, realhost, &ssh->cfg, - ssh->cfg.addressfamily); + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); + addr = name_lookup(host, port, realhost, ssh->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -3021,7 +3101,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, */ ssh->fn = &fn_table; ssh->s = new_connection(addr, *realhost, port, - 0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg); + 0, 1, nodelay, keepalive, (Plug) ssh, ssh->conf); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; notify_remote_exit(ssh->frontend); @@ -3032,9 +3112,10 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, * 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) + sshprot = conf_get_int(ssh->conf, CONF_sshprot); + if (sshprot == 0) ssh->version = 1; - if (ssh->cfg.sshprot == 3) { + if (sshprot == 3) { ssh->version = 2; ssh_send_verstring(ssh, NULL); } @@ -3042,9 +3123,9 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, /* * loghost, if configured, overrides realhost. */ - if (*ssh->cfg.loghost) { + if (*loghost) { sfree(*realhost); - *realhost = dupstr(ssh->cfg.loghost); + *realhost = dupstr(loghost); } return NULL; @@ -3137,6 +3218,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) Ssh ssh = c->ssh; void *sentreply = reply; + c->u.a.outstanding_requests--; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ sentreply = "\0\0\0\1\5"; @@ -3156,6 +3238,12 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) } if (reply) sfree(reply); + /* + * If we've already seen an incoming EOF but haven't sent an + * outgoing one, this may be the moment to send it. + */ + if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF)) + sshfwd_write_eof(c); } /* @@ -3199,9 +3287,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, { int i, j, ret; unsigned char cookie[8], *ptr; - struct RSAKey servkey, hostkey; struct MD5Context md5c; struct do_ssh1_login_state { + int crLine; int len; unsigned char *rsabuf, *keystr1, *keystr2; unsigned long supported_ciphers_mask, supported_auths_mask; @@ -3209,7 +3297,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int tis_auth_refused, ccard_auth_refused; unsigned char session_id[16]; int cipher_type; - char username[100]; void *publickey_blob; int publickey_bloblen; char *publickey_comment; @@ -3226,10 +3313,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char *commentp; int commentlen; int dlgret; + Filename *keyfile; + struct RSAKey servkey, hostkey; }; crState(do_ssh1_login_state); - crBegin(ssh->do_ssh1_login_crstate); + crBeginState; if (!pktin) crWaitUntil(pktin); @@ -3248,8 +3337,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } memcpy(cookie, ptr, 8); - if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) || - !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) { + if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) || + !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) { bombout(("Failed to read SSH-1 public keys from public key packet")); crStop(0); } @@ -3261,9 +3350,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char logmsg[80]; logevent("Host key fingerprint is:"); strcpy(logmsg, " "); - hostkey.comment = NULL; + s->hostkey.comment = NULL; rsa_fingerprint(logmsg + strlen(logmsg), - sizeof(logmsg) - strlen(logmsg), &hostkey); + sizeof(logmsg) - strlen(logmsg), &s->hostkey); logevent(logmsg); } @@ -3278,8 +3367,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER; MD5Init(&md5c); - MD5Update(&md5c, s->keystr2, hostkey.bytes); - MD5Update(&md5c, s->keystr1, servkey.bytes); + MD5Update(&md5c, s->keystr2, s->hostkey.bytes); + MD5Update(&md5c, s->keystr1, s->servkey.bytes); MD5Update(&md5c, cookie, 8); MD5Final(s->session_id, &md5c); @@ -3289,13 +3378,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Verify that the `bits' and `bytes' parameters match. */ - if (hostkey.bits > hostkey.bytes * 8 || - servkey.bits > servkey.bytes * 8) { + if (s->hostkey.bits > s->hostkey.bytes * 8 || + s->servkey.bits > s->servkey.bytes * 8) { bombout(("SSH-1 public keys were badly formatted")); crStop(0); } - s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes); + s->len = (s->hostkey.bytes > s->servkey.bytes ? + s->hostkey.bytes : s->servkey.bytes); s->rsabuf = snewn(s->len, unsigned char); @@ -3306,11 +3396,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * First format the key into a string. */ - int len = rsastr_len(&hostkey); + int len = rsastr_len(&s->hostkey); char fingerprint[100]; char *keystr = snewn(len, char); - rsastr_fmt(keystr, &hostkey); - rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey); + rsastr_fmt(keystr, &s->hostkey); + rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); ssh_set_frozen(ssh, 1); s->dlgret = verify_ssh_host_key(ssh->frontend, @@ -3344,14 +3434,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->rsabuf[i] ^= s->session_id[i]; } - if (hostkey.bytes > servkey.bytes) { - ret = rsaencrypt(s->rsabuf, 32, &servkey); + if (s->hostkey.bytes > s->servkey.bytes) { + ret = rsaencrypt(s->rsabuf, 32, &s->servkey); if (ret) - ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey); + ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey); } else { - ret = rsaencrypt(s->rsabuf, 32, &hostkey); + ret = rsaencrypt(s->rsabuf, 32, &s->hostkey); if (ret) - ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey); + ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey); } if (!ret) { bombout(("SSH-1 public key encryptions failed due to bad formatting")); @@ -3365,7 +3455,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char *cipher_string = NULL; int i; for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) { - int next_cipher = ssh->cfg.ssh_cipherlist[i]; + int next_cipher = conf_get_int_int(ssh->conf, + CONF_ssh_cipherlist, i); if (next_cipher == CIPHER_WARN) { /* If/when we choose a cipher, warn about it */ warn = 1; @@ -3453,21 +3544,21 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->crcda_ctx = crcda_make_context(); logevent("Installing CRC compensation attack detector"); - if (servkey.modulus) { - sfree(servkey.modulus); - servkey.modulus = NULL; + if (s->servkey.modulus) { + sfree(s->servkey.modulus); + s->servkey.modulus = NULL; } - if (servkey.exponent) { - sfree(servkey.exponent); - servkey.exponent = NULL; + if (s->servkey.exponent) { + sfree(s->servkey.exponent); + s->servkey.exponent = NULL; } - if (hostkey.modulus) { - sfree(hostkey.modulus); - hostkey.modulus = NULL; + if (s->hostkey.modulus) { + sfree(s->hostkey.modulus); + s->hostkey.modulus = NULL; } - if (hostkey.exponent) { - sfree(hostkey.exponent); - hostkey.exponent = NULL; + if (s->hostkey.exponent) { + sfree(s->hostkey.exponent); + s->hostkey.exponent = NULL; } crWaitUntil(pktin); @@ -3480,14 +3571,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, fflush(stdout); /* FIXME eh? */ { - if (!get_remote_username(&ssh->cfg, s->username, - sizeof(s->username))) { + if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, - lenof(s->username)); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3503,14 +3592,13 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStop(0); } - memcpy(s->username, s->cur_prompt->prompts[0]->result, - lenof(s->username)); + ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } - send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END); + send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END); { - char *userlog = dupprintf("Sent username \"%s\"", s->username); + char *userlog = dupprintf("Sent username \"%s\"", ssh->username); logevent(userlog); if (flags & FLAG_INTERACTIVE && (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) { @@ -3533,24 +3621,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Load the public half of any configured keyfile for later use. */ - if (!filename_is_null(ssh->cfg.keyfile)) { + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", - filename_to_str(&ssh->cfg.keyfile)); - keytype = key_type(&ssh->cfg.keyfile); + filename_to_str(s->keyfile)); + keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH1) { const char *error; - if (rsakey_pubblob(&ssh->cfg.keyfile, + if (rsakey_pubblob(s->keyfile, &s->publickey_blob, &s->publickey_bloblen, &s->publickey_comment, &error)) { - s->publickey_encrypted = rsakey_encrypted(&ssh->cfg.keyfile, + s->publickey_encrypted = rsakey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -3562,7 +3651,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -3574,7 +3663,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, while (pktin->type == SSH1_SMSG_FAILURE) { s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; - if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) { + if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) { /* * Attempt RSA authentication using Pageant. */ @@ -3604,7 +3693,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, if (s->response && s->responselen >= 5 && s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { s->p = s->response + 5; - s->nkeys = GET_32BIT(s->p); + s->nkeys = toint(GET_32BIT(s->p)); + if (s->nkeys < 0) { + logeventf(ssh, "Pageant reported negative key count %d", + s->nkeys); + s->nkeys = 0; + } s->p += 4; logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys); for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { @@ -3614,22 +3708,23 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int n, ok = FALSE; do { /* do while (0) to make breaking easy */ n = ssh1_read_bignum - (s->p, s->responselen-(s->p-s->response), + (s->p, toint(s->responselen-(s->p-s->response)), &s->key.exponent); if (n < 0) break; s->p += n; n = ssh1_read_bignum - (s->p, s->responselen-(s->p-s->response), + (s->p, toint(s->responselen-(s->p-s->response)), &s->key.modulus); if (n < 0) - break; + break; s->p += n; if (s->responselen - (s->p-s->response) < 4) break; - s->commentlen = GET_32BIT(s->p); + s->commentlen = toint(GET_32BIT(s->p)); s->p += 4; - if (s->responselen - (s->p-s->response) < + if (s->commentlen < 0 || + toint(s->responselen - (s->p-s->response)) < s->commentlen) break; s->commentp = (char *)s->p; @@ -3758,8 +3853,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int got_passphrase; /* need not be kept over crReturn */ if (flags & FLAG_VERBOSE) c_write_str(ssh, "Trying public key authentication.\r\n"); + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); logeventf(ssh, "Trying public key \"%s\"", - filename_to_str(&ssh->cfg.keyfile)); + filename_to_str(s->keyfile)); s->tried_publickey = 1; got_passphrase = FALSE; while (!got_passphrase) { @@ -3779,8 +3875,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", - s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + s->publickey_comment), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3801,10 +3896,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Try decrypting key with passphrase. */ - ret = loadrsakey(&ssh->cfg.keyfile, &s->key, passphrase, + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + ret = loadrsakey(s->keyfile, &s->key, passphrase, &error); if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (ret == 1) { @@ -3812,7 +3908,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, got_passphrase = TRUE; } else if (ret == 0) { c_write_str(ssh, "Couldn't load private key from "); - c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile)); + c_write_str(ssh, filename_to_str(s->keyfile)); c_write_str(ssh, " ("); c_write_str(ssh, error); c_write_str(ssh, ").\r\n"); @@ -3895,7 +3991,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, */ s->cur_prompt = new_prompts(ssh->frontend); - if (ssh->cfg.try_tis_auth && + if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && !s->tis_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; @@ -3934,11 +4030,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } - if (ssh->cfg.try_tis_auth && + if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) && !s->ccard_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; @@ -3977,7 +4073,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } @@ -3988,9 +4084,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); - add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", - s->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", + ssh->username, ssh->savedhost), + FALSE); } /* @@ -4167,70 +4263,65 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crFinish(1); } -void sshfwd_close(struct ssh_channel *c) +static void ssh_channel_try_eof(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + assert(c->pending_eof); /* precondition for calling us */ + if (c->halfopen) + return; /* can't close: not even opened yet */ + if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0) + return; /* can't send EOF: pending outgoing data */ + + c->pending_eof = FALSE; /* we're about to send it */ + if (ssh->version == 1) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + c->closes |= CLOSES_SENT_EOF; + } else { + struct Packet *pktout; + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes |= CLOSES_SENT_EOF; + ssh2_channel_check_close(c); + } +} + +void sshfwd_write_eof(struct ssh_channel *c) { Ssh ssh = c->ssh; if (ssh->state == SSH_STATE_CLOSED) return; - if (!c->closes) { - /* - * If halfopen is true, we have sent - * CHANNEL_OPEN for this channel, but it hasn't even been - * acknowledged by the server. So we must set a close flag - * on it now, and then when the server acks the channel - * open, we can close it then. - */ - if (!c->halfopen) { - if (ssh->version == 1) { - send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, - PKT_END); - c->closes = 1; /* sent MSG_CLOSE */ - } else { - int bytes_to_send = bufchain_size(&c->v.v2.outbuffer); - if (bytes_to_send > 0) { - /* - * If we still have unsent data in our outgoing - * buffer for this channel, we can't actually - * initiate a close operation yet or that data - * will be lost. Instead, set the pending_close - * flag so that when we do clear the buffer - * we'll start closing the channel. - */ - char logmsg[160] = {'\0'}; - sprintf( - logmsg, - "Forwarded port pending to be closed : " - "%d bytes remaining", - bytes_to_send); - logevent(logmsg); - - c->pending_close = TRUE; - } else { - /* - * No locally buffered data, so we can send the - * close message immediately. - */ - struct Packet *pktout; - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - c->closes = 1; /* sent MSG_CLOSE */ - logevent("Nothing left to send, closing channel"); - } - } - } + if (c->closes & CLOSES_SENT_EOF) + return; - if (c->type == CHAN_X11) { - c->u.x11.s = NULL; - logevent("Forwarded X11 connection terminated"); - } else if (c->type == CHAN_SOCKDATA || - c->type == CHAN_SOCKDATA_DORMANT) { - c->u.pfd.s = NULL; - logevent("Forwarded port closed"); - } + c->pending_eof = TRUE; + ssh_channel_try_eof(c); +} + +void sshfwd_unclean_close(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + + if (ssh->state == SSH_STATE_CLOSED) + return; + + switch (c->type) { + case CHAN_X11: + x11_close(c->u.x11.s); + logevent("Forwarded X11 connection terminated due to local error"); + break; + case CHAN_SOCKDATA: + case CHAN_SOCKDATA_DORMANT: + pfd_close(c->u.pfd.s); + logevent("Forwarded port closed due to local error"); + break; } + c->type = CHAN_ZOMBIE; + + ssh2_channel_check_close(c); } int sshfwd_write(struct ssh_channel *c, char *buf, int len) @@ -4289,27 +4380,26 @@ static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin) if (qh->msg1 > 0) { assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg1] = NULL; + ssh->packet_dispatch[qh->msg1] = ssh->q_saved_handler1; } if (qh->msg2 > 0) { assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg2] = NULL; + ssh->packet_dispatch[qh->msg2] = ssh->q_saved_handler2; } if (qh->next) { ssh->qhead = qh->next; if (ssh->qhead->msg1 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler; } if (ssh->qhead->msg2 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler; } } else { ssh->qhead = ssh->qtail = NULL; - ssh->packet_dispatch[pktin->type] = NULL; } qh->handler(ssh, pktin, qh->ctx); @@ -4333,11 +4423,11 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2, ssh->qhead = qh; if (qh->msg1 > 0) { - assert(ssh->packet_dispatch[qh->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler; } if (qh->msg2 > 0) { - assert(ssh->packet_dispatch[qh->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler; } } else { @@ -4365,11 +4455,11 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx) } } -static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) +static void ssh_setup_portfwd(Ssh ssh, Conf *conf) { - const char *portfwd_strptr = cfg->portfwd; struct ssh_portfwd *epf; int i; + char *key, *val; if (!ssh->portfwds) { ssh->portfwds = newtree234(ssh_portcmp); @@ -4387,64 +4477,61 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) epf->status = DESTROY; } - while (*portfwd_strptr) { + for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { + char *kp, *kp2, *vp, *vp2; char address_family, type; int sport,dport,sserv,dserv; - char sports[256], dports[256], saddr[256], host[256]; - int n; + char *sports, *dports, *saddr, *host; + + kp = key; address_family = 'A'; type = 'L'; - if (*portfwd_strptr == 'A' || - *portfwd_strptr == '4' || - *portfwd_strptr == '6') - address_family = *portfwd_strptr++; - if (*portfwd_strptr == 'L' || - *portfwd_strptr == 'R' || - *portfwd_strptr == 'D') - type = *portfwd_strptr++; - - saddr[0] = '\0'; - - n = 0; - while (*portfwd_strptr && *portfwd_strptr != '\t') { - if (*portfwd_strptr == ':') { - /* - * We've seen a colon in the middle of the - * source port number. This means that - * everything we've seen until now is the - * source _address_, so we'll move it into - * saddr and start sports from the beginning - * again. - */ - portfwd_strptr++; - sports[n] = '\0'; - if (ssh->version == 1 && type == 'R') { - logeventf(ssh, "SSH-1 cannot handle remote source address " - "spec \"%s\"; ignoring", sports); - } else - strcpy(saddr, sports); - n = 0; - } - if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++; + if (*kp == 'A' || *kp == '4' || *kp == '6') + address_family = *kp++; + if (*kp == 'L' || *kp == 'R') + type = *kp++; + + if ((kp2 = strchr(kp, ':')) != NULL) { + /* + * There's a colon in the middle of the source port + * string, which means that the part before it is + * actually a source address. + */ + saddr = dupprintf("%.*s", (int)(kp2 - kp), kp); + sports = kp2+1; + } else { + saddr = NULL; + sports = kp; } - sports[n] = 0; - if (type != 'D') { - if (*portfwd_strptr == '\t') - portfwd_strptr++; - n = 0; - while (*portfwd_strptr && *portfwd_strptr != ':') { - if (n < lenof(host)-1) host[n++] = *portfwd_strptr++; - } - host[n] = 0; - if (*portfwd_strptr == ':') - portfwd_strptr++; - n = 0; - while (*portfwd_strptr) { - if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++; + sport = atoi(sports); + sserv = 0; + if (sport == 0) { + sserv = 1; + sport = net_service_lookup(sports); + if (!sport) { + logeventf(ssh, "Service lookup failed for source" + " port \"%s\"", sports); } - dports[n] = 0; - portfwd_strptr++; + } + + if (type == 'L' && !strcmp(val, "D")) { + /* dynamic forwarding */ + host = NULL; + dports = NULL; + dport = -1; + dserv = 0; + type = 'D'; + } else { + /* ordinary forwarding */ + vp = val; + vp2 = vp + strcspn(vp, ":"); + host = dupprintf("%.*s", (int)(vp2 - vp), vp); + if (vp2) + vp2++; + dports = vp2; dport = atoi(dports); dserv = 0; if (dport == 0) { @@ -4455,33 +4542,18 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) " port \"%s\"", dports); } } - } else { - while (*portfwd_strptr) portfwd_strptr++; - host[0] = 0; - dports[0] = 0; - dport = dserv = -1; - portfwd_strptr++; /* eat the NUL and move to next one */ - } - sport = atoi(sports); - sserv = 0; - if (sport == 0) { - sserv = 1; - sport = net_service_lookup(sports); - if (!sport) { - logeventf(ssh, "Service lookup failed for source" - " port \"%s\"", sports); - } } + if (sport && dport) { /* Set up a description of the source port. */ struct ssh_portfwd *pfrec, *epfrec; pfrec = snew(struct ssh_portfwd); pfrec->type = type; - pfrec->saddr = *saddr ? dupstr(saddr) : NULL; + pfrec->saddr = saddr; pfrec->sserv = sserv ? dupstr(sports) : NULL; pfrec->sport = sport; - pfrec->daddr = *host ? dupstr(host) : NULL; + pfrec->daddr = host; pfrec->dserv = dserv ? dupstr(dports) : NULL; pfrec->dport = dport; pfrec->local = NULL; @@ -4509,6 +4581,9 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } else { pfrec->status = CREATE; } + } else { + sfree(saddr); + sfree(host); } } @@ -4562,13 +4637,13 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); - } else if (ssh->cfg.rport_acceptall) { - /* XXX: ssh->cfg.rport_acceptall may not represent + } else if (conf_get_int(conf, CONF_rport_acceptall)) { + /* XXX: rport_acceptall may not represent * what was used to open the original connection, * since it's reconfigurable. */ - ssh2_pkt_addstring(pktout, "0.0.0.0"); + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4612,7 +4687,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) if (epf->type == 'L') { const char *err = pfd_addforward(epf->daddr, epf->dport, epf->saddr, epf->sport, - ssh, cfg, + ssh, conf, &epf->local, epf->addressfamily); @@ -4624,7 +4699,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } else if (epf->type == 'D') { const char *err = pfd_addforward(NULL, -1, epf->saddr, epf->sport, - ssh, cfg, + ssh, conf, &epf->local, epf->addressfamily); @@ -4680,10 +4755,10 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) ssh2_pkt_addbool(pktout, 1);/* want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); - } else if (cfg->rport_acceptall) { - ssh2_pkt_addstring(pktout, "0.0.0.0"); + } else if (conf_get_int(conf, CONF_rport_acceptall)) { + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4736,7 +4811,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->ssh = ssh; if (x11_init(&c->u.x11.s, ssh->x11disp, c, - NULL, -1, &ssh->cfg) != NULL) { + NULL, -1, ssh->conf) != NULL) { logevent("Opening X11 forward connection failed"); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, @@ -4748,7 +4823,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->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); @@ -4778,10 +4853,12 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.message = NULL; + c->u.a.outstanding_requests = 0; add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, @@ -4793,14 +4870,11 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) { /* Remote side is trying to open a channel to talk to a * forwarded port. Give them back a local channel number. */ - struct ssh_channel *c; struct ssh_rportfwd pf, *pfp; int remoteid; int hostsize, port; char *host; const char *e; - c = snew(struct ssh_channel); - c->ssh = ssh; remoteid = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &host, &hostsize); @@ -4819,10 +4893,13 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); } else { + struct ssh_channel *c = snew(struct ssh_channel); + c->ssh = ssh; + logeventf(ssh, "Received remote port open request for %s:%d", pf.dhost, port); e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port, - c, &ssh->cfg, pfp->pfrec->addressfamily); + c, ssh->conf, pfp->pfrec->addressfamily); if (e != NULL) { logeventf(ssh, "Port open failed: %s", e); sfree(c); @@ -4833,7 +4910,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->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); @@ -4860,15 +4937,14 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) pfd_confirm(c->u.pfd.s); } - if (c && c->closes) { + if (c && c->pending_eof) { /* * We have a pending close on this channel, * which we decided on before the server acked * the channel open. So now we know the * remoteid, we can close it again. */ - send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, - PKT_INT, c->remoteid, PKT_END); + ssh_channel_try_eof(c); } } @@ -4893,34 +4969,62 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin) struct ssh_channel *c; c = find234(ssh->channels, &i, ssh_channelfind); if (c && !c->halfopen) { - int closetype; - closetype = - (pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2); - - if ((c->closes == 0) && (c->type == CHAN_X11)) { - logevent("Forwarded X11 connection terminated"); - assert(c->u.x11.s != NULL); - x11_close(c->u.x11.s); - c->u.x11.s = NULL; - } - if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) { - logevent("Forwarded port closed"); - assert(c->u.pfd.s != NULL); - pfd_close(c->u.pfd.s); - c->u.pfd.s = NULL; - } - c->closes |= (closetype << 2); /* seen this message */ - if (!(c->closes & closetype)) { - send_packet(ssh, pktin->type, PKT_INT, c->remoteid, - PKT_END); - c->closes |= closetype; /* sent it too */ - } + if (pktin->type == SSH1_MSG_CHANNEL_CLOSE && + !(c->closes & CLOSES_RCVD_EOF)) { + /* + * Received CHANNEL_CLOSE, which we translate into + * outgoing EOF. + */ + int send_close = FALSE; - if (c->closes == 15) { - del234(ssh->channels, c); - sfree(c); - } + c->closes |= CLOSES_RCVD_EOF; + + switch (c->type) { + case CHAN_X11: + if (c->u.x11.s) + x11_send_eof(c->u.x11.s); + else + send_close = TRUE; + break; + case CHAN_SOCKDATA: + if (c->u.pfd.s) + pfd_send_eof(c->u.pfd.s); + else + send_close = TRUE; + break; + case CHAN_AGENT: + send_close = TRUE; + break; + } + + if (send_close && !(c->closes & CLOSES_SENT_EOF)) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + c->closes |= CLOSES_SENT_EOF; + } + } + + if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION && + !(c->closes & CLOSES_RCVD_CLOSE)) { + + if (!(c->closes & CLOSES_SENT_EOF)) { + bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d" + " for which we never sent CHANNEL_CLOSE\n", i)); + } + + c->closes |= CLOSES_RCVD_CLOSE; + } + + if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) && + !(c->closes & CLOSES_SENT_CLOSE)) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION, + PKT_INT, c->remoteid, PKT_END); + c->closes |= CLOSES_SENT_CLOSE; + } + + if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) + ssh_channel_destroy(c); } else { bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n", pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" : @@ -4980,6 +5084,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, @@ -5054,7 +5159,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data; ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status; - if (ssh->cfg.agentfwd && agent_exists()) { + if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) { logevent("Requesting agent forwarding"); send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END); do { @@ -5073,9 +5178,9 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - if (ssh->cfg.x11_forward && - (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, - ssh->cfg.x11_auth, &ssh->cfg))) { + if (conf_get_int(ssh->conf, CONF_x11_forward) && + (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), + conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) { logevent("Requesting X11 forwarding"); /* * Note that while we blank the X authentication data here, we don't @@ -5116,24 +5221,23 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - ssh_setup_portfwd(ssh, &ssh->cfg); + ssh_setup_portfwd(ssh, ssh->conf); ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open; - if (!ssh->cfg.nopty) { + if (!conf_get_int(ssh->conf, CONF_nopty)) { struct Packet *pkt; /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ - sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed); + sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); /* Send the pty request. */ pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY); - ssh_pkt_addstring(pkt, ssh->cfg.termtype); + ssh_pkt_addstring(pkt, conf_get_str(ssh->conf, CONF_termtype)); ssh_pkt_adduint32(pkt, ssh->term_height); ssh_pkt_adduint32(pkt, ssh->term_width); ssh_pkt_adduint32(pkt, 0); /* width in pixels */ ssh_pkt_adduint32(pkt, 0); /* height in pixels */ - parse_ttymodes(ssh, ssh->cfg.ttymodes, - ssh1_send_ttymode, (void *)pkt); + parse_ttymodes(ssh, ssh1_send_ttymode, (void *)pkt); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED); ssh_pkt_adduint32(pkt, ssh->ispeed); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED); @@ -5151,14 +5255,16 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } else if (pktin->type == SSH1_SMSG_FAILURE) { c_write_str(ssh, "Server refused to allocate pty\r\n"); ssh->editing = ssh->echoing = 1; - } - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); + } else { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } } else { ssh->editing = ssh->echoing = 1; } - if (ssh->cfg.compression) { + if (conf_get_int(ssh->conf, CONF_compression)) { send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END); do { crReturnV; @@ -5186,12 +5292,11 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, * exists, we fall straight back to that. */ { - char *cmd = ssh->cfg.remote_cmd_ptr; - - if (!cmd) cmd = ssh->cfg.remote_cmd; + char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd); - if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) { - cmd = ssh->cfg.remote_cmd_ptr2; + if (conf_get_int(ssh->conf, CONF_ssh_subsys) && + conf_get_str(ssh->conf, CONF_remote_cmd2)) { + cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); ssh->fallback_cmd = TRUE; } if (*cmd) @@ -5396,11 +5501,12 @@ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, /* * Handle the SSH-2 transport layer. */ -static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, +static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin) { unsigned char *in = (unsigned char *)vin; struct do_ssh2_transport_state { + int crLine; int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher; Bignum p, g, e, f, K; void *our_kexinit; @@ -5434,7 +5540,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, }; crState(do_ssh2_transport_state); - crBegin(ssh->do_ssh2_transport_crstate); + crBeginState; s->cscipher_tobe = s->sccipher_tobe = NULL; s->csmac_tobe = s->scmac_tobe = NULL; @@ -5455,14 +5561,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, begin_key_exchange: ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { - int i, j, commalist_started; + int i, j, k, commalist_started; /* * Set up the preferred key exchange. (NULL => warn below here) */ s->n_preferred_kex = 0; for (i = 0; i < KEX_MAX; i++) { - switch (ssh->cfg.ssh_kexlist[i]) { + switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) { case KEX_DHGEX: s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_gex; @@ -5494,12 +5600,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ s->n_preferred_ciphers = 0; for (i = 0; i < CIPHER_MAX; i++) { - switch (ssh->cfg.ssh_cipherlist[i]) { + switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) { case CIPHER_BLOWFISH: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish; break; case CIPHER_DES: - if (ssh->cfg.ssh2_des_cbc) { + if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) { s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des; } break; @@ -5525,7 +5631,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, /* * Set up preferred compression. */ - if (ssh->cfg.compression) + if (conf_get_int(ssh->conf, CONF_compression)) s->preferred_comp = &ssh_zlib; else s->preferred_comp = &ssh_comp_none; @@ -5567,46 +5673,30 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (i < lenof(hostkey_algs) - 1) ssh2_pkt_addstring_str(s->pktout, ","); } - /* List client->server encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; + /* List encryption algorithms (client->server then server->client). */ + for (k = 0; k < 2; k++) { + ssh2_pkt_addstring_start(s->pktout); + commalist_started = 0; + for (i = 0; i < s->n_preferred_ciphers; i++) { + const struct ssh2_ciphers *c = s->preferred_ciphers[i]; + if (!c) continue; /* warning flag */ + for (j = 0; j < c->nciphers; j++) { + if (commalist_started) + ssh2_pkt_addstring_str(s->pktout, ","); + ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); + commalist_started = 1; + } } } - /* List server->client encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) + /* List MAC algorithms (client->server then server->client). */ + for (j = 0; j < 2; j++) { + ssh2_pkt_addstring_start(s->pktout); + for (i = 0; i < s->nmacs; i++) { + ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); + if (i < s->nmacs - 1) ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; } } - /* List client->server MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } - /* List server->client MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the * same set twice.) */ @@ -5652,7 +5742,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_send_noqueue(ssh, s->pktout); if (!pktin) - crWaitUntil(pktin); + crWaitUntilV(pktin); /* * Now examine the other side's KEXINIT to see what we're up @@ -5664,7 +5754,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (pktin->type != SSH2_MSG_KEXINIT) { bombout(("expected key exchange packet from server")); - crStop(0); + crStopV; } ssh->kex = NULL; ssh->hostkey = NULL; @@ -5699,7 +5789,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!ssh->kex) { bombout(("Couldn't agree a key exchange algorithm (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } /* * Note that the server's guess is considered wrong if it doesn't match @@ -5714,6 +5804,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } } + if (!ssh->hostkey) { + bombout(("Couldn't agree a host key algorithm (available: %s)", + str ? str : "(null)")); + crStopV; + } + s->guessok = s->guessok && first_in_commasep_string(hostkey_algs[0]->name, str, len); ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ @@ -5735,7 +5831,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->cscipher_tobe) { bombout(("Couldn't agree a client-to-server cipher (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */ @@ -5757,7 +5853,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->sccipher_tobe) { bombout(("Couldn't agree a server-to-client cipher (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* client->server mac */ @@ -5814,6 +5910,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; + ssh->exhash = ssh->kex->hash->init(); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); + hash_string(ssh->kex->hash, ssh->exhash, + s->our_kexinit, s->our_kexinitlen); + sfree(s->our_kexinit); + if (pktin->length > 5) + hash_string(ssh->kex->hash, ssh->exhash, + pktin->data + 5, pktin->length - 5); + if (s->warn_kex) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "key-exchange algorithm", @@ -5821,11 +5927,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5834,7 +5940,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at kex warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } @@ -5846,11 +5952,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5859,7 +5965,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } @@ -5871,11 +5977,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5884,22 +5990,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } - ssh->exhash = ssh->kex->hash->init(); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); - hash_string(ssh->kex->hash, ssh->exhash, - s->our_kexinit, s->our_kexinitlen); - sfree(s->our_kexinit); - if (pktin->length > 5) - hash_string(ssh->kex->hash, ssh->exhash, - pktin->data + 5, pktin->length - 5); - if (s->ignorepkt) /* first_kex_packet_follows */ - crWaitUntil(pktin); /* Ignore packet */ + crWaitUntilV(pktin); /* Ignore packet */ } if (ssh->kex->main_type == KEXTYPE_DH) { @@ -5936,16 +6032,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_adduint32(s->pktout, s->pbits); ssh2_pkt_send_noqueue(ssh, s->pktout); - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { bombout(("expected key exchange group packet from server")); - crStop(0); + crStopV; } 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); + crStopV; } ssh->kex_ctx = dh_setup_gex(s->p, s->g); s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; @@ -5971,10 +6067,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_send_noqueue(ssh, s->pktout); set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != s->kex_reply_value) { bombout(("expected key exchange reply packet from server")); - crStop(0); + crStopV; } set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); @@ -5982,7 +6078,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->f = ssh2_pkt_getmp(pktin); if (!s->f) { bombout(("unable to parse key exchange reply packet")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); @@ -6015,10 +6111,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { bombout(("expected RSA public key packet from server")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); @@ -6037,7 +6133,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->rsakey) { sfree(s->rsakeydata); bombout(("unable to parse RSA public key from server")); - crStop(0); + crStopV; } hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen); @@ -6097,11 +6193,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_rsakex_freekey(s->rsakey); - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_DONE) { sfree(s->rsakeydata); bombout(("expected signature packet from server")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); @@ -6125,7 +6221,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); - crStop(0); + crStopV; } /* @@ -6142,11 +6238,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while waiting" " for user host key response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -6155,7 +6251,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at host key verification", NULL, 0, TRUE); - crStop(0); + crStopV; } if (!s->got_session_id) { /* don't bother logging this in rekeys */ logevent("Host key fingerprint is:"); @@ -6224,7 +6320,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->csmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s client->server encryption", @@ -6245,10 +6341,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, /* * Expect SSH2_MSG_NEWKEYS from server. */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_NEWKEYS) { bombout(("expected new-keys packet from server")); - crStop(0); + crStopV; } ssh->incoming_data_size = 0; /* start counting from here */ @@ -6290,7 +6386,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->scmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s server->client encryption", ssh->sccipher->text_name); @@ -6321,24 +6417,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ ssh->kex_in_progress = FALSE; ssh->last_rekey = GETTICKCOUNT(); - if (ssh->cfg.ssh_rekey_time != 0) - ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) + ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); /* - * If this is the first key exchange phase, we must pass the - * SSH2_MSG_NEWKEYS packet to the next layer, not because it - * wants to see it but because it will need time to initialise - * itself before it sees an actual packet. In subsequent key - * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because - * it would only confuse the layer above. - */ - if (s->activated_authconn) { - crReturn(0); - } - s->activated_authconn = TRUE; - - /* * Now we're encrypting. Begin returning 1 to the protocol main * function so that other things can run on top of the * transport. If we ever see a KEXINIT, we must go back to the @@ -6356,7 +6439,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) || (!pktin && inlen < 0))) { wait_for_rekey: - crReturn(1); + if (!ssh->protocol_initial_phase_done) { + ssh->protocol_initial_phase_done = TRUE; + /* + * Allow authconn to initialise itself. + */ + do_ssh2_authconn(ssh, NULL, 0, NULL); + } + crReturnV; } if (pktin) { logevent("Server initiated key re-exchange"); @@ -6403,9 +6493,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * hit the event log _too_ often. */ ssh->outgoing_data_size = 0; ssh->incoming_data_size = 0; - if (ssh->cfg.ssh_rekey_time != 0) { + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) { ssh->next_rekey = - schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, + schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); } goto wait_for_rekey; /* this is still utterly horrid */ @@ -6415,7 +6505,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } goto begin_key_exchange; - crFinish(1); + crFinishV; } /* @@ -6434,6 +6524,7 @@ static int ssh2_try_send(struct ssh_channel *c) { Ssh ssh = c->ssh; struct Packet *pktout; + int ret; while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) { int len; @@ -6458,14 +6549,23 @@ static int ssh2_try_send(struct ssh_channel *c) * After having sent as much data as we can, return the amount * still buffered. */ - return bufchain_size(&c->v.v2.outbuffer); + ret = bufchain_size(&c->v.v2.outbuffer); + + /* + * And if there's no data pending but we need to send an EOF, send + * it. + */ + if (!ret && c->pending_eof) + ssh_channel_try_eof(c); + + return ret; } static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) { int bufsize; - if (c->closes) - return; /* don't send on closing channels */ + if (c->closes & CLOSES_SENT_EOF) + return; /* don't send on channels we've EOFed */ bufsize = ssh2_try_send(c); if (bufsize == 0) { switch (c->type) { @@ -6485,19 +6585,6 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) break; } } - - /* - * If we've emptied the channel's output buffer and there's a - * pending close event, start the channel-closing procedure. - */ - if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) { - struct Packet *pktout; - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - c->closes = 1; - c->pending_close = FALSE; - } } /* @@ -6508,28 +6595,95 @@ static void ssh2_channel_init(struct ssh_channel *c) Ssh ssh = c->ssh; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->pending_close = FALSE; + c->pending_eof = FALSE; 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; + conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.chanreq_head = NULL; c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); } /* - * Potentially enlarge the window on an SSH-2 channel. + * Construct the common parts of a CHANNEL_OPEN. */ -static void ssh2_set_window(struct ssh_channel *c, int newwin) +static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type) { - Ssh ssh = c->ssh; + struct Packet *pktout; + + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); + ssh2_pkt_addstring(pktout, type); + ssh2_pkt_adduint32(pktout, c->localid); + ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ + ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + return 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. + */ +static void ssh2_queue_chanreq_handler(struct ssh_channel *c, + cchandler_fn_t handler, void *ctx) +{ + struct outstanding_channel_request *ocr = + snew(struct outstanding_channel_request); + + assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); + ocr->handler = handler; + ocr->ctx = ctx; + ocr->next = NULL; + if (!c->v.v2.chanreq_head) + c->v.v2.chanreq_head = ocr; + else + c->v.v2.chanreq_tail->next = ocr; + c->v.v2.chanreq_tail = ocr; +} + +/* + * Construct the common parts of a CHANNEL_REQUEST. If handler is not + * NULL then a reply will be requested and the handler will be called + * when it arrives. The returned packet is ready to have any + * request-specific data added and be sent. Note that if a handler is + * provided, it's essential that the request actually be sent. + * + * The handler will usually be passed the response packet in pktin. + * If pktin is NULL, this means that no reply will ever be forthcoming + * (e.g. because the entire connection is being destroyed) and the + * handler should free any storage it's holding. + */ +static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type, + cchandler_fn_t handler, void *ctx) +{ + struct Packet *pktout; + + assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, type); + ssh2_pkt_addbool(pktout, handler != NULL); + if (handler != NULL) + ssh2_queue_chanreq_handler(c, handler, ctx); + return pktout; +} + +/* + * Potentially enlarge the window on an SSH-2 channel. + */ +static void ssh2_handle_winadj_response(struct ssh_channel *, struct Packet *, + void *); +static void ssh2_set_window(struct ssh_channel *c, int newwin) +{ + Ssh ssh = c->ssh; /* - * Never send WINDOW_ADJUST for a channel that the remote side - * already thinks it's closed; there's no point, since it won't - * be sending any more data anyway. + * Never send WINDOW_ADJUST for a channel that the remote side has + * already sent EOF on; there's no point, since it won't be + * sending any more data anyway. Ditto if _we've_ already sent + * CLOSE. */ - if (c->closes != 0) + if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE)) return; /* @@ -6539,7 +6693,6 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ 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 @@ -6550,7 +6703,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; - struct winadj *wa; + unsigned *up; /* * In order to keep track of how much window the client @@ -6561,33 +6714,15 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) * 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); + !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) { + up = snew(unsigned); + *up = newwin - c->v.v2.locwindow; + pktout = ssh2_chanreq_init(c, "winadj@putty.projects.tartarus.org", + ssh2_handle_winadj_response, up); 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 { @@ -6627,14 +6762,21 @@ static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) return c; } -static int ssh2_handle_winadj_response(struct ssh_channel *c) +static void ssh2_handle_winadj_response(struct ssh_channel *c, + struct Packet *pktin, void *ctx) { - struct winadj *wa = c->v.v2.winadj_head; - if (!wa) - return FALSE; - c->v.v2.winadj_head = wa->next; - c->v.v2.remlocwin += wa->size; - sfree(wa); + unsigned *sizep = ctx; + + /* + * Winadj responses should always be failures. However, at least + * one server ("boks_sshd") is known to return SUCCESS for channel + * requests it's never heard of, such as "winadj@putty". Raised + * with foxt.com as bug 090916-090424, but for the sake of a quiet + * life, we don't worry about what kind of response we got. + */ + + c->v.v2.remlocwin += *sizep; + sfree(sizep); /* * 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 @@ -6642,51 +6784,28 @@ static int ssh2_handle_winadj_response(struct ssh_channel *c) */ if (c->v.v2.throttle_state == UNTHROTTLING) c->v.v2.throttle_state = UNTHROTTLED; - return TRUE; } -static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin) { - /* - * This should never get called. All channel requests are either - * sent with want_reply false, are sent before this handler gets - * installed, or are "winadj@putty" requests, which servers should - * never respond to with success. - * - * However, at least one server ("boks_sshd") is known to return - * SUCCESS for channel requests it's never heard of, such as - * "winadj@putty". Raised with foxt.com as bug 090916-090424, but - * for the sake of a quiet life, we handle it just the same as the - * expected FAILURE. - */ - struct ssh_channel *c; + struct ssh_channel *c = ssh2_channel_msg(ssh, pktin); + struct outstanding_channel_request *ocr; - c = ssh2_channel_msg(ssh, pktin); - if (!c) + if (!c) return; + ocr = c->v.v2.chanreq_head; + if (!ocr) { + ssh2_msg_unexpected(ssh, pktin); return; - if (!ssh2_handle_winadj_response(c)) - 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) -{ + } + ocr->handler(c, pktin, ocr->ctx); + c->v.v2.chanreq_head = ocr->next; + sfree(ocr); /* - * 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. + * We may now initiate channel-closing procedures, if that + * CHANNEL_REQUEST was the last thing outstanding before we send + * CHANNEL_CLOSE. */ - struct ssh_channel *c; - - c = ssh2_channel_msg(ssh, pktin); - if (!c) - return; - if (!ssh2_handle_winadj_response(c)) - ssh_disconnect(ssh, NULL, - "Received unsolicited SSH_MSG_CHANNEL_FAILURE", - SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + ssh2_channel_check_close(c); } static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) @@ -6695,7 +6814,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) c = ssh2_channel_msg(ssh, pktin); if (!c) return; - if (!c->closes) { + if (!(c->closes & CLOSES_SENT_EOF)) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(ssh, c); } @@ -6761,12 +6880,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); + c->u.a.message = NULL; c->u.a.lensofar = 0; } } @@ -6796,7 +6917,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) * throttle the whole channel. */ if ((bufsize > c->v.v2.locmaxwin || - (ssh->cfg.ssh_simple && bufsize > 0)) && + (conf_get_int(ssh->conf, CONF_ssh_simple) && bufsize > 0)) && !c->throttling_conn) { c->throttling_conn = 1; ssh_throttle_conn(ssh, +1); @@ -6804,93 +6925,202 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) } } -static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) +static void ssh_channel_destroy(struct ssh_channel *c) { - struct ssh_channel *c; + Ssh ssh = c->ssh; - c = ssh2_channel_msg(ssh, pktin); - if (!c) - return; + switch (c->type) { + case CHAN_MAINSESSION: + ssh->mainchan = NULL; + update_specials_menu(ssh->frontend); + break; + case CHAN_X11: + if (c->u.x11.s != NULL) + x11_close(c->u.x11.s); + logevent("Forwarded X11 connection terminated"); + break; + case CHAN_AGENT: + sfree(c->u.a.message); + break; + case CHAN_SOCKDATA: + if (c->u.pfd.s != NULL) + pfd_close(c->u.pfd.s); + logevent("Forwarded port closed"); + break; + } + + del234(ssh->channels, c); + if (ssh->version == 2) { + bufchain_clear(&c->v.v2.outbuffer); + assert(c->v.v2.chanreq_head == NULL); + } + sfree(c); + + /* + * See if that was the last channel left open. + * (This is only our termination condition if we're + * not running in -N mode.) + */ + if (ssh->version == 2 && + !conf_get_int(ssh->conf, CONF_ssh_no_shell) && + count234(ssh->channels) == 0) { + /* + * We used to send SSH_MSG_DISCONNECT here, + * because I'd believed that _every_ conforming + * SSH-2 connection had to end with a disconnect + * being sent by at least one side; apparently + * I was wrong and it's perfectly OK to + * unceremoniously slam the connection shut + * when you're done, and indeed OpenSSH feels + * this is more polite than sending a + * DISCONNECT. So now we don't. + */ + ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE); + } +} + +static void ssh2_channel_check_close(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + struct Packet *pktout; + + if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) || + c->type == CHAN_ZOMBIE) && + !c->v.v2.chanreq_head && + !(c->closes & CLOSES_SENT_CLOSE)) { + /* + * We have both sent and received EOF (or the channel is a + * zombie), and we have no outstanding channel requests, which + * means the channel is in final wind-up. But we haven't sent + * CLOSE, so let's do so now. + */ + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE; + } + + if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) { + assert(c->v.v2.chanreq_head == NULL); + /* + * We have both sent and received CLOSE, which means we're + * completely done with the channel. + */ + ssh_channel_destroy(c); + } +} + +static void ssh2_channel_got_eof(struct ssh_channel *c) +{ + if (c->closes & CLOSES_RCVD_EOF) + return; /* already seen EOF */ + c->closes |= CLOSES_RCVD_EOF; if (c->type == CHAN_X11) { - /* - * Remote EOF on an X11 channel means we should - * wrap up and close the channel ourselves. - */ - x11_close(c->u.x11.s); - c->u.x11.s = NULL; - sshfwd_close(c); + x11_send_eof(c->u.x11.s); } else if (c->type == CHAN_AGENT) { - sshfwd_close(c); + if (c->u.a.outstanding_requests == 0) { + /* Manufacture an outgoing EOF in response to the incoming one. */ + sshfwd_write_eof(c); + } } else if (c->type == CHAN_SOCKDATA) { - pfd_close(c->u.pfd.s); - c->u.pfd.s = NULL; - sshfwd_close(c); + pfd_send_eof(c->u.pfd.s); + } else if (c->type == CHAN_MAINSESSION) { + Ssh ssh = c->ssh; + + if (!ssh->sent_console_eof && + (from_backend_eof(ssh->frontend) || ssh->got_pty)) { + /* + * Either from_backend_eof told us that the front end + * wants us to close the outgoing side of the connection + * as soon as we see EOF from the far end, or else we've + * unilaterally decided to do that because we've allocated + * a remote pty and hence EOF isn't a particularly + * meaningful concept. + */ + sshfwd_write_eof(c); + } + ssh->sent_console_eof = TRUE; } + + ssh2_channel_check_close(c); +} + +static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) +{ + struct ssh_channel *c; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + ssh2_channel_got_eof(c); } static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; - struct Packet *pktout; c = ssh2_channel_msg(ssh, pktin); if (!c) return; - /* Do pre-close processing on the channel. */ - switch (c->type) { - case CHAN_MAINSESSION: - ssh->mainchan = NULL; - update_specials_menu(ssh->frontend); - break; - case CHAN_X11: - if (c->u.x11.s != NULL) - x11_close(c->u.x11.s); - sshfwd_close(c); - break; - case CHAN_AGENT: - sshfwd_close(c); - break; - case CHAN_SOCKDATA: - if (c->u.pfd.s != NULL) - pfd_close(c->u.pfd.s); - sshfwd_close(c); - break; - } - if (c->closes == 0) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); + + /* + * When we receive CLOSE on a channel, we assume it comes with an + * implied EOF if we haven't seen EOF yet. + */ + ssh2_channel_got_eof(c); + + /* + * And we also send an outgoing EOF, if we haven't already, on the + * assumption that CLOSE is a pretty forceful announcement that + * the remote side is doing away with the entire channel. (If it + * had wanted to send us EOF and continue receiving data from us, + * it would have just sent CHANNEL_EOF.) + */ + if (!(c->closes & CLOSES_SENT_EOF)) { + /* + * Make sure we don't read any more from whatever our local + * data source is for this channel. + */ + switch (c->type) { + case CHAN_MAINSESSION: + ssh->send_ok = 0; /* stop trying to read from stdin */ + break; + case CHAN_X11: + x11_override_throttle(c->u.x11.s, 1); + break; + case CHAN_SOCKDATA: + pfd_override_throttle(c->u.pfd.s, 1); + break; + } + + /* + * Abandon any buffered data we still wanted to send to this + * channel. Receiving a CHANNEL_CLOSE is an indication that + * the server really wants to get on and _destroy_ this + * channel, and it isn't going to send us any further + * WINDOW_ADJUSTs to permit us to send pending stuff. + */ + bufchain_clear(&c->v.v2.outbuffer); + + /* + * Send outgoing EOF. + */ + sshfwd_write_eof(c); } - del234(ssh->channels, c); - bufchain_clear(&c->v.v2.outbuffer); - sfree(c); /* - * See if that was the last channel left open. - * (This is only our termination condition if we're - * not running in -N mode.) + * Now process the actual close. */ - if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) { - /* - * We used to send SSH_MSG_DISCONNECT here, - * because I'd believed that _every_ conforming - * SSH-2 connection had to end with a disconnect - * being sent by at least one side; apparently - * I was wrong and it's perfectly OK to - * unceremoniously slam the connection shut - * when you're done, and indeed OpenSSH feels - * this is more polite than sending a - * DISCONNECT. So now we don't. - */ - ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE); + if (!(c->closes & CLOSES_RCVD_CLOSE)) { + c->closes |= CLOSES_RCVD_CLOSE; + ssh2_channel_check_close(c); } } static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; - struct Packet *pktout; c = ssh2_channel_msg(ssh, pktin); if (!c) @@ -6904,17 +7134,8 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); if (c->u.pfd.s) pfd_confirm(c->u.pfd.s); - if (c->closes) { - /* - * We have a pending close on this channel, - * which we decided on before the server acked - * the channel open. So now we know the - * remoteid, we can close it again. - */ - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - } + if (c->pending_eof) + ssh_channel_try_eof(c); } static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) @@ -7003,16 +7224,18 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) is_int = FALSE; } else { int maybe_int = FALSE, maybe_str = FALSE; -#define CHECK_HYPOTHESIS(offset, result) \ - do { \ - long q = offset; \ - if (q >= 0 && q+4 <= len) { \ - q = q + 4 + GET_32BIT(p+q); \ - if (q >= 0 && q+4 <= len && \ - ((q = q + 4 + GET_32BIT(p+q))!= 0) && q == len) \ - result = TRUE; \ - } \ - } while(0) +#define CHECK_HYPOTHESIS(offset, result) \ + do \ + { \ + int q = toint(offset); \ + if (q >= 0 && q+4 <= len) { \ + q = toint(q + 4 + GET_32BIT(p+q)); \ + if (q >= 0 && q+4 <= len && \ + ((q = toint(q + 4 + GET_32BIT(p+q))) != 0) && \ + q == len) \ + result = TRUE; \ + } \ + } while(0) CHECK_HYPOTHESIS(4+1, maybe_int); CHECK_HYPOTHESIS(4+num+1, maybe_str); #undef CHECK_HYPOTHESIS @@ -7186,7 +7409,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (!ssh->X11_fwd_enabled) error = "X11 forwarding is not enabled"; else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c, - addrstr, peerport, &ssh->cfg)) != NULL) { + addrstr, peerport, ssh->conf)) != NULL) { logeventf(ssh, "Local X11 connection failed: %s", x11err); error = "Unable to open an X11 connection"; } else { @@ -7213,7 +7436,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) const char *e = pfd_newconnect(&c->u.pfd.s, realpf->dhost, realpf->dport, c, - &ssh->cfg, + ssh->conf, realpf->pfrec->addressfamily); logeventf(ssh, "Attempting to forward remote port to " "%s:%d", realpf->dhost, realpf->dport); @@ -7232,6 +7455,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) else { c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.outstanding_requests = 0; } } else { error = "Unsupported channel type requested"; @@ -7269,7 +7493,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin) { /* Arbitrary limit to prevent unbounded inflation of buffer */ - if (ssh->cfg.ssh_show_banner && + if (conf_get_int(ssh->conf, CONF_ssh_show_banner) && bufchain_size(&ssh->banner) <= 131072) { char *banner = NULL; int size = 0; @@ -7299,13 +7523,220 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val) ssh2_pkt_adduint32(pktout, arg); } +static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_x11_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_x11_state, ctx); + + crBeginState; + + logevent("Requesting X11 forwarding"); + pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req", + ssh2_setup_x11, s); + ssh2_pkt_addbool(pktout, 0); /* many connections */ + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthprotoname); + /* + * Note that while we blank the X authentication data here, we don't + * take any special action to blank the start of an X11 channel, + * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection + * without having session blanking enabled is likely to leak your + * cookie into the log. + */ + dont_log_password(ssh, pktout, PKTLOG_BLANK); + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthdatastring); + end_log_omission(ssh, pktout); + ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum); + ssh2_pkt_send(ssh, pktout); + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logevent("X11 forwarding enabled"); + ssh->X11_fwd_enabled = TRUE; + } else + logevent("X11 forwarding refused"); + } + + crFinishFreeV; +} + +static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_agent_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_agent_state, ctx); + + crBeginState; + + logevent("Requesting OpenSSH-style agent forwarding"); + pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com", + ssh2_setup_agent, s); + ssh2_pkt_send(ssh, pktout); + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logevent("Agent forwarding enabled"); + ssh->agentfwd_enabled = TRUE; + } else + logevent("Agent forwarding refused"); + } + + crFinishFreeV; +} + +static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_pty_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_pty_state, ctx); + + crBeginState; + + /* Unpick the terminal-speed string. */ + /* XXX perhaps we should allow no speeds to be sent. */ + ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ + sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); + /* Build the pty request. */ + pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req", + ssh2_setup_pty, s); + ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype)); + ssh2_pkt_adduint32(pktout, ssh->term_width); + ssh2_pkt_adduint32(pktout, ssh->term_height); + ssh2_pkt_adduint32(pktout, 0); /* pixel width */ + ssh2_pkt_adduint32(pktout, 0); /* pixel height */ + ssh2_pkt_addstring_start(pktout); + parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED); + ssh2_pkt_adduint32(pktout, ssh->ispeed); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED); + ssh2_pkt_adduint32(pktout, ssh->ospeed); + ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */ + ssh2_pkt_send(ssh, pktout); + ssh->state = SSH_STATE_INTERMED; + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } else { + c_write_str(ssh, "Server refused to allocate pty\r\n"); + ssh->editing = ssh->echoing = 1; + } + } + + crFinishFreeV; +} + +static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_env_state { + int crLine; + int num_env, env_left, env_ok; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_env_state, ctx); + + crBeginState; + + /* + * Send environment variables. + * + * Simplest thing here is to send all the requests at once, and + * then wait for a whole bunch of successes or failures. + */ + s->num_env = 0; + { + char *key, *val; + + for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { + pktout = ssh2_chanreq_init(ssh->mainchan, "env", ssh2_setup_env, s); + ssh2_pkt_addstring(pktout, key); + ssh2_pkt_addstring(pktout, val); + ssh2_pkt_send(ssh, pktout); + + s->num_env++; + } + if (s->num_env) + logeventf(ssh, "Sent %d environment variables", s->num_env); + } + + if (s->num_env) { + s->env_ok = 0; + s->env_left = s->num_env; + + while (s->env_left > 0) { + /* Wait to be called back with either a response packet, + * or NULL meaning clean up and free our data */ + crReturnV; + if (!pktin) goto out; + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) + s->env_ok++; + s->env_left--; + } + + if (s->env_ok == s->num_env) { + logevent("All environment variables successfully set"); + } else if (s->env_ok == 0) { + logevent("All environment variables refused"); + c_write_str(ssh, "Server refused to set environment variables\r\n"); + } else { + logeventf(ssh, "%d environment variables refused", + s->num_env - s->env_ok); + c_write_str(ssh, "Server refused to set all environment variables\r\n"); + } + } + out:; + crFinishFreeV; +} + /* * Handle the SSH-2 userauth and connection layers. */ +static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_authconn(ssh, NULL, 0, pktin); +} + +static void ssh2_response_authconn(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + do_ssh2_authconn(c->ssh, NULL, 0, pktin); +} + static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { struct do_ssh2_authconn_state { + int crLine; enum { AUTH_TYPE_NONE, AUTH_TYPE_PUBLICKEY, @@ -7327,7 +7758,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int we_are_in, userauth_success; prompts_t *cur_prompt; int num_prompts; - char username[100]; + char *username; char *password; int got_username; void *publickey_blob; @@ -7344,8 +7775,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int siglen, retlen, len; char *q, *agentreq, *ret; int try_send; - int num_env, env_left, env_ok; struct Packet *pktout; + Filename *keyfile; #ifndef NO_GSSAPI struct ssh_gss_library *gsslib; Ssh_gss_ctx gss_ctx; @@ -7357,15 +7788,37 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, }; crState(do_ssh2_authconn_state); - crBegin(ssh->do_ssh2_authconn_crstate); - + crBeginState; + + /* Register as a handler for all the messages this coroutine handles. */ + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_authconn; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_authconn; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_authconn; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_authconn; + s->done_service_req = FALSE; s->we_are_in = s->userauth_success = FALSE; #ifndef NO_GSSAPI s->tried_gssapi = FALSE; #endif - if (!ssh->cfg.ssh_no_userauth) { + if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) { /* * Request userauth protocol, and await a response to it. */ @@ -7408,28 +7861,29 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Load the public half of any configured public key file * for later use. */ - if (!filename_is_null(ssh->cfg.keyfile)) { + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", - filename_to_str(&ssh->cfg.keyfile)); - keytype = key_type(&ssh->cfg.keyfile); + filename_to_str(s->keyfile)); + keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH2) { const char *error; s->publickey_blob = - ssh2_userkey_loadpub(&ssh->cfg.keyfile, + ssh2_userkey_loadpub(s->keyfile, &s->publickey_algorithm, &s->publickey_bloblen, &s->publickey_comment, &error); if (s->publickey_blob) { s->publickey_encrypted = - ssh2_userkey_encrypted(&ssh->cfg.keyfile, NULL); + ssh2_userkey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -7440,7 +7894,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -7455,7 +7909,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->nkeys = 0; s->agent_response = NULL; s->pkblob_in_agent = NULL; - if (ssh->cfg.tryagent && agent_exists()) { + if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) { void *r; @@ -7483,13 +7937,53 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int keyi; unsigned char *p; p = s->agent_response + 5; - s->nkeys = GET_32BIT(p); + s->nkeys = toint(GET_32BIT(p)); + + /* + * Vet the Pageant response to ensure that the key + * count and blob lengths make sense. + */ + if (s->nkeys < 0) { + logeventf(ssh, "Pageant response contained a negative" + " key count %d", s->nkeys); + s->nkeys = 0; + goto done_agent_query; + } else { + unsigned char *q = p + 4; + int lenleft = s->agent_responselen - 5 - 4; + + for (keyi = 0; keyi < s->nkeys; keyi++) { + int bloblen, commentlen; + if (lenleft < 4) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + bloblen = toint(GET_32BIT(q)); + if (bloblen < 0 || bloblen > lenleft) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + lenleft -= 4 + bloblen; + q += 4 + bloblen; + commentlen = toint(GET_32BIT(q)); + if (commentlen < 0 || commentlen > lenleft) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + lenleft -= 4 + commentlen; + q += 4 + commentlen; + } + } + p += 4; logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys); if (s->publickey_blob) { /* See if configured key is in agent. */ for (keyi = 0; keyi < s->nkeys; keyi++) { - s->pklen = GET_32BIT(p); + s->pklen = toint(GET_32BIT(p)); if (s->pklen == s->publickey_bloblen && !memcmp(p+4, s->publickey_blob, s->publickey_bloblen)) { @@ -7500,7 +7994,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, break; } p += 4 + s->pklen; - p += GET_32BIT(p) + 4; /* comment */ + p += toint(GET_32BIT(p)) + 4; /* comment */ } if (!s->pkblob_in_agent) { logevent("Configured key file not in Pageant"); @@ -7510,6 +8004,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { logevent("Failed to get reply from Pageant"); } + done_agent_query:; } } @@ -7538,26 +8033,23 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * the username they will want to be able to get back and * retype it! */ - s->username[0] = '\0'; s->got_username = FALSE; while (!s->we_are_in) { /* * Get a username. */ - if (s->got_username && !ssh->cfg.change_username) { + if (s->got_username && !conf_get_int(ssh->conf, CONF_change_username)) { /* * We got a username last time round this loop, and * with change_username turned off we don't try to get * it again. */ - } else if (!get_remote_username(&ssh->cfg, s->username, - sizeof(s->username))) { + } else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, - lenof(s->username)); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -7574,13 +8066,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStopV; } - memcpy(s->username, s->cur_prompt->prompts[0]->result, - lenof(s->username)); + ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } else { char *stuff; if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { - stuff = dupprintf("Using username \"%s\".\r\n", s->username); + stuff = dupprintf("Using username \"%s\".\r\n", ssh->username); c_write_str(ssh, stuff); sfree(stuff); } @@ -7595,7 +8086,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */ ssh2_pkt_addstring(s->pktout, "none"); /* method */ ssh2_pkt_send(ssh, s->pktout); @@ -7725,7 +8216,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, logevent("Password authentication failed"); c_write_str(ssh, "Access denied\r\n"); - if (ssh->cfg.change_username) { + if (conf_get_int(ssh->conf, CONF_change_username)) { /* XXX perhaps we should allow * keyboard-interactive to do this too? */ s->we_are_in = FALSE; @@ -7741,12 +8232,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("publickey", methods, methlen); s->can_passwd = in_commasep_string("password", methods, methlen); - s->can_keyb_inter = ssh->cfg.try_ki_auth && + s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) && in_commasep_string("keyboard-interactive", methods, methlen); #ifndef NO_GSSAPI if (!ssh->gsslibs) - ssh->gsslibs = ssh_gss_setup(&ssh->cfg); - s->can_gssapi = ssh->cfg.try_gssapi_auth && + ssh->gsslibs = ssh_gss_setup(ssh->conf); + s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) && in_commasep_string("gssapi-with-mic", methods, methlen) && ssh->gsslibs->nlibraries > 0; #endif @@ -7765,13 +8256,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, logeventf(ssh, "Trying Pageant key #%d", s->keyi); /* Unpack key from agent response */ - s->pklen = GET_32BIT(s->agentp); + s->pklen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->pkblob = (char *)s->agentp; s->agentp += s->pklen; - s->alglen = GET_32BIT(s->pkblob); + s->alglen = toint(GET_32BIT(s->pkblob)); s->alg = s->pkblob + 4; - s->commentlen = GET_32BIT(s->agentp); + s->commentlen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->commentp = (char *)s->agentp; s->agentp += s->commentlen; @@ -7779,7 +8270,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* See if server will accept it */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -7814,7 +8305,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Construct a SIGN_REQUEST. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -7875,7 +8366,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->ret = vret; sfree(s->agentreq); if (s->ret) { - if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) { + if (s->retlen >= 9 && + s->ret[4] == SSH2_AGENT_SIGN_RESPONSE && + GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) { logevent("Sending Pageant's response"); ssh2_add_sigblob(ssh, s->pktout, s->pkblob, s->pklen, @@ -7918,7 +8411,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * willing to accept it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ @@ -7964,7 +8457,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -7992,11 +8485,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Try decrypting the key. */ - key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase, - &error); + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + key = ssh2_load_userkey(s->keyfile, passphrase, &error); if (passphrase) { /* burn the evidence */ - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (key == SSH2_WRONG_PASSPHRASE || key == NULL) { @@ -8026,7 +8519,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Hallelujah. Generate a signature and send it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -8100,7 +8593,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int i, j; s->gsslib = NULL; for (i = 0; i < ngsslibs; i++) { - int want_id = ssh->cfg.ssh_gsslist[i]; + int want_id = conf_get_int_int(ssh->conf, + CONF_ssh_gsslist, i); for (j = 0; j < ssh->gsslibs->nlibraries; j++) if (ssh->gsslibs->libraries[j].id == want_id) { s->gsslib = &ssh->gsslibs->libraries[j]; @@ -8123,7 +8617,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); logevent("Attempting GSSAPI authentication"); @@ -8195,7 +8689,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, (s->gsslib, &s->gss_ctx, s->gss_srv_name, - ssh->cfg.gssapifwd, + conf_get_int(ssh->conf, CONF_gssapifwd), &s->gss_rcvtok, &s->gss_sndtok); @@ -8251,7 +8745,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_pkt_addstring_start(s->pktout); ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); - ssh_pkt_addstring(s->pktout, s->username); + ssh_pkt_addstring(s->pktout, ssh->username); ssh_pkt_addstring(s->pktout, "ssh-connection"); ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); @@ -8282,7 +8776,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "keyboard-interactive"); @@ -8343,7 +8837,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), - echo, SSH_MAX_PASSWORD_LEN); + echo); } if (name_len) { @@ -8444,10 +8938,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); - add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", - s->username, + add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", + ssh->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { @@ -8485,7 +8979,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); @@ -8549,11 +9043,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ add_prompt(s->cur_prompt, dupstr("Current password (blank for previously entered password): "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Enter new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); /* * Loop until the user manages to enter the same @@ -8574,7 +9068,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ /* burn the evidence */ free_prompts(s->cur_prompt); - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); ssh_disconnect(ssh, NULL, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER, @@ -8590,7 +9084,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * re-enter it if they louse up the new password.) */ if (s->cur_prompt->prompts[0]->result[0]) { - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); s->password = @@ -8614,7 +9108,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); @@ -8657,7 +9151,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * We don't need the old password any more, in any * case. Burn the evidence. */ - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); } else { @@ -8719,67 +9213,32 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Create the main session channel. */ - if (ssh->cfg.ssh_no_shell) { + if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) { ssh->mainchan = NULL; - } else if (*ssh->cfg.ssh_nc_host) { - /* - * Just start a direct-tcpip channel and use it as the main - * channel. - */ + } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = 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); - 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); - ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port); - /* - * There's nothing meaningful to put in the originator - * fields, but some servers insist on syntactically correct - * information. - */ - ssh2_pkt_addstring(s->pktout, "0.0.0.0"); - ssh2_pkt_adduint32(s->pktout, 0); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a direct-tcpip channel")); - crStopV; - /* FIXME: error data comes back in FAILURE packet */ - } - if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) { - bombout(("Server's channel confirmation cited wrong channel")); - crStopV; + if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) { + /* + * Just start a direct-tcpip channel and use it as the main + * channel. + */ + ssh_send_port_open(ssh->mainchan, + conf_get_str(ssh->conf, CONF_ssh_nc_host), + conf_get_int(ssh->conf, CONF_ssh_nc_port), + "main channel"); + ssh->ncmode = TRUE; + } else { + s->pktout = ssh2_chanopen_init(ssh->mainchan, "session"); + logevent("Opening session as main channel"); + ssh2_pkt_send(ssh, s->pktout); + ssh->ncmode = FALSE; } - ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); - ssh->mainchan->halfopen = FALSE; - ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); - ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - add234(ssh->channels, ssh->mainchan); - update_specials_menu(ssh->frontend); - logevent("Opened direct-tcpip channel"); - ssh->ncmode = TRUE; - } else { - ssh->mainchan = snew(struct ssh_channel); - ssh->mainchan->ssh = 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); - 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); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a session")); + bombout(("Server refused to open channel")); crStopV; /* FIXME: error data comes back in FAILURE packet */ } @@ -8794,8 +9253,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); - logevent("Opened channel for session"); - ssh->ncmode = FALSE; + logevent("Opened main channel"); } /* @@ -8815,265 +9273,114 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_msg_channel_request; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response; - if (ssh->mainchan && ssh->cfg.ssh_simple) { + + if (ssh->mainchan && conf_get_int(ssh->conf, CONF_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 */ + s->pktout = ssh2_chanreq_init(ssh->mainchan, + "simple@putty.projects.tartarus.org", + NULL, NULL); ssh2_pkt_send(ssh, s->pktout); } /* - * Potentially enable X11 forwarding. + * Enable port forwardings. */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward && - (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, - ssh->cfg.x11_auth, &ssh->cfg))) { - logevent("Requesting X11 forwarding"); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "x11-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addbool(s->pktout, 0); /* many connections */ - ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname); + ssh_setup_portfwd(ssh, ssh->conf); + + if (ssh->mainchan && !ssh->ncmode) { /* - * Note that while we blank the X authentication data here, we don't - * take any special action to blank the start of an X11 channel, - * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection - * without having session blanking enabled is likely to leak your - * cookie into the log. + * Send the CHANNEL_REQUESTS for the main session channel. + * Each one is handled by its own little asynchronous + * co-routine. */ - dont_log_password(ssh, s->pktout, PKTLOG_BLANK); - ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring); - end_log_omission(ssh, s->pktout); - ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); + /* Potentially enable X11 forwarding. */ + if (conf_get_int(ssh->conf, CONF_x11_forward) && + (ssh->x11disp = + x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), + conf_get_int(ssh->conf, CONF_x11_auth), + ssh->conf))) + ssh2_setup_x11(ssh->mainchan, NULL, NULL); - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to X11 forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("X11 forwarding refused"); - } else { - logevent("X11 forwarding enabled"); - ssh->X11_fwd_enabled = TRUE; - } - } + /* Potentially enable agent forwarding. */ + if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) + ssh2_setup_agent(ssh->mainchan, NULL, NULL); - /* - * Enable port forwardings. - */ - ssh_setup_portfwd(ssh, &ssh->cfg); + /* Now allocate a pty for the session. */ + if (!conf_get_int(ssh->conf, CONF_nopty)) + ssh2_setup_pty(ssh->mainchan, NULL, NULL); - /* - * Potentially enable agent forwarding. - */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) { - logevent("Requesting OpenSSH-style agent forwarding"); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_send(ssh, s->pktout); + /* Send environment variables. */ + ssh2_setup_env(ssh->mainchan, NULL, NULL); - crWaitUntilV(pktin); + /* + * Start a shell or a remote command. We may have to attempt + * this twice if the config data has provided a second choice + * of command. + */ + while (1) { + int subsys; + char *cmd; - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to agent forwarding request:" - " packet type %d", pktin->type)); - crStopV; + if (ssh->fallback_cmd) { + subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2); + cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); + } else { + subsys = conf_get_int(ssh->conf, CONF_ssh_subsys); + cmd = conf_get_str(ssh->conf, CONF_remote_cmd); } - logevent("Agent forwarding refused"); - } else { - logevent("Agent forwarding enabled"); - ssh->agentfwd_enabled = TRUE; - } - } - - /* - * Now allocate a pty for the session. - */ - if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) { - /* Unpick the terminal-speed string. */ - /* XXX perhaps we should allow no speeds to be sent. */ - ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ - sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed); - /* Build the pty request. */ - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */ - ssh2_pkt_addstring(s->pktout, "pty-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, ssh->cfg.termtype); - ssh2_pkt_adduint32(s->pktout, ssh->term_width); - ssh2_pkt_adduint32(s->pktout, ssh->term_height); - ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */ - ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */ - ssh2_pkt_addstring_start(s->pktout); - parse_ttymodes(ssh, ssh->cfg.ttymodes, - ssh2_send_ttymode, (void *)s->pktout); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ispeed); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ospeed); - ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */ - ssh2_pkt_send(ssh, s->pktout); - ssh->state = SSH_STATE_INTERMED; - - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to pty request:" - " packet type %d", pktin->type)); - crStopV; + if (subsys) { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem", + ssh2_response_authconn, NULL); + ssh2_pkt_addstring(s->pktout, cmd); + } else if (*cmd) { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec", + ssh2_response_authconn, NULL); + ssh2_pkt_addstring(s->pktout, cmd); + } else { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell", + ssh2_response_authconn, NULL); } - c_write_str(ssh, "Server refused to allocate pty\r\n"); - ssh->editing = ssh->echoing = 1; - } else { - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); - } - } else { - ssh->editing = ssh->echoing = 1; - } - - /* - * Send environment variables. - * - * Simplest thing here is to send all the requests at once, and - * then wait for a whole bunch of successes or failures. - */ - if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) { - char *e = ssh->cfg.environmt; - char *var, *varend, *val; - - s->num_env = 0; - - while (*e) { - var = e; - while (*e && *e != '\t') e++; - varend = e; - if (*e == '\t') e++; - val = e; - while (*e) e++; - e++; - - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "env"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring_start(s->pktout); - ssh2_pkt_addstring_data(s->pktout, var, varend-var); - ssh2_pkt_addstring(s->pktout, val); ssh2_pkt_send(ssh, s->pktout); - s->num_env++; - } - - logeventf(ssh, "Sent %d environment variables", s->num_env); - - s->env_ok = 0; - s->env_left = s->num_env; - - while (s->env_left > 0) { crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to environment request:" + bombout(("Unexpected response to shell/command request:" " packet type %d", pktin->type)); crStopV; } - } else { - s->env_ok++; - } - - s->env_left--; - } - - if (s->env_ok == s->num_env) { - logevent("All environment variables successfully set"); - } else if (s->env_ok == 0) { - logevent("All environment variables refused"); - c_write_str(ssh, "Server refused to set environment variables\r\n"); - } else { - logeventf(ssh, "%d environment variables refused", - s->num_env - s->env_ok); - c_write_str(ssh, "Server refused to set all environment variables\r\n"); - } - } - - /* - * Start a shell or a remote command. We may have to attempt - * this twice if the config data has provided a second choice - * of command. - */ - if (ssh->mainchan && !ssh->ncmode) while (1) { - int subsys; - char *cmd; - - if (ssh->fallback_cmd) { - subsys = ssh->cfg.ssh_subsys2; - cmd = ssh->cfg.remote_cmd_ptr2; - } else { - subsys = ssh->cfg.ssh_subsys; - cmd = ssh->cfg.remote_cmd_ptr; - if (!cmd) cmd = ssh->cfg.remote_cmd; - } - - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */ - if (subsys) { - ssh2_pkt_addstring(s->pktout, "subsystem"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, cmd); - } else if (*cmd) { - ssh2_pkt_addstring(s->pktout, "exec"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, cmd); - } else { - ssh2_pkt_addstring(s->pktout, "shell"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - } - ssh2_pkt_send(ssh, s->pktout); - - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to shell/command request:" - " packet type %d", pktin->type)); + /* + * We failed to start the command. If this is the + * fallback command, we really are finished; if it's + * not, and if the fallback command exists, try falling + * back to it before complaining. + */ + if (!ssh->fallback_cmd && + *conf_get_str(ssh->conf, CONF_remote_cmd2)) { + logevent("Primary command failed; attempting fallback"); + ssh->fallback_cmd = TRUE; + continue; + } + bombout(("Server refused to start a shell/command")); crStopV; + } else { + logevent("Started a shell/command"); } - /* - * We failed to start the command. If this is the - * fallback command, we really are finished; if it's - * not, and if the fallback command exists, try falling - * back to it before complaining. - */ - if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) { - logevent("Primary command failed; attempting fallback"); - ssh->fallback_cmd = TRUE; - continue; - } - bombout(("Server refused to start a shell/command")); - crStopV; - } else { - logevent("Started a shell/command"); + break; } - break; + } else { + ssh->editing = ssh->echoing = TRUE; } ssh->state = SSH_STATE_SESSION; @@ -9083,13 +9390,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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! */ if (ssh->ldisc) @@ -9175,6 +9475,25 @@ static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Remote debug message: %.*s", msglen, msg); } +static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_transport(ssh, NULL, 0, pktin); +} + +/* + * Called if we receive a packet that isn't allowed by the protocol. + * This only applies to packets whose meaning PuTTY understands. + * Entirely unknown packets are handled below. + */ +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin) +{ + char *buf = dupprintf("Server protocol violation: unexpected %s packet", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type)); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); +} + static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) { struct Packet *pktout; @@ -9201,60 +9520,62 @@ static void ssh2_protocol_setup(Ssh ssh) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* - * Any message we actually understand, we set to NULL so that - * the coroutines will get it. + * Initially, we only accept transport messages (and a few generic + * ones). do_ssh2_authconn will add more when it starts. + * Messages that are understood but not currently acceptable go to + * ssh2_msg_unexpected. */ - ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXINIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = NULL; + ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_KEXINIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = ssh2_msg_transport; + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = ssh2_msg_transport; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = ssh2_msg_transport; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_unexpected; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_unexpected; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_unexpected; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected; /* - * These special message types we install handlers for. + * These messages have a special handler from the start. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */ ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } -static void ssh2_timer(void *ctx, long now) +static void ssh2_timer(void *ctx, unsigned long now) { Ssh ssh = (Ssh)ctx; if (ssh->state == SSH_STATE_CLOSED) return; - if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 && - now - ssh->next_rekey >= 0) { + if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 && + now == ssh->next_rekey) { do_ssh2_transport(ssh, "timeout", -1, NULL); } } @@ -9274,24 +9595,17 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen, do_ssh2_transport(ssh, "too much data received", -1, NULL); } - if (pktin && ssh->packet_dispatch[pktin->type]) { + if (pktin) ssh->packet_dispatch[pktin->type](ssh, pktin); - return; - } - - if (!ssh->protocol_initial_phase_done || - (pktin && pktin->type >= 20 && pktin->type < 50)) { - if (do_ssh2_transport(ssh, in, inlen, pktin) && - !ssh->protocol_initial_phase_done) { - ssh->protocol_initial_phase_done = TRUE; - /* - * Allow authconn to initialise itself. - */ - do_ssh2_authconn(ssh, NULL, 0, NULL); - } - } else { + else if (!ssh->protocol_initial_phase_done) + do_ssh2_transport(ssh, in, inlen, pktin); + else do_ssh2_authconn(ssh, in, inlen, pktin); - } +} + +static void ssh_cache_conf_values(Ssh ssh) +{ + ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata); } /* @@ -9300,15 +9614,15 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen, * Returns an error message, or NULL on success. */ static const char *ssh_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive) + Conf *conf, char *host, int port, char **realhost, + int nodelay, int keepalive) { const char *p; Ssh ssh; ssh = snew(struct ssh_tag); - ssh->cfg = *cfg; /* STRUCTURE COPY */ + ssh->conf = conf_copy(conf); + ssh_cache_conf_values(ssh); ssh->version = 0; /* when not ready yet */ ssh->s = NULL; ssh->cipher = NULL; @@ -9348,12 +9662,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; ssh->ssh2_rdpkt_crstate = 0; - ssh->do_ssh_init_crstate = 0; ssh->ssh_gotdata_crstate = 0; ssh->do_ssh1_connection_crstate = 0; - ssh->do_ssh1_login_crstate = 0; - ssh->do_ssh2_transport_crstate = 0; - ssh->do_ssh2_authconn_crstate = 0; ssh->do_ssh_init_state = NULL; ssh->do_ssh1_login_state = NULL; ssh->do_ssh2_transport_state = NULL; @@ -9370,6 +9680,9 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->deferred_rekey_reason = NULL; bufchain_init(&ssh->queued_incoming_data); ssh->frozen = FALSE; + ssh->username = NULL; + ssh->sent_console_eof = FALSE; + ssh->got_pty = FALSE; *backend_handle = ssh; @@ -9379,8 +9692,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, #endif ssh->frontend = frontend_handle; - ssh->term_width = ssh->cfg.width; - ssh->term_height = ssh->cfg.height; + ssh->term_width = conf_get_int(ssh->conf, CONF_width); + ssh->term_height = conf_get_int(ssh->conf, CONF_height); ssh->channels = NULL; ssh->rportfwds = NULL; @@ -9401,7 +9714,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->incoming_data_size = ssh->outgoing_data_size = ssh->deferred_data_size = 0L; - ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data); + ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, + CONF_ssh_rekey_data)); ssh->kex_in_progress = FALSE; #ifndef NO_GSSAPI @@ -9456,7 +9770,7 @@ static void ssh_free(void *handle) while (ssh->qhead) { struct queued_handler *qh = ssh->qhead; ssh->qhead = qh->next; - sfree(ssh->qhead); + sfree(qh); } ssh->qhead = ssh->qtail = NULL; @@ -9473,6 +9787,17 @@ static void ssh_free(void *handle) pfd_close(c->u.pfd.s); break; } + if (ssh->version == 2) { + struct outstanding_channel_request *ocr, *nocr; + ocr = c->v.v2.chanreq_head; + while (ocr) { + ocr->handler(c, NULL, ocr->ctx); + nocr = ocr->next; + sfree(ocr); + ocr = nocr; + } + bufchain_clear(&c->v.v2.outbuffer); + } sfree(c); } freetree234(ssh->channels); @@ -9505,6 +9830,8 @@ static void ssh_free(void *handle) if (ssh->pinger) pinger_free(ssh->pinger); bufchain_clear(&ssh->queued_incoming_data); + sfree(ssh->username); + conf_free(ssh->conf); #ifndef NO_GSSAPI if (ssh->gsslibs) ssh_gss_cleanup(ssh->gsslibs); @@ -9517,22 +9844,24 @@ static void ssh_free(void *handle) /* * Reconfigure the SSH backend. */ -static void ssh_reconfig(void *handle, Config *cfg) +static void ssh_reconfig(void *handle, Conf *conf) { Ssh ssh = (Ssh) handle; char *rekeying = NULL, rekey_mandatory = FALSE; unsigned long old_max_data_size; + int i, rekey_time; - pinger_reconfig(ssh->pinger, &ssh->cfg, cfg); + pinger_reconfig(ssh->pinger, ssh->conf, conf); if (ssh->portfwds) - ssh_setup_portfwd(ssh, cfg); + ssh_setup_portfwd(ssh, conf); - if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time && - cfg->ssh_rekey_time != 0) { - long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC; - long now = GETTICKCOUNT(); + rekey_time = conf_get_int(conf, CONF_ssh_rekey_time); + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time && + rekey_time != 0) { + unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC; + unsigned long now = GETTICKCOUNT(); - if (new_next - now < 0) { + if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) { rekeying = "timeout shortened"; } else { ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh); @@ -9540,7 +9869,8 @@ static void ssh_reconfig(void *handle, Config *cfg) } old_max_data_size = ssh->max_data_size; - ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data); + ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, + CONF_ssh_rekey_data)); if (old_max_data_size != ssh->max_data_size && ssh->max_data_size != 0) { if (ssh->outgoing_data_size > ssh->max_data_size || @@ -9548,19 +9878,27 @@ static void ssh_reconfig(void *handle, Config *cfg) rekeying = "data limit lowered"; } - if (ssh->cfg.compression != cfg->compression) { + if (conf_get_int(ssh->conf, CONF_compression) != + conf_get_int(conf, CONF_compression)) { rekeying = "compression setting changed"; rekey_mandatory = TRUE; } - if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc || - memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist, - sizeof(ssh->cfg.ssh_cipherlist))) { + for (i = 0; i < CIPHER_MAX; i++) + if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) != + conf_get_int_int(conf, CONF_ssh_cipherlist, i)) { + rekeying = "cipher settings changed"; + rekey_mandatory = TRUE; + } + if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) != + conf_get_int(conf, CONF_ssh2_des_cbc)) { rekeying = "cipher settings changed"; rekey_mandatory = TRUE; } - ssh->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(ssh->conf); + ssh->conf = conf_copy(conf); + ssh_cache_conf_values(ssh); if (rekeying) { if (!ssh->kex_in_progress) { @@ -9608,7 +9946,7 @@ static int ssh_sendbuffer(void *handle) if (ssh->version == 1) { return override_value; } else if (ssh->version == 2) { - if (!ssh->mainchan || ssh->mainchan->closes > 0) + if (!ssh->mainchan) return override_value; else return (override_value + @@ -9638,17 +9976,15 @@ static void ssh_size(void *handle, int width, int height) ssh->size_needed = TRUE; /* buffer for later */ break; case SSH_STATE_SESSION: - if (!ssh->cfg.nopty) { + if (!conf_get_int(ssh->conf, CONF_nopty)) { if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_WINDOW_SIZE, PKT_INT, ssh->term_height, PKT_INT, ssh->term_width, PKT_INT, 0, PKT_INT, 0, PKT_END); } else if (ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "window-change"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "window-change", + NULL, NULL); ssh2_pkt_adduint32(pktout, ssh->term_width); ssh2_pkt_adduint32(pktout, ssh->term_height); ssh2_pkt_adduint32(pktout, 0); @@ -9757,9 +10093,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_EOF, PKT_END); } else if (ssh->mainchan) { - struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_send(ssh, pktout); + sshfwd_write_eof(ssh->mainchan); ssh->send_ok = 0; /* now stop trying to read from stdin */ } logevent("Sent EOF message"); @@ -9786,10 +10120,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (ssh->version == 1) { logevent("Unable to send BREAK signal in SSH-1"); } else if (ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "break"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "break", NULL, NULL); ssh2_pkt_adduint32(pktout, 0); /* default break length */ ssh2_pkt_send(ssh, pktout); } @@ -9814,10 +10145,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (signame) { /* It's a signal. */ if (ssh->version == 2 && ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "signal"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "signal", NULL, NULL); ssh2_pkt_addstring(pktout, signame); ssh2_pkt_send(ssh, pktout); logeventf(ssh, "Sent signal SIG%s", signame); @@ -9862,7 +10190,7 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh2_set_window(ssh->mainchan, bufsize < ssh->mainchan->v.v2.locmaxwin ? ssh->mainchan->v.v2.locmaxwin - bufsize : 0); - if (ssh->cfg.ssh_simple) + if (conf_get_int(ssh->conf, CONF_ssh_simple)) buflimit = 0; else buflimit = ssh->mainchan->v.v2.locmaxwin; @@ -9872,6 +10200,12 @@ static void ssh_unthrottle(void *handle, int bufsize) } } } + + /* + * Now process any SSH connection data that was stashed in our + * queue while we were frozen. + */ + ssh_process_queued_incoming_data(ssh); } void ssh_send_port_open(void *channel, char *hostname, int port, char *org) @@ -9880,7 +10214,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) Ssh ssh = c->ssh; struct Packet *pktout; - logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port); + logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org); if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_PORT_OPEN, @@ -9890,11 +10224,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) /* PKT_STR, , */ PKT_END); } else { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); - ssh2_pkt_addstring(pktout, "direct-tcpip"); - ssh2_pkt_adduint32(pktout, c->localid); - ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ - ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + pktout = ssh2_chanopen_init(c, "direct-tcpip"); ssh2_pkt_addstring(pktout, hostname); ssh2_pkt_adduint32(pktout, port); /* diff --git a/contrib/putty/SSH.H b/contrib/putty/SSH.H index 605b608..55ed7d2 100644 --- a/contrib/putty/SSH.H +++ b/contrib/putty/SSH.H @@ -9,8 +9,9 @@ struct ssh_channel; -extern void sshfwd_close(struct ssh_channel *c); extern int sshfwd_write(struct ssh_channel *c, char *, int); +extern void sshfwd_write_eof(struct ssh_channel *c); +extern void sshfwd_unclean_close(struct ssh_channel *c); extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize); /* @@ -132,9 +133,9 @@ typedef struct { uint32 lenhi, lenlo; } SHA_State; void SHA_Init(SHA_State * s); -void SHA_Bytes(SHA_State * s, void *p, int len); +void SHA_Bytes(SHA_State * s, const void *p, int len); void SHA_Final(SHA_State * s, unsigned char *output); -void SHA_Simple(void *p, int len, unsigned char *output); +void SHA_Simple(const void *p, int len, unsigned char *output); void hmac_sha1_simple(void *key, int keylen, void *data, int datalen, unsigned char *output); @@ -296,6 +297,7 @@ extern const struct ssh_mac ssh_hmac_sha1; extern const struct ssh_mac ssh_hmac_sha1_buggy; extern const struct ssh_mac ssh_hmac_sha1_96; extern const struct ssh_mac ssh_hmac_sha1_96_buggy; +extern const struct ssh_mac ssh_hmac_sha256; void *aes_make_context(void); void aes_free_context(void *handle); @@ -334,16 +336,15 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org); /* Exports from portfwd.c */ extern const char *pfd_newconnect(Socket * s, char *hostname, int port, - void *c, const Config *cfg, - int addressfamily); + void *c, Conf *conf, int addressfamily); /* desthost == NULL indicates dynamic (SOCKS) port forwarding */ extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr, - int port, void *backhandle, - const Config *cfg, void **sockdata, - int address_family); + int port, void *backhandle, Conf *conf, + void **sockdata, int address_family); extern void pfd_close(Socket s); extern void pfd_terminate(void *sockdata); extern int pfd_send(Socket s, char *data, int len); +extern void pfd_send_eof(Socket s); extern void pfd_confirm(Socket s); extern void pfd_unthrottle(Socket s); extern void pfd_override_throttle(Socket s, int enable); @@ -393,18 +394,18 @@ struct X11Display { * details are looked up by calling platform_get_x11_auth. */ extern struct X11Display *x11_setup_display(char *display, int authtype, - const Config *); + Conf *); void x11_free_display(struct X11Display *disp); extern const char *x11_init(Socket *, struct X11Display *, void *, - const char *, int, const Config *); + const char *, int, Conf *); extern void x11_close(Socket); extern int x11_send(Socket, char *, int); +extern void x11_send_eof(Socket s); extern void x11_unthrottle(Socket s); extern void x11_override_throttle(Socket s, int enable); char *x11_display(const char *display); /* Platform-dependent X11 functions */ -extern void platform_get_x11_auth(struct X11Display *display, - const Config *); +extern void platform_get_x11_auth(struct X11Display *display, Conf *); /* examine a mostly-filled-in X11Display and fill in localauth* */ extern const int platform_uses_x11_unix_by_default; /* choose default X transport in the absence of a specified one */ @@ -550,7 +551,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam); Bignum primegen(int bits, int modulus, int residue, Bignum factor, - int phase, progfn_t pfn, void *pfnparam); + int phase, progfn_t pfn, void *pfnparam, unsigned firstbits); +void invent_firstbits(unsigned *one, unsigned *two); /* diff --git a/contrib/putty/SSHAES.C b/contrib/putty/SSHAES.C index 7684cd9..c4b1986 100644 --- a/contrib/putty/SSHAES.C +++ b/contrib/putty/SSHAES.C @@ -1157,7 +1157,7 @@ void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_encrypt_cbc(blk, len, &ctx); - memset(&ctx, 0, sizeof(ctx)); + smemclr(&ctx, sizeof(ctx)); } void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) @@ -1166,7 +1166,7 @@ void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_decrypt_cbc(blk, len, &ctx); - memset(&ctx, 0, sizeof(ctx)); + smemclr(&ctx, sizeof(ctx)); } static const struct ssh2_cipher ssh_aes128_ctr = { diff --git a/contrib/putty/SSHARCF.C b/contrib/putty/SSHARCF.C index e0b247e..3f4376c 100644 --- a/contrib/putty/SSHARCF.C +++ b/contrib/putty/SSHARCF.C @@ -75,7 +75,7 @@ static void arcfour_stir(ArcfourContext *ctx) unsigned char *junk = snewn(1536, unsigned char); memset(junk, 0, 1536); arcfour_block(ctx, junk, 1536); - memset(junk, 0, 1536); + smemclr(junk, 1536); sfree(junk); } diff --git a/contrib/putty/SSHBN.C b/contrib/putty/SSHBN.C index 51cecdf..f9657b7 100644 --- a/contrib/putty/SSHBN.C +++ b/contrib/putty/SSHBN.C @@ -6,6 +6,7 @@ #include #include #include +#include #include "misc.h" @@ -120,7 +121,11 @@ Bignum Zero = bnZero, One = bnOne; static Bignum newbn(int length) { - Bignum b = snewn(length + 1, BignumInt); + Bignum b; + + assert(length >= 0 && length < INT_MAX / BIGNUM_INT_BITS); + + b = snewn(length + 1, BignumInt); if (!b) abort(); /* FIXME */ memset(b, 0, (length + 1) * sizeof(*b)); @@ -148,13 +153,17 @@ void freebn(Bignum b) /* * Burn the evidence, just in case. */ - memset(b, 0, sizeof(b[0]) * (b[0] + 1)); + smemclr(b, sizeof(b[0]) * (b[0] + 1)); sfree(b); } Bignum bn_power_2(int n) { - Bignum ret = newbn(n / BIGNUM_INT_BITS + 1); + Bignum ret; + + assert(n >= 0); + + ret = newbn(n / BIGNUM_INT_BITS + 1); bignum_set_bit(ret, n, 1); return ret; } @@ -598,6 +607,7 @@ static void internal_add_shifted(BignumInt *number, addend = (BignumDblInt)n << bshift; while (addend) { + assert(word <= number[0]); addend += number[word]; number[word] = (BignumInt) addend & BIGNUM_INT_MASK; addend >>= BIGNUM_INT_BITS; @@ -624,6 +634,7 @@ static void internal_mod(BignumInt *a, int alen, int i, k; m0 = m[0]; + assert(m0 >> (BIGNUM_INT_BITS-1) == 1); if (mlen > 1) m1 = m[1]; else @@ -815,20 +826,15 @@ Bignum modpow_simple(Bignum base_in, Bignum exp, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < 2 * mlen; i++) - a[i] = 0; + smemclr(a, 2 * mlen * sizeof(*a)); sfree(a); - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * mlen; i++) - b[i] = 0; + smemclr(b, 2 * mlen * sizeof(*b)); sfree(b); - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < mlen; i++) - n[i] = 0; + smemclr(n, mlen * sizeof(*n)); sfree(n); freebn(base); @@ -873,6 +879,7 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod) len = mod[0]; r = bn_power_2(BIGNUM_INT_BITS * len); inv = modinv(mod, r); + assert(inv); /* cannot fail, since mod is odd and r is a power of 2 */ /* * Multiply the base by r mod n, to get it into Montgomery @@ -965,23 +972,17 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * len; i++) - a[i] = 0; + smemclr(a, 2 * len * sizeof(*a)); sfree(a); - for (i = 0; i < 2 * len; i++) - b[i] = 0; + smemclr(b, 2 * len * sizeof(*b)); sfree(b); - for (i = 0; i < len; i++) - mninv[i] = 0; + smemclr(mninv, len * sizeof(*mninv)); sfree(mninv); - for (i = 0; i < len; i++) - n[i] = 0; + smemclr(n, len * sizeof(*n)); sfree(n); - for (i = 0; i < len; i++) - x[i] = 0; + smemclr(x, len * sizeof(*x)); sfree(x); return result; @@ -999,6 +1000,12 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) int pqlen, mlen, rlen, i, j; Bignum result; + /* + * The most significant word of mod needs to be non-zero. It + * should already be, but let's make sure. + */ + assert(mod[mod[0]] != 0); + /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; @@ -1018,6 +1025,13 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) pqlen = (p[0] > q[0] ? p[0] : q[0]); + /* + * Make sure that we're allowing enough space. The shifting below + * will underflow the vectors we allocate if pqlen is too small. + */ + if (2*pqlen <= mlen) + pqlen = mlen/2 + 1; + /* Allocate n of size pqlen, copy p to n */ n = snewn(pqlen, BignumInt); i = pqlen - p[0]; @@ -1064,20 +1078,15 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * pqlen; i++) - a[i] = 0; + smemclr(a, 2 * pqlen * sizeof(*a)); sfree(a); - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < pqlen; i++) - n[i] = 0; + smemclr(n, pqlen * sizeof(*n)); sfree(n); - for (i = 0; i < pqlen; i++) - o[i] = 0; + smemclr(o, pqlen * sizeof(*o)); sfree(o); return result; @@ -1096,6 +1105,12 @@ static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) int mshift; int plen, mlen, i, j; + /* + * The most significant word of mod needs to be non-zero. It + * should already be, but let's make sure. + */ + assert(mod[mod[0]] != 0); + /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; @@ -1147,11 +1162,9 @@ static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) } /* Free temporary arrays */ - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < plen; i++) - n[i] = 0; + smemclr(n, plen * sizeof(*n)); sfree(n); } @@ -1171,6 +1184,8 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes) Bignum result; int w, i; + assert(nbytes >= 0 && nbytes < INT_MAX/8); + w = (nbytes + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES; /* bytes->words */ result = newbn(w); @@ -1247,7 +1262,7 @@ int ssh2_bignum_length(Bignum bn) */ int bignum_byte(Bignum bn, int i) { - if (i >= (int)(BIGNUM_INT_BYTES * bn[0])) + if (i < 0 || i >= (int)(BIGNUM_INT_BYTES * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BYTES + 1] >> @@ -1259,7 +1274,7 @@ int bignum_byte(Bignum bn, int i) */ int bignum_bit(Bignum bn, int i) { - if (i >= (int)(BIGNUM_INT_BITS * bn[0])) + if (i < 0 || i >= (int)(BIGNUM_INT_BITS * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BITS + 1] >> (i % BIGNUM_INT_BITS)) & 1; @@ -1270,7 +1285,7 @@ int bignum_bit(Bignum bn, int i) */ void bignum_set_bit(Bignum bn, int bitnum, int value) { - if (bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) + if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) abort(); /* beyond the end */ else { int v = bitnum / BIGNUM_INT_BITS + 1; @@ -1306,7 +1321,18 @@ int ssh1_write_bignum(void *data, Bignum bn) int bignum_cmp(Bignum a, Bignum b) { int amax = a[0], bmax = b[0]; - int i = (amax > bmax ? amax : bmax); + int i; + + /* Annoyingly we have two representations of zero */ + if (amax == 1 && a[amax] == 0) + amax = 0; + if (bmax == 1 && b[bmax] == 0) + bmax = 0; + + assert(amax == 0 || a[amax] != 0); + assert(bmax == 0 || b[bmax] != 0); + + i = (amax > bmax ? amax : bmax); while (i) { BignumInt aval = (i > amax ? 0 : a[i]); BignumInt bval = (i > bmax ? 0 : b[i]); @@ -1328,6 +1354,8 @@ Bignum bignum_rshift(Bignum a, int shift) int i, shiftw, shiftb, shiftbb, bits; BignumInt ai, ai1; + assert(shift >= 0); + bits = bignum_bitcount(a) - shift; ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS); @@ -1398,8 +1426,7 @@ Bignum bigmuladd(Bignum a, Bignum b, Bignum addend) } ret[0] = maxspot; - for (i = 0; i < wslen; i++) - workspace[i] = 0; + smemclr(workspace, wslen * sizeof(*workspace)); sfree(workspace); return ret; } @@ -1629,9 +1656,26 @@ Bignum modinv(Bignum number, Bignum modulus) Bignum x = copybn(One); int sign = +1; + assert(number[number[0]] != 0); + assert(modulus[modulus[0]] != 0); + while (bignum_cmp(b, One) != 0) { - Bignum t = newbn(b[0]); - Bignum q = newbn(a[0]); + Bignum t, q; + + if (bignum_cmp(b, Zero) == 0) { + /* + * Found a common factor between the inputs, so we cannot + * return a modular inverse at all. + */ + freebn(b); + freebn(a); + freebn(xp); + freebn(x); + return NULL; + } + + t = newbn(b[0]); + q = newbn(a[0]); bigdivmod(a, b, t, q); while (t[0] > 1 && t[t[0]] == 0) t[0]--; @@ -1750,6 +1794,7 @@ char *bignum_decimal(Bignum x) /* * Done. */ + smemclr(workspace, x[0] * sizeof(*workspace)); sfree(workspace); return ret; } @@ -1761,7 +1806,7 @@ char *bignum_decimal(Bignum x) #include /* - * gcc -g -O0 -DTESTBN -o testbn sshbn.c misc.c -I unix -I charset + * gcc -Wall -g -O0 -DTESTBN -o testbn sshbn.c misc.c conf.c tree234.c unix/uxmisc.c -I. -I unix -I charset * * Then feed to this program's standard input the output of * testdata/bignum.py . @@ -1835,7 +1880,7 @@ int main(int argc, char **argv) Bignum a, b, c, p; if (ptrnum != 3) { - printf("%d: mul with %d parameters, expected 3\n", line); + printf("%d: mul with %d parameters, expected 3\n", line, ptrnum); exit(1); } a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); @@ -1864,11 +1909,49 @@ int main(int argc, char **argv) freebn(b); freebn(c); freebn(p); + } else if (!strcmp(buf, "modmul")) { + Bignum a, b, m, c, p; + + if (ptrnum != 4) { + printf("%d: modmul with %d parameters, expected 4\n", + line, ptrnum); + exit(1); + } + a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); + b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]); + m = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]); + c = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]); + p = modmul(a, b, m); + + if (bignum_cmp(c, p) == 0) { + passes++; + } else { + char *as = bignum_decimal(a); + char *bs = bignum_decimal(b); + char *ms = bignum_decimal(m); + char *cs = bignum_decimal(c); + char *ps = bignum_decimal(p); + + printf("%d: fail: %s * %s mod %s gave %s expected %s\n", + line, as, bs, ms, ps, cs); + fails++; + + sfree(as); + sfree(bs); + sfree(ms); + sfree(cs); + sfree(ps); + } + freebn(a); + freebn(b); + freebn(m); + freebn(c); + freebn(p); } else if (!strcmp(buf, "pow")) { Bignum base, expt, modulus, expected, answer; if (ptrnum != 4) { - printf("%d: mul with %d parameters, expected 3\n", line); + printf("%d: mul with %d parameters, expected 4\n", line, ptrnum); exit(1); } diff --git a/contrib/putty/SSHDES.C b/contrib/putty/SSHDES.C index 0beb273..ad584f3 100644 --- a/contrib/putty/SSHDES.C +++ b/contrib/putty/SSHDES.C @@ -858,7 +858,7 @@ void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_decrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) @@ -871,7 +871,7 @@ void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_encrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, @@ -887,7 +887,7 @@ void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_decrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, @@ -903,7 +903,7 @@ void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_encrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } static void des_keysetup_xdmauth(unsigned char *keydata, DESContext *dc) diff --git a/contrib/putty/SSHDSS.C b/contrib/putty/SSHDSS.C index dba1db1..6ffcf6d 100644 --- a/contrib/putty/SSHDSS.C +++ b/contrib/putty/SSHDSS.C @@ -20,7 +20,7 @@ static void sha_mpint(SHA_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void sha512_mpint(SHA512_State * s, Bignum b) @@ -34,7 +34,7 @@ static void sha512_mpint(SHA512_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void getstring(char **data, int *datalen, char **p, int *length) @@ -42,7 +42,9 @@ static void getstring(char **data, int *datalen, char **p, int *length) *p = NULL; if (*datalen < 4) return; - *length = GET_32BIT(*data); + *length = toint(GET_32BIT(*data)); + if (*length < 0) + return; *datalen -= 4; *data += 4; if (*datalen < *length) @@ -70,6 +72,9 @@ static Bignum get160(char **data, int *datalen) { Bignum b; + if (*datalen < 20) + return NULL; + b = bignum_from_bytes((unsigned char *)*data, 20); *data += 20; *datalen -= 20; @@ -77,6 +82,8 @@ static Bignum get160(char **data, int *datalen) return b; } +static void dss_freekey(void *key); /* forward reference */ + static void *dss_newkey(char *data, int len) { char *p; @@ -84,8 +91,6 @@ static void *dss_newkey(char *data, int len) struct dss_key *dss; dss = snew(struct dss_key); - if (!dss) - return NULL; getstring(&data, &len, &p, &slen); #ifdef DEBUG_DSS @@ -98,7 +103,7 @@ static void *dss_newkey(char *data, int len) } #endif - if (!p || memcmp(p, "ssh-dss", 7)) { + if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { sfree(dss); return NULL; } @@ -106,6 +111,14 @@ static void *dss_newkey(char *data, int len) dss->q = getmp(&data, &len); dss->g = getmp(&data, &len); dss->y = getmp(&data, &len); + dss->x = NULL; + + if (!dss->p || !dss->q || !dss->g || !dss->y || + !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { + /* Invalid key. */ + dss_freekey(dss); + return NULL; + } return dss; } @@ -113,10 +126,16 @@ static void *dss_newkey(char *data, int len) static void dss_freekey(void *key) { struct dss_key *dss = (struct dss_key *) key; - freebn(dss->p); - freebn(dss->q); - freebn(dss->g); - freebn(dss->y); + if (dss->p) + freebn(dss->p); + if (dss->q) + freebn(dss->q); + if (dss->g) + freebn(dss->g); + if (dss->y) + freebn(dss->y); + if (dss->x) + freebn(dss->x); sfree(dss); } @@ -249,13 +268,29 @@ static int dss_verifysig(void *key, char *sig, int siglen, } r = get160(&sig, &siglen); s = get160(&sig, &siglen); - if (!r || !s) + if (!r || !s) { + if (r) + freebn(r); + if (s) + freebn(s); return 0; + } + + if (!bignum_cmp(s, Zero)) { + freebn(r); + freebn(s); + return 0; + } /* * Step 1. w <- s^-1 mod q. */ w = modinv(s, dss->q); + if (!w) { + freebn(r); + freebn(s); + return 0; + } /* * Step 2. u1 <- SHA(message) * w mod q. @@ -287,6 +322,8 @@ static int dss_verifysig(void *key, char *sig, int siglen, freebn(w); freebn(sha); + freebn(u1); + freebn(u2); freebn(gu1p); freebn(yu2p); freebn(gu1yu2p); @@ -377,7 +414,13 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, Bignum ytest; dss = dss_newkey((char *) pub_blob, pub_len); + if (!dss) + return NULL; dss->x = getmp(&pb, &priv_len); + if (!dss->x) { + dss_freekey(dss); + return NULL; + } /* * Check the obsolete hash in the old DSS key format. @@ -402,6 +445,7 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); + freebn(ytest); return NULL; } freebn(ytest); @@ -415,8 +459,6 @@ static void *dss_openssh_createkey(unsigned char **blob, int *len) struct dss_key *dss; dss = snew(struct dss_key); - if (!dss) - return NULL; dss->p = getmp(b, len); dss->q = getmp(b, len); @@ -424,14 +466,11 @@ static void *dss_openssh_createkey(unsigned char **blob, int *len) dss->y = getmp(b, len); dss->x = getmp(b, len); - if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x) { - sfree(dss->p); - sfree(dss->q); - sfree(dss->g); - sfree(dss->y); - sfree(dss->x); - sfree(dss); - return NULL; + if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x || + !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { + /* Invalid key. */ + dss_freekey(dss); + return NULL; } return dss; @@ -471,6 +510,8 @@ static int dss_pubkey_bits(void *blob, int len) int ret; dss = dss_newkey((char *) blob, len); + if (!dss) + return -1; ret = bignum_bitcount(dss->p); dss_freekey(dss); @@ -573,18 +614,34 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); SHA512_Bytes(&ss, digest, sizeof(digest)); - SHA512_Final(&ss, digest512); - memset(&ss, 0, sizeof(ss)); + while (1) { + SHA512_State ss2 = ss; /* structure copy */ + SHA512_Final(&ss2, digest512); + + smemclr(&ss2, sizeof(ss2)); + + /* + * Now convert the result into a bignum, and reduce it mod q. + */ + proto_k = bignum_from_bytes(digest512, 64); + k = bigmod(proto_k, dss->q); + freebn(proto_k); + kinv = modinv(k, dss->q); /* k^-1 mod q */ + if (!kinv) { /* very unlikely */ + freebn(k); + /* Perturb the hash to think of a different k. */ + SHA512_Bytes(&ss, "x", 1); + /* Go round and try again. */ + continue; + } + + break; + } - /* - * Now convert the result into a bignum, and reduce it mod q. - */ - proto_k = bignum_from_bytes(digest512, 64); - k = bigmod(proto_k, dss->q); - freebn(proto_k); + smemclr(&ss, sizeof(ss)); - memset(digest512, 0, sizeof(digest512)); + smemclr(digest512, sizeof(digest512)); /* * Now we have k, so just go ahead and compute the signature. @@ -594,11 +651,11 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) freebn(gkp); hash = bignum_from_bytes(digest, 20); - kinv = modinv(k, dss->q); /* k^-1 mod q */ hxr = bigmuladd(dss->x, r, hash); /* hash + x*r */ s = modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash + x*r) mod q */ freebn(hxr); freebn(kinv); + freebn(k); freebn(hash); /* diff --git a/contrib/putty/SSHDSSG.C b/contrib/putty/SSHDSSG.C index a19bc27..d0ed73c 100644 --- a/contrib/putty/SSHDSSG.C +++ b/contrib/putty/SSHDSSG.C @@ -9,6 +9,7 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam) { Bignum qm1, power, g, h, tmp; + unsigned pfirst, qfirst; int progress; /* @@ -70,15 +71,16 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, pfn(pfnparam, PROGFN_READY, 0, 0); + invent_firstbits(&pfirst, &qfirst); /* * Generate q: a prime of length 160. */ - key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam); + key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam, qfirst); /* * Now generate p: a prime of length `bits', such that p-1 is * divisible by q. */ - key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam); + key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam, pfirst); /* * Next we need g. Raise 2 to the power (p-1)/q modulo p, and diff --git a/contrib/putty/SSHGSS.H b/contrib/putty/SSHGSS.H index 5d8fca1..61f35a2 100644 --- a/contrib/putty/SSHGSS.H +++ b/contrib/putty/SSHGSS.H @@ -47,7 +47,7 @@ struct ssh_gss_liblist { struct ssh_gss_library *libraries; int nlibraries; }; -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg); +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf); void ssh_gss_cleanup(struct ssh_gss_liblist *list); /* diff --git a/contrib/putty/SSHMD5.C b/contrib/putty/SSHMD5.C index 80474df..b27d001 100644 --- a/contrib/putty/SSHMD5.C +++ b/contrib/putty/SSHMD5.C @@ -249,7 +249,7 @@ void hmacmd5_key(void *handle, void const *keyv, int len) MD5Init(&keys[1]); MD5Update(&keys[1], foo, 64); - memset(foo, 0, 64); /* burn the evidence */ + smemclr(foo, 64); /* burn the evidence */ } static void hmacmd5_key_16(void *handle, unsigned char *key) @@ -312,11 +312,7 @@ static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len, { unsigned char seqbuf[16]; - seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF); - seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF); - seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF); - seqbuf[3] = (unsigned char) ((seq) & 0xFF); - + PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac); } diff --git a/contrib/putty/SSHNOGSS.C b/contrib/putty/SSHNOGSS.C index 27ec760..2aff9c1 100644 --- a/contrib/putty/SSHNOGSS.C +++ b/contrib/putty/SSHNOGSS.C @@ -3,7 +3,7 @@ /* For platforms not supporting GSSAPI */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *); list->libraries = NULL; diff --git a/contrib/putty/SSHPRIME.C b/contrib/putty/SSHPRIME.C index e283b52..8cd2391 100644 --- a/contrib/putty/SSHPRIME.C +++ b/contrib/putty/SSHPRIME.C @@ -123,1061 +123,706 @@ * for i in list[1:]: sys.stdout.write("%d," % i) */ static const unsigned short primes[] = { - 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, - 71, 73, 79, 83, 89, 97, 101, - 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, - 277, 281, 283, 293, - 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, - 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, - 499, 503, 509, 521, - 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, - 617, 619, 631, 641, - 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, - 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, - 859, 863, 877, 881, - 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, - 991, 997, 1009, - 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, - 1091, 1093, - 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, - 1193, 1201, - 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, - 1291, 1297, - 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, - 1423, 1427, - 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, - 1493, 1499, - 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, - 1601, 1607, - 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, - 1699, 1709, - 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, - 1811, 1823, - 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, - 1931, 1933, - 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, - 2029, 2039, - 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, - 2137, 2141, - 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, - 2267, 2269, - 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, - 2357, 2371, - 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, - 2459, 2467, - 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, - 2593, 2609, - 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, - 2693, 2699, - 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, - 2791, 2797, - 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, - 2903, 2909, - 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, - 3023, 3037, - 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, - 3167, 3169, - 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, - 3271, 3299, - 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, - 3373, 3389, - 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, - 3511, 3517, - 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, - 3607, 3613, - 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, - 3709, 3719, - 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, - 3833, 3847, - 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, - 3931, 3943, - 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, - 4057, 4073, - 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, - 4177, 4201, - 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, - 4283, 4289, - 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, - 4423, 4441, - 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, - 4547, 4549, - 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, - 4657, 4663, - 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, - 4789, 4793, - 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, - 4931, 4933, - 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, - 5011, 5021, - 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, - 5147, 5153, - 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, - 5279, 5281, - 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, - 5413, 5417, - 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, - 5507, 5519, - 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, - 5647, 5651, - 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, - 5743, 5749, - 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, - 5857, 5861, - 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, - 6007, 6011, - 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, - 6121, 6131, - 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, - 6247, 6257, - 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, - 6343, 6353, - 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, - 6473, 6481, - 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, - 6607, 6619, - 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, - 6733, 6737, - 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, - 6857, 6863, - 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, - 6971, 6977, - 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, - 7103, 7109, - 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, - 7229, 7237, - 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, - 7369, 7393, - 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, - 7517, 7523, - 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, - 7603, 7607, - 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, - 7723, 7727, - 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, - 7873, 7877, - 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, - 8009, 8011, - 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, - 8123, 8147, - 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, - 8263, 8269, - 8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, - 8387, 8389, - 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, - 8537, 8539, - 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, - 8663, 8669, - 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, - 8753, 8761, - 8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, - 8867, 8887, - 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, - 9011, 9013, - 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, - 9151, 9157, - 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241, 9257, - 9277, 9281, - 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, - 9397, 9403, - 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, - 9491, 9497, - 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, - 9631, 9643, - 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 9739, 9743, 9749, - 9767, 9769, - 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, - 9871, 9883, + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, + 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, + 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, + 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, + 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, + 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, + 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, + 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, + 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, + 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, + 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, + 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, + 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, + 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, + 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, + 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, + 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, + 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, + 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, + 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, + 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, + 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, + 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, + 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, + 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, + 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, + 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, + 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, + 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, + 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, + 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, + 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, + 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, + 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, + 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, + 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, + 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, + 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, + 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, + 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, + 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, + 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, + 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, + 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, + 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, + 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, + 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, + 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, + 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, + 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, + 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, + 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, + 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, + 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, + 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, + 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, + 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, + 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, + 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, + 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, + 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, + 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, + 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, + 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, + 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, + 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, + 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, + 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, + 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, + 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, + 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, + 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, + 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, + 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, + 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, + 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, + 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, + 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, + 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, + 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, + 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, + 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, + 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, + 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, + 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, + 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, + 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, + 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, + 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, + 8647, 8663, 8669, 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, + 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, + 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, + 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, + 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, + 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, + 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, + 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, 9397, 9403, 9413, + 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, + 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, + 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, + 9719, 9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, + 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, - 10009, 10037, - 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, - 10133, 10139, - 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, - 10243, 10247, - 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, - 10331, 10333, - 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, - 10457, 10459, - 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, - 10589, 10597, - 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, - 10687, 10691, - 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, - 10799, 10831, - 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, - 10909, 10937, - 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, - 11057, 11059, - 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, - 11159, 11161, - 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, - 11273, 11279, - 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, - 11393, 11399, - 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, - 11497, 11503, - 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, - 11633, 11657, - 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, - 11779, 11783, - 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, - 11867, 11887, - 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, - 11969, 11971, - 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, - 12097, 12101, - 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, - 12203, 12211, - 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, - 12301, 12323, - 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, - 12421, 12433, - 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, - 12517, 12527, - 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, - 12613, 12619, - 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, - 12721, 12739, - 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, - 12841, 12853, - 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, - 12959, 12967, - 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, - 13049, 13063, - 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163, - 13171, 13177, - 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, - 13297, 13309, - 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, - 13417, 13421, - 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523, - 13537, 13553, - 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, - 13679, 13681, - 13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, - 13757, 13759, - 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, - 13877, 13879, - 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, - 13997, 13999, - 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, - 14107, 14143, - 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, - 14251, 14281, - 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, - 14401, 14407, - 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, - 14503, 14519, - 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, - 14621, 14627, - 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717, - 14723, 14731, - 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, - 14813, 14821, - 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897, - 14923, 14929, - 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, - 15061, 15073, - 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, - 15161, 15173, - 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, - 15271, 15277, - 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, - 15361, 15373, - 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, - 15467, 15473, - 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, - 15601, 15607, - 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, - 15683, 15727, - 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, - 15797, 15803, - 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, - 15913, 15919, - 15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057, - 16061, 16063, - 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, - 16141, 16183, - 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, - 16273, 16301, - 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, - 16421, 16427, - 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, - 16547, 16553, - 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651, - 16657, 16661, - 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, - 16787, 16811, - 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, - 16921, 16927, - 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021, - 17027, 17029, - 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, - 17137, 17159, - 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, - 17291, 17293, - 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, - 17387, 17389, - 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, - 17483, 17489, - 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, - 17597, 17599, - 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, - 17729, 17737, - 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, - 17851, 17863, - 17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, - 17959, 17971, - 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, - 18061, 18077, - 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, 18149, 18169, - 18181, 18191, - 18199, 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, - 18287, 18289, - 18301, 18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379, - 18397, 18401, - 18413, 18427, 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, - 18503, 18517, - 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, - 18661, 18671, - 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773, - 18787, 18793, - 18797, 18803, 18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, - 18947, 18959, - 18973, 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, - 19079, 19081, - 19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, - 19213, 19219, - 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, - 19333, 19373, - 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, - 19433, 19441, - 19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489, 19501, 19507, - 19531, 19541, - 19543, 19553, 19559, 19571, 19577, 19583, 19597, 19603, 19609, 19661, - 19681, 19687, - 19697, 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, - 19777, 19793, - 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, - 19913, 19919, - 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, 19993, 19997, - 20011, 20021, - 20023, 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113, - 20117, 20123, - 20129, 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, - 20231, 20233, - 20249, 20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, - 20353, 20357, - 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, - 20477, 20479, - 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593, - 20599, 20611, - 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719, 20731, - 20743, 20747, - 20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, - 20873, 20879, - 20887, 20897, 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, - 20981, 20983, - 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, - 21089, 21101, - 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, - 21191, 21193, - 21211, 21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319, - 21323, 21341, - 21347, 21377, 21379, 21383, 21391, 21397, 21401, 21407, 21419, 21433, - 21467, 21481, - 21487, 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, - 21559, 21563, - 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, - 21649, 21661, - 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, - 21773, 21787, - 21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871, - 21881, 21893, - 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003, 22013, - 22027, 22031, - 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, - 22111, 22123, - 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, - 22247, 22259, - 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, 22343, 22349, - 22367, 22369, - 22381, 22391, 22397, 22409, 22433, 22441, 22447, 22453, 22469, 22481, - 22483, 22501, - 22511, 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, - 22621, 22637, - 22639, 22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, - 22721, 22727, - 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, - 22853, 22859, - 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, - 22973, 22993, - 23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, - 23059, 23063, - 23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173, - 23189, 23197, - 23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, - 23311, 23321, - 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, - 23459, 23473, - 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, 23563, 23567, - 23581, 23593, - 23599, 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, - 23677, 23687, - 23689, 23719, 23741, 23743, 23747, 23753, 23761, 23767, 23773, 23789, - 23801, 23813, - 23819, 23827, 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, - 23899, 23909, - 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, - 24019, 24023, - 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, - 24107, 24109, - 24113, 24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203, - 24223, 24229, - 24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, - 24379, 24391, - 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, - 24509, 24517, - 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, - 24671, 24677, - 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793, - 24799, 24809, - 24821, 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917, 24919, - 24923, 24943, - 24953, 24967, 24971, 24977, 24979, 24989, 25013, 25031, 25033, 25037, - 25057, 25073, - 25087, 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, - 25171, 25183, - 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, - 25307, 25309, - 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, 25409, 25411, - 25423, 25439, - 25447, 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, - 25577, 25579, - 25583, 25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, - 25667, 25673, - 25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, - 25793, 25799, - 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, - 25919, 25931, - 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017, - 26021, 26029, - 26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119, 26141, 26153, - 26161, 26171, - 26177, 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261, - 26263, 26267, - 26293, 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, - 26393, 26399, - 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, - 26501, 26513, - 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, - 26669, 26681, - 26683, 26687, 26693, 26699, 26701, 26711, 26713, 26717, 26723, 26729, - 26731, 26737, - 26759, 26777, 26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861, - 26863, 26879, - 26881, 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, - 26981, 26987, - 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, - 27091, 27103, - 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241, - 27253, 27259, - 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397, - 27407, 27409, - 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527, - 27529, 27539, - 27541, 27551, 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, - 27689, 27691, - 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, - 27773, 27779, - 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847, 27851, - 27883, 27893, - 27901, 27917, 27919, 27941, 27943, 27947, 27953, 27961, 27967, 27983, - 27997, 28001, - 28019, 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, - 28109, 28111, - 28123, 28151, 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, - 28279, 28283, - 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, - 28409, 28411, - 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, - 28537, 28541, - 28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607, - 28619, 28621, - 28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687, 28697, - 28703, 28711, - 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, - 28817, 28837, - 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, - 28949, 28961, - 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, 29063, 29077, - 29101, 29123, - 29129, 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201, - 29207, 29209, - 29221, 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311, 29327, - 29333, 29339, - 29347, 29363, 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, - 29437, 29443, - 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, - 29581, 29587, - 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717, - 29723, 29741, - 29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863, - 29867, 29873, - 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, - 30013, 30029, - 30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, - 30133, 30137, - 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, - 30253, 30259, - 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367, - 30389, 30391, - 30403, 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509, - 30517, 30529, - 30539, 30553, 30557, 30559, 30577, 30593, 30631, 30637, 30643, 30649, - 30661, 30671, - 30677, 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, - 30781, 30803, - 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, - 30881, 30893, - 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, - 31033, 31039, - 31051, 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, - 31151, 31153, - 31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237, - 31247, 31249, - 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, - 31337, 31357, - 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, - 31513, 31517, - 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627, - 31643, 31649, - 31657, 31663, 31667, 31687, 31699, 31721, 31723, 31727, 31729, 31741, - 31751, 31769, - 31771, 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891, - 31907, 31957, - 31963, 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, - 32059, 32063, - 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, - 32173, 32183, - 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297, - 32299, 32303, - 32309, 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371, - 32377, 32381, - 32401, 32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, - 32497, 32503, - 32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, - 32603, 32609, - 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, - 32719, 32749, - 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833, 32839, - 32843, 32869, - 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, - 32983, 32987, - 32993, 32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073, - 33083, 33091, - 33107, 33113, 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, - 33203, 33211, - 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, - 33347, 33349, - 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461, - 33469, 33479, - 33487, 33493, 33503, 33521, 33529, 33533, 33547, 33563, 33569, 33577, - 33581, 33587, - 33589, 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, - 33647, 33679, - 33703, 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, - 33791, 33797, - 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, - 33911, 33923, - 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, - 34057, 34061, - 34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183, 34211, - 34213, 34217, - 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297, 34301, 34303, - 34313, 34319, - 34327, 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, - 34439, 34457, - 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, - 34543, 34549, - 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651, 34667, - 34673, 34679, - 34687, 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763, - 34781, 34807, - 34819, 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913, - 34919, 34939, - 34949, 34961, 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, - 35081, 35083, - 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, - 35171, 35201, - 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317, - 35323, 35327, - 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437, - 35447, 35449, - 35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, - 35569, 35573, - 35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, - 35753, 35759, - 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, - 35869, 35879, - 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, - 35993, 35999, - 36007, 36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097, - 36107, 36109, - 36131, 36137, 36151, 36161, 36187, 36191, 36209, 36217, 36229, 36241, - 36251, 36263, - 36269, 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, - 36373, 36383, - 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, - 36523, 36527, - 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, - 36629, 36637, - 36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, - 36739, 36749, - 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833, - 36847, 36857, - 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, - 36943, 36947, - 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, - 37061, 37087, - 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199, 37201, - 37217, 37223, - 37243, 37253, 37273, 37277, 37307, 37309, 37313, 37321, 37337, 37339, - 37357, 37361, - 37363, 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, - 37489, 37493, - 37501, 37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, - 37571, 37573, - 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, - 37691, 37693, - 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, - 37853, 37861, - 37871, 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, - 37991, 37993, - 37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, - 38153, 38167, - 38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, - 38273, 38281, - 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, - 38377, 38393, - 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, 38557, 38561, - 38567, 38569, - 38593, 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, - 38677, 38693, - 38699, 38707, 38711, 38713, 38723, 38729, 38737, 38747, 38749, 38767, - 38783, 38791, - 38803, 38821, 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, - 38917, 38921, - 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, - 39043, 39047, - 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157, - 39161, 39163, - 39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, - 39251, 39293, - 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, - 39383, 39397, - 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, - 39521, 39541, - 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, - 39671, 39679, - 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, - 39799, 39821, - 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883, - 39887, 39901, - 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, - 40037, 40039, - 40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, - 40163, 40169, - 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, - 40289, 40343, - 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471, - 40483, 40487, - 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, - 40591, 40597, - 40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, - 40759, 40763, - 40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, - 40853, 40867, - 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, - 40993, 41011, - 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, - 41131, 41141, - 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, - 41221, 41227, - 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, - 41351, 41357, - 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, - 41491, 41507, - 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, - 41609, 41611, - 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, - 41719, 41729, - 41737, 41759, 41761, 41771, 41777, 41801, 41809, 41813, 41843, 41849, - 41851, 41863, - 41879, 41887, 41893, 41897, 41903, 41911, 41927, 41941, 41947, 41953, - 41957, 41959, - 41969, 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, - 42071, 42073, - 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, - 42193, 42197, - 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299, - 42307, 42323, - 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407, - 42409, 42433, - 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491, - 42499, 42509, - 42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, - 42667, 42677, - 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, - 42751, 42767, - 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853, 42859, - 42863, 42899, - 42901, 42923, 42929, 42937, 42943, 42953, 42961, 42967, 42979, 42989, - 43003, 43013, - 43019, 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, - 43151, 43159, - 43177, 43189, 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, - 43313, 43319, - 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, - 43457, 43481, - 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, - 43607, 43609, - 43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, - 43721, 43753, - 43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867, - 43889, 43891, - 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, - 43997, 44017, - 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, - 44111, 44119, - 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, 44203, 44207, - 44221, 44249, - 44257, 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357, - 44371, 44381, - 44383, 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501, 44507, - 44519, 44531, - 44533, 44537, 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, - 44633, 44641, - 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, - 44753, 44771, - 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867, - 44879, 44887, - 44893, 44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983, - 44987, 45007, - 45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, - 45139, 45161, - 45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, - 45293, 45307, - 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, - 45413, 45427, - 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553, - 45557, 45569, - 45587, 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677, - 45691, 45697, - 45707, 45737, 45751, 45757, 45763, 45767, 45779, 45817, 45821, 45823, - 45827, 45833, - 45841, 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, - 45971, 45979, - 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, - 46103, 46133, - 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, 46219, 46229, - 46237, 46261, - 46271, 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, - 46381, 46399, - 46411, 46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499, - 46507, 46511, - 46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, - 46639, 46643, - 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, - 46751, 46757, - 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861, - 46867, 46877, - 46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017, 47041, 47051, - 47057, 47059, - 47087, 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149, - 47161, 47189, - 47207, 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, - 47309, 47317, - 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, - 47431, 47441, - 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543, - 47563, 47569, - 47581, 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657, 47659, - 47681, 47699, - 47701, 47711, 47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, - 47797, 47807, - 47809, 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, - 47933, 47939, - 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, - 48073, 48079, - 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193, - 48197, 48221, - 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, - 48353, 48371, - 48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479, - 48481, 48487, - 48491, 48497, 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, - 48593, 48611, - 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, - 48751, 48757, - 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823, - 48847, 48857, - 48859, 48869, 48871, 48883, 48889, 48907, 48947, 48953, 48973, 48989, - 48991, 49003, - 49009, 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103, - 49109, 49117, - 49121, 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, - 49207, 49211, - 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, - 49363, 49367, - 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, - 49463, 49477, - 49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597, - 49603, 49613, - 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711, 49727, - 49739, 49741, - 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, - 49843, 49853, - 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, - 49991, 49993, - 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, 50077, 50087, - 50093, 50101, - 50111, 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207, - 50221, 50227, - 50231, 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333, - 50341, 50359, - 50363, 50377, 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, - 50497, 50503, - 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, - 50599, 50627, - 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773, - 50777, 50789, - 50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, - 50923, 50929, - 50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, - 51059, 51061, - 51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, - 51199, 51203, - 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, - 51341, 51343, - 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, - 51437, 51439, - 51449, 51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521, - 51539, 51551, - 51563, 51577, 51581, 51593, 51599, 51607, 51613, 51631, 51637, 51647, - 51659, 51673, - 51679, 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, - 51797, 51803, - 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, - 51907, 51913, - 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, - 52051, 52057, - 52067, 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, - 52181, 52183, - 52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291, - 52301, 52313, - 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, - 52489, 52501, - 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, - 52583, 52609, - 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, 52711, 52721, - 52727, 52733, - 52747, 52757, 52769, 52783, 52807, 52813, 52817, 52837, 52859, 52861, - 52879, 52883, - 52889, 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, - 52981, 52999, - 53003, 53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, - 53113, 53117, - 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, - 53233, 53239, - 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359, - 53377, 53381, - 53401, 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, - 53527, 53549, - 53551, 53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, - 53633, 53639, - 53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, - 53777, 53783, - 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, - 53897, 53899, - 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001, 54011, - 54013, 54037, - 54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, - 54167, 54181, - 54193, 54217, 54251, 54269, 54277, 54287, 54293, 54311, 54319, 54323, - 54331, 54347, - 54361, 54367, 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, - 54437, 54443, - 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, - 54547, 54559, - 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647, - 54667, 54673, - 54679, 54709, 54713, 54721, 54727, 54751, 54767, 54773, 54779, 54787, - 54799, 54829, - 54833, 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, - 54959, 54973, - 54979, 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, - 55079, 55103, - 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, - 55219, 55229, - 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343, - 55351, 55373, - 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501, 55511, - 55529, 55541, - 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631, 55633, 55639, - 55661, 55663, - 55667, 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, - 55787, 55793, - 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, - 55871, 55889, - 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, 55967, 55987, - 55997, 56003, - 56009, 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, - 56123, 56131, - 56149, 56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249, - 56263, 56267, - 56269, 56299, 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, - 56417, 56431, - 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, - 56509, 56519, - 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, - 56633, 56659, - 56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747, - 56767, 56773, - 56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873, - 56891, 56893, - 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, - 56983, 56989, - 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, - 57107, 57119, - 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, - 57221, 57223, - 57241, 57251, 57259, 57269, 57271, 57283, 57287, 57301, 57329, 57331, - 57347, 57349, - 57367, 57373, 57383, 57389, 57397, 57413, 57427, 57457, 57467, 57487, - 57493, 57503, - 57527, 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, - 57649, 57653, - 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, - 57751, 57773, - 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853, - 57859, 57881, - 57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, - 58027, 58031, - 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129, - 58147, 58151, - 58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, - 58231, 58237, - 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, - 58391, 58393, - 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477, 58481, - 58511, 58537, - 58543, 58549, 58567, 58573, 58579, 58601, 58603, 58613, 58631, 58657, - 58661, 58679, - 58687, 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, - 58787, 58789, - 58831, 58889, 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, - 58963, 58967, - 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, - 59063, 59069, - 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, - 59167, 59183, - 59197, 59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, - 59281, 59333, - 59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407, - 59417, 59419, - 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, - 59539, 59557, - 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, - 59663, 59669, - 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, 59753, 59771, - 59779, 59791, - 59797, 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957, - 59971, 59981, - 59999, 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089, 60091, - 60101, 60103, - 60107, 60127, 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, - 60223, 60251, - 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, - 60373, 60383, - 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, 60509, 60521, - 60527, 60539, - 60589, 60601, 60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649, - 60659, 60661, - 60679, 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, - 60773, 60779, - 60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, - 60917, 60919, - 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, - 61051, 61057, - 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223, - 61231, 61253, - 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363, - 61379, 61381, - 61403, 61409, 61417, 61441, 61463, 61469, 61471, 61483, 61487, 61493, - 61507, 61511, - 61519, 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, - 61627, 61631, - 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, - 61723, 61729, - 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, 61871, 61879, - 61909, 61927, - 61933, 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, - 62017, 62039, - 62047, 62053, 62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137, - 62141, 62143, - 62171, 62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, - 62299, 62303, - 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, - 62467, 62473, - 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581, - 62591, 62597, - 62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687, 62701, - 62723, 62731, - 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861, - 62869, 62873, - 62897, 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, - 62987, 62989, - 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, - 63131, 63149, - 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311, - 63313, 63317, - 63331, 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391, 63397, - 63409, 63419, - 63421, 63439, 63443, 63463, 63467, 63473, 63487, 63493, 63499, 63521, - 63527, 63533, - 63541, 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, - 63629, 63647, - 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, - 63727, 63737, - 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839, - 63841, 63853, - 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, - 64013, 64019, - 64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153, - 64157, 64171, - 64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, - 64303, 64319, - 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, - 64483, 64489, - 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613, - 64621, 64627, - 64633, 64661, 64663, 64667, 64679, 64693, 64709, 64717, 64747, 64763, - 64781, 64783, - 64793, 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901, - 64919, 64921, - 64927, 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, - 65053, 65063, - 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, - 65167, 65171, - 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, - 65293, 65309, - 65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419, - 65423, 65437, - 65447, 65449, 65479, 65497, 65519, 65521, + 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, + 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, + 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, + 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, + 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, + 10429, 10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499, + 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, + 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, + 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, + 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, + 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, + 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, + 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113, 11117, + 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, + 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, + 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, + 11393, 11399, 11411, 11423, 11437, 11443, 11447, 11467, 11471, + 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, + 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677, + 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, + 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, + 11833, 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, + 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, + 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, + 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, + 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, + 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, + 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, + 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, + 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, + 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, + 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, + 12721, 12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, + 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, + 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 12973, + 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, + 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, + 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, + 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, + 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, + 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, + 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, + 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, + 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, + 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, + 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903, 13907, + 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, + 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, + 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, + 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, + 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, + 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, + 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, + 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, + 14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, + 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, + 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869, 14879, + 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, + 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, + 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, + 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, + 15259, 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, + 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, + 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, + 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, + 15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, + 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, + 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, + 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, + 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, + 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, + 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, + 16141, 16183, 16187, 16189, 16193, 16217, 16223, 16229, 16231, + 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, + 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 16433, + 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, + 16547, 16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, + 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, + 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, 16823, + 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, + 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, + 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, + 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, + 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, + 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, + 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417, 17419, + 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, + 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, + 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, + 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, + 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, + 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, + 17959, 17971, 17977, 17981, 17987, 17989, 18013, 18041, 18043, + 18047, 18049, 18059, 18061, 18077, 18089, 18097, 18119, 18121, + 18127, 18131, 18133, 18143, 18149, 18169, 18181, 18191, 18199, + 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, + 18287, 18289, 18301, 18307, 18311, 18313, 18329, 18341, 18353, + 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, + 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, + 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, + 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, + 18749, 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, + 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973, + 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, + 19079, 19081, 19087, 19121, 19139, 19141, 19157, 19163, 19181, + 19183, 19207, 19211, 19213, 19219, 19231, 19237, 19249, 19259, + 19267, 19273, 19289, 19301, 19309, 19319, 19333, 19373, 19379, + 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, + 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, 19483, + 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, + 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, + 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, + 19777, 19793, 19801, 19813, 19819, 19841, 19843, 19853, 19861, + 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961, + 19963, 19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, + 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113, + 20117, 20123, 20129, 20143, 20147, 20149, 20161, 20173, 20177, + 20183, 20201, 20219, 20231, 20233, 20249, 20261, 20269, 20287, + 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, 20359, + 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, + 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, + 20551, 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, + 20681, 20693, 20707, 20717, 20719, 20731, 20743, 20747, 20749, + 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, + 20873, 20879, 20887, 20897, 20899, 20903, 20921, 20929, 20939, + 20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, + 21019, 21023, 21031, 21059, 21061, 21067, 21089, 21101, 21107, + 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, + 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, 21283, + 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, + 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, + 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, + 21559, 21563, 21569, 21577, 21587, 21589, 21599, 21601, 21611, + 21613, 21617, 21647, 21649, 21661, 21673, 21683, 21701, 21713, + 21727, 21737, 21739, 21751, 21757, 21767, 21773, 21787, 21799, + 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871, + 21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, + 21997, 22003, 22013, 22027, 22031, 22037, 22039, 22051, 22063, + 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, 22129, + 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, + 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, + 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, + 22433, 22441, 22447, 22453, 22469, 22481, 22483, 22501, 22511, + 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, + 22621, 22637, 22639, 22643, 22651, 22669, 22679, 22691, 22697, + 22699, 22709, 22717, 22721, 22727, 22739, 22741, 22751, 22769, + 22777, 22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861, + 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, + 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, 23039, + 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, + 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, + 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, + 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, + 23417, 23431, 23447, 23459, 23473, 23497, 23509, 23531, 23537, + 23539, 23549, 23557, 23561, 23563, 23567, 23581, 23593, 23599, + 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, + 23677, 23687, 23689, 23719, 23741, 23743, 23747, 23753, 23761, + 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833, + 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, 23911, + 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, + 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, + 24091, 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, + 24151, 24169, 24179, 24181, 24197, 24203, 24223, 24229, 24239, + 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, + 24379, 24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, + 24473, 24481, 24499, 24509, 24517, 24527, 24533, 24547, 24551, + 24571, 24593, 24611, 24623, 24631, 24659, 24671, 24677, 24683, + 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793, + 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889, + 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, + 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, + 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, + 25171, 25183, 25189, 25219, 25229, 25237, 25243, 25247, 25253, + 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343, 25349, + 25357, 25367, 25373, 25391, 25409, 25411, 25423, 25439, 25447, + 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, + 25577, 25579, 25583, 25589, 25601, 25603, 25609, 25621, 25633, + 25639, 25643, 25657, 25667, 25673, 25679, 25693, 25703, 25717, + 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, 25801, + 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, + 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, + 25999, 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, + 26107, 26111, 26113, 26119, 26141, 26153, 26161, 26171, 26177, + 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261, + 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339, 26347, + 26357, 26371, 26387, 26393, 26399, 26407, 26417, 26423, 26431, + 26437, 26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539, + 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, + 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, 26713, + 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, + 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, + 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, + 26981, 26987, 26993, 27011, 27017, 27031, 27043, 27059, 27061, + 27067, 27073, 27077, 27091, 27103, 27107, 27109, 27127, 27143, + 27179, 27191, 27197, 27211, 27239, 27241, 27253, 27259, 27271, + 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397, + 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, + 27487, 27509, 27527, 27529, 27539, 27541, 27551, 27581, 27583, + 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, 27697, + 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, + 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, + 27827, 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, + 27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019, + 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, + 28109, 28111, 28123, 28151, 28163, 28181, 28183, 28201, 28211, + 28219, 28229, 28277, 28279, 28283, 28289, 28297, 28307, 28309, + 28319, 28349, 28351, 28387, 28393, 28403, 28409, 28411, 28429, + 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, + 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, 28591, + 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, + 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, + 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, + 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909, + 28921, 28927, 28933, 28949, 28961, 28979, 29009, 29017, 29021, + 29023, 29027, 29033, 29059, 29063, 29077, 29101, 29123, 29129, + 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201, + 29207, 29209, 29221, 29231, 29243, 29251, 29269, 29287, 29297, + 29303, 29311, 29327, 29333, 29339, 29347, 29363, 29383, 29387, + 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453, + 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, + 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, + 29671, 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, + 29803, 29819, 29833, 29837, 29851, 29863, 29867, 29873, 29879, + 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, + 30013, 30029, 30047, 30059, 30071, 30089, 30091, 30097, 30103, + 30109, 30113, 30119, 30133, 30137, 30139, 30161, 30169, 30181, + 30187, 30197, 30203, 30211, 30223, 30241, 30253, 30259, 30269, + 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367, + 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, 30491, + 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, + 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, + 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, + 30781, 30803, 30809, 30817, 30829, 30839, 30841, 30851, 30853, + 30859, 30869, 30871, 30881, 30893, 30911, 30931, 30937, 30941, + 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051, + 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, + 31151, 31153, 31159, 31177, 31181, 31183, 31189, 31193, 31219, + 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267, 31271, + 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, 31379, + 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, + 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, + 31601, 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, + 31699, 31721, 31723, 31727, 31729, 31741, 31751, 31769, 31771, + 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891, + 31907, 31957, 31963, 31973, 31981, 31991, 32003, 32009, 32027, + 32029, 32051, 32057, 32059, 32063, 32069, 32077, 32083, 32089, + 32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, + 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297, + 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359, + 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, + 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, + 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, + 32603, 32609, 32611, 32621, 32633, 32647, 32653, 32687, 32693, + 32707, 32713, 32717, 32719, 32749, 32771, 32779, 32783, 32789, + 32797, 32801, 32803, 32831, 32833, 32839, 32843, 32869, 32887, + 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, + 32983, 32987, 32993, 32999, 33013, 33023, 33029, 33037, 33049, + 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, + 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223, + 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, + 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, + 33427, 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, + 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, + 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, + 33647, 33679, 33703, 33713, 33721, 33739, 33749, 33751, 33757, + 33767, 33769, 33773, 33791, 33797, 33809, 33811, 33827, 33829, + 33851, 33857, 33863, 33871, 33889, 33893, 33911, 33923, 33931, + 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, + 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159, + 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, + 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, + 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, + 34439, 34457, 34469, 34471, 34483, 34487, 34499, 34501, 34511, + 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, + 34607, 34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, + 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763, + 34781, 34807, 34819, 34841, 34843, 34847, 34849, 34871, 34877, + 34883, 34897, 34913, 34919, 34939, 34949, 34961, 34963, 34981, + 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089, + 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, + 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, + 35291, 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, + 35393, 35401, 35407, 35419, 35423, 35437, 35447, 35449, 35461, + 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, + 35569, 35573, 35591, 35593, 35597, 35603, 35617, 35671, 35677, + 35729, 35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, + 35809, 35831, 35837, 35839, 35851, 35863, 35869, 35879, 35897, + 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, + 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, 36067, + 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, + 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, + 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, + 36373, 36383, 36389, 36433, 36451, 36457, 36467, 36469, 36473, + 36479, 36493, 36497, 36523, 36527, 36529, 36541, 36551, 36559, + 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637, 36643, + 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, + 36739, 36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, + 36809, 36821, 36833, 36847, 36857, 36871, 36877, 36887, 36899, + 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973, + 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, + 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, + 37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, + 37307, 37309, 37313, 37321, 37337, 37339, 37357, 37361, 37363, + 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, + 37489, 37493, 37501, 37507, 37511, 37517, 37529, 37537, 37547, + 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591, 37607, + 37619, 37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, + 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, + 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957, + 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, + 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, + 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, + 38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, + 38333, 38351, 38371, 38377, 38393, 38431, 38447, 38449, 38453, + 38459, 38461, 38501, 38543, 38557, 38561, 38567, 38569, 38593, + 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, + 38677, 38693, 38699, 38707, 38711, 38713, 38723, 38729, 38737, + 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, + 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, 38923, + 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, + 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, + 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, + 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, + 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, + 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, + 39503, 39509, 39511, 39521, 39541, 39551, 39563, 39569, 39581, + 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, + 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, + 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, + 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, + 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, + 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, + 40163, 40169, 40177, 40189, 40193, 40213, 40231, 40237, 40241, + 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, + 40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, + 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, + 40591, 40597, 40609, 40627, 40637, 40639, 40693, 40697, 40699, + 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, + 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, + 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, + 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, + 41081, 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, + 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, + 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, + 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, + 41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, + 41543, 41549, 41579, 41593, 41597, 41603, 41609, 41611, 41617, + 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, + 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, 41809, + 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, + 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, + 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, + 42071, 42073, 42083, 42089, 42101, 42131, 42139, 42157, 42169, + 42179, 42181, 42187, 42193, 42197, 42209, 42221, 42223, 42227, + 42239, 42257, 42281, 42283, 42293, 42299, 42307, 42323, 42331, + 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407, + 42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, + 42473, 42487, 42491, 42499, 42509, 42533, 42557, 42569, 42571, + 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, 42683, + 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, + 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, + 42841, 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, + 42943, 42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019, + 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, + 43151, 43159, 43177, 43189, 43201, 43207, 43223, 43237, 43261, + 43271, 43283, 43291, 43313, 43319, 43321, 43331, 43391, 43397, + 43399, 43403, 43411, 43427, 43441, 43451, 43457, 43481, 43487, + 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, + 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, 43669, + 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, + 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, + 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, + 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, + 44087, 44089, 44101, 44111, 44119, 44123, 44129, 44131, 44159, + 44171, 44179, 44189, 44201, 44203, 44207, 44221, 44249, 44257, + 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357, + 44371, 44381, 44383, 44389, 44417, 44449, 44453, 44483, 44491, + 44497, 44501, 44507, 44519, 44531, 44533, 44537, 44543, 44549, + 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647, + 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, + 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, + 44843, 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, + 44939, 44953, 44959, 44963, 44971, 44983, 44987, 45007, 45013, + 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, + 45139, 45161, 45179, 45181, 45191, 45197, 45233, 45247, 45259, + 45263, 45281, 45289, 45293, 45307, 45317, 45319, 45329, 45337, + 45341, 45343, 45361, 45377, 45389, 45403, 45413, 45427, 45433, + 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553, + 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659, + 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, + 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, + 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, + 45971, 45979, 45989, 46021, 46027, 46049, 46051, 46061, 46073, + 46091, 46093, 46099, 46103, 46133, 46141, 46147, 46153, 46171, + 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271, + 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, + 46381, 46399, 46411, 46439, 46441, 46447, 46451, 46457, 46471, + 46477, 46489, 46499, 46507, 46511, 46523, 46549, 46559, 46567, + 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, 46649, + 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, + 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, + 46831, 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, + 46957, 46993, 46997, 47017, 47041, 47051, 47057, 47059, 47087, + 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149, + 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279, 47287, + 47293, 47297, 47303, 47309, 47317, 47339, 47351, 47353, 47363, + 47381, 47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, + 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543, + 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, 47639, + 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, + 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, + 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, + 47933, 47939, 47947, 47951, 47963, 47969, 47977, 47981, 48017, + 48023, 48029, 48049, 48073, 48079, 48091, 48109, 48119, 48121, + 48131, 48157, 48163, 48179, 48187, 48193, 48197, 48221, 48239, + 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, + 48353, 48371, 48383, 48397, 48407, 48409, 48413, 48437, 48449, + 48463, 48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, + 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, 48619, + 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, + 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, + 48817, 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, + 48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, + 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103, + 49109, 49117, 49121, 49123, 49139, 49157, 49169, 49171, 49177, + 49193, 49199, 49201, 49207, 49211, 49223, 49253, 49261, 49277, + 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367, 49369, + 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, + 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, 49547, + 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, + 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, + 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, + 49843, 49853, 49871, 49877, 49891, 49919, 49921, 49927, 49937, + 49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, + 50047, 50051, 50053, 50069, 50077, 50087, 50093, 50101, 50111, + 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207, + 50221, 50227, 50231, 50261, 50263, 50273, 50287, 50291, 50311, + 50321, 50329, 50333, 50341, 50359, 50363, 50377, 50383, 50387, + 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, + 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, + 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, + 50753, 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, + 50857, 50867, 50873, 50891, 50893, 50909, 50923, 50929, 50951, + 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, + 51059, 51061, 51071, 51109, 51131, 51133, 51137, 51151, 51157, + 51169, 51193, 51197, 51199, 51203, 51217, 51229, 51239, 51241, + 51257, 51263, 51283, 51287, 51307, 51329, 51341, 51343, 51347, + 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, + 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, 51503, + 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, + 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, + 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, + 51797, 51803, 51817, 51827, 51829, 51839, 51853, 51859, 51869, + 51871, 51893, 51899, 51907, 51913, 51929, 51941, 51949, 51971, + 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067, + 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, + 52181, 52183, 52189, 52201, 52223, 52237, 52249, 52253, 52259, + 52267, 52289, 52291, 52301, 52313, 52321, 52361, 52363, 52369, + 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, 52511, + 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, + 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, + 52709, 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, + 52807, 52813, 52817, 52837, 52859, 52861, 52879, 52883, 52889, + 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, + 52981, 52999, 53003, 53017, 53047, 53051, 53069, 53077, 53087, + 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161, + 53171, 53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, + 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359, + 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, 53453, + 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, + 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, + 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, + 53777, 53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, + 53881, 53887, 53891, 53897, 53899, 53917, 53923, 53927, 53939, + 53951, 53959, 53987, 53993, 54001, 54011, 54013, 54037, 54049, + 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, + 54167, 54181, 54193, 54217, 54251, 54269, 54277, 54287, 54293, + 54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, + 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, 54449, + 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, + 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, + 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, + 54727, 54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, + 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, + 54959, 54973, 54979, 54983, 55001, 55009, 55021, 55049, 55051, + 55057, 55061, 55073, 55079, 55103, 55109, 55117, 55127, 55147, + 55163, 55171, 55201, 55207, 55213, 55217, 55219, 55229, 55243, + 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343, + 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, + 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, + 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, + 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, + 55787, 55793, 55799, 55807, 55813, 55817, 55819, 55823, 55829, + 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, + 55927, 55931, 55933, 55949, 55967, 55987, 55997, 56003, 56009, + 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, + 56123, 56131, 56149, 56167, 56171, 56179, 56197, 56207, 56209, + 56237, 56239, 56249, 56263, 56267, 56269, 56299, 56311, 56333, + 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437, + 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, + 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, + 56599, 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, + 56701, 56711, 56713, 56731, 56737, 56747, 56767, 56773, 56779, + 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873, + 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, + 56951, 56957, 56963, 56983, 56989, 56993, 56999, 57037, 57041, + 57047, 57059, 57073, 57077, 57089, 57097, 57107, 57119, 57131, + 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, + 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, 57287, + 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, + 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, + 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, + 57649, 57653, 57667, 57679, 57689, 57697, 57709, 57713, 57719, + 57727, 57731, 57737, 57751, 57773, 57781, 57787, 57791, 57793, + 57803, 57809, 57829, 57839, 57847, 57853, 57859, 57881, 57899, + 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, + 58027, 58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, + 58109, 58111, 58129, 58147, 58151, 58153, 58169, 58171, 58189, + 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, 58243, + 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, + 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, + 58453, 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, + 58579, 58601, 58603, 58613, 58631, 58657, 58661, 58679, 58687, + 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, + 58787, 58789, 58831, 58889, 58897, 58901, 58907, 58909, 58913, + 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997, 59009, + 59011, 59021, 59023, 59029, 59051, 59053, 59063, 59069, 59077, + 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, + 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, 59239, + 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, + 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, + 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, + 59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, + 59629, 59651, 59659, 59663, 59669, 59671, 59693, 59699, 59707, + 59723, 59729, 59743, 59747, 59753, 59771, 59779, 59791, 59797, + 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957, + 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041, 60077, + 60083, 60089, 60091, 60101, 60103, 60107, 60127, 60133, 60139, + 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257, + 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, + 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, + 60497, 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, + 60617, 60623, 60631, 60637, 60647, 60649, 60659, 60661, 60679, + 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, + 60773, 60779, 60793, 60811, 60821, 60859, 60869, 60887, 60889, + 60899, 60901, 60913, 60917, 60919, 60923, 60937, 60943, 60953, + 60961, 61001, 61007, 61027, 61031, 61043, 61051, 61057, 61091, + 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223, + 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, + 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, + 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, + 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, + 61627, 61631, 61637, 61643, 61651, 61657, 61667, 61673, 61681, + 61687, 61703, 61717, 61723, 61729, 61751, 61757, 61781, 61813, + 61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933, + 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, + 62017, 62039, 62047, 62053, 62057, 62071, 62081, 62099, 62119, + 62129, 62131, 62137, 62141, 62143, 62171, 62189, 62191, 62201, + 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, 62311, + 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, + 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, + 62549, 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, + 62639, 62653, 62659, 62683, 62687, 62701, 62723, 62731, 62743, + 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861, + 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969, + 62971, 62981, 62983, 62987, 62989, 63029, 63031, 63059, 63067, + 63073, 63079, 63097, 63103, 63113, 63127, 63131, 63149, 63179, + 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311, + 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, 63377, + 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, + 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, + 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, + 63629, 63647, 63649, 63659, 63667, 63671, 63689, 63691, 63697, + 63703, 63709, 63719, 63727, 63737, 63743, 63761, 63773, 63781, + 63793, 63799, 63803, 63809, 63823, 63839, 63841, 63853, 63857, + 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, + 64013, 64019, 64033, 64037, 64063, 64067, 64081, 64091, 64109, + 64123, 64151, 64153, 64157, 64171, 64187, 64189, 64217, 64223, + 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, 64327, + 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, + 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, + 64601, 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, + 64679, 64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, + 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901, + 64919, 64921, 64927, 64937, 64951, 64969, 64997, 65003, 65011, + 65027, 65029, 65033, 65053, 65063, 65071, 65089, 65099, 65101, + 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173, + 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, + 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, 65393, + 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, + 65519, 65521, }; #define NPRIMES (sizeof(primes) / sizeof(*primes)) @@ -1193,11 +838,19 @@ static const unsigned short primes[] = { * more than a multiple of a dirty great bignum. In this case * `bits' gives the size of the factor by which we _multiply_ * that bignum, rather than the size of the whole number. + * + * - for the basically cosmetic purposes of generating keys of the + * length actually specified rather than off by one bit, we permit + * the caller to provide an unsigned integer 'firstbits' which will + * match the top few bits of the returned prime. (That is, there + * will exist some n such that (returnvalue >> n) == firstbits.) If + * 'firstbits' is not needed, specifying it to either 0 or 1 is + * an adequate no-op. */ Bignum primegen(int bits, int modulus, int residue, Bignum factor, - int phase, progfn_t pfn, void *pfnparam) + int phase, progfn_t pfn, void *pfnparam, unsigned firstbits) { - int i, k, v, byte, bitsleft, check, checks; + int i, k, v, byte, bitsleft, check, checks, fbsize; unsigned long delta; unsigned long moduli[NPRIMES + 1]; unsigned long residues[NPRIMES + 1]; @@ -1208,6 +861,10 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, byte = 0; bitsleft = 0; + fbsize = 0; + while (firstbits >> fbsize) /* work out how to align this */ + fbsize++; + STARTOVER: pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress); @@ -1220,9 +877,11 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, */ p = bn_power_2(bits - 1); for (i = 0; i < bits; i++) { - if (i == 0 || i == bits - 1) + if (i == 0 || i == bits - 1) { v = (i != 0 || !factor) ? 1 : 0; - else { + } else if (i >= bits - fbsize) { + v = (firstbits >> (i - (bits - fbsize))) & 1; + } else { if (bitsleft <= 0) bitsleft = 8, byte = random_byte(); v = byte & 1; @@ -1396,3 +1055,32 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, freebn(pm1); return p; } + +/* + * Invent a pair of values suitable for use as 'firstbits' in the + * above function, such that their product is at least 2. + * + * This is used for generating both RSA and DSA keys which have + * exactly the specified number of bits rather than one fewer - if you + * generate an a-bit and a b-bit number completely at random and + * multiply them together, you could end up with either an (ab-1)-bit + * number or an (ab)-bit number. The former happens log(2)*2-1 of the + * time (about 39%) and, though actually harmless, every time it + * occurs it has a non-zero probability of sparking a user email along + * the lines of 'Hey, I asked PuTTYgen for a 2048-bit key and I only + * got 2047 bits! Bug!' + */ +void invent_firstbits(unsigned *one, unsigned *two) +{ + /* + * Our criterion is that any number in the range [one,one+1) + * multiplied by any number in the range [two,two+1) should have + * the highest bit set. It should be clear that we can trivially + * test this by multiplying the smallest values in each interval, + * i.e. the ones we actually invented. + */ + do { + *one = 0x100 | random_byte(); + *two = 0x100 | random_byte(); + } while (*one * *two < 0x20000); +} diff --git a/contrib/putty/SSHPUBK.C b/contrib/putty/SSHPUBK.C index 7b5a690..f13e33b 100644 --- a/contrib/putty/SSHPUBK.C +++ b/contrib/putty/SSHPUBK.C @@ -67,14 +67,15 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, i += 4; /* Now the serious stuff. An ordinary SSH-1 public key. */ - i += makekey(buf + i, len, key, NULL, 1); - if (i < 0) + j = makekey(buf + i, len, key, NULL, 1); + if (j < 0) goto end; /* overran */ + i += j; /* Next, the comment field. */ - j = GET_32BIT(buf + i); + j = toint(GET_32BIT(buf + i)); i += 4; - if (len - i < j) + if (j < 0 || len - i < j) goto end; comment = snewn(j + 1, char); if (comment) { @@ -108,7 +109,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7); - memset(keybuf, 0, sizeof(keybuf)); /* burn the evidence */ + smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* @@ -150,7 +151,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, ret = 1; end: - memset(buf, 0, sizeof(buf)); /* burn the evidence */ + smemclr(buf, sizeof(buf)); /* burn the evidence */ return ret; } @@ -162,7 +163,7 @@ int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase, int ret = 0; const char *error = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; @@ -203,7 +204,7 @@ int rsakey_encrypted(const Filename *filename, char **comment) FILE *fp; char buf[64]; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) return 0; /* doesn't even exist */ @@ -241,7 +242,7 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen, *bloblen = 0; ret = 0; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; @@ -257,8 +258,8 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen, *blob = rsa_public_blob(&key, bloblen); freersakey(&key); ret = 1; - fp = NULL; } + fp = NULL; /* loadrsakey_main unconditionally closes fp */ } else { error = "not an SSH-1 RSA file"; } @@ -358,13 +359,13 @@ int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase) MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_encrypt_pubkey(keybuf, estart, p - estart); - memset(keybuf, 0, sizeof(keybuf)); /* burn the evidence */ + smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* * Done. Write the result to the file. */ - fp = f_open(*filename, "wb", TRUE); + fp = f_open(filename, "wb", TRUE); if (fp) { int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf)); if (fclose(fp)) @@ -462,7 +463,7 @@ static int read_header(FILE * fp, char *header) int len = 39; int c; - while (len > 0) { + while (1) { c = fgetc(fp); if (c == '\n' || c == '\r' || c == EOF) return 0; /* failure */ @@ -632,7 +633,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, encryption = comment = mac = NULL; public_blob = private_blob = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; @@ -647,6 +648,11 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, /* this is an old key file; warn and then continue */ old_keyfile_warning(); old_fmt = 1; + } else if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) { + /* this is a key file FROM THE FUTURE; refuse it, but with a + * more specific error message than the generic one below */ + error = "PuTTY key format too new"; + goto error; } else { error = "not a PuTTY SSH-2 private key"; goto error; @@ -674,7 +680,6 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, cipher = 0; cipherblk = 1; } else { - sfree(encryption); goto error; } @@ -794,14 +799,14 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, hmac_sha1_simple(mackey, 20, macdata, maclen, binary); - memset(mackey, 0, sizeof(mackey)); - memset(&s, 0, sizeof(s)); + smemclr(mackey, sizeof(mackey)); + smemclr(&s, sizeof(s)); } else { SHA_Simple(macdata, maclen, binary); } if (free_macdata) { - memset(macdata, 0, maclen); + smemclr(macdata, maclen); sfree(macdata); } @@ -881,7 +886,7 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm, public_blob = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; @@ -891,7 +896,10 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm, if (!read_header(fp, header) || (0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { - error = "not a PuTTY SSH-2 private key"; + if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) + error = "PuTTY key format too new"; + else + error = "not a PuTTY SSH-2 private key"; goto error; } error = "file format error"; @@ -962,7 +970,7 @@ int ssh2_userkey_encrypted(const Filename *filename, char **commentptr) if (commentptr) *commentptr = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) return 0; if (!read_header(fp, header) @@ -1000,6 +1008,8 @@ int ssh2_userkey_encrypted(const Filename *filename, char **commentptr) if (commentptr) *commentptr = comment; + else + sfree(comment); fclose(fp); if (!strcmp(b, "aes256-cbc")) @@ -1116,10 +1126,10 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, SHA_Bytes(&s, passphrase, strlen(passphrase)); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac); - memset(macdata, 0, maclen); + smemclr(macdata, maclen); sfree(macdata); - memset(mackey, 0, sizeof(mackey)); - memset(&s, 0, sizeof(s)); + smemclr(mackey, sizeof(mackey)); + smemclr(&s, sizeof(s)); } if (passphrase) { @@ -1139,11 +1149,11 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, aes256_encrypt_pubkey(key, priv_blob_encrypted, priv_encrypted_len); - memset(key, 0, sizeof(key)); - memset(&s, 0, sizeof(s)); + smemclr(key, sizeof(key)); + smemclr(&s, sizeof(s)); } - fp = f_open(*filename, "w", TRUE); + fp = f_open(filename, "w", TRUE); if (!fp) return 0; fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name); @@ -1160,7 +1170,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, fclose(fp); sfree(pub_blob); - memset(priv_blob, 0, priv_blob_len); + smemclr(priv_blob, priv_blob_len); sfree(priv_blob); sfree(priv_blob_encrypted); return 1; @@ -1179,7 +1189,7 @@ int key_type(const Filename *filename) const char openssh_sig[] = "-----BEGIN "; int i; - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) return SSH_KEYTYPE_UNOPENABLE; i = fread(buf, 1, sizeof(buf), fp); diff --git a/contrib/putty/SSHRAND.C b/contrib/putty/SSHRAND.C index 91d9b37..e5f3186 100644 --- a/contrib/putty/SSHRAND.C +++ b/contrib/putty/SSHRAND.C @@ -49,6 +49,10 @@ static struct RandPool pool; int random_active = 0; long next_noise_collection; +#ifdef RANDOM_DIAGNOSTICS +int random_diagnostics = 0; +#endif + static void random_stir(void) { word32 block[HASHINPUT / sizeof(word32)]; @@ -65,6 +69,30 @@ static void random_stir(void) noise_get_light(random_add_noise); +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir starting\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics++; + } +#endif + SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); pool.incomingpos = 0; @@ -116,6 +144,29 @@ static void random_stir(void) for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) ((word32 *) (pool.pool + j))[k] = digest[k]; } + +#ifdef RANDOM_DIAGNOSTICS + if (i == 0) { + int p, q; + printf("random stir midpoint\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + } +#endif } /* @@ -128,6 +179,30 @@ static void random_stir(void) pool.poolpos = sizeof(pool.incoming); pool.stir_pending = FALSE; + +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir done\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics--; + } +#endif } void random_add_noise(void *noise, int length) @@ -199,9 +274,9 @@ static void random_add_heavynoise_bitbybit(void *noise, int length) pool.poolpos = i; } -static void random_timer(void *ctx, long now) +static void random_timer(void *ctx, unsigned long now) { - if (random_active > 0 && now - next_noise_collection >= 0) { + if (random_active > 0 && now == next_noise_collection) { noise_regular(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); @@ -213,27 +288,30 @@ void random_ref(void) if (!random_active) { memset(&pool, 0, sizeof(pool)); /* just to start with */ + random_active++; + noise_get_heavy(random_add_heavynoise_bitbybit); random_stir(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); } - - random_active++; } void random_unref(void) { + assert(random_active > 0); + if (random_active == 1) { + random_save_seed(); + expire_timer_context(&pool); + } random_active--; - assert(random_active >= 0); - if (random_active) return; - - expire_timer_context(&pool); } int random_byte(void) { + assert(random_active); + if (pool.poolpos >= POOLSIZE) random_stir(); diff --git a/contrib/putty/SSHRSA.C b/contrib/putty/SSHRSA.C index ea6440b..02b87d0 100644 --- a/contrib/putty/SSHRSA.C +++ b/contrib/putty/SSHRSA.C @@ -110,7 +110,7 @@ static void sha512_mpint(SHA512_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } /* @@ -273,9 +273,18 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key) bignum_cmp(random, key->modulus) >= 0) { freebn(random); continue; - } else { - break; } + + /* + * Also, make sure it has an inverse mod modulus. + */ + random_inverse = modinv(random, key->modulus); + if (!random_inverse) { + freebn(random); + continue; + } + + break; } /* @@ -294,7 +303,6 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key) */ random_encrypted = crt_modpow(random, key->exponent, key->modulus, key->p, key->q, key->iqmp); - random_inverse = modinv(random, key->modulus); input_blinded = modmul(input, random_encrypted, key->modulus); ret_blinded = crt_modpow(input_blinded, key->private_exponent, key->modulus, key->p, key->q, key->iqmp); @@ -413,16 +421,18 @@ int rsa_verify(struct RSAKey *key) pm1 = copybn(key->p); decbn(pm1); ed = modmul(key->exponent, key->private_exponent, pm1); + freebn(pm1); cmp = bignum_cmp(ed, One); - sfree(ed); + freebn(ed); if (cmp != 0) return 0; qm1 = copybn(key->q); decbn(qm1); ed = modmul(key->exponent, key->private_exponent, qm1); + freebn(qm1); cmp = bignum_cmp(ed, One); - sfree(ed); + freebn(ed); if (cmp != 0) return 0; @@ -441,6 +451,8 @@ int rsa_verify(struct RSAKey *key) freebn(key->iqmp); key->iqmp = modinv(key->q, key->p); + if (!key->iqmp) + return 0; } /* @@ -448,7 +460,7 @@ int rsa_verify(struct RSAKey *key) */ n = modmul(key->iqmp, key->q, key->p); cmp = bignum_cmp(n, One); - sfree(n); + freebn(n); if (cmp != 0) return 0; @@ -525,7 +537,9 @@ static void getstring(char **data, int *datalen, char **p, int *length) *p = NULL; if (*datalen < 4) return; - *length = GET_32BIT(*data); + *length = toint(GET_32BIT(*data)); + if (*length < 0) + return; *datalen -= 4; *data += 4; if (*datalen < *length) @@ -547,6 +561,8 @@ static Bignum getmp(char **data, int *datalen) return b; } +static void rsa2_freekey(void *key); /* forward reference */ + static void *rsa2_newkey(char *data, int len) { char *p; @@ -554,8 +570,6 @@ static void *rsa2_newkey(char *data, int len) struct RSAKey *rsa; rsa = snew(struct RSAKey); - if (!rsa) - return NULL; getstring(&data, &len, &p, &slen); if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { @@ -568,6 +582,11 @@ static void *rsa2_newkey(char *data, int len) rsa->p = rsa->q = rsa->iqmp = NULL; rsa->comment = NULL; + if (!rsa->exponent || !rsa->modulus) { + rsa2_freekey(rsa); + return NULL; + } + return rsa; } @@ -690,8 +709,6 @@ static void *rsa2_openssh_createkey(unsigned char **blob, int *len) struct RSAKey *rsa; rsa = snew(struct RSAKey); - if (!rsa) - return NULL; rsa->comment = NULL; rsa->modulus = getmp(b, len); @@ -703,13 +720,12 @@ static void *rsa2_openssh_createkey(unsigned char **blob, int *len) if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent || !rsa->iqmp || !rsa->p || !rsa->q) { - sfree(rsa->modulus); - sfree(rsa->exponent); - sfree(rsa->private_exponent); - sfree(rsa->iqmp); - sfree(rsa->p); - sfree(rsa->q); - sfree(rsa); + rsa2_freekey(rsa); + return NULL; + } + + if (!rsa_verify(rsa)) { + rsa2_freekey(rsa); return NULL; } @@ -838,6 +854,8 @@ static int rsa2_verifysig(void *key, char *sig, int siglen, return 0; } in = getmp(&sig, &siglen); + if (!in) + return 0; out = modpow(in, rsa->exponent, rsa->modulus); freebn(in); diff --git a/contrib/putty/SSHRSAG.C b/contrib/putty/SSHRSAG.C index b19d3c1..9392a9f 100644 --- a/contrib/putty/SSHRSAG.C +++ b/contrib/putty/SSHRSAG.C @@ -2,6 +2,8 @@ * RSA key generation. */ +#include + #include "ssh.h" #define RSA_EXPONENT 37 /* we like this prime */ @@ -10,6 +12,7 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam) { Bignum pm1, qm1, phi_n; + unsigned pfirst, qfirst; /* * Set up the phase limits for the progress report. We do this @@ -59,10 +62,11 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, * general that's slightly more fiddly to arrange. By choosing * a prime e, we can simplify the criterion.) */ + invent_firstbits(&pfirst, &qfirst); key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL, - 1, pfn, pfnparam); + 1, pfn, pfnparam, pfirst); key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL, - 2, pfn, pfnparam); + 2, pfn, pfnparam, qfirst); /* * Ensure p > q, by swapping them if not. @@ -90,8 +94,10 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, freebn(pm1); freebn(qm1); key->private_exponent = modinv(key->exponent, phi_n); + assert(key->private_exponent); pfn(pfnparam, PROGFN_PROGRESS, 3, 4); key->iqmp = modinv(key->q, key->p); + assert(key->iqmp); pfn(pfnparam, PROGFN_PROGRESS, 3, 5); /* diff --git a/contrib/putty/SSHSH256.C b/contrib/putty/SSHSH256.C index 538982a..49fb842 100644 --- a/contrib/putty/SSHSH256.C +++ b/contrib/putty/SSHSH256.C @@ -218,6 +218,116 @@ const struct ssh_hash ssh_sha256 = { sha256_init, sha256_bytes, sha256_final, 32, "SHA-256" }; +/* ---------------------------------------------------------------------- + * The above is the SHA-256 algorithm itself. Now we implement the + * HMAC wrapper on it. + */ + +static void *sha256_make_context(void) +{ + return snewn(3, SHA256_State); +} + +static void sha256_free_context(void *handle) +{ + sfree(handle); +} + +static void sha256_key_internal(void *handle, unsigned char *key, int len) +{ + SHA256_State *keys = (SHA256_State *)handle; + unsigned char foo[64]; + int i; + + memset(foo, 0x36, 64); + for (i = 0; i < len && i < 64; i++) + foo[i] ^= key[i]; + SHA256_Init(&keys[0]); + SHA256_Bytes(&keys[0], foo, 64); + + memset(foo, 0x5C, 64); + for (i = 0; i < len && i < 64; i++) + foo[i] ^= key[i]; + SHA256_Init(&keys[1]); + SHA256_Bytes(&keys[1], foo, 64); + + smemclr(foo, 64); /* burn the evidence */ +} + +static void sha256_key(void *handle, unsigned char *key) +{ + sha256_key_internal(handle, key, 32); +} + +static void hmacsha256_start(void *handle) +{ + SHA256_State *keys = (SHA256_State *)handle; + + keys[2] = keys[0]; /* structure copy */ +} + +static void hmacsha256_bytes(void *handle, unsigned char const *blk, int len) +{ + SHA256_State *keys = (SHA256_State *)handle; + SHA256_Bytes(&keys[2], (void *)blk, len); +} + +static void hmacsha256_genresult(void *handle, unsigned char *hmac) +{ + SHA256_State *keys = (SHA256_State *)handle; + SHA256_State s; + unsigned char intermediate[32]; + + s = keys[2]; /* structure copy */ + SHA256_Final(&s, intermediate); + s = keys[1]; /* structure copy */ + SHA256_Bytes(&s, intermediate, 32); + SHA256_Final(&s, hmac); +} + +static void sha256_do_hmac(void *handle, unsigned char *blk, int len, + unsigned long seq, unsigned char *hmac) +{ + unsigned char seqbuf[4]; + + PUT_32BIT_MSB_FIRST(seqbuf, seq); + hmacsha256_start(handle); + hmacsha256_bytes(handle, seqbuf, 4); + hmacsha256_bytes(handle, blk, len); + hmacsha256_genresult(handle, hmac); +} + +static void sha256_generate(void *handle, unsigned char *blk, int len, + unsigned long seq) +{ + sha256_do_hmac(handle, blk, len, seq, blk + len); +} + +static int hmacsha256_verresult(void *handle, unsigned char const *hmac) +{ + unsigned char correct[32]; + hmacsha256_genresult(handle, correct); + return !memcmp(correct, hmac, 32); +} + +static int sha256_verify(void *handle, unsigned char *blk, int len, + unsigned long seq) +{ + unsigned char correct[32]; + sha256_do_hmac(handle, blk, len, seq, correct); + return !memcmp(correct, blk + len, 32); +} + +const struct ssh_mac ssh_hmac_sha256 = { + sha256_make_context, sha256_free_context, sha256_key, + sha256_generate, sha256_verify, + hmacsha256_start, hmacsha256_bytes, + hmacsha256_genresult, hmacsha256_verresult, + "hmac-sha2-256", + 32, + "HMAC-SHA-256" +}; + #ifdef TEST #include diff --git a/contrib/putty/SSHSHA.C b/contrib/putty/SSHSHA.C index d1c7981..8d8b5e2 100644 --- a/contrib/putty/SSHSHA.C +++ b/contrib/putty/SSHSHA.C @@ -28,6 +28,21 @@ void SHATransform(word32 * digest, word32 * block) word32 a, b, c, d, e; int t; +#ifdef RANDOM_DIAGNOSTICS + { + extern int random_diagnostics; + if (random_diagnostics) { + int i; + printf("SHATransform:"); + for (i = 0; i < 5; i++) + printf(" %08x", digest[i]); + printf(" +"); + for (i = 0; i < 16; i++) + printf(" %08x", block[i]); + } + } +#endif + for (t = 0; t < 16; t++) w[t] = block[t]; @@ -83,6 +98,19 @@ void SHATransform(word32 * digest, word32 * block) digest[2] += c; digest[3] += d; digest[4] += e; + +#ifdef RANDOM_DIAGNOSTICS + { + extern int random_diagnostics; + if (random_diagnostics) { + int i; + printf(" ="); + for (i = 0; i < 5; i++) + printf(" %08x", digest[i]); + printf("\n"); + } + } +#endif } /* ---------------------------------------------------------------------- @@ -98,9 +126,9 @@ void SHA_Init(SHA_State * s) s->lenhi = s->lenlo = 0; } -void SHA_Bytes(SHA_State * s, void *p, int len) +void SHA_Bytes(SHA_State * s, const void *p, int len) { - unsigned char *q = (unsigned char *) p; + const unsigned char *q = (const unsigned char *) p; uint32 wordblock[16]; uint32 lenw = len; int i; @@ -179,7 +207,7 @@ void SHA_Final(SHA_State * s, unsigned char *output) } } -void SHA_Simple(void *p, int len, unsigned char *output) +void SHA_Simple(const void *p, int len, unsigned char *output) { SHA_State s; @@ -253,7 +281,7 @@ static void sha1_key_internal(void *handle, unsigned char *key, int len) SHA_Init(&keys[1]); SHA_Bytes(&keys[1], foo, 64); - memset(foo, 0, 64); /* burn the evidence */ + smemclr(foo, 64); /* burn the evidence */ } static void sha1_key(void *handle, unsigned char *key) @@ -297,11 +325,7 @@ static void sha1_do_hmac(void *handle, unsigned char *blk, int len, { unsigned char seqbuf[4]; - seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF); - seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF); - seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF); - seqbuf[3] = (unsigned char) ((seq) & 0xFF); - + PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacsha1_start(handle); hmacsha1_bytes(handle, seqbuf, 4); hmacsha1_bytes(handle, blk, len); diff --git a/contrib/putty/SSHZLIB.C b/contrib/putty/SSHZLIB.C index 9c780a4..7a2f613 100644 --- a/contrib/putty/SSHZLIB.C +++ b/contrib/putty/SSHZLIB.C @@ -38,6 +38,7 @@ */ #include +#include #include #ifdef ZLIB_STANDALONE diff --git a/contrib/putty/STORAGE.H b/contrib/putty/STORAGE.H index 0e0a7c0..e7963ec 100644 --- a/contrib/putty/STORAGE.H +++ b/contrib/putty/STORAGE.H @@ -9,9 +9,9 @@ /* ---------------------------------------------------------------------- * Functions to save and restore PuTTY sessions. Note that this is * only the low-level code to do the reading and writing. The - * higher-level code that translates a Config structure into a set - * of (key,value) pairs is elsewhere, since it doesn't (mostly) - * change between platforms. + * higher-level code that translates an internal Conf structure into + * a set of (key,value) pairs in their external storage format is + * elsewhere, since it doesn't (mostly) change between platforms. */ /* @@ -31,8 +31,8 @@ void *open_settings_w(const char *sessionname, char **errmsg); void write_setting_s(void *handle, const char *key, const char *value); void write_setting_i(void *handle, const char *key, int value); -void write_setting_filename(void *handle, const char *key, Filename value); -void write_setting_fontspec(void *handle, const char *key, FontSpec font); +void write_setting_filename(void *handle, const char *key, Filename *value); +void write_setting_fontspec(void *handle, const char *key, FontSpec *font); void close_settings_w(void *handle); /* @@ -41,22 +41,21 @@ void close_settings_w(void *handle); * number of calls to read_setting_s() and read_setting_i(), and * then close it using close_settings_r(). * - * read_setting_s() writes into the provided buffer and returns a - * pointer to the same buffer. + * read_setting_s() returns a dynamically allocated string which the + * caller must free. read_setting_filename() and + * read_setting_fontspec() likewise return dynamically allocated + * structures. * * If a particular string setting is not present in the session, * read_setting_s() can return NULL, in which case the caller * should invent a sensible default. If an integer setting is not * present, read_setting_i() returns its provided default. - * - * read_setting_filename() and read_setting_fontspec() each read into - * the provided buffer, and return zero if they failed to. */ void *open_settings_r(const char *sessionname); -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen); +char *read_setting_s(void *handle, const char *key); int read_setting_i(void *handle, const char *key, int defvalue); -int read_setting_filename(void *handle, const char *key, Filename *value); -int read_setting_fontspec(void *handle, const char *key, FontSpec *font); +Filename *read_setting_filename(void *handle, const char *key); +FontSpec *read_setting_fontspec(void *handle, const char *key); void close_settings_r(void *handle); /* diff --git a/contrib/putty/TELNET.C b/contrib/putty/TELNET.C index 8fbe886..0d630c7 100644 --- a/contrib/putty/TELNET.C +++ b/contrib/putty/TELNET.C @@ -4,6 +4,7 @@ #include #include +#include #include "putty.h" @@ -181,6 +182,7 @@ typedef struct telnet_tag { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; void *frontend; void *ldisc; @@ -201,7 +203,7 @@ typedef struct telnet_tag { SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; - Config cfg; + Conf *conf; Pinger pinger; } *Telnet; @@ -363,42 +365,46 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option) static void process_subneg(Telnet telnet) { - unsigned char b[2048], *p, *q; - int var, value, n; - char *e; + unsigned char *b, *p, *q; + int var, value, n, bsize; + char *e, *eval, *ekey, *user; switch (telnet->sb_opt) { case TELOPT_TSPEED: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termspeed = conf_get_str(telnet->conf, CONF_termspeed); + b = snewn(20 + strlen(termspeed), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED; b[3] = TELQUAL_IS; - strcpy((char *)(b + 4), telnet->cfg.termspeed); - n = 4 + strlen(telnet->cfg.termspeed); + strcpy((char *)(b + 4), termspeed); + n = 4 + strlen(termspeed); b[n] = IAC; b[n + 1] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2); logevent(telnet->frontend, "server:\tSB TSPEED SEND"); - logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed); + logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TSPEED "); break; case TELOPT_TTYPE: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termtype = conf_get_str(telnet->conf, CONF_termtype); + b = snewn(20 + strlen(termtype), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE; b[3] = TELQUAL_IS; - for (n = 0; telnet->cfg.termtype[n]; n++) - b[n + 4] = (telnet->cfg.termtype[n] >= 'a' - && telnet->cfg.termtype[n] <= - 'z' ? telnet->cfg.termtype[n] + 'A' - - 'a' : telnet->cfg.termtype[n]); + for (n = 0; termtype[n]; n++) + b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ? + termtype[n] + 'A' - 'a' : + termtype[n]); b[n + 4] = IAC; b[n + 5] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6); @@ -407,6 +413,7 @@ static void process_subneg(Telnet telnet) logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TTYPE \r\n"); break; @@ -421,7 +428,7 @@ static void process_subneg(Telnet telnet) logevent(telnet->frontend, logbuf); sfree(logbuf); if (telnet->sb_opt == TELOPT_OLD_ENVIRON) { - if (telnet->cfg.rfc_environ) { + if (conf_get_int(telnet->conf, CONF_rfc_environ)) { value = RFC_VALUE; var = RFC_VAR; } else { @@ -449,50 +456,75 @@ static void process_subneg(Telnet telnet) value = RFC_VALUE; var = RFC_VAR; } + bsize = 20; + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) + bsize += strlen(ekey) + strlen(eval) + 2; + user = get_remote_username(telnet->conf); + if (user) + bsize += 6 + strlen(user); + + b = snewn(bsize, unsigned char); b[0] = IAC; b[1] = SB; b[2] = telnet->sb_opt; b[3] = TELQUAL_IS; n = 4; - e = telnet->cfg.environmt; - while (*e) { + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { b[n++] = var; - while (*e && *e != '\t') - b[n++] = *e++; - if (*e == '\t') - e++; + for (e = ekey; *e; e++) + b[n++] = *e; b[n++] = value; - while (*e) - b[n++] = *e++; - e++; + for (e = eval; *e; e++) + b[n++] = *e; } - { - char user[sizeof(telnet->cfg.username)]; - (void) get_remote_username(&telnet->cfg, user, sizeof(user)); - if (*user) { - b[n++] = var; - b[n++] = 'U'; - b[n++] = 'S'; - b[n++] = 'E'; - b[n++] = 'R'; - b[n++] = value; - e = user; - while (*e) - b[n++] = *e++; - } - b[n++] = IAC; - b[n++] = SE; - telnet->bufsize = sk_write(telnet->s, (char *)b, n); - logbuf = dupprintf("client:\tSB %s IS %s%s%s%s", - telopt(telnet->sb_opt), - *user ? "USER=" : "", - user, - *user ? " " : "", - n == 6 ? "" : - (*telnet->cfg.environmt ? "" : "")); + if (user) { + b[n++] = var; + b[n++] = 'U'; + b[n++] = 'S'; + b[n++] = 'E'; + b[n++] = 'R'; + b[n++] = value; + for (e = user; *e; e++) + b[n++] = *e; + } + b[n++] = IAC; + b[n++] = SE; + telnet->bufsize = sk_write(telnet->s, (char *)b, n); + if (n == 6) { + logbuf = dupprintf("client:\tSB %s IS ", + telopt(telnet->sb_opt)); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } else { + logbuf = dupprintf("client:\tSB %s IS:", + telopt(telnet->sb_opt)); logevent(telnet->frontend, logbuf); sfree(logbuf); + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { + logbuf = dupprintf("\t%s=%s", ekey, eval); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } + if (user) { + logbuf = dupprintf("\tUSER=%s", user); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } } + sfree(b); + sfree(user); } break; } @@ -630,6 +662,7 @@ static void telnet_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(telnet->frontend, msg); + sfree(msg); } static int telnet_closing(Plug plug, const char *error_msg, int error_code, @@ -637,9 +670,17 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code, { Telnet telnet = (Telnet) plug; + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + if (telnet->s) { sk_close(telnet->s); telnet->s = NULL; + if (error_msg) + telnet->closed_on_socket_error = TRUE; notify_remote_exit(telnet->frontend); } if (error_msg) { @@ -674,9 +715,8 @@ static void telnet_sent(Plug plug, int bufsize) * freed by the caller. */ static const char *telnet_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, - int nodelay, int keepalive) + Conf *conf, char *host, int port, + char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { telnet_log, @@ -687,19 +727,22 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Telnet telnet; + char *loghost; + int addressfamily; telnet = snew(struct telnet_tag); telnet->fn = &fn_table; - telnet->cfg = *cfg; /* STRUCTURE COPY */ + telnet->conf = conf_copy(conf); telnet->s = NULL; + telnet->closed_on_socket_error = FALSE; telnet->echoing = TRUE; telnet->editing = TRUE; telnet->activated = FALSE; telnet->sb_buf = NULL; telnet->sb_size = 0; telnet->frontend = frontend_handle; - telnet->term_width = telnet->cfg.width; - telnet->term_height = telnet->cfg.height; + telnet->term_width = conf_get_int(telnet->conf, CONF_width); + telnet->term_height = conf_get_int(telnet->conf, CONF_height); telnet->state = TOP_LEVEL; telnet->ldisc = NULL; telnet->pinger = NULL; @@ -710,14 +753,15 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, */ { char *buf; + addressfamily = conf_get_int(telnet->conf, CONF_addressfamily); buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(telnet->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, telnet->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -730,16 +774,16 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, * Open socket. */ telnet->s = new_connection(addr, *realhost, port, 0, 1, - nodelay, keepalive, (Plug) telnet, &telnet->cfg); + nodelay, keepalive, (Plug) telnet, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) return err; - telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet); + telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet); /* * Initialise option states. */ - if (telnet->cfg.passive_telnet) { + if (conf_get_int(telnet->conf, CONF_passive_telnet)) { const struct Opt *const *o; for (o = opts; *o; o++) @@ -768,11 +812,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, /* * loghost overrides realhost, if specified. */ - if (*telnet->cfg.loghost) { + loghost = conf_get_str(telnet->conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(telnet->cfg.loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -796,6 +841,7 @@ static void telnet_free(void *handle) sk_close(telnet->s); if (telnet->pinger) pinger_free(telnet->pinger); + conf_free(telnet->conf); sfree(telnet); } /* @@ -803,11 +849,12 @@ static void telnet_free(void *handle) * necessary, in this backend: we just save the fresh config for * any subsequent negotiations. */ -static void telnet_reconfig(void *handle, Config *cfg) +static void telnet_reconfig(void *handle, Conf *conf) { Telnet telnet = (Telnet) handle; - pinger_reconfig(telnet->pinger, &telnet->cfg, cfg); - telnet->cfg = *cfg; /* STRUCTURE COPY */ + pinger_reconfig(telnet->pinger, telnet->conf, conf); + conf_free(telnet->conf); + telnet->conf = conf_copy(conf); } /* @@ -1055,6 +1102,8 @@ static int telnet_exitcode(void *handle) Telnet telnet = (Telnet) handle; if (telnet->s != NULL) return -1; /* still connected */ + else if (telnet->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* Telnet doesn't transmit exit codes back to the client */ return 0; diff --git a/contrib/putty/TERMINAL.C b/contrib/putty/TERMINAL.C index 77ddfb1..7af66ae 100644 --- a/contrib/putty/TERMINAL.C +++ b/contrib/putty/TERMINAL.C @@ -989,7 +989,7 @@ static void resizeline(Terminal *term, termline *line, int cols) static int sblines(Terminal *term) { int sblines = count234(term->scrollback); - if (term->cfg.erase_to_scrollback && + if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { sblines += term->alt_sblines; } @@ -1015,7 +1015,7 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) assert(!screen); - if (term->cfg.erase_to_scrollback && + if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { altlines = term->alt_sblines; } @@ -1065,32 +1065,32 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); -static void term_timer(void *ctx, long now) +static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; int update = FALSE; - if (term->tblink_pending && now - term->next_tblink >= 0) { + if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = FALSE; term_schedule_tblink(term); update = TRUE; } - if (term->cblink_pending && now - term->next_cblink >= 0) { + if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = FALSE; term_schedule_cblink(term); update = TRUE; } - if (term->in_vbell && now - term->vbell_end >= 0) { + if (term->in_vbell && now == term->vbell_end) { term->in_vbell = FALSE; update = TRUE; } if (update || - (term->window_update_pending && now - term->next_update >= 0)) + (term->window_update_pending && now == term->next_update)) term_update(term); } @@ -1133,7 +1133,7 @@ static void term_schedule_tblink(Terminal *term) */ static void term_schedule_cblink(Terminal *term) { - if (term->cfg.blink_cur && term->has_focus) { + if (term->blink_cur && term->has_focus) { if (!term->cblink_pending) term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term); term->cblink_pending = TRUE; @@ -1197,11 +1197,11 @@ static void power_on(Terminal *term, int clear) for (i = 0; i < term->cols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } - term->alt_om = term->dec_om = term->cfg.dec_om; + term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); term->alt_ins = term->insert = FALSE; term->alt_wnext = term->wrapnext = term->save_wnext = term->alt_save_wnext = FALSE; - term->alt_wrap = term->wrap = term->cfg.wrap_mode; + term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0; term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0; term->utf_state = 0; @@ -1216,19 +1216,22 @@ static void power_on(Terminal *term, int clear) term->default_attr = term->save_attr = term->alt_save_attr = term->curr_attr = ATTR_DEFAULT; term->term_editing = term->term_echoing = FALSE; - term->app_cursor_keys = term->cfg.app_cursor; - term->app_keypad_keys = term->cfg.app_keypad; - term->use_bce = term->cfg.bce; - term->blink_is_real = term->cfg.blinktext; + term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor); + term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad); + term->use_bce = conf_get_int(term->conf, CONF_bce); + term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); term->xterm_mouse = 0; + term->xterm_extended_mouse = 0; + term->urxvt_extended_mouse = 0; set_raw_mouse_mode(term->frontend, FALSE); + term->bracketed_paste = FALSE; { int i; for (i = 0; i < 256; i++) - term->wordness[i] = term->cfg.wordness[i]; + term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); } if (term->screen) { swap_screen(term, 1, FALSE, FALSE); @@ -1261,7 +1264,7 @@ void term_update(Terminal *term) ctx = get_ctx(term->frontend); if (ctx) { int need_sbar_update = term->seen_disp_event; - if (term->seen_disp_event && term->cfg.scroll_on_disp) { + if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = 0; need_sbar_update = TRUE; @@ -1300,7 +1303,7 @@ void term_seen_key_event(Terminal *term) /* * Reset the scrollback on keypress, if we're doing that. */ - if (term->cfg.scroll_on_key) { + if (term->scroll_on_key) { term->disptop = 0; /* return to main screen */ seen_disp_event(term); } @@ -1328,12 +1331,82 @@ static void set_erase_char(Terminal *term) } /* + * We copy a bunch of stuff out of the Conf structure into local + * fields in the Terminal structure, to avoid the repeated tree234 + * lookups which would be involved in fetching them from the former + * every time. + */ +void term_copy_stuff_from_conf(Terminal *term) +{ + term->ansi_colour = conf_get_int(term->conf, CONF_ansi_colour); + term->arabicshaping = conf_get_int(term->conf, CONF_arabicshaping); + term->beep = conf_get_int(term->conf, CONF_beep); + term->bellovl = conf_get_int(term->conf, CONF_bellovl); + term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n); + term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s); + term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t); + term->bidi = conf_get_int(term->conf, CONF_bidi); + term->bksp_is_delete = conf_get_int(term->conf, CONF_bksp_is_delete); + term->blink_cur = conf_get_int(term->conf, CONF_blink_cur); + term->blinktext = conf_get_int(term->conf, CONF_blinktext); + term->cjk_ambig_wide = conf_get_int(term->conf, CONF_cjk_ambig_wide); + term->conf_height = conf_get_int(term->conf, CONF_height); + term->conf_width = conf_get_int(term->conf, CONF_width); + term->crhaslf = conf_get_int(term->conf, CONF_crhaslf); + term->erase_to_scrollback = conf_get_int(term->conf, CONF_erase_to_scrollback); + term->funky_type = conf_get_int(term->conf, CONF_funky_type); + term->lfhascr = conf_get_int(term->conf, CONF_lfhascr); + term->logflush = conf_get_int(term->conf, CONF_logflush); + term->logtype = conf_get_int(term->conf, CONF_logtype); + term->mouse_override = conf_get_int(term->conf, CONF_mouse_override); + term->nethack_keypad = conf_get_int(term->conf, CONF_nethack_keypad); + term->no_alt_screen = conf_get_int(term->conf, CONF_no_alt_screen); + term->no_applic_c = conf_get_int(term->conf, CONF_no_applic_c); + term->no_applic_k = conf_get_int(term->conf, CONF_no_applic_k); + term->no_dbackspace = conf_get_int(term->conf, CONF_no_dbackspace); + term->no_mouse_rep = conf_get_int(term->conf, CONF_no_mouse_rep); + term->no_remote_charset = conf_get_int(term->conf, CONF_no_remote_charset); + term->no_remote_resize = conf_get_int(term->conf, CONF_no_remote_resize); + term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle); + term->rawcnp = conf_get_int(term->conf, CONF_rawcnp); + term->rect_select = conf_get_int(term->conf, CONF_rect_select); + term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); + term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend); + term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp); + term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key); + term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour); + + /* + * Parse the control-character escapes in the configured + * answerback string. + */ + { + char *answerback = conf_get_str(term->conf, CONF_answerback); + int maxlen = strlen(answerback); + + term->answerback = snewn(maxlen, char); + term->answerbacklen = 0; + + while (*answerback) { + char *n; + char c = ctrlparse(answerback, &n); + if (n) { + term->answerback[term->answerbacklen++] = c; + answerback = n; + } else { + term->answerback[term->answerbacklen++] = *answerback++; + } + } + } +} + +/* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the * user has disabled mouse reporting, and abandon a print job if * the user has disabled printing. */ -void term_reconfig(Terminal *term, Config *cfg) +void term_reconfig(Terminal *term, Conf *conf) { /* * Before adopting the new config, check all those terminal @@ -1345,21 +1418,28 @@ void term_reconfig(Terminal *term, Config *cfg) int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; int i; - reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode); - reset_decom = (term->cfg.dec_om != cfg->dec_om); - reset_bce = (term->cfg.bce != cfg->bce); - reset_tblink = (term->cfg.blinktext != cfg->blinktext); + reset_wrap = (conf_get_int(term->conf, CONF_wrap_mode) != + conf_get_int(conf, CONF_wrap_mode)); + reset_decom = (conf_get_int(term->conf, CONF_dec_om) != + conf_get_int(conf, CONF_dec_om)); + reset_bce = (conf_get_int(term->conf, CONF_bce) != + conf_get_int(conf, CONF_bce)); + reset_tblink = (conf_get_int(term->conf, CONF_blinktext) != + conf_get_int(conf, CONF_blinktext)); reset_charclass = 0; - for (i = 0; i < lenof(term->cfg.wordness); i++) - if (term->cfg.wordness[i] != cfg->wordness[i]) + for (i = 0; i < 256; i++) + if (conf_get_int_int(term->conf, CONF_wordness, i) != + conf_get_int_int(conf, CONF_wordness, i)) reset_charclass = 1; /* * If the bidi or shaping settings have changed, flush the bidi * cache completely. */ - if (term->cfg.arabicshaping != cfg->arabicshaping || - term->cfg.bidi != cfg->bidi) { + if (conf_get_int(term->conf, CONF_arabicshaping) != + conf_get_int(conf, CONF_arabicshaping) || + conf_get_int(term->conf, CONF_bidi) != + conf_get_int(conf, CONF_bidi)) { for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); @@ -1370,39 +1450,41 @@ void term_reconfig(Terminal *term, Config *cfg) } } - term->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(term->conf); + term->conf = conf_copy(conf); if (reset_wrap) - term->alt_wrap = term->wrap = term->cfg.wrap_mode; + term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); if (reset_decom) - term->alt_om = term->dec_om = term->cfg.dec_om; + term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); if (reset_bce) { - term->use_bce = term->cfg.bce; + term->use_bce = conf_get_int(term->conf, CONF_bce); set_erase_char(term); } if (reset_tblink) { - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); } if (reset_charclass) for (i = 0; i < 256; i++) - term->wordness[i] = term->cfg.wordness[i]; + term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); - if (term->cfg.no_alt_screen) + if (conf_get_int(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, FALSE, FALSE); - if (term->cfg.no_mouse_rep) { + if (conf_get_int(term->conf, CONF_no_mouse_rep)) { term->xterm_mouse = 0; set_raw_mouse_mode(term->frontend, 0); } - if (term->cfg.no_remote_charset) { + if (conf_get_int(term->conf, CONF_no_remote_charset)) { term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII; term->sco_acs = term->alt_sco_acs = 0; term->utf = 0; } - if (!*term->cfg.printer) { + if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } term_schedule_tblink(term); term_schedule_cblink(term); + term_copy_stuff_from_conf(term); } /* @@ -1423,7 +1505,7 @@ void term_clrsb(Terminal *term) /* * Initialise the terminal. */ -Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, +Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, void *frontend) { Terminal *term; @@ -1435,7 +1517,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, term = snew(Terminal); term->frontend = frontend; term->ucsdata = ucsdata; - term->cfg = *mycfg; /* STRUCTURE COPY */ + term->conf = conf_copy(myconf); term->logctx = NULL; term->compatibility_level = TM_PUTTY; strcpy(term->id_string, "\033[?6c"); @@ -1459,6 +1541,8 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, term->selstate = NO_SELECTION; term->curstype = 0; + term_copy_stuff_from_conf(term); + term->screen = term->alt_screen = term->scrollback = NULL; term->tempsblines = 0; term->alt_sblines = 0; @@ -1537,12 +1621,18 @@ void term_free(Terminal *term) for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); + sfree(term->post_bidi_cache[i].forward); + sfree(term->post_bidi_cache[i].backward); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); + sfree(term->tabs); + expire_timer_context(term); + conf_free(term->conf); + sfree(term); } @@ -1627,7 +1717,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) while (term->rows > newrows) { if (term->curs.y < term->rows - 1) { /* delete bottom row, unless it contains the cursor */ - sfree(delpos234(term->screen, term->rows - 1)); + line = delpos234(term->screen, term->rows - 1); + freeline(line); } else { /* push top row to scrollback */ line = delpos234(term->screen, 0); @@ -1889,7 +1980,7 @@ static void check_selection(Terminal *term, pos from, pos to) static void scroll(Terminal *term, int topline, int botline, int lines, int sb) { termline *line; - int i, seltop; + int i, seltop, scrollwinsize; #ifdef OPTIMISE_SCROLL int olddisptop, shift; #endif /* OPTIMISE_SCROLL */ @@ -1901,8 +1992,14 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) olddisptop = term->disptop; shift = lines; #endif /* OPTIMISE_SCROLL */ + + scrollwinsize = botline - topline + 1; + if (lines < 0) { - while (lines < 0) { + lines = -lines; + if (lines > scrollwinsize) + lines = scrollwinsize; + while (lines-- > 0) { line = delpos234(term->screen, botline); resizeline(term, line, term->cols); for (i = 0; i < term->cols; i++) @@ -1924,11 +2021,11 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) term->selend.x = 0; } } - - lines++; } } else { - while (lines > 0) { + if (lines > scrollwinsize) + lines = scrollwinsize; + while (lines-- > 0) { line = delpos234(term->screen, topline); #ifdef TERM_CC_DIAGS cc_check(line); @@ -2014,8 +2111,6 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) } } } - - lines--; } } #ifdef OPTIMISE_SCROLL @@ -2243,7 +2338,7 @@ static void erase_lots(Terminal *term, if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; - if (term->cfg.erase_to_scrollback && erasing_lines_from_top) { + if (term->erase_to_scrollback && erasing_lines_from_top) { /* If it's a whole number of lines, starting at the top, and * we're fully erasing them, erase by scrolling and keep the * lines in the scrollback. */ @@ -2334,13 +2429,13 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = term->blinktext; } term_schedule_tblink(term); break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, state ? 132 : 80, term->rows); term->reset_132 = state; term->alt_t = term->marg_t = 0; @@ -2387,7 +2482,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 47: /* alternate screen */ compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE); + swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE); term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ @@ -2398,28 +2493,37 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) term->xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(term->frontend, state); break; + case 1006: /* xterm extended mouse */ + term->xterm_extended_mouse = state ? 1 : 0; + break; + case 1015: /* urxvt extended mouse */ + term->urxvt_extended_mouse = state ? 1 : 0; + break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE); + swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE); term->disptop = 0; break; case 1048: /* save/restore cursor */ - if (!term->cfg.no_alt_screen) + if (!term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); break; case 1049: /* cursor & alternate screen */ - if (state && !term->cfg.no_alt_screen) + if (state && !term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE); - if (!state && !term->cfg.no_alt_screen) + swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE); + if (!state && !term->no_alt_screen) save_cursor(term, state); term->disptop = 0; break; + case 2004: /* xterm bracketed paste */ + term->bracketed_paste = state ? TRUE : FALSE; + break; } else switch (mode) { case 4: /* IRM: set insert mode */ @@ -2454,14 +2558,14 @@ static void do_osc(Terminal *term) switch (term->esc_args[0]) { case 0: case 1: - if (!term->cfg.no_remote_wintitle) + if (!term->no_remote_wintitle) set_icon(term->frontend, term->osc_string); if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: - if (!term->cfg.no_remote_wintitle) + if (!term->no_remote_wintitle) set_title(term->frontend, term->osc_string); break; } @@ -2471,10 +2575,10 @@ static void do_osc(Terminal *term) /* * ANSI printing routines. */ -static void term_print_setup(Terminal *term) +static void term_print_setup(Terminal *term, char *printer) { bufchain_clear(&term->printer_buf); - term->print_job = printer_start_job(term->cfg.printer); + term->print_job = printer_start_job(printer); } static void term_print_flush(Terminal *term) { @@ -2549,7 +2653,7 @@ static void term_out(Terminal *term) * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ - if (term->cfg.logtype == LGTYP_DEBUG && term->logctx) + if (term->logtype == LGTYP_DEBUG && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG); } else { c = unget; @@ -2741,7 +2845,7 @@ static void term_out(Terminal *term) term->curs.x--; term->wrapnext = FALSE; /* destructive backspace might be disabled */ - if (!term->cfg.no_dbackspace) { + if (!term->no_dbackspace) { check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); copy_termchar(scrlineptr(term->curs.y), @@ -2761,19 +2865,8 @@ static void term_out(Terminal *term) */ compatibility(ANSIMIN); if (term->ldisc) { - char abuf[lenof(term->cfg.answerback)], *s, *d; - for (s = term->cfg.answerback, d = abuf; *s;) { - char *n; - char c = ctrlparse(s, &n); - if (n) { - *d++ = c; - s = n; - } else { - *d++ = *s++; - } - } lpage_send(term->ldisc, DEFAULT_CODEPAGE, - abuf, d - abuf, 0); + term->answerback, term->answerbacklen, 0); } break; case '\007': /* BEL: Bell */ @@ -2800,7 +2893,7 @@ static void term_out(Terminal *term) * t seconds ago. */ while (term->beephead && - term->beephead->ticks < ticks - term->cfg.bellovl_t) { + term->beephead->ticks < ticks - term->bellovl_t) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); @@ -2809,16 +2902,16 @@ static void term_out(Terminal *term) term->nbeeps--; } - if (term->cfg.bellovl && term->beep_overloaded && - ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) { + if (term->bellovl && term->beep_overloaded && + ticks - term->lastbeep >= (unsigned)term->bellovl_s) { /* * If we're currently overloaded and the * last beep was more than s seconds ago, * leave overload mode. */ term->beep_overloaded = FALSE; - } else if (term->cfg.bellovl && !term->beep_overloaded && - term->nbeeps >= term->cfg.bellovl_n) { + } else if (term->bellovl && !term->beep_overloaded && + term->nbeeps >= term->bellovl_n) { /* * Now, if we have n or more beeps * remaining in the queue, go into overload @@ -2831,10 +2924,10 @@ static void term_out(Terminal *term) /* * Perform an actual beep if we're not overloaded. */ - if (!term->cfg.bellovl || !term->beep_overloaded) { - do_beep(term->frontend, term->cfg.beep); + if (!term->bellovl || !term->beep_overloaded) { + do_beep(term->frontend, term->beep); - if (term->cfg.beep == BELL_VISUAL) { + if (term->beep == BELL_VISUAL) { term_schedule_vbell(term, FALSE, 0); } } @@ -2876,12 +2969,12 @@ static void term_out(Terminal *term) 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->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; @@ -2901,7 +2994,7 @@ static void term_out(Terminal *term) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; - if (term->cfg.lfhascr) + if (term->lfhascr) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); @@ -2943,9 +3036,9 @@ static void term_out(Terminal *term) if (DIRECT_CHAR(c)) width = 1; if (!width) - width = (term->cfg.cjk_ambig_wide ? - mk_wcwidth_cjk((wchar_t) c) : - mk_wcwidth((wchar_t) c)); + width = (term->cjk_ambig_wide ? + mk_wcwidth_cjk((unsigned int) c) : + mk_wcwidth((unsigned int) c)); if (term->wrapnext && term->wrap && width > 0) { cline->lattr |= LATTR_WRAPPED; @@ -3166,7 +3259,7 @@ static void term_out(Terminal *term) if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); if (term->reset_132) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); term->reset_132 = 0; } @@ -3231,55 +3324,55 @@ static void term_out(Terminal *term) /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_GBCHR; break; case ANSI('B', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_ASCII; break; case ANSI('0', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_LINEDRW; break; case ANSI('U', '('): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_SCOACS; break; /* G1D4: G1-designate 94-set */ case ANSI('A', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_GBCHR; break; case ANSI('B', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_ASCII; break; case ANSI('0', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_LINEDRW; break; case ANSI('U', ')'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_SCOACS; break; /* DOCS: Designate other coding system */ case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->utf = 1; break; case ANSI('@', '%'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->utf = 0; break; } @@ -3463,12 +3556,15 @@ static void term_out(Terminal *term) case ANSI_QUE('i'): compatibility(VT100); { + char *printer; if (term->esc_nargs != 1) break; - if (term->esc_args[0] == 5 && *term->cfg.printer) { + if (term->esc_args[0] == 5 && + (printer = conf_get_str(term->conf, + CONF_printer))[0]) { term->printing = TRUE; term->only_printing = !term->esc_query; term->print_state = 0; - term_print_setup(term); + term_print_setup(term, printer); } else if (term->esc_args[0] == 4 && term->printing) { term_print_finish(term); @@ -3591,15 +3687,15 @@ static void term_out(Terminal *term) break; case 10: /* SCO acs off */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 0; break; case 11: /* SCO acs on */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 1; break; case 12: /* SCO acs on, |0x80 */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); @@ -3722,7 +3818,7 @@ static void term_out(Terminal *term) && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], 24)); deselect(term); @@ -3742,7 +3838,7 @@ static void term_out(Terminal *term) break; case 3: if (term->esc_nargs >= 3) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) move_window(term->frontend, def(term->esc_args[1], 0), def(term->esc_args[2], 0)); @@ -3767,10 +3863,10 @@ static void term_out(Terminal *term) break; case 8: if (term->esc_nargs >= 3) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, - def(term->esc_args[2], term->cfg.width), - def(term->esc_args[1], term->cfg.height)); + def(term->esc_args[2], term->conf_width), + def(term->esc_args[1], term->conf_height)); } break; case 9: @@ -3825,8 +3921,8 @@ static void term_out(Terminal *term) break; case 20: if (term->ldisc && - term->cfg.remote_qtitle_action != TITLE_NONE) { - if(term->cfg.remote_qtitle_action == TITLE_REAL) + term->remote_qtitle_action != TITLE_NONE) { + if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, TRUE); else p = EMPTY_WINDOW_TITLE; @@ -3838,8 +3934,8 @@ static void term_out(Terminal *term) break; case 21: if (term->ldisc && - term->cfg.remote_qtitle_action != TITLE_NONE) { - if(term->cfg.remote_qtitle_action == TITLE_REAL) + term->remote_qtitle_action != TITLE_NONE) { + if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, FALSE); else p = EMPTY_WINDOW_TITLE; @@ -3875,10 +3971,10 @@ static void term_out(Terminal *term) */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], - term->cfg.height)); + term->conf_height)); deselect(term); } break; @@ -3890,10 +3986,11 @@ static void term_out(Terminal *term) */ compatibility(VT340TEXT); if (term->esc_nargs <= 1) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[0], - term->cfg.width), term->rows); + term->conf_width), + term->rows); deselect(term); } break; @@ -4095,7 +4192,7 @@ static void term_out(Terminal *term) * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { - if (!term->cfg.no_remote_resize) { + if (!term->no_remote_resize) { if (term->reset_132) request_resize(132, 24); else @@ -4330,7 +4427,7 @@ static void term_out(Terminal *term) * emulation. */ term->vt52_mode = FALSE; - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = term->blinktext; term_schedule_tblink(term); break; #if 0 @@ -4474,7 +4571,7 @@ static void term_out(Terminal *term) } term_print_flush(term); - if (term->cfg.logflush) + if (term->logflush) logflush(term->logctx); } @@ -4577,7 +4674,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int it; /* Do Arabic shaping and bidi. */ - if(!term->cfg.bidi || !term->cfg.arabicshaping) { + if(!term->bidi || !term->arabicshaping) { if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) { @@ -4595,7 +4692,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, switch (uc & CSET_MASK) { case CSET_LINEDRW: - if (!term->cfg.rawcnp) { + if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } @@ -4616,19 +4713,19 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, } term->wcFrom[it].origwc = term->wcFrom[it].wc = - (wchar_t)uc; + (unsigned int)uc; term->wcFrom[it].index = it; } - if(!term->cfg.bidi) + if(!term->bidi) do_bidi(term->wcFrom, term->cols); /* this is saved iff done from inside the shaping */ - if(!term->cfg.bidi && term->cfg.arabicshaping) + if(!term->bidi && term->arabicshaping) for(it=0; itcols; it++) term->wcTo[it] = term->wcFrom[it]; - if(!term->cfg.arabicshaping) + if(!term->arabicshaping) do_shape(term->wcFrom, term->wcTo, term->cols); if (term->ltemp_size < ldata->size) { @@ -4690,14 +4787,14 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) /* Depends on: * screen array, disptop, scrtop, * selection, rv, - * cfg.blinkpc, blink_is_real, tblinker, - * curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext + * blinkpc, blink_is_real, tblinker, + * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext */ /* Has the cursor position or type changed ? */ if (term->cursor_on) { if (term->has_focus) { - if (term->cblinker || !term->cfg.blink_cur) + if (term->cblinker || !term->blink_cur) cursor = TATTR_ACTCURS; else cursor = 0; @@ -4805,11 +4902,11 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) tchar = d->chr; tattr = d->attr; - if (!term->cfg.ansi_colour) + if (!term->ansi_colour) tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | ATTR_DEFFG | ATTR_DEFBG; - if (!term->cfg.xterm_256_colour) { + if (!term->xterm_256_colour) { int colour; colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT; if (colour >= 16 && colour < 256) @@ -4939,11 +5036,13 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) break_run = ((tattr ^ attr) & term->attr_mask) != 0; +#ifdef USES_VTLINE_HACK /* Special hack for VT100 Linedraw glyphs */ if ((tchar >= 0x23BA && tchar <= 0x23BD) || (j > 0 && (newline[j-1].chr >= 0x23BA && newline[j-1].chr <= 0x23BD))) break_run = TRUE; +#endif /* * Separate out sequences of characters that have the @@ -4991,10 +5090,17 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) dirty_run = TRUE; } - if (ccount >= chlen) { + if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } + +#ifdef PLATFORM_IS_UTF16 + if (tchar > 0x10000 && tchar < 0x110000) { + ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(tchar); + ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(tchar); + } else +#endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) tchar; if (d->cc_next) { @@ -5018,10 +5124,17 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) break; } - if (ccount >= chlen) { + if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } + +#ifdef PLATFORM_IS_UTF16 + if (schar > 0x10000 && schar < 0x110000) { + ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(schar); + ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(schar); + } else +#endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) schar; } @@ -5267,7 +5380,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) switch (uc & CSET_MASK) { case CSET_LINEDRW: - if (!term->cfg.rawcnp) { + if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } @@ -5521,7 +5634,8 @@ static pos sel_spread_half(Terminal *term, pos p, int dir) else break; } else { - if (ldata->lattr & LATTR_WRAPPED) { + if (p.y+1 < term->rows && + (ldata->lattr & LATTR_WRAPPED)) { termline *ldata2; ldata2 = lineptr(p.y+1); if (wordtype(term, UCSGET(ldata2->chars, 0)) @@ -5606,7 +5720,12 @@ void term_do_paste(Terminal *term) if (term->paste_buffer) sfree(term->paste_buffer); term->paste_pos = term->paste_hold = term->paste_len = 0; - term->paste_buffer = snewn(len, wchar_t); + term->paste_buffer = snewn(len + 12, wchar_t); + + if (term->bracketed_paste) { + memcpy(term->paste_buffer, L"\033[200~", 6 * sizeof(wchar_t)); + term->paste_len += 6; + } p = q = data; while (p < data + len) { @@ -5630,6 +5749,12 @@ void term_do_paste(Terminal *term) q = p; } + if (term->bracketed_paste) { + memcpy(term->paste_buffer + term->paste_len, + L"\033[201~", 6 * sizeof(wchar_t)); + term->paste_len += 6; + } + /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) @@ -5649,8 +5774,8 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, pos selpoint; termline *ldata; int raw_mouse = (term->xterm_mouse && - !term->cfg.no_mouse_rep && - !(term->cfg.mouse_override && shift)); + !term->no_mouse_rep && + !(term->mouse_override && shift)); int default_seltype; if (y < 0) { @@ -5701,25 +5826,26 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c; - char abuf[16]; + char abuf[32]; + int len = 0; if (term->ldisc) { switch (braw) { case MBT_LEFT: - encstate = 0x20; /* left button down */ + encstate = 0x00; /* left button down */ break; case MBT_MIDDLE: - encstate = 0x21; + encstate = 0x01; break; case MBT_RIGHT: - encstate = 0x22; + encstate = 0x02; break; case MBT_WHEEL_UP: - encstate = 0x60; + encstate = 0x40; break; case MBT_WHEEL_DOWN: - encstate = 0x61; + encstate = 0x41; break; default: break; /* placate gcc warning about enum use */ } @@ -5730,7 +5856,9 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate += 0x20; break; case MA_RELEASE: - encstate = 0x23; + /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */ + if (!term->xterm_extended_mouse) + encstate = 0x03; term->mouse_is_down = 0; break; case MA_CLICK: @@ -5744,11 +5872,18 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate += 0x04; if (ctrl) encstate += 0x10; - r = y + 33; - c = x + 33; - - sprintf(abuf, "\033[M%c%c%c", encstate, c, r); - ldisc_send(term->ldisc, abuf, 6, 0); + r = y + 1; + c = x + 1; + + /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */ + if (term->xterm_extended_mouse) { + len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M'); + } else if (term->urxvt_extended_mouse) { + len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r); + } else if (c <= 223 && r <= 223) { + len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32); + } + ldisc_send(term->ldisc, abuf, len, 0); } return; } @@ -5757,7 +5892,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, * Set the selection type (rectangular or normal) at the start * of a selection attempt, from the state of Alt. */ - if (!alt ^ !term->cfg.rect_select) + if (!alt ^ !term->rect_select) default_seltype = RECTANGULAR; else default_seltype = LEXICOGRAPHIC; @@ -5867,6 +6002,13 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, request_paste(term->frontend); } + /* + * Since terminal output is suppressed during drag-selects, we + * should make sure to write any pending output if one has just + * finished. + */ + if (term->selstate != DRAGGING) + term_out(term); term_update(term); } @@ -5877,7 +6019,7 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", xkey); else { - int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c); + int app_flg = (term->app_cursor_keys && !term->no_applic_c); #if 0 /* * RDB: VT100 & VT102 manuals both state the app cursor @@ -5906,433 +6048,6 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) return p - buf; } -void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen, - unsigned int modifiers, unsigned int flags) -{ - char output[10]; - char *p = output; - int prependesc = FALSE; -#if 0 - int i; - - fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen); - for (i = 0; i < tlen; i++) - fprintf(stderr, " %04x", (unsigned)text[i]); - fprintf(stderr, "\n"); -#endif - - /* XXX Num Lock */ - if ((flags & PKF_REPEAT) && term->repeat_off) - return; - - /* Currently, Meta always just prefixes everything with ESC. */ - if (modifiers & PKM_META) - prependesc = TRUE; - modifiers &= ~PKM_META; - - /* - * Alt is only used for Alt+keypad, which isn't supported yet, so - * ignore it. - */ - modifiers &= ~PKM_ALT; - - /* Standard local function keys */ - switch (modifiers & (PKM_SHIFT | PKM_CONTROL)) { - case PKM_SHIFT: - if (keysym == PK_PAGEUP) - /* scroll up one page */; - if (keysym == PK_PAGEDOWN) - /* scroll down on page */; - if (keysym == PK_INSERT) - term_do_paste(term); - break; - case PKM_CONTROL: - if (keysym == PK_PAGEUP) - /* scroll up one line */; - if (keysym == PK_PAGEDOWN) - /* scroll down one line */; - /* Control-Numlock for app-keypad mode switch */ - if (keysym == PK_PF1) - term->app_keypad_keys ^= 1; - break; - } - - if (modifiers & PKM_ALT) { - /* Alt+F4 (close) */ - /* Alt+Return (full screen) */ - /* Alt+Space (system menu) */ - } - - if (keysym == PK_NULL && (modifiers & PKM_CONTROL) && tlen == 1 && - text[0] >= 0x20 && text[0] <= 0x7e) { - /* ASCII chars + Control */ - if ((text[0] >= 0x40 && text[0] <= 0x5f) || - (text[0] >= 0x61 && text[0] <= 0x7a)) - text[0] &= 0x1f; - else { - /* - * Control-2 should return ^@ (0x00), Control-6 should return - * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since - * the DOS keyboard handling did it, and we have nothing better - * to do with the key combo in question, we'll also map - * Control-Backquote to ^\ (0x1C). - */ - switch (text[0]) { - case ' ': text[0] = 0x00; break; - case '-': text[0] = 0x1f; break; - case '/': text[0] = 0x1f; break; - case '2': text[0] = 0x00; break; - case '3': text[0] = 0x1b; break; - case '4': text[0] = 0x1c; break; - case '5': text[0] = 0x1d; break; - case '6': text[0] = 0x1e; break; - case '7': text[0] = 0x1f; break; - case '8': text[0] = 0x7f; break; - case '`': text[0] = 0x1c; break; - } - } - } - - /* Nethack keypad */ - if (term->cfg.nethack_keypad) { - char c = 0; - switch (keysym) { - case PK_KP1: c = 'b'; break; - case PK_KP2: c = 'j'; break; - case PK_KP3: c = 'n'; break; - case PK_KP4: c = 'h'; break; - case PK_KP5: c = '.'; break; - case PK_KP6: c = 'l'; break; - case PK_KP7: c = 'y'; break; - case PK_KP8: c = 'k'; break; - case PK_KP9: c = 'u'; break; - default: break; /* else gcc warns `enum value not used' */ - } - if (c != 0) { - if (c != '.') { - if (modifiers & PKM_CONTROL) - c &= 0x1f; - else if (modifiers & PKM_SHIFT) - c = toupper((unsigned char)c); - } - *p++ = c; - goto done; - } - } - - /* Numeric Keypad */ - if (PK_ISKEYPAD(keysym)) { - int xkey = 0; - - /* - * In VT400 mode, PFn always emits an escape sequence. In - * Linux and tilde modes, this only happens in app keypad mode. - */ - if (term->cfg.funky_type == FUNKY_VT400 || - ((term->cfg.funky_type == FUNKY_LINUX || - term->cfg.funky_type == FUNKY_TILDE) && - term->app_keypad_keys && !term->cfg.no_applic_k)) { - switch (keysym) { - case PK_PF1: xkey = 'P'; break; - case PK_PF2: xkey = 'Q'; break; - case PK_PF3: xkey = 'R'; break; - case PK_PF4: xkey = 'S'; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - if (term->app_keypad_keys && !term->cfg.no_applic_k) { - switch (keysym) { - case PK_KP0: xkey = 'p'; break; - case PK_KP1: xkey = 'q'; break; - case PK_KP2: xkey = 'r'; break; - case PK_KP3: xkey = 's'; break; - case PK_KP4: xkey = 't'; break; - case PK_KP5: xkey = 'u'; break; - case PK_KP6: xkey = 'v'; break; - case PK_KP7: xkey = 'w'; break; - case PK_KP8: xkey = 'x'; break; - case PK_KP9: xkey = 'y'; break; - case PK_KPDECIMAL: xkey = 'n'; break; - case PK_KPENTER: xkey = 'M'; break; - default: break; /* else gcc warns `enum value not used' */ - } - if (term->cfg.funky_type == FUNKY_XTERM && tlen > 0) { - /* - * xterm can't see the layout of the keypad, so it has - * to rely on the X keysyms returned by the keys. - * Hence, we look at the strings here, not the PuTTY - * keysyms (which describe the layout). - */ - switch (text[0]) { - case '+': - if (modifiers & PKM_SHIFT) - xkey = 'l'; - else - xkey = 'k'; - break; - case '/': xkey = 'o'; break; - case '*': xkey = 'j'; break; - case '-': xkey = 'm'; break; - } - } else { - /* - * In all other modes, we try to retain the layout of - * the DEC keypad in application mode. - */ - switch (keysym) { - case PK_KPBIGPLUS: - /* This key covers the '-' and ',' keys on a VT220 */ - if (modifiers & PKM_SHIFT) - xkey = 'm'; /* VT220 '-' */ - else - xkey = 'l'; /* VT220 ',' */ - break; - case PK_KPMINUS: xkey = 'm'; break; - case PK_KPCOMMA: xkey = 'l'; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - } - if (xkey) { - if (term->vt52_mode) { - if (xkey >= 'P' && xkey <= 'S') - p += sprintf((char *) p, "\x1B%c", xkey); - else - p += sprintf((char *) p, "\x1B?%c", xkey); - } else - p += sprintf((char *) p, "\x1BO%c", xkey); - goto done; - } - /* Not in application mode -- treat the number pad as arrow keys? */ - if ((flags & PKF_NUMLOCK) == 0) { - switch (keysym) { - case PK_KP0: keysym = PK_INSERT; break; - case PK_KP1: keysym = PK_END; break; - case PK_KP2: keysym = PK_DOWN; break; - case PK_KP3: keysym = PK_PAGEDOWN; break; - case PK_KP4: keysym = PK_LEFT; break; - case PK_KP5: keysym = PK_REST; break; - case PK_KP6: keysym = PK_RIGHT; break; - case PK_KP7: keysym = PK_HOME; break; - case PK_KP8: keysym = PK_UP; break; - case PK_KP9: keysym = PK_PAGEUP; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - } - - /* Miscellaneous keys */ - switch (keysym) { - case PK_ESCAPE: - *p++ = 0x1b; - goto done; - case PK_BACKSPACE: - if (modifiers == 0) - *p++ = (term->cfg.bksp_is_delete ? 0x7F : 0x08); - else if (modifiers == PKM_SHIFT) - /* We do the opposite of what is configured */ - *p++ = (term->cfg.bksp_is_delete ? 0x08 : 0x7F); - else break; - goto done; - case PK_TAB: - if (modifiers == 0) - *p++ = 0x09; - else if (modifiers == PKM_SHIFT) - *p++ = 0x1B, *p++ = '[', *p++ = 'Z'; - else break; - goto done; - /* XXX window.c has ctrl+shift+space sending 0xa0 */ - case PK_PAUSE: - if (modifiers == PKM_CONTROL) - *p++ = 26; - else break; - goto done; - case PK_RETURN: - case PK_KPENTER: /* Odd keypad modes handled above */ - if (modifiers == 0) { - *p++ = 0x0d; - if (term->cr_lf_return) - *p++ = 0x0a; - goto done; - } - default: break; /* else gcc warns `enum value not used' */ - } - - /* SCO function keys and editing keys */ - if (term->cfg.funky_type == FUNKY_SCO) { - if (PK_ISFKEY(keysym) && keysym <= PK_F12) { - static char const codes[] = - "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{"; - int index = keysym - PK_F1; - - if (modifiers & PKM_SHIFT) index += 12; - if (modifiers & PKM_CONTROL) index += 24; - p += sprintf((char *) p, "\x1B[%c", codes[index]); - goto done; - } - if (PK_ISEDITING(keysym)) { - int xkey = 0; - - switch (keysym) { - case PK_DELETE: *p++ = 0x7f; goto done; - case PK_HOME: xkey = 'H'; break; - case PK_INSERT: xkey = 'L'; break; - case PK_END: xkey = 'F'; break; - case PK_PAGEUP: xkey = 'I'; break; - case PK_PAGEDOWN: xkey = 'G'; break; - default: break; /* else gcc warns `enum value not used' */ - } - p += sprintf((char *) p, "\x1B[%c", xkey); - } - } - - if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) { - int code; - - if (term->cfg.funky_type == FUNKY_XTERM) { - /* Xterm shuffles these keys, apparently. */ - switch (keysym) { - case PK_HOME: keysym = PK_INSERT; break; - case PK_INSERT: keysym = PK_HOME; break; - case PK_DELETE: keysym = PK_END; break; - case PK_END: keysym = PK_PAGEUP; break; - case PK_PAGEUP: keysym = PK_DELETE; break; - case PK_PAGEDOWN: keysym = PK_PAGEDOWN; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - - /* RXVT Home/End */ - if (term->cfg.rxvt_homeend && - (keysym == PK_HOME || keysym == PK_END)) { - p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw"); - goto done; - } - - if (term->vt52_mode) { - int xkey; - - /* - * A real VT52 doesn't have these, and a VT220 doesn't - * send anything for them in VT52 mode. - */ - switch (keysym) { - case PK_HOME: xkey = 'H'; break; - case PK_INSERT: xkey = 'L'; break; - case PK_DELETE: xkey = 'M'; break; - case PK_END: xkey = 'E'; break; - case PK_PAGEUP: xkey = 'I'; break; - case PK_PAGEDOWN: xkey = 'G'; break; - default: xkey=0; break; /* else gcc warns `enum value not used'*/ - } - p += sprintf((char *) p, "\x1B%c", xkey); - goto done; - } - - switch (keysym) { - case PK_HOME: code = 1; break; - case PK_INSERT: code = 2; break; - case PK_DELETE: code = 3; break; - case PK_END: code = 4; break; - case PK_PAGEUP: code = 5; break; - case PK_PAGEDOWN: code = 6; break; - default: code = 0; break; /* else gcc warns `enum value not used' */ - } - p += sprintf((char *) p, "\x1B[%d~", code); - goto done; - } - - if (PK_ISFKEY(keysym)) { - /* Map Shift+F1-F10 to F11-F20 */ - if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT)) - keysym += 10; - if ((term->vt52_mode || term->cfg.funky_type == FUNKY_VT100P) && - keysym <= PK_F14) { - /* XXX This overrides the XTERM/VT52 mode below */ - int offt = 0; - if (keysym >= PK_F6) offt++; - if (keysym >= PK_F12) offt++; - p += sprintf((char *) p, term->vt52_mode ? "\x1B%c" : "\x1BO%c", - 'P' + keysym - PK_F1 - offt); - goto done; - } - if (term->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) { - p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1); - goto done; - } - if (term->cfg.funky_type == FUNKY_XTERM && keysym <= PK_F4) { - if (term->vt52_mode) - p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1); - else - p += sprintf((char *) p, "\x1BO%c", 'P' + keysym - PK_F1); - goto done; - } - p += sprintf((char *) p, "\x1B[%d~", 11 + keysym - PK_F1); - goto done; - } - - if (PK_ISCURSOR(keysym)) { - int xkey; - - switch (keysym) { - case PK_UP: xkey = 'A'; break; - case PK_DOWN: xkey = 'B'; break; - case PK_RIGHT: xkey = 'C'; break; - case PK_LEFT: xkey = 'D'; break; - case PK_REST: xkey = 'G'; break; /* centre key on number pad */ - default: xkey = 0; break; /* else gcc warns `enum value not used' */ - } - p += format_arrow_key(p, term, xkey, modifiers == PKM_CONTROL); - goto done; - } - - done: - if (p > output || tlen > 0) { - /* - * Interrupt an ongoing paste. I'm not sure - * this is sensible, but for the moment it's - * preferable to having to faff about buffering - * things. - */ - term_nopaste(term); - - /* - * We need not bother about stdin backlogs - * here, because in GUI PuTTY we can't do - * anything about it anyway; there's no means - * of asking Windows to hold off on KEYDOWN - * messages. We _have_ to buffer everything - * we're sent. - */ - term_seen_key_event(term); - - if (prependesc) { -#if 0 - fprintf(stderr, "sending ESC\n"); -#endif - ldisc_send(term->ldisc, "\x1b", 1, 1); - } - - if (p > output) { -#if 0 - fprintf(stderr, "sending %d bytes:", p - output); - for (i = 0; i < p - output; i++) - fprintf(stderr, " %02x", output[i]); - fprintf(stderr, "\n"); -#endif - ldisc_send(term->ldisc, output, p - output, 1); - } else if (tlen > 0) { -#if 0 - fprintf(stderr, "sending %d unichars:", tlen); - for (i = 0; i < tlen; i++) - fprintf(stderr, " %04x", (unsigned) text[i]); - fprintf(stderr, "\n"); -#endif - luni_send(term->ldisc, text, tlen, 1); - } - } -} - void term_nopaste(Terminal *term) { if (term->paste_len == 0) @@ -6393,6 +6108,14 @@ void term_deselect(Terminal *term) { deselect(term); term_update(term); + + /* + * Since terminal output is suppressed during drag-selects, we + * should make sure to write any pending output if one has just + * finished. + */ + if (term->selstate != DRAGGING) + term_out(term); } int term_ldisc(Terminal *term, int option) @@ -6480,9 +6203,9 @@ char *term_get_ttymode(Terminal *term, const char *mode) { char *val = NULL; if (strcmp(mode, "ERASE") == 0) { - val = term->cfg.bksp_is_delete ? "^?" : "^H"; + val = term->bksp_is_delete ? "^?" : "^H"; } - /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */ + /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */ /* FIXME: or ECHO and friends based on local echo state? */ return dupstr(val); } @@ -6528,7 +6251,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } } @@ -6555,8 +6278,8 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, case 10: case 13: term_data(term, 0, "\r\n", 2); + prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos] = '\0'; - pr->result[pr->result_len - 1] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = 0; @@ -6591,10 +6314,9 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, * when we're doing password input, because some people * have control characters in their passwords. */ - if ((!pr->echo || - (c >= ' ' && c <= '~') || - ((unsigned char) c >= 160)) - && s->pos < pr->result_len - 1) { + if (!pr->echo || (c >= ' ' && c <= '~') || + ((unsigned char) c >= 160)) { + prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos++] = c; if (pr->echo) term_data(term, 0, &c, 1); diff --git a/contrib/putty/TERMINAL.H b/contrib/putty/TERMINAL.H index 6d3b1c5..2c61d17 100644 --- a/contrib/putty/TERMINAL.H +++ b/contrib/putty/TERMINAL.H @@ -152,8 +152,12 @@ struct terminal_tag { int big_cursor; int xterm_mouse; /* send mouse messages to host */ + int xterm_extended_mouse; + int urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ + int bracketed_paste; + int cset_attr[2]; /* @@ -233,13 +237,13 @@ struct terminal_tag { struct unicode_data *ucsdata; /* - * We maintain a full _copy_ of a Config structure here, not - * merely a pointer to it. That way, when we're passed a new - * one for reconfiguration, we can check the differences and - * adjust the _current_ setting of (e.g.) auto wrap mode rather - * than only the default. + * We maintain a full copy of a Conf here, not merely a pointer + * to it. That way, when we're passed a new one for + * reconfiguration, we can check the differences and adjust the + * _current_ setting of (e.g.) auto wrap mode rather than only + * the default. */ - Config cfg; + Conf *conf; /* * from_backend calls term_out, but it can also be called from @@ -273,6 +277,52 @@ struct terminal_tag { int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; + + /* + * We copy a bunch of stuff out of the Conf structure into local + * fields in the Terminal structure, to avoid the repeated + * tree234 lookups which would be involved in fetching them from + * the former every time. + */ + int ansi_colour; + char *answerback; + int answerbacklen; + int arabicshaping; + int beep; + int bellovl; + int bellovl_n; + int bellovl_s; + int bellovl_t; + int bidi; + int bksp_is_delete; + int blink_cur; + int blinktext; + int cjk_ambig_wide; + int conf_height; + int conf_width; + int crhaslf; + int erase_to_scrollback; + int funky_type; + int lfhascr; + int logflush; + int logtype; + int mouse_override; + int nethack_keypad; + int no_alt_screen; + int no_applic_c; + int no_applic_k; + int no_dbackspace; + int no_mouse_rep; + int no_remote_charset; + int no_remote_resize; + int no_remote_wintitle; + int rawcnp; + int rect_select; + int remote_qtitle_action; + int rxvt_homeend; + int scroll_on_disp; + int scroll_on_key; + int xterm_256_colour; }; #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8) diff --git a/contrib/putty/TESTBACK.C b/contrib/putty/TESTBACK.C index 0dd26d9..5f9f1e8 100644 --- a/contrib/putty/TESTBACK.C +++ b/contrib/putty/TESTBACK.C @@ -1,4 +1,4 @@ -/* $Id: testback.c 7628 2007-06-30 21:56:44Z jacob $ */ +/* $Id: testback.c 9214 2011-07-14 18:52:21Z simon $ */ /* * Copyright (c) 1999 Simon Tatham * Copyright (c) 1999 Ben Harris @@ -33,13 +33,13 @@ #include "putty.h" -static const char *null_init(void *, void **, Config *, char *, int, char **, +static const char *null_init(void *, void **, Conf *, char *, int, char **, int, int); -static const char *loop_init(void *, void **, Config *, char *, int, char **, +static const char *loop_init(void *, void **, Conf *, char *, int, char **, int, int); static void null_free(void *); static void loop_free(void *); -static void null_reconfig(void *, Config *); +static void null_reconfig(void *, Conf *); static int null_send(void *, char *, int); static int loop_send(void *, char *, int); static int null_sendbuffer(void *); @@ -74,14 +74,14 @@ struct loop_state { }; static const char *null_init(void *frontend_handle, void **backend_handle, - Config *cfg, char *host, int port, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { return NULL; } static const char *loop_init(void *frontend_handle, void **backend_handle, - Config *cfg, char *host, int port, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { struct loop_state *st = snew(struct loop_state); @@ -101,7 +101,7 @@ static void loop_free(void *handle) sfree(handle); } -static void null_reconfig(void *handle, Config *cfg) { +static void null_reconfig(void *handle, Conf *conf) { } diff --git a/contrib/putty/TESTDATA/BIGNUM.PY b/contrib/putty/TESTDATA/BIGNUM.PY index 0a24780..b2e2e7f 100644 --- a/contrib/putty/TESTDATA/BIGNUM.PY +++ b/contrib/putty/TESTDATA/BIGNUM.PY @@ -103,6 +103,15 @@ for i in range(1,4200): a, b, p = findprod((1< @@ -18,12 +36,13 @@ struct timer { timer_fn_t fn; void *ctx; - long now; + unsigned long now; + unsigned long when_set; }; static tree234 *timers = NULL; static tree234 *timer_contexts = NULL; -static long now = 0L; +static unsigned long now = 0L; static int compare_timers(void *av, void *bv) { @@ -41,14 +60,12 @@ static int compare_timers(void *av, void *bv) * Failing that, compare on the other two fields, just so that * we don't get unwanted equality. */ -#ifdef __LCC__ +#if defined(__LCC__) || defined(__clang__) /* lcc won't let us compare function pointers. Legal, but annoying. */ { int c = memcmp(&a->fn, &b->fn, sizeof(a->fn)); - if (c < 0) - return -1; - else if (c > 0) - return +1; + if (c) + return c; } #else if (a->fn < b->fn) @@ -89,14 +106,15 @@ static void init_timers(void) } } -long schedule_timer(int ticks, timer_fn_t fn, void *ctx) +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { - long when; + unsigned long when; struct timer *t, *first; init_timers(); - when = ticks + GETTICKCOUNT(); + now = GETTICKCOUNT(); + when = ticks + now; /* * Just in case our various defences against timing skew fail @@ -110,6 +128,7 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx) t->fn = fn; t->ctx = ctx; t->now = when; + t->when_set = now; if (t != add234(timers, t)) { sfree(t); /* identical timer already exists */ @@ -134,65 +153,13 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx) * Returns the time (in ticks) expected until the next timer after * that triggers. */ -int run_timers(long anow, long *next) +int run_timers(unsigned long anow, unsigned long *next) { struct timer *first; init_timers(); -#ifdef TIMING_SYNC - /* - * In this ifdef I put some code which deals with the - * possibility that `anow' disagrees with GETTICKCOUNT by a - * significant margin. Our strategy for dealing with it differs - * depending on platform, because on some platforms - * GETTICKCOUNT is more likely to be right whereas on others - * `anow' is a better gold standard. - */ - { - long tnow = GETTICKCOUNT(); - - if (tnow + TICKSPERSEC/50 - anow < 0 || - anow + TICKSPERSEC/50 - tnow < 0 - ) { -#if defined TIMING_SYNC_ANOW - /* - * If anow is accurate and the tick count is wrong, - * this is likely to be because the tick count is - * derived from the system clock which has changed (as - * can occur on Unix). Therefore, we resolve this by - * inventing an offset which is used to adjust all - * future output from GETTICKCOUNT. - * - * A platform which defines TIMING_SYNC_ANOW is - * expected to have also defined this offset variable - * in (its platform-specific adjunct to) putty.h. - * Therefore we can simply reference it here and assume - * that it will exist. - */ - tickcount_offset += anow - tnow; -#elif defined TIMING_SYNC_TICKCOUNT - /* - * If the tick count is more likely to be accurate, we - * simply use that as our time value, which may mean we - * run no timers in this call (because we got called - * early), or alternatively it may mean we run lots of - * timers in a hurry because we were called late. - */ - anow = tnow; -#else -/* - * Any platform which defines TIMING_SYNC must also define one of the two - * auxiliary symbols TIMING_SYNC_ANOW and TIMING_SYNC_TICKCOUNT, to - * indicate which measurement to trust when the two disagree. - */ -#error TIMING_SYNC definition incomplete -#endif - } - } -#endif - - now = anow; + now = GETTICKCOUNT(); while (1) { first = (struct timer *)index234(timers, 0); @@ -207,7 +174,8 @@ int run_timers(long anow, long *next) */ delpos234(timers, 0); sfree(first); - } else if (first->now - now <= 0) { + } else if (now - (first->when_set - 10) > + first->now - (first->when_set - 10)) { /* * This timer is active and has reached its running * time. Run it. diff --git a/contrib/putty/TREE234.C b/contrib/putty/TREE234.C index 4e2da9d..b2a80a3 100644 --- a/contrib/putty/TREE234.C +++ b/contrib/putty/TREE234.C @@ -29,12 +29,18 @@ #include #include -#include "puttymem.h" #include "tree234.h" #ifdef TEST #define LOG(x) (printf x) +#define snew(type) ((type *)malloc(sizeof(type))) +#define snewn(n, type) ((type *)malloc((n) * sizeof(type))) +#define sresize(ptr, n, type) \ + ((type *)realloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ + (n) * sizeof(type))) +#define sfree(ptr) free(ptr) #else +#include "puttymem.h" #define LOG(x) #endif @@ -220,7 +226,7 @@ static void *add234_internal(tree234 * t, void *e, int index) n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d [%p] %p/%d at position %d\n", - left, lcount, e, right, rcount, np - n->kids)); + left, lcount, e, right, rcount, (int)(np - n->kids))); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. @@ -1469,7 +1475,8 @@ int main(void) printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); - printf("deleting string %s from index %d\n", array[j], j); + printf("deleting string %s from index %d\n", + (const char *)array[j], j); delpostest(j); } diff --git a/contrib/putty/UNIX/CONFIGUR.AC b/contrib/putty/UNIX/CONFIGUR.AC index 382bf2a..40bbd0e 100644 --- a/contrib/putty/UNIX/CONFIGUR.AC +++ b/contrib/putty/UNIX/CONFIGUR.AC @@ -4,18 +4,41 @@ # * Automake (for aclocal) # If you've got them, running "autoreconf" should work. -AC_INIT +# Version number is substituted by Buildscr for releases, snapshots +# and custom builds out of svn; X.XX shows up in ad-hoc developer +# builds, which shouldn't matter +AC_INIT(putty, X.XX) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_INSTALL -AC_PROG_CC -if test "X$GCC" = Xyes; then - PUTTYCFLAGS="-Wall -Werror" -else - PUTTYCFLAGS="" -fi -AC_SUBST(PUTTYCFLAGS) +AC_PROG_RANLIB + +# Mild abuse of the '--enable' option format to allow manual +# specification of setuid or setgid setup in pterm. +setidtype=none +AC_ARG_ENABLE([setuid], + [AS_HELP_STRING([--enable-setuid=USER], + [make pterm setuid to a given user])], + [case "$enableval" in + no) setidtype=none;; + *) setidtype=setuid; setidval="$enableval";; + esac]) +AC_ARG_ENABLE([setgid], + [AS_HELP_STRING([--enable-setgid=GROUP], + [make pterm setgid to a given group])], + [case "$enableval" in + no) setidtype=none;; + *) setidtype=setgid; setidval="$enableval";; + esac]) +AM_CONDITIONAL(HAVE_SETID_CMD, [test "$setidtype" != "none"]) +AS_IF([test "x$setidtype" = "xsetuid"], + [SETID_CMD="chown $setidval"; SETID_MODE="4755"]) +AS_IF([test "x$setidtype" = "xsetgid"], + [SETID_CMD="chgrp $setidval"; SETID_MODE="2755"]) +AC_SUBST(SETID_CMD) +AC_SUBST(SETID_MODE) AC_ARG_WITH([gssapi], [AS_HELP_STRING([--without-gssapi], @@ -27,18 +50,55 @@ WITH_GSSAPI= AS_IF([test "x$with_gssapi" != xno], [AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])]) +AC_ARG_WITH([gtk], + [AS_HELP_STRING([--with-gtk=VER], + [specify GTK version to use (`1' or `2')]) +AS_HELP_STRING([--without-gtk], + [do not use GTK (build command-line tools only)])], + [gtk_version_desired="$withval"], + [gtk_version_desired="any"]) + +case "$gtk_version_desired" in + 1 | 2 | any | no) ;; + yes) gtk_version_desired="any" ;; + *) AC_ERROR([Invalid GTK version specified]) +esac + AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[ #include #include ]) -# Look for both GTK 1 and GTK 2. -AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none]) -AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) -if test "$gtk" = "none"; then - all_targets="all-cli" -else - all_targets="all-cli all-gtk" -fi +# Look for both GTK 2 and GTK 1, in descending order of preference. If +# we can't find either, have the makefile only build the CLI programs. + +gtk=none + +case "$gtk_version_desired:$gtk" in + 2:none | any:none) + ifdef([AM_PATH_GTK_2_0],[ + AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) + ],[AC_WARNING([generating configure script without GTK 2 autodetection])]) + ;; +esac + +case "$gtk_version_desired:$gtk" in + 1:none | any:none) + ifdef([AM_PATH_GTK],[ + AM_PATH_GTK([1.2.0], [gtk=1], []) + ],[ + # manual check for gtk1 + AC_PATH_PROG(GTK1_CONFIG, gtk-config, absent) + if test "$GTK1_CONFIG" != "absent"; then + GTK_CFLAGS=`"$GTK1_CONFIG" --cflags` + GTK_LIBS=`"$GTK1_CONFIG" --libs` + gtk=1 + fi + ]) + ;; +esac + +AM_CONDITIONAL(HAVE_GTK, [test "$gtk" != "none"]) + if test "$gtk" = "2"; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" @@ -48,7 +108,6 @@ if test "$gtk" = "2"; then CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi -AC_SUBST([all_targets]) AC_SEARCH_LIBS([socket], [xnet]) @@ -63,12 +122,42 @@ AS_IF([test "x$with_gssapi" != xno], [], [AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])]) -AC_CHECK_LIB(X11, XOpenDisplay) +AC_CHECK_LIB(X11, XOpenDisplay, + [GTK_LIBS="-lX11 $GTK_LIBS" + AC_DEFINE([HAVE_LIBX11],[],[Define if libX11.a is available])]) -AC_CHECK_FUNCS([getaddrinfo ptsname setresuid strsignal updwtmpx]) +AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx]) +AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include ]]) +AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])]) + +if test "x$GCC" = "xyes"; then + : + AC_SUBST(WARNINGOPTS, ['-Wall -Werror']) +else + : + AC_SUBST(WARNINGOPTS, []) +fi AC_OUTPUT +if test "$gtk_version_desired" = "no"; then cat <ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && - c->generic.context.i == offsetof(Config,scrollbar)) { + c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-on-left one. @@ -89,29 +89,29 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) "Fonts for displaying non-bold text"); ctrl_fontsel(s, "Font used for ordinary text", 'f', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,font))); + conf_fontsel_handler, I(CONF_font)); ctrl_fontsel(s, "Font used for wide (CJK) text", 'w', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,widefont))); + conf_fontsel_handler, I(CONF_widefont)); s = ctrl_getset(b, "Window/Fonts", "fontbold", "Fonts for displaying bolded text"); ctrl_fontsel(s, "Font used for bolded text", 'b', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,boldfont))); + conf_fontsel_handler, I(CONF_boldfont)); ctrl_fontsel(s, "Font used for bold wide text", 'i', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,wideboldfont))); + conf_fontsel_handler, I(CONF_wideboldfont)); ctrl_checkbox(s, "Use shadow bold instead of bold fonts", 'u', HELPCTX(no_help), - dlg_stdcheckbox_handler, - I(offsetof(Config,shadowbold))); + conf_checkbox_handler, + I(CONF_shadowbold)); ctrl_text(s, "(Note that bold fonts or shadow bolding are only" " used if you have not requested bolding to be done by" " changing the text colour.)", HELPCTX(no_help)); ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20, - HELPCTX(no_help), dlg_stdeditbox_handler, - I(offsetof(Config,shadowboldoffset)), I(-1)); + HELPCTX(no_help), conf_editbox_handler, + I(CONF_shadowboldoffset), I(-1)); /* * Markus Kuhn feels, not totally unreasonably, that it's good @@ -125,8 +125,8 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) "Character set translation on received data"); ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l', HELPCTX(translation_utf8_override), - dlg_stdcheckbox_handler, - I(offsetof(Config,utf8_override))); + conf_checkbox_handler, + I(CONF_utf8_override)); if (!midsession) { /* @@ -137,8 +137,7 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) s = ctrl_getset(b, "Window/Behaviour", "x11", "X Window System settings"); ctrl_editbox(s, "Window class name:", 'z', 50, - HELPCTX(no_help), dlg_stdeditbox_handler, - I(offsetof(Config,winclass)), - I(sizeof(((Config *)0)->winclass))); + HELPCTX(no_help), conf_editbox_handler, + I(CONF_winclass), I(1)); } } diff --git a/contrib/putty/UNIX/GTKCOLS.H b/contrib/putty/UNIX/GTKCOLS.H index 295e650..8061266 100644 --- a/contrib/putty/UNIX/GTKCOLS.H +++ b/contrib/putty/UNIX/GTKCOLS.H @@ -8,7 +8,7 @@ #define COLUMNS_H #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/contrib/putty/UNIX/GTKDLG.C b/contrib/putty/UNIX/GTKDLG.C index fc25e78..764c3e9 100644 --- a/contrib/putty/UNIX/GTKDLG.C +++ b/contrib/putty/UNIX/GTKDLG.C @@ -330,11 +330,10 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) * The first call to "changed", if allowed to proceed normally, * will cause an EVENT_VALCHANGE event on the edit box, causing * a call to dlg_editbox_get() which will read the empty string - * out of the GtkEntry - and promptly write it straight into - * the Config structure, which is precisely where our `text' - * pointer is probably pointing, so the second editing - * operation will insert that instead of the string we - * originally asked for. + * out of the GtkEntry - and promptly write it straight into the + * Conf structure, which is precisely where our `text' pointer + * is probably pointing, so the second editing operation will + * insert that instead of the string we originally asked for. * * Hence, we must take our own copy of the text before we do * this. @@ -344,7 +343,7 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) sfree(tmpstring); } -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) +char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); @@ -353,25 +352,16 @@ void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) #if GTK_CHECK_VERSION(2,4,0) if (uc->combo) { #if GTK_CHECK_VERSION(2,6,0) - strncpy(buffer, - gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)), - length); + return dupstr(gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo))); #else - strncpy(buffer, - gtk_entry_get_text - (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))), - length); + return dupstr(gtk_entry_get_text + (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo))))); #endif - buffer[length-1] = '\0'; - return; } #endif if (uc->entry) { - strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - length); - buffer[length-1] = '\0'; - return; + return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } assert(!"We shouldn't get here"); @@ -916,44 +906,48 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text) } } -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); + /* We must copy fn->path before passing it to gtk_entry_set_text. + * See comment in dlg_editbox_set() for the reasons. */ + char *duppath = dupstr(fn->path); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); - gtk_entry_set_text(GTK_ENTRY(uc->entry), fn.path); + gtk_entry_set_text(GTK_ENTRY(uc->entry), duppath); + sfree(duppath); } -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn) +Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); - strncpy(fn->path, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - lenof(fn->path)); - fn->path[lenof(fn->path)-1] = '\0'; + return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs) +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); + /* We must copy fs->name before passing it to gtk_entry_set_text. + * See comment in dlg_editbox_set() for the reasons. */ + char *dupname = dupstr(fs->name); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); - gtk_entry_set_text(GTK_ENTRY(uc->entry), fs.name); + gtk_entry_set_text(GTK_ENTRY(uc->entry), dupname); + sfree(dupname); } -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs) +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); - strncpy(fs->name, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - lenof(fs->name)); - fs->name[lenof(fs->name)-1] = '\0'; + return fontspec_new(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } /* @@ -2826,12 +2820,12 @@ void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w) #endif } -int do_config_box(const char *title, Config *cfg, int midsession, +int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo) { GtkWidget *window, *hbox, *vbox, *cols, *label, *tree, *treescroll, *panels, *panelvbox; - int index, level; + int index, level, protocol; struct controlbox *ctrlbox; char *path; #if GTK_CHECK_VERSION(2,0,0) @@ -2859,8 +2853,9 @@ int do_config_box(const char *title, Config *cfg, int midsession, window = gtk_dialog_new(); ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo); - unix_setup_config_box(ctrlbox, midsession, cfg->protocol); + protocol = conf_get_int(conf, CONF_protocol); + setup_config_box(ctrlbox, midsession, protocol, protcfginfo); + unix_setup_config_box(ctrlbox, midsession, protocol); gtk_setup_config_box(ctrlbox, midsession, window); gtk_window_set_title(GTK_WINDOW(window), title); @@ -3095,7 +3090,7 @@ int do_config_box(const char *title, Config *cfg, int midsession, } #endif - dp.data = cfg; + dp.data = conf; dlg_refresh(NULL, &dp); dp.shortcuts = &selparams[0].shortcuts; @@ -3267,7 +3262,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) return dp.retval; } -static int string_width(char *text) +int string_width(char *text) { GtkWidget *label = gtk_label_new(text); GtkRequisition req; @@ -3394,6 +3389,13 @@ void fatal_message_box(void *window, char *msg) "OK", 'o', 1, 1, NULL); } +void nonfatal_message_box(void *window, char *msg) +{ + messagebox(window, "PuTTY Error", msg, + string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), + "OK", 'o', 1, 1, NULL); +} + void fatalbox(char *p, ...) { va_list ap; @@ -3406,6 +3408,17 @@ void fatalbox(char *p, ...) cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + char *msg; + va_start(ap, p); + msg = dupvprintf(p, ap); + va_end(ap); + fatal_message_box(NULL, msg); + sfree(msg); +} + static GtkWidget *aboutbox = NULL; static void about_close_clicked(GtkButton *button, gpointer data) @@ -3419,7 +3432,7 @@ static void licence_clicked(GtkButton *button, gpointer data) char *title; char *licence = - "Copyright 1997-2011 Simon Tatham.\n\n" + "Copyright 1997-2013 Simon Tatham.\n\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " @@ -3500,7 +3513,7 @@ void about_box(void *window) w, FALSE, FALSE, 5); gtk_widget_show(w); - w = gtk_label_new("Copyright 1997-2011 Simon Tatham. All rights reserved"); + w = gtk_label_new("Copyright 1997-2013 Simon Tatham. All rights reserved"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); @@ -3752,7 +3765,7 @@ void logevent_dlg(void *estuff, const char *string) es->nevents++; } -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -3764,7 +3777,7 @@ int askappend(void *frontend, Filename filename, char *mbtitle; int mbret; - message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = messagebox(get_window(frontend), mbtitle, message, diff --git a/contrib/putty/UNIX/GTKFONT.C b/contrib/putty/UNIX/GTKFONT.C index e382183..cf983e4 100644 --- a/contrib/putty/UNIX/GTKFONT.C +++ b/contrib/putty/UNIX/GTKFONT.C @@ -30,12 +30,6 @@ * and in particular whether it's client- or server-side, * during the progress of the font selector. * - * - all the GDK font functions used in the x11font subclass are - * deprecated, so one day they may go away. When this happens - - * or before, if I'm feeling proactive - it oughtn't to be too - * difficult in principle to convert the whole thing to use - * actual Xlib font calls. - * * - it would be nice if we could move the processing of * underline and VT100 double width into this module, so that * instead of using the ghastly pixmap-stretching technique @@ -47,6 +41,11 @@ * I haven't the energy. */ +#if !GLIB_CHECK_VERSION(1,3,7) +#define g_ascii_strcasecmp g_strcasecmp +#define g_ascii_strncasecmp g_strncasecmp +#endif + /* * Ad-hoc vtable mechanism to allow font structures to be * polymorphic. @@ -77,9 +76,12 @@ struct unifont_vtable { */ unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); + unifont *(*create_fallback)(GtkWidget *widget, int height, int wide, + int bold, int shadowoffset, int shadowalways); void (*destroy)(unifont *font); + int (*has_glyph)(unifont *font, wchar_t glyph); void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, int wide, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); void (*enum_fonts)(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); @@ -94,11 +96,12 @@ struct unifont_vtable { }; /* ---------------------------------------------------------------------- - * GDK-based X11 font implementation. + * X11 font implementation, directly using Xlib calls. */ +static int x11font_has_glyph(unifont *font, wchar_t glyph); static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *x11font_create(GtkWidget *widget, const char *name, int wide, int bold, @@ -124,12 +127,12 @@ struct x11font { * failed, so that we don't keep trying and failing * subsequently). */ - GdkFont *fonts[4]; + XFontStruct *fonts[4]; int allocated[4]; /* * `sixteen_bit' is true iff the font object is indexed by * values larger than a byte. That is, this flag tells us - * whether we use gdk_draw_text_wc() or gdk_draw_text(). + * whether we use XDrawString or XDrawString16, etc. */ int sixteen_bit; /* @@ -139,6 +142,14 @@ struct x11font { */ int variable; /* + * real_charset is the charset used when translating text into the + * font's internal encoding inside draw_text(). This need not be + * the same as the public_charset provided to the client; for + * example, public_charset might be CS_ISO8859_1 while + * real_charset is CS_ISO8859_1_X11. + */ + int real_charset; + /* * Data passed in to unifont_create(). */ int wide, bold, shadowoffset, shadowalways; @@ -146,7 +157,9 @@ struct x11font { static const struct unifont_vtable x11font_vtable = { x11font_create, + NULL, /* no fallback fonts in X11 */ x11font_destroy, + x11font_has_glyph, x11font_draw_text, x11font_enum_fonts, x11font_canonify_fontname, @@ -154,10 +167,9 @@ static const struct unifont_vtable x11font_vtable = { "server", }; -char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) +static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide) { - XFontStruct *xfs = GDK_FONT_XFONT(font); - Display *disp = GDK_FONT_XDISPLAY(font); + Display *disp = GDK_DISPLAY(); Atom fontprop = XInternAtom(disp, "FONT", False); unsigned long ret; if (XGetFontProperty(xfs, fontprop, &ret)) { @@ -179,8 +191,10 @@ char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) p++; } - if (nstr < lenof(strings)) + if (nstr < lenof(strings)) { + sfree(dupname); return NULL; /* XLFD was malformed */ + } if (bold) strings[2] = "bold"; @@ -206,16 +220,78 @@ char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) return NULL; } -static int x11_font_width(GdkFont *font, int sixteen_bit) +static int x11_font_width(XFontStruct *xfs, int sixteen_bit) { if (sixteen_bit) { XChar2b space; space.byte1 = 0; space.byte2 = '0'; - return gdk_text_width(font, (const gchar *)&space, 2); + return XTextWidth16(xfs, &space, 1); + } else { + return XTextWidth(xfs, "0", 1); + } +} + +static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2) +{ + int index; + + /* + * Not to be confused with x11font_has_glyph, which is a method of + * the x11font 'class' and hence takes a unifont as argument. This + * is the low-level function which grubs about in an actual + * XFontStruct to see if a given glyph exists. + * + * We must do this ourselves rather than letting Xlib's + * XTextExtents16 do the job, because XTextExtents will helpfully + * substitute the font's default_char for any missing glyph and + * not tell us it did so, which precisely won't help us find out + * which glyphs _are_ missing. + * + * The man page for XQueryFont is rather confusing about how the + * per_char array in the XFontStruct is laid out, because it gives + * formulae for determining the two-byte X character code _from_ + * an index into the per_char array. Going the other way, it's + * rather simpler: + * + * The valid character codes have byte1 between min_byte1 and + * max_byte1 inclusive, and byte2 between min_char_or_byte2 and + * max_char_or_byte2 inclusive. This gives a rectangle of size + * (max_byte2-min_byte1+1) by + * (max_char_or_byte2-min_char_or_byte2+1), which is precisely the + * rectangle encoded in the per_char array. Hence, given a + * character code which is valid in the sense that it falls + * somewhere in that rectangle, its index in per_char is given by + * setting + * + * x = byte2 - min_char_or_byte2 + * y = byte1 - min_byte1 + * index = y * (max_char_or_byte2-min_char_or_byte2+1) + x + * + * If min_byte1 and min_byte2 are both zero, that's a special case + * which can be treated as if min_byte2 was 1 instead, i.e. the + * per_char array just runs from min_char_or_byte2 to + * max_char_or_byte2 inclusive, and byte1 should always be zero. + */ + + if (byte2 < xfs->min_char_or_byte2 || byte2 > xfs->max_char_or_byte2) + return FALSE; + + if (xfs->min_byte1 == 0 && xfs->max_byte1 == 0) { + index = byte2 - xfs->min_char_or_byte2; } else { - return gdk_char_width(font, '0'); + if (byte1 < xfs->min_byte1 || byte1 > xfs->max_byte1) + return FALSE; + index = ((byte2 - xfs->min_char_or_byte2) + + ((byte1 - xfs->min_byte1) * + (xfs->max_char_or_byte2 - xfs->min_char_or_byte2 + 1))); } + + if (!xfs->per_char) /* per_char NULL => everything in range exists */ + return TRUE; + + return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 || + xfs->per_char[index].width > 0); } static unifont *x11font_create(GtkWidget *widget, const char *name, @@ -223,21 +299,17 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, int shadowoffset, int shadowalways) { struct x11font *xfont; - GdkFont *font; XFontStruct *xfs; - Display *disp; + Display *disp = GDK_DISPLAY(); Atom charset_registry, charset_encoding, spacing; unsigned long registry_ret, encoding_ret, spacing_ret; int pubcs, realcs, sixteen_bit, variable; int i; - font = gdk_font_load(name); - if (!font) + xfs = XLoadQueryFont(disp, name); + if (!xfs) return NULL; - xfs = GDK_FONT_XFONT(font); - disp = GDK_FONT_XDISPLAY(font); - charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False); charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False); @@ -266,27 +338,19 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, } /* - * Hack for X line-drawing characters: if the primary - * font is encoded as ISO-8859-1, and has valid glyphs - * in the first 32 char positions, it is assumed that - * those glyphs are the VT100 line-drawing character - * set. - * - * Actually, we'll hack even harder by only checking - * position 0x19 (vertical line, VT100 linedrawing - * `x'). Then we can check it easily by seeing if the - * ascent and descent differ. + * Hack for X line-drawing characters: if the primary font + * is encoded as ISO-8859-1, and has valid glyphs in the + * low character positions, it is assumed that those + * glyphs are the VT100 line-drawing character set. */ if (pubcs == CS_ISO8859_1) { - int lb, rb, wid, asc, desc; - gchar text[2]; - - text[1] = '\0'; - text[0] = '\x12'; - gdk_string_extents(font, text, &lb, &rb, &wid, &asc, &desc); - if (asc != desc) - realcs = CS_ISO8859_1_X11; - } + int ch; + for (ch = 1; ch < 32; ch++) + if (!x11_font_has_glyph(xfs, 0, ch)) + break; + if (ch == 32) + realcs = CS_ISO8859_1_X11; + } sfree(encoding); } @@ -303,13 +367,14 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, xfont = snew(struct x11font); xfont->u.vt = &x11font_vtable; - xfont->u.width = x11_font_width(font, sixteen_bit); - xfont->u.ascent = font->ascent; - xfont->u.descent = font->descent; + xfont->u.width = x11_font_width(xfs, sixteen_bit); + xfont->u.ascent = xfs->ascent; + xfont->u.descent = xfs->descent; xfont->u.height = xfont->u.ascent + xfont->u.descent; xfont->u.public_charset = pubcs; - xfont->u.real_charset = realcs; - xfont->fonts[0] = font; + xfont->u.want_fallback = TRUE; + xfont->real_charset = realcs; + xfont->fonts[0] = xfs; xfont->allocated[0] = TRUE; xfont->sixteen_bit = sixteen_bit; xfont->variable = variable; @@ -328,63 +393,150 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, static void x11font_destroy(unifont *font) { + Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; int i; for (i = 0; i < lenof(xfont->fonts); i++) if (xfont->fonts[i]) - gdk_font_unref(xfont->fonts[i]); + XFreeFont(disp, xfont->fonts[i]); sfree(font); } static void x11_alloc_subfont(struct x11font *xfont, int sfid) { + Display *disp = GDK_DISPLAY(); char *derived_name = x11_guess_derived_font_name (xfont->fonts[0], sfid & 1, !!(sfid & 2)); - xfont->fonts[sfid] = gdk_font_load(derived_name); /* may be NULL */ + xfont->fonts[sfid] = XLoadQueryFont(disp, derived_name); xfont->allocated[sfid] = TRUE; sfree(derived_name); + /* Note that xfont->fonts[sfid] may still be NULL, if XLQF failed. */ +} + +static int x11font_has_glyph(unifont *font, wchar_t glyph) +{ + struct x11font *xfont = (struct x11font *)font; + + if (xfont->sixteen_bit) { + /* + * This X font has 16-bit character indices, which means + * we can directly use our Unicode input value. + */ + return x11_font_has_glyph(xfont->fonts[0], glyph >> 8, glyph & 0xFF); + } else { + /* + * This X font has 8-bit indices, so we must convert to the + * appropriate character set. + */ + char sbstring[2]; + int sblen = wc_to_mb(xfont->real_charset, 0, &glyph, 1, + sbstring, 2, "", NULL, NULL); + if (sblen == 0 || !sbstring[0]) + return FALSE; /* not even in the charset */ + + return x11_font_has_glyph(xfont->fonts[0], 0, + (unsigned char)sbstring[0]); + } } -static void x11font_really_draw_text(GdkDrawable *target, GdkFont *font, - GdkGC *gc, int x, int y, - const gchar *string, int clen, int nchars, - int shadowbold, int shadowoffset, - int fontvariable, int cellwidth) +#if !GTK_CHECK_VERSION(2,0,0) +#define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */ +#endif + +static void x11font_really_draw_text_16(GdkDrawable *target, XFontStruct *xfs, + GC gc, int x, int y, + const XChar2b *string, int nchars, + int shadowoffset, + int fontvariable, int cellwidth) { - int step = clen * nchars, nsteps = 1, centre = FALSE; + Display *disp = GDK_DISPLAY(); + int step, nsteps, centre; if (fontvariable) { /* * In a variable-pitch font, we draw one character at a * time, and centre it in the character cell. */ - step = clen; + step = 1; nsteps = nchars; centre = TRUE; + } else { + /* + * In a fixed-pitch font, we can draw the whole lot in one go. + */ + step = nchars; + nsteps = 1; + centre = FALSE; } while (nsteps-- > 0) { int X = x; if (centre) - X += (cellwidth - gdk_text_width(font, string, step)) / 2; + X += (cellwidth - XTextWidth16(xfs, string, step)) / 2; - gdk_draw_text(target, font, gc, X, y, string, step); - if (shadowbold) - gdk_draw_text(target, font, gc, X + shadowoffset, y, string, step); + XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, + X, y, string, step); + if (shadowoffset) + XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, + X + shadowoffset, y, string, step); x += cellwidth; string += step; } } -static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, +static void x11font_really_draw_text(GdkDrawable *target, XFontStruct *xfs, + GC gc, int x, int y, + const char *string, int nchars, + int shadowoffset, + int fontvariable, int cellwidth) +{ + Display *disp = GDK_DISPLAY(); + int step, nsteps, centre; + + if (fontvariable) { + /* + * In a variable-pitch font, we draw one character at a + * time, and centre it in the character cell. + */ + step = 1; + nsteps = nchars; + centre = TRUE; + } else { + /* + * In a fixed-pitch font, we can draw the whole lot in one go. + */ + step = nchars; + nsteps = 1; + centre = FALSE; + } + + while (nsteps-- > 0) { + int X = x; + if (centre) + X += (cellwidth - XTextWidth(xfs, string, step)) / 2; + + XDrawString(disp, GDK_DRAWABLE_XID(target), gc, + X, y, string, step); + if (shadowoffset) + XDrawString(disp, GDK_DRAWABLE_XID(target), gc, + X + shadowoffset, y, string, step); + + x += cellwidth; + string += step; + } +} + +static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { + Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; + GC gc = GDK_GC_XGC(gdkgc); int sfid; - int shadowbold = FALSE; + int shadowoffset = 0; int mult = (wide ? 2 : 1); wide -= xfont->wide; @@ -395,7 +547,7 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, * use shadow bold. */ if (xfont->shadowalways && bold) { - shadowbold = TRUE; + shadowoffset = xfont->shadowoffset; bold = 0; } sfid = 2 * wide + bold; @@ -403,7 +555,7 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, x11_alloc_subfont(xfont, sfid); if (bold && !xfont->fonts[sfid]) { bold = 0; - shadowbold = TRUE; + shadowoffset = xfont->shadowoffset; sfid = 2 * wide + bold; if (!xfont->allocated[sfid]) x11_alloc_subfont(xfont, sfid); @@ -412,46 +564,38 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, if (!xfont->fonts[sfid]) return; /* we've tried our best, but no luck */ + XSetFont(disp, gc, xfont->fonts[sfid]->fid); + if (xfont->sixteen_bit) { /* * This X font has 16-bit character indices, which means - * we expect our string to have been passed in UTF-8. + * we can directly use our Unicode input string. */ XChar2b *xcs; - wchar_t *wcs; - int nchars, maxchars, i; + int i; - /* - * Convert the input string to wide-character Unicode. - */ - maxchars = 0; - for (i = 0; i < len; i++) - if ((unsigned char)string[i] <= 0x7F || - (unsigned char)string[i] >= 0xC0) - maxchars++; - wcs = snewn(maxchars+1, wchar_t); - nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars, - CS_UTF8, NULL, NULL, 0); - assert(nchars <= maxchars); - wcs[nchars] = L'\0'; - - xcs = snewn(nchars, XChar2b); - for (i = 0; i < nchars; i++) { - xcs[i].byte1 = wcs[i] >> 8; - xcs[i].byte2 = wcs[i]; + xcs = snewn(len, XChar2b); + for (i = 0; i < len; i++) { + xcs[i].byte1 = string[i] >> 8; + xcs[i].byte2 = string[i]; } - x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y, - (gchar *)xcs, 2, nchars, - shadowbold, xfont->shadowoffset, - xfont->variable, cellwidth * mult); + x11font_really_draw_text_16(target, xfont->fonts[sfid], gc, x, y, + xcs, len, shadowoffset, + xfont->variable, cellwidth * mult); sfree(xcs); - sfree(wcs); } else { + /* + * This X font has 8-bit indices, so we must convert to the + * appropriate character set. + */ + char *sbstring = snewn(len+1, char); + int sblen = wc_to_mb(xfont->real_charset, 0, string, len, + sbstring, len+1, ".", NULL, NULL); x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y, - string, 1, len, - shadowbold, xfont->shadowoffset, + sbstring, sblen, shadowoffset, xfont->variable, cellwidth * mult); + sfree(sbstring); } } @@ -524,21 +668,21 @@ static void x11font_enum_fonts(GtkWidget *widget, style = p; p += sprintf(p, "%s", components[2][0] ? components[2] : "regular"); - if (!g_strcasecmp(components[3], "i")) + if (!g_ascii_strcasecmp(components[3], "i")) p += sprintf(p, " italic"); - else if (!g_strcasecmp(components[3], "o")) + else if (!g_ascii_strcasecmp(components[3], "o")) p += sprintf(p, " oblique"); - else if (!g_strcasecmp(components[3], "ri")) + else if (!g_ascii_strcasecmp(components[3], "ri")) p += sprintf(p, " reverse italic"); - else if (!g_strcasecmp(components[3], "ro")) + else if (!g_ascii_strcasecmp(components[3], "ro")) p += sprintf(p, " reverse oblique"); - else if (!g_strcasecmp(components[3], "ot")) + else if (!g_ascii_strcasecmp(components[3], "ot")) p += sprintf(p, " other-slant"); - if (components[4][0] && g_strcasecmp(components[4], "normal")) + if (components[4][0] && g_ascii_strcasecmp(components[4], "normal")) p += sprintf(p, " %s", components[4]); - if (!g_strcasecmp(components[10], "m")) + if (!g_ascii_strcasecmp(components[10], "m")) p += sprintf(p, " [M]"); - if (!g_strcasecmp(components[10], "c")) + if (!g_ascii_strcasecmp(components[10], "c")) p += sprintf(p, " [C]"); if (components[5][0]) p += sprintf(p, " %s", components[5]); @@ -550,23 +694,23 @@ static void x11font_enum_fonts(GtkWidget *widget, */ p++; stylekey = p; - if (!g_strcasecmp(components[2], "medium") || - !g_strcasecmp(components[2], "regular") || - !g_strcasecmp(components[2], "normal") || - !g_strcasecmp(components[2], "book")) + if (!g_ascii_strcasecmp(components[2], "medium") || + !g_ascii_strcasecmp(components[2], "regular") || + !g_ascii_strcasecmp(components[2], "normal") || + !g_ascii_strcasecmp(components[2], "book")) weightkey = 0; - else if (!g_strncasecmp(components[2], "demi", 4) || - !g_strncasecmp(components[2], "semi", 4)) + else if (!g_ascii_strncasecmp(components[2], "demi", 4) || + !g_ascii_strncasecmp(components[2], "semi", 4)) weightkey = 1; else weightkey = 2; - if (!g_strcasecmp(components[3], "r")) + if (!g_ascii_strcasecmp(components[3], "r")) slantkey = 0; - else if (!g_strncasecmp(components[3], "r", 1)) + else if (!g_ascii_strncasecmp(components[3], "r", 1)) slantkey = 2; else slantkey = 1; - if (!g_strcasecmp(components[4], "normal")) + if (!g_ascii_strcasecmp(components[4], "normal")) setwidthkey = 0; else setwidthkey = 1; @@ -638,19 +782,16 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, * _aliases_, unless specifically asked to, because the font * selector treats them as worthwhile in their own right. */ - GdkFont *font = gdk_font_load(name); XFontStruct *xfs; - Display *disp; + Display *disp = GDK_DISPLAY(); Atom fontprop, fontprop2; unsigned long ret; - if (!font) - return NULL; /* didn't make sense to us, sorry */ + xfs = XLoadQueryFont(disp, name); - gdk_font_ref(font); + if (!xfs) + return NULL; /* didn't make sense to us, sorry */ - xfs = GDK_FONT_XFONT(font); - disp = GDK_FONT_XDISPLAY(font); fontprop = XInternAtom(disp, "FONT", False); if (XGetFontProperty(xfs, fontprop, &ret)) { @@ -661,7 +802,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False); if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) { *size = fsize; - gdk_font_unref(font); + XFreeFont(disp, xfs); if (flags) { if (name[0] == '-' || resolve_aliases) *flags = FONTFLAG_SERVERSIDE; @@ -674,7 +815,8 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, } } - gdk_font_unref(font); + XFreeFont(disp, xfs); + return NULL; /* something went wrong */ } @@ -694,12 +836,16 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name, #define PANGO_PRE_1POINT6 /* make life easier for pre-1.4 folk */ #endif +static int pangofont_has_glyph(unifont *font, wchar_t glyph); static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *pangofont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); +static unifont *pangofont_create_fallback(GtkWidget *widget, int height, + int wide, int bold, + int shadowoffset, int shadowalways); static void pangofont_destroy(unifont *font); static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); @@ -728,7 +874,9 @@ struct pangofont { static const struct unifont_vtable pangofont_vtable = { pangofont_create, + pangofont_create_fallback, pangofont_destroy, + pangofont_has_glyph, pangofont_draw_text, pangofont_enum_fonts, pangofont_canonify_fontname, @@ -774,8 +922,8 @@ static int pangofont_check_desc_makes_sense(PangoContext *ctx, matched = FALSE; for (i = 0; i < nfamilies; i++) { - if (!g_strcasecmp(pango_font_family_get_name(families[i]), - pango_font_description_get_family(desc))) { + if (!g_ascii_strcasecmp(pango_font_family_get_name(families[i]), + pango_font_description_get_family(desc))) { matched = TRUE; break; } @@ -785,31 +933,19 @@ static int pangofont_check_desc_makes_sense(PangoContext *ctx, return matched; } -static unifont *pangofont_create(GtkWidget *widget, const char *name, - int wide, int bold, - int shadowoffset, int shadowalways) +static unifont *pangofont_create_internal(GtkWidget *widget, + PangoContext *ctx, + PangoFontDescription *desc, + int wide, int bold, + int shadowoffset, int shadowalways) { struct pangofont *pfont; - PangoContext *ctx; #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif - PangoFontDescription *desc; PangoFontset *fset; PangoFontMetrics *metrics; - desc = pango_font_description_from_string(name); - if (!desc) - return NULL; - ctx = gtk_widget_get_pango_context(widget); - if (!ctx) { - pango_font_description_free(desc); - return NULL; - } - if (!pangofont_check_desc_makes_sense(ctx, desc)) { - pango_font_description_free(desc); - return NULL; - } #ifndef PANGO_PRE_1POINT6 map = pango_context_get_font_map(ctx); if (!map) { @@ -841,9 +977,9 @@ static unifont *pangofont_create(GtkWidget *widget, const char *name, pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; + pfont->u.want_fallback = FALSE; /* The Pango API is hardwired to UTF-8 */ pfont->u.public_charset = CS_UTF8; - pfont->u.real_charset = CS_UTF8; pfont->desc = desc; pfont->fset = fset; pfont->widget = widget; @@ -856,6 +992,49 @@ static unifont *pangofont_create(GtkWidget *widget, const char *name, return (unifont *)pfont; } +static unifont *pangofont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + PangoContext *ctx; + PangoFontDescription *desc; + + desc = pango_font_description_from_string(name); + if (!desc) + return NULL; + ctx = gtk_widget_get_pango_context(widget); + if (!ctx) { + pango_font_description_free(desc); + return NULL; + } + if (!pangofont_check_desc_makes_sense(ctx, desc)) { + pango_font_description_free(desc); + return NULL; + } + return pangofont_create_internal(widget, ctx, desc, wide, bold, + shadowoffset, shadowalways); +} + +static unifont *pangofont_create_fallback(GtkWidget *widget, int height, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + PangoContext *ctx; + PangoFontDescription *desc; + + desc = pango_font_description_from_string("Monospace"); + if (!desc) + return NULL; + ctx = gtk_widget_get_pango_context(widget); + if (!ctx) { + pango_font_description_free(desc); + return NULL; + } + pango_font_description_set_absolute_size(desc, height * PANGO_SCALE); + return pangofont_create_internal(widget, ctx, desc, wide, bold, + shadowoffset, shadowalways); +} + static void pangofont_destroy(unifont *font) { struct pangofont *pfont = (struct pangofont *)font; @@ -864,13 +1043,21 @@ static void pangofont_destroy(unifont *font) sfree(font); } +static int pangofont_has_glyph(unifont *font, wchar_t glyph) +{ + /* Pango implements font fallback, so assume it has everything */ + return TRUE; +} + static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { struct pangofont *pfont = (struct pangofont *)font; PangoLayout *layout; PangoRectangle rect; + char *utfstring, *utfptr; + int utflen; int shadowbold = FALSE; if (wide) @@ -891,7 +1078,16 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, } } - while (len > 0) { + /* + * Pango always expects UTF-8, so convert the input wide character + * string to UTF-8. + */ + utfstring = snewn(len*6+1, char); /* UTF-8 has max 6 bytes/char */ + utflen = wc_to_mb(CS_UTF8, 0, string, len, + utfstring, len*6+1, ".", NULL, NULL); + + utfptr = utfstring; + while (utflen > 0) { int clen, n; /* @@ -923,42 +1119,50 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, * string. */ clen = 1; - while (clen < len && - (unsigned char)string[clen] >= 0x80 && - (unsigned char)string[clen] < 0xC0) + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) clen++; n = 1; - /* - * See if that character has the width we expect. - */ - pango_layout_set_text(layout, string, clen); - pango_layout_get_pixel_extents(layout, NULL, &rect); - - if (rect.width == cellwidth) { - /* - * Try extracting more characters, for as long as they - * stay well-behaved. - */ - while (clen < len) { - int oldclen = clen; - clen++; /* skip UTF-8 introducer byte */ - while (clen < len && - (unsigned char)string[clen] >= 0x80 && - (unsigned char)string[clen] < 0xC0) - clen++; - n++; - pango_layout_set_text(layout, string, clen); - pango_layout_get_pixel_extents(layout, NULL, &rect); - if (rect.width != n * cellwidth) { - clen = oldclen; - n--; - break; - } - } - } + /* + * If it's a right-to-left character, we must display it on + * its own, to stop Pango helpfully re-reversing our already + * reversed text. + */ + if (!is_rtl(string[0])) { - pango_layout_set_text(layout, string, clen); + /* + * See if that character has the width we expect. + */ + pango_layout_set_text(layout, utfptr, clen); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + if (rect.width == cellwidth) { + /* + * Try extracting more characters, for as long as they + * stay well-behaved. + */ + while (clen < utflen) { + int oldclen = clen; + clen++; /* skip UTF-8 introducer byte */ + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) + clen++; + n++; + pango_layout_set_text(layout, utfptr, clen); + pango_layout_get_pixel_extents(layout, NULL, &rect); + if (rect.width != n * cellwidth) { + clen = oldclen; + n--; + break; + } + } + } + } + + pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2, y + (pfont->u.height - rect.height)/2, layout); @@ -966,11 +1170,14 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset, y + (pfont->u.height - rect.height)/2, layout); - len -= clen; - string += clen; + utflen -= clen; + utfptr += clen; + string += n; x += n * cellwidth; } + sfree(utfstring); + g_object_unref(layout); } @@ -1233,6 +1440,8 @@ static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, * event that the same font name is valid as both a Pango and an * X11 font, it will be interpreted as the former in the absence * of an explicit type-disambiguating prefix.) + * + * The 'multifont' subclass is omitted here, as discussed above. */ static const struct unifont_vtable *unifont_types[] = { #if GTK_CHECK_VERSION(2,0,0) @@ -1309,13 +1518,131 @@ void unifont_destroy(unifont *font) } void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { font->vt->draw_text(target, gc, font, x, y, string, len, wide, bold, cellwidth); } +/* ---------------------------------------------------------------------- + * Multiple-font wrapper. This is a type of unifont which encapsulates + * up to two other unifonts, permitting missing glyphs in the main + * font to be filled in by a fallback font. + * + * This is a type of unifont just like the previous two, but it has a + * separate constructor which is manually called by the client, so it + * doesn't appear in the list of available font types enumerated by + * unifont_create. This means it's not used by unifontsel either, so + * it doesn't need to support any methods except draw_text and + * destroy. + */ + +static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth); +static void multifont_destroy(unifont *font); + +struct multifont { + struct unifont u; + unifont *main; + unifont *fallback; +}; + +static const struct unifont_vtable multifont_vtable = { + NULL, /* creation is done specially */ + NULL, + multifont_destroy, + NULL, + multifont_draw_text, + NULL, + NULL, + NULL, + "client", +}; + +unifont *multifont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + int i; + unifont *font, *fallback; + struct multifont *mfont; + + font = unifont_create(widget, name, wide, bold, + shadowoffset, shadowalways); + if (!font) + return NULL; + + fallback = NULL; + if (font->want_fallback) { + for (i = 0; i < lenof(unifont_types); i++) { + if (unifont_types[i]->create_fallback) { + fallback = unifont_types[i]->create_fallback + (widget, font->height, wide, bold, + shadowoffset, shadowalways); + if (fallback) + break; + } + } + } + + /* + * Construct our multifont. Public members are all copied from the + * primary font we're wrapping. + */ + mfont = snew(struct multifont); + mfont->u.vt = &multifont_vtable; + mfont->u.width = font->width; + mfont->u.ascent = font->ascent; + mfont->u.descent = font->descent; + mfont->u.height = font->height; + mfont->u.public_charset = font->public_charset; + mfont->u.want_fallback = FALSE; /* shouldn't be needed, but just in case */ + mfont->main = font; + mfont->fallback = fallback; + + return (unifont *)mfont; +} + +static void multifont_destroy(unifont *font) +{ + struct multifont *mfont = (struct multifont *)font; + unifont_destroy(mfont->main); + if (mfont->fallback) + unifont_destroy(mfont->fallback); + sfree(font); +} + +static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth) +{ + struct multifont *mfont = (struct multifont *)font; + int ok, i; + + while (len > 0) { + /* + * Find a maximal sequence of characters which are, or are + * not, supported by our main font. + */ + ok = mfont->main->vt->has_glyph(mfont->main, string[0]); + for (i = 1; + i < len && + !mfont->main->vt->has_glyph(mfont->main, string[i]) == !ok; + i++); + + /* + * Now display it. + */ + unifont_draw_text(target, gc, ok ? mfont->main : mfont->fallback, + x, y, string, i, wide, bold, cellwidth); + string += i; + len -= i; + x += i * cellwidth; + } +} + #if GTK_CHECK_VERSION(2,0,0) /* ---------------------------------------------------------------------- @@ -1393,7 +1720,7 @@ static int strnullcasecmp(const char *a, const char *b) /* * Otherwise, ordinary strcasecmp. */ - return g_strcasecmp(a, b); + return g_ascii_strcasecmp(a, b); } static int fontinfo_realname_compare(void *av, void *bv) @@ -1692,11 +2019,11 @@ static void unifontsel_draw_preview_text(unifontsel_internal *fs) */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent, - "bankrupt jilted showmen quiz convex fogey", + L"bankrupt jilted showmen quiz convex fogey", 41, FALSE, FALSE, font->width); info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height, - "BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY", + L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY", 41, FALSE, FALSE, font->width); /* * The ordering of punctuation here is also selected @@ -1712,7 +2039,7 @@ static void unifontsel_draw_preview_text(unifontsel_internal *fs) */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height * 2, - "0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", + L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", 42, FALSE, FALSE, font->width); } gdk_gc_unref(gc); @@ -1963,6 +2290,8 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs, */ below = findrelpos234(fs->fonts_by_selorder, &info2, NULL, REL234_LE, &pos); + if (!below) + pos = -1; above = index234(fs->fonts_by_selorder, pos+1); /* @@ -1970,7 +2299,7 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs, * case. If we have, it'll be in `below' and not `above', * because we did a REL234_LE rather than REL234_LT search. */ - if (!fontinfo_selorder_compare(&info2, below)) + if (below && !fontinfo_selorder_compare(&info2, below)) return below; /* diff --git a/contrib/putty/UNIX/GTKFONT.H b/contrib/putty/UNIX/GTKFONT.H index 41c0505..d702eb8 100644 --- a/contrib/putty/UNIX/GTKFONT.H +++ b/contrib/putty/UNIX/GTKFONT.H @@ -21,19 +21,21 @@ typedef struct unifont { /* * public_charset is the charset used when the user asks for * `Use font encoding'. - * - * real_charset is the charset used when translating text into - * a form suitable for sending to unifont_draw_text(). - * - * They can differ. For example, public_charset might be - * CS_ISO8859_1 while real_charset is CS_ISO8859_1_X11. */ - int public_charset, real_charset; + int public_charset; /* * Font dimensions needed by clients. */ int width, height, ascent, descent; + + /* + * Indicates whether this font is capable of handling all glyphs + * (Pango fonts can do this because Pango automatically supplies + * missing glyphs from other fonts), or whether it would like a + * fallback font to cope with missing glyphs. + */ + int want_fallback; } unifont; unifont *unifont_create(GtkWidget *widget, const char *name, @@ -41,10 +43,22 @@ unifont *unifont_create(GtkWidget *widget, const char *name, int shadowoffset, int shadowalways); void unifont_destroy(unifont *font); void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); /* + * This function behaves exactly like the low-level unifont_create, + * except that as well as the requested font it also allocates (if + * necessary) a fallback font for filling in replacement glyphs. + * + * Return value is usable with unifont_destroy and unifont_draw_text + * as if it were an ordinary unifont. + */ +unifont *multifont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways); + +/* * Unified font selector dialog. I can't be bothered to do a * proper GTK subclassing today, so this will just be an ordinary * data structure with some useful members. diff --git a/contrib/putty/UNIX/GTKWIN.C b/contrib/putty/UNIX/GTKWIN.C index f2a13ae..bc1bedf 100644 --- a/contrib/putty/UNIX/GTKWIN.C +++ b/contrib/putty/UNIX/GTKWIN.C @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,14 @@ #include #include +#if GTK_CHECK_VERSION(2,0,0) +#include +#endif + #define PUTTY_DO_GLOBALS /* actually _define_ globals */ +#define MAY_REFER_TO_GTK_IN_HEADERS + #include "putty.h" #include "terminal.h" #include "gtkfont.h" @@ -45,7 +52,6 @@ ASSERT(sizeof(long) <= sizeof(gpointer)); #endif /* Colours come in two flavours: configurable, and xterm-extended. */ -#define NCFGCOLOURS (lenof(((Config *)0)->colours)) #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) @@ -69,6 +75,9 @@ struct gui_data { *restartitem; GtkWidget *sessionsmenu; GdkPixmap *pixmap; +#if GTK_CHECK_VERSION(2,0,0) + GtkIMContext *imc; +#endif unifont *fonts[4]; /* normal, bold, wide, widebold */ int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; @@ -88,8 +97,8 @@ struct gui_data { guint term_exit_idle_id; int alt_keycode; int alt_digits; - char wintitle[sizeof(((Config *)0)->wintitle)]; - char icontitle[sizeof(((Config *)0)->wintitle)]; + char *wintitle; + char *icontitle; int master_fd, master_func_id; void *ldisc; Backend *back; @@ -98,14 +107,25 @@ struct gui_data { void *logctx; int exited; struct unicode_data ucsdata; - Config cfg; + Conf *conf; void *eventlogstuff; char *progname, **gtkargvstart; int ngtkargs; guint32 input_event_time; /* Timestamp of the most recent input event. */ int reconfiguring; + /* Cached things out of conf that we refer to a lot */ + int bold_style; + int window_border; + int cursor_type; }; +static void cache_conf_values(struct gui_data *inst) +{ + inst->bold_style = conf_get_int(inst->conf, CONF_bold_style); + inst->window_border = conf_get_int(inst->conf, CONF_window_border); + inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type); +} + struct draw_ctx { GdkGC *gc; struct gui_data *inst; @@ -134,31 +154,27 @@ void connection_fatal(void *frontend, char *p, ...) inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); - if (inst->cfg.close_on_exit == FORCE_ON) + if (conf_get_int(inst->conf, CONF_close_on_exit) == FORCE_ON) cleanup_exit(1); } /* * Default settings that are specific to pterm. */ -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; if (!strcmp(name, "Font")) - strcpy(ret.name, "server:fixed"); + return fontspec_new("server:fixed"); else - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *platform_default_s(const char *name) @@ -200,6 +216,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len) return term_data_untrusted(inst->term, data, len); } +int from_backend_eof(void *frontend) +{ + return TRUE; /* do respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { struct gui_data *inst = (struct gui_data *)p->frontend; @@ -394,7 +415,7 @@ char *get_window_title(void *frontend, int icon) gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; - if (!inst->exited && inst->cfg.warn_on_close) { + if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) { if (!reallyclose(inst)) return TRUE; } @@ -425,7 +446,7 @@ static void update_mouseptr(struct gui_data *inst) static void show_mouseptr(struct gui_data *inst, int show) { - if (!inst->cfg.hide_mouseptr) + if (!conf_get_int(inst->conf, CONF_hide_mouseptr)) show = 1; inst->mouseptr_visible = show; update_mouseptr(inst); @@ -436,8 +457,8 @@ void draw_backing_rect(struct gui_data *inst) GdkGC *gc = gdk_gc_new(inst->area->window); gdk_gc_set_foreground(gc, &inst->cols[258]); /* default background */ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0, - inst->cfg.width * inst->font_width + 2*inst->cfg.window_border, - inst->cfg.height * inst->font_height + 2*inst->cfg.window_border); + inst->width * inst->font_width + 2*inst->window_border, + inst->height * inst->font_height + 2*inst->window_border); gdk_gc_unref(gc); } @@ -450,11 +471,13 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) * See if the terminal size has changed, in which case we must * let the terminal know. */ - w = (event->width - 2*inst->cfg.window_border) / inst->font_width; - h = (event->height - 2*inst->cfg.window_border) / inst->font_height; + w = (event->width - 2*inst->window_border) / inst->font_width; + h = (event->height - 2*inst->window_border) / inst->font_height; if (w != inst->width || h != inst->height) { - inst->cfg.width = inst->width = w; - inst->cfg.height = inst->height = h; + inst->width = w; + inst->height = h; + conf_set_int(inst->conf, CONF_width, inst->width); + conf_set_int(inst->conf, CONF_height, inst->height); need_size = 1; } @@ -464,20 +487,22 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) } inst->pixmap = gdk_pixmap_new(widget->window, - (inst->cfg.width * inst->font_width + - 2*inst->cfg.window_border), - (inst->cfg.height * inst->font_height + - 2*inst->cfg.window_border), -1); + (w * inst->font_width + 2*inst->window_border), + (h * inst->font_height + 2*inst->window_border), -1); draw_backing_rect(inst); if (need_size && inst->term) { - term_size(inst->term, h, w, inst->cfg.savelines); + term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines)); } if (inst->term) term_invalidate(inst->term); +#if GTK_CHECK_VERSION(2,0,0) + gtk_im_context_set_client_window(inst->imc, widget->window); +#endif + return TRUE; } @@ -509,6 +534,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) char output[256]; wchar_t ucsoutput[2]; int ucsval, start, end, special, output_charset, use_ucsoutput; + int nethack_mode, app_keypad_mode; /* Remember the timestamp. */ inst->input_event_time = event->time; @@ -528,21 +554,26 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * inconvenience in having to type a zero before a single-digit * character code. */ - if (event->type == GDK_KEY_RELEASE && - (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L || - event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) && - inst->alt_keycode >= 0 && inst->alt_digits > 1) { + if (event->type == GDK_KEY_RELEASE) { + if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L || + event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) && + inst->alt_keycode >= 0 && inst->alt_digits > 1) { #ifdef KEY_DEBUGGING - printf("Alt key up, keycode = %d\n", inst->alt_keycode); + printf("Alt key up, keycode = %d\n", inst->alt_keycode); +#endif + /* + * FIXME: we might usefully try to do something clever here + * about interpreting the generated key code in a way that's + * appropriate to the line code page. + */ + output[0] = inst->alt_keycode; + end = 1; + goto done; + } +#if GTK_CHECK_VERSION(2,0,0) + if (gtk_im_context_filter_keypress(inst->imc, event)) + return TRUE; #endif - /* - * FIXME: we might usefully try to do something clever here - * about interpreting the generated key code in a way that's - * appropriate to the line code page. - */ - output[0] = inst->alt_keycode; - end = 1; - goto done; } if (event->type == GDK_KEY_PRESS) { @@ -618,7 +649,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * at all. */ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) { - term_scroll(inst->term, 0, -inst->cfg.height/2); + term_scroll(inst->term, 0, -inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) { @@ -626,7 +657,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) { - term_scroll(inst->term, 0, +inst->cfg.height/2); + term_scroll(inst->term, 0, +inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) { @@ -645,6 +676,10 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) special = FALSE; use_ucsoutput = FALSE; + nethack_mode = conf_get_int(inst->conf, CONF_nethack_keypad); + app_keypad_mode = (inst->term->app_keypad_keys && + !conf_get_int(inst->conf, CONF_no_applic_k)); + /* ALT+things gives leading Escape. */ output[0] = '\033'; #if !GTK_CHECK_VERSION(2,0,0) @@ -658,13 +693,73 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) output_charset = CS_ISO8859_1; strncpy(output+1, event->string, lenof(output)-1); #else + /* + * Most things can now be passed to + * gtk_im_context_filter_keypress without breaking anything + * below this point. An exception is the numeric keypad if + * we're in Nethack or application mode: the IM will eat + * numeric keypad presses if Num Lock is on, but we don't want + * it to. + */ + if (app_keypad_mode && + (event->keyval == GDK_Num_Lock || + event->keyval == GDK_KP_Divide || + event->keyval == GDK_KP_Multiply || + event->keyval == GDK_KP_Subtract || + event->keyval == GDK_KP_Add || + event->keyval == GDK_KP_Enter || + event->keyval == GDK_KP_0 || + event->keyval == GDK_KP_Insert || + event->keyval == GDK_KP_1 || + event->keyval == GDK_KP_End || + event->keyval == GDK_KP_2 || + event->keyval == GDK_KP_Down || + event->keyval == GDK_KP_3 || + event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_KP_4 || + event->keyval == GDK_KP_Left || + event->keyval == GDK_KP_5 || + event->keyval == GDK_KP_Begin || + event->keyval == GDK_KP_6 || + event->keyval == GDK_KP_Right || + event->keyval == GDK_KP_7 || + event->keyval == GDK_KP_Home || + event->keyval == GDK_KP_8 || + event->keyval == GDK_KP_Up || + event->keyval == GDK_KP_9 || + event->keyval == GDK_KP_Page_Up || + event->keyval == GDK_KP_Decimal || + event->keyval == GDK_KP_Delete)) { + /* app keypad; do nothing */ + } else if (nethack_mode && + (event->keyval == GDK_KP_1 || + event->keyval == GDK_KP_End || + event->keyval == GDK_KP_2 || + event->keyval == GDK_KP_Down || + event->keyval == GDK_KP_3 || + event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_KP_4 || + event->keyval == GDK_KP_Left || + event->keyval == GDK_KP_5 || + event->keyval == GDK_KP_Begin || + event->keyval == GDK_KP_6 || + event->keyval == GDK_KP_Right || + event->keyval == GDK_KP_7 || + event->keyval == GDK_KP_Home || + event->keyval == GDK_KP_8 || + event->keyval == GDK_KP_Up || + event->keyval == GDK_KP_9 || + event->keyval == GDK_KP_Page_Up)) { + /* nethack mode; do nothing */ + } else { + if (gtk_im_context_filter_keypress(inst->imc, event)) + return TRUE; + } + /* * GDK 2.0 arranges to have done some translation for us: in * GDK 2.0, event->string is encoded in the current locale. * - * (However, it's also deprecated; we really ought to be - * using a GTKIMContext.) - * * So we use the standard C library function mbstowcs() to * convert from the current locale into Unicode; from there * we can convert to whatever PuTTY is currently working in. @@ -675,7 +770,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) */ output_charset = CS_UTF8; { - wchar_t widedata[32], *wp; + wchar_t widedata[32]; + const wchar_t *wp; int wlen; int ulen; @@ -755,7 +851,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* We don't let GTK tell us what Backspace is! We know better. */ if (event->keyval == GDK_BackSpace && !(event->state & GDK_SHIFT_MASK)) { - output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08'; + output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? + '\x7F' : '\x08'; use_ucsoutput = FALSE; end = 2; special = TRUE; @@ -763,7 +860,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* For Shift Backspace, do opposite of what is configured. */ if (event->keyval == GDK_BackSpace && (event->state & GDK_SHIFT_MASK)) { - output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F'; + output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? + '\x08' : '\x7F'; use_ucsoutput = FALSE; end = 2; special = TRUE; @@ -786,7 +884,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* * NetHack keypad mode. */ - if (inst->cfg.nethack_keypad) { + if (nethack_mode) { char *keys = NULL; switch (event->keyval) { case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break; @@ -815,7 +913,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* * Application keypad mode. */ - if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) { + if (app_keypad_mode) { int xkey = 0; switch (event->keyval) { case GDK_Num_Lock: xkey = 'P'; break; @@ -829,7 +927,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * in xterm function key mode we change which two... */ case GDK_KP_Add: - if (inst->cfg.funky_type == FUNKY_XTERM) { + if (conf_get_int(inst->conf, CONF_funky_type) == FUNKY_XTERM) { if (event->state & GDK_SHIFT_MASK) xkey = 'l'; else @@ -876,6 +974,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) */ { int code = 0; + int funky_type = conf_get_int(inst->conf, CONF_funky_type); switch (event->keyval) { case GDK_F1: code = (event->state & GDK_SHIFT_MASK ? 23 : 11); @@ -959,7 +1058,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) break; } /* Reorder edit keys to physical order */ - if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6) + if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (inst->term->vt52_mode && code > 0 && code <= 6) { @@ -968,7 +1067,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) goto done; } - if (inst->cfg.funky_type == FUNKY_SCO && /* SCO function keys */ + if (funky_type == FUNKY_SCO && /* SCO function keys */ code >= 11 && code <= 34) { char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; @@ -992,7 +1091,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { @@ -1004,7 +1103,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) && + if ((inst->term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) @@ -1020,12 +1119,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11); use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (inst->term->vt52_mode) end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11); else @@ -1033,7 +1132,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) { + if ((code == 1 || code == 4) && + conf_get_int(inst->conf, CONF_rxvt_homeend)) { end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw"); use_ucsoutput = FALSE; goto done; @@ -1119,6 +1219,17 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) return TRUE; } +#if GTK_CHECK_VERSION(2,0,0) +void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) +{ + struct gui_data *inst = (struct gui_data *)data; + if (inst->ldisc) + lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), 1); + show_mouseptr(inst, 0); + term_seen_key_event(inst->term); +} +#endif + gboolean button_internal(struct gui_data *inst, guint32 timestamp, GdkEventType type, guint ebutton, guint state, gdouble ex, gdouble ey) @@ -1166,12 +1277,13 @@ gboolean button_internal(struct gui_data *inst, guint32 timestamp, default: return FALSE; /* don't know this event type */ } - if (send_raw_mouse && !(inst->cfg.mouse_override && shift) && + if (send_raw_mouse && !(shift && conf_get_int(inst->conf, + CONF_mouse_override)) && act != MA_CLICK && act != MA_RELEASE) return TRUE; /* we ignore these in raw mouse mode */ - x = (ex - inst->cfg.window_border) / inst->font_width; - y = (ey - inst->cfg.window_border) / inst->font_height; + x = (ex - inst->window_border) / inst->font_width; + y = (ey - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), act, x, y, shift, ctrl, alt); @@ -1231,8 +1343,8 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) else return FALSE; /* don't even know what button! */ - x = (event->x - inst->cfg.window_border) / inst->font_width; - y = (event->y - inst->cfg.window_border) / inst->font_height; + x = (event->x - inst->window_border) / inst->font_width; + y = (event->y - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), MA_DRAG, x, y, shift, ctrl, alt); @@ -1249,31 +1361,30 @@ void frontend_keypress(void *handle) * any keypress. */ if (inst->exited) - exit(0); + cleanup_exit(0); } static gint idle_exit_func(gpointer data) { struct gui_data *inst = (struct gui_data *)data; - int exitcode; + int exitcode, close_on_exit; if (!inst->exited && (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) { inst->exited = TRUE; - if (inst->cfg.close_on_exit == FORCE_ON || - (inst->cfg.close_on_exit == AUTO && exitcode == 0)) + close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit); + if (close_on_exit == FORCE_ON || + (close_on_exit == AUTO && exitcode == 0)) gtk_main_quit(); /* just go */ if (inst->ldisc) { ldisc_free(inst->ldisc); inst->ldisc = NULL; } - if (inst->back) { - inst->back->free(inst->backhandle); - inst->backhandle = NULL; - inst->back = NULL; - term_provide_resize_fn(inst->term, NULL, NULL); - update_specials_menu(inst); - } + inst->back->free(inst->backhandle); + inst->backhandle = NULL; + inst->back = NULL; + term_provide_resize_fn(inst->term, NULL, NULL); + update_specials_menu(inst); gtk_widget_set_sensitive(inst->restartitem, TRUE); } @@ -1290,13 +1401,18 @@ void notify_remote_exit(void *frontend) static gint timer_trigger(gpointer data) { - long now = GPOINTER_TO_LONG(data); - long next; + unsigned long now = GPOINTER_TO_LONG(data); + unsigned long next, then; long ticks; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger, + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + timer_id = gtk_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next)); } @@ -1307,7 +1423,7 @@ static gint timer_trigger(gpointer data) return FALSE; } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { long ticks; @@ -1364,7 +1480,7 @@ void set_busy_status(void *frontend, int status) void set_raw_mouse_mode(void *frontend, int activate) { struct gui_data *inst = (struct gui_data *)frontend; - activate = activate && !inst->cfg.no_mouse_rep; + activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep); send_raw_mouse = activate; update_mouseptr(inst); } @@ -1412,8 +1528,8 @@ void request_resize(void *frontend, int w, int h) offset_x = outer.width - inner.width; offset_y = outer.height - inner.height; - area_x = inst->font_width * w + 2*inst->cfg.window_border; - area_y = inst->font_height * h + 2*inst->cfg.window_border; + area_x = inst->font_width * w + 2*inst->window_border; + area_y = inst->font_height * h + 2*inst->window_border; /* * Now we must set the size request on the drawing area back to @@ -1480,7 +1596,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) struct gui_data *inst = (struct gui_data *)frontend; if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; real_palette_set(inst, n, r, g, b); if (n == 258) { @@ -1495,7 +1611,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) void palette_reset(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; - /* This maps colour indices in inst->cfg to those used in inst->cols. */ + /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, @@ -1513,9 +1629,12 @@ void palette_reset(void *frontend) } for (i = 0; i < NCFGCOLOURS; i++) { - inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101; - inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101; - inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101; + inst->cols[ww[i]].red = + conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101; + inst->cols[ww[i]].green = + conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101; + inst->cols[ww[i]].blue = + conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101; } for (i = 0; i < NEXTCOLOURS; i++) { @@ -1537,8 +1656,10 @@ void palette_reset(void *frontend) for (i = 0; i < NALLCOLOURS; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, i, inst->cfg.colours[i][0], - inst->cfg.colours[i][1], inst->cfg.colours[i][2]); + appname, i, + conf_get_int_int(inst->conf, CONF_colours, i*3+0), + conf_get_int_int(inst->conf, CONF_colours, i*3+1), + conf_get_int_int(inst->conf, CONF_colours, i*3+2)); } /* Since Default Background may have changed, ensure that space @@ -1611,7 +1732,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des * if we aren't in direct-to-font mode using the D800 hack. */ if (!inst->direct_to_font) { - wchar_t *tmp = data; + const wchar_t *tmp = data; int tmplen = len; XTextProperty tp; char *list[1]; @@ -1898,30 +2019,40 @@ static void set_window_titles(struct gui_data *inst) * is life. */ gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle); - if (!inst->cfg.win_name_always) + if (!conf_get_int(inst->conf, CONF_win_name_always)) gdk_window_set_icon_name(inst->window->window, inst->icontitle); } void set_title(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; - strncpy(inst->wintitle, title, lenof(inst->wintitle)); - inst->wintitle[lenof(inst->wintitle)-1] = '\0'; + sfree(inst->wintitle); + inst->wintitle = dupstr(title); set_window_titles(inst); } void set_icon(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; - strncpy(inst->icontitle, title, lenof(inst->icontitle)); - inst->icontitle[lenof(inst->icontitle)-1] = '\0'; + sfree(inst->icontitle); + inst->icontitle = dupstr(title); + set_window_titles(inst); +} + +void set_title_and_icon(void *frontend, char *title, char *icon) +{ + struct gui_data *inst = (struct gui_data *)frontend; + sfree(inst->wintitle); + inst->wintitle = dupstr(title); + sfree(inst->icontitle); + inst->icontitle = dupstr(icon); set_window_titles(inst); } void set_sbar(void *frontend, int total, int start, int page) { struct gui_data *inst = (struct gui_data *)frontend; - if (!inst->cfg.scrollbar) + if (!conf_get_int(inst->conf, CONF_scrollbar)) return; inst->sbar_adjust->lower = 0; inst->sbar_adjust->upper = total; @@ -1938,7 +2069,7 @@ void scrollbar_moved(GtkAdjustment *adj, gpointer data) { struct gui_data *inst = (struct gui_data *)data; - if (!inst->cfg.scrollbar) + if (!conf_get_int(inst->conf, CONF_scrollbar)) return; if (!inst->ignore_sbar) term_scroll(inst->term, 1, (int)adj->value); @@ -2027,11 +2158,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = nbg; nbg = t; } - if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) { + if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) { + if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -2049,7 +2180,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, widefactor = 1; } - if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) { + if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) { bold = 1; fontid |= 1; } else { @@ -2086,8 +2217,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, { GdkRectangle r; - r.x = x*inst->font_width+inst->cfg.window_border; - r.y = y*inst->font_height+inst->cfg.window_border; + r.x = x*inst->font_width+inst->window_border; + r.y = y*inst->font_height+inst->window_border; r.width = rlen*widefactor*inst->font_width; r.height = inst->font_height; gdk_gc_set_clip_rectangle(gc, &r); @@ -2095,43 +2226,27 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, gdk_gc_set_foreground(gc, &inst->cols[nbg]); gdk_draw_rectangle(inst->pixmap, gc, 1, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, inst->font_height); gdk_gc_set_foreground(gc, &inst->cols[nfg]); - { - gchar *gcs; - - /* - * FIXME: this length is hardwired on the assumption that - * conversions from wide to multibyte characters will - * never generate more than 10 bytes for a single wide - * character. - */ - gcs = snewn(len*10+1, gchar); - - for (combining = 0; combining < ncombining; combining++) { - int mblen = wc_to_mb(inst->fonts[fontid]->real_charset, 0, - text + combining, len, gcs, len*10+1, ".", - NULL, NULL); - unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent, - gcs, mblen, widefactor > 1, bold, inst->font_width); - } - - sfree(gcs); + for (combining = 0; combining < ncombining; combining++) { + unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+inst->fonts[0]->ascent, + text + combining, len, widefactor > 1, + bold, inst->font_width); } if (attr & ATTR_UNDER) { int uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; - gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border, - y*inst->font_height + uheight + inst->cfg.window_border, - (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border, - y*inst->font_height + uheight + inst->cfg.window_border); + gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->window_border, + y*inst->font_height + uheight + inst->window_border, + (x+len)*widefactor*inst->font_width-1+inst->window_border, + y*inst->font_height + uheight + inst->window_border); } if ((lattr & LATTR_MODE) != LATTR_NORM) { @@ -2146,10 +2261,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, int i; for (i = 0; i < len * widefactor * inst->font_width; i++) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border + 2*i, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border + 2*i+1, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border + 2*i, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border + 2*i+1, + y*inst->font_height+inst->window_border, len * widefactor * inst->font_width - i, inst->font_height); } len *= 2; @@ -2162,10 +2277,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, dt = 1, db = 0; for (i = 0; i < inst->font_height; i+=2) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+dt*i+db, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+dt*(i+1), + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+dt*i+db, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+dt*(i+1), len * widefactor * inst->font_width, inst->font_height-i-1); } } @@ -2198,10 +2313,10 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len, } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); } @@ -2219,7 +2334,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, passive = 1; } else passive = 0; - if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) { + if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) { attr &= ~TATTR_ACTCURS; active = 1; } else @@ -2244,7 +2359,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, len *= 2; } - if (inst->cfg.cursor_type == 0) { + if (inst->cursor_type == 0) { /* * An active block cursor will already have been done by * the above do_text call, so we only need to do anything @@ -2253,8 +2368,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, if (passive) { gdk_gc_set_foreground(gc, &inst->cols[261]); gdk_draw_rectangle(inst->pixmap, gc, 0, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width-1, inst->font_height-1); } } else { @@ -2268,13 +2383,13 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, else char_width = inst->font_width; - if (inst->cfg.cursor_type == 1) { + if (inst->cursor_type == 1) { uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; - startx = x * inst->font_width + inst->cfg.window_border; - starty = y * inst->font_height + inst->cfg.window_border + uheight; + startx = x * inst->font_width + inst->window_border; + starty = y * inst->font_height + inst->window_border + uheight; dx = 1; dy = 0; length = len * widefactor * char_width; @@ -2282,8 +2397,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, int xadjust = 0; if (attr & TATTR_RIGHTCURS) xadjust = char_width - 1; - startx = x * inst->font_width + inst->cfg.window_border + xadjust; - starty = y * inst->font_height + inst->cfg.window_border; + startx = x * inst->font_width + inst->window_border + xadjust; + starty = y * inst->font_height + inst->window_border; dx = 0; dy = 1; length = inst->font_height; @@ -2305,11 +2420,22 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); + +#if GTK_CHECK_VERSION(2,0,0) + { + GdkRectangle cursorrect; + cursorrect.x = x*inst->font_width+inst->window_border; + cursorrect.y = y*inst->font_height+inst->window_border; + cursorrect.width = len*widefactor*inst->font_width; + cursorrect.height = inst->font_height; + gtk_im_context_set_cursor_location(inst->imc, &cursorrect); + } +#endif } GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val) @@ -2467,8 +2593,15 @@ static void help(FILE *fp) { } } +static void version(FILE *fp) { + if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) { + perror("output error"); + exit(1); + } +} + int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, - struct gui_data *inst, Config *cfg) + struct gui_data *inst, Conf *conf) { int err = 0; char *val; @@ -2507,7 +2640,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, p = "-title"; ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - do_everything ? 1 : -1, cfg); + do_everything ? 1 : -1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", p); @@ -2519,34 +2652,41 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } if (!strcmp(p, "-fn") || !strcmp(p, "-font")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->font.name, val, sizeof(cfg->font.name)); - cfg->font.name[sizeof(cfg->font.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_font, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fb")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name)); - cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_boldfont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fw")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name)); - cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_widefont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fwb")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name)); - cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_wideboldfont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-cs")) { EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage)); - cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0'; + conf_set_str(conf, CONF_line_codepage, val); } else if (!strcmp(p, "-geometry")) { int flags, x, y; @@ -2556,9 +2696,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, flags = XParseGeometry(val, &x, &y, &w, &h); if (flags & WidthValue) - cfg->width = (int)w; + conf_set_int(conf, CONF_width, w); if (flags & HeightValue) - cfg->height = (int)h; + conf_set_int(conf, CONF_height, h); if (flags & (XValue | YValue)) { inst->xpos = x; @@ -2571,7 +2711,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } else if (!strcmp(p, "-sl")) { EXPECTS_ARG; SECOND_PASS_ONLY; - cfg->savelines = atoi(val); + conf_set_int(conf, CONF_savelines, atoi(val)); } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") || !strcmp(p, "-bfg") || !strcmp(p, "-bbg") || @@ -2593,9 +2733,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, !strcmp(p, "-cfg") ? 4 : !strcmp(p, "-cbg") ? 5 : -1); assert(index != -1); - cfg->colours[index][0] = col.red / 256; - cfg->colours[index][1] = col.green / 256; - cfg->colours[index][2] = col.blue / 256; + conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256); + conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256); + conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256); } } else if (use_pty_argv && !strcmp(p, "-e")) { @@ -2618,43 +2758,44 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } else if (!strcmp(p, "-title")) { EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->wintitle, val, sizeof(cfg->wintitle)); - cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0'; + conf_set_str(conf, CONF_wintitle, val); } else if (!strcmp(p, "-log")) { + Filename *fn; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path)); - cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0'; - cfg->logtype = LGTYP_DEBUG; + fn = filename_from_str(val); + conf_set_filename(conf, CONF_logfilename, fn); + conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); + filename_free(fn); } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) { SECOND_PASS_ONLY; - cfg->stamp_utmp = 0; + conf_set_int(conf, CONF_stamp_utmp, 0); } else if (!strcmp(p, "-ut")) { SECOND_PASS_ONLY; - cfg->stamp_utmp = 1; + conf_set_int(conf, CONF_stamp_utmp, 1); } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) { SECOND_PASS_ONLY; - cfg->login_shell = 0; + conf_set_int(conf, CONF_login_shell, 0); } else if (!strcmp(p, "-ls")) { SECOND_PASS_ONLY; - cfg->login_shell = 1; + conf_set_int(conf, CONF_login_shell, 1); } else if (!strcmp(p, "-nethack")) { SECOND_PASS_ONLY; - cfg->nethack_keypad = 1; + conf_set_int(conf, CONF_nethack_keypad, 1); } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) { SECOND_PASS_ONLY; - cfg->scrollbar = 0; + conf_set_int(conf, CONF_scrollbar, 0); } else if (!strcmp(p, "-sb")) { SECOND_PASS_ONLY; - cfg->scrollbar = 0; + conf_set_int(conf, CONF_scrollbar, 1); } else if (!strcmp(p, "-name")) { EXPECTS_ARG; @@ -2668,12 +2809,16 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, help(stdout); exit(0); + } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) { + version(stdout); + exit(0); + } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else if(p[0] != '-' && (!do_everything || - process_nonoption_arg(p, cfg, + process_nonoption_arg(p, conf, allow_launch))) { /* do nothing */ @@ -2699,86 +2844,112 @@ void uxsel_input_remove(int id) { gdk_input_remove(id); } -void setup_fonts_ucs(struct gui_data *inst) -{ - if (inst->fonts[0]) - unifont_destroy(inst->fonts[0]); - if (inst->fonts[1]) - unifont_destroy(inst->fonts[1]); - if (inst->fonts[2]) - unifont_destroy(inst->fonts[2]); - if (inst->fonts[3]) - unifont_destroy(inst->fonts[3]); - - inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name, - FALSE, FALSE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[0]) { - fprintf(stderr, "%s: unable to load font \"%s\"\n", appname, - inst->cfg.font.name); - exit(1); +int frontend_net_pending_error_idle_id; +int frontend_got_net_pending_errors = FALSE; +gboolean frontend_net_pending_errors(gpointer data) +{ + net_pending_errors(); + gtk_idle_remove(frontend_net_pending_error_idle_id); + frontend_got_net_pending_errors = FALSE; + return FALSE; +} +void frontend_net_error_pending(void) +{ + if (!frontend_got_net_pending_errors) { + frontend_got_net_pending_errors = TRUE; + frontend_net_pending_error_idle_id = + gtk_idle_add(frontend_net_pending_errors, NULL); } +} + +char *setup_fonts_ucs(struct gui_data *inst) +{ + int shadowbold = conf_get_int(inst->conf, CONF_shadowbold); + int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset); + FontSpec *fs; + unifont *fonts[4]; + int i; - if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) { - inst->fonts[1] = NULL; + fs = conf_get_fontspec(inst->conf, CONF_font); + fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE, + shadowboldoffset, shadowbold); + if (!fonts[0]) { + return dupprintf("unable to load font \"%s\"", fs->name); + } + + fs = conf_get_fontspec(inst->conf, CONF_boldfont); + if (shadowbold || !fs->name[0]) { + fonts[1] = NULL; } else { - inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name, - FALSE, TRUE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[1]) { - fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname, - inst->cfg.boldfont.name); - exit(1); + fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE, + shadowboldoffset, shadowbold); + if (!fonts[1]) { + if (fonts[0]) + unifont_destroy(fonts[0]); + return dupprintf("unable to load bold font \"%s\"", fs->name); } } - if (inst->cfg.widefont.name[0]) { - inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name, - TRUE, FALSE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[2]) { - fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname, - inst->cfg.widefont.name); - exit(1); + fs = conf_get_fontspec(inst->conf, CONF_widefont); + if (fs->name[0]) { + fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE, + shadowboldoffset, shadowbold); + if (!fonts[2]) { + for (i = 0; i < 2; i++) + if (fonts[i]) + unifont_destroy(fonts[i]); + return dupprintf("%s: unable to load wide font \"%s\"", fs->name); } } else { - inst->fonts[2] = NULL; + fonts[2] = NULL; } - if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) { - inst->fonts[3] = NULL; + fs = conf_get_fontspec(inst->conf, CONF_wideboldfont); + if (shadowbold || !fs->name[0]) { + fonts[3] = NULL; } else { - inst->fonts[3] = unifont_create(inst->area, - inst->cfg.wideboldfont.name, TRUE, - TRUE, inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[3]) { - fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname, - inst->cfg.boldfont.name); - exit(1); + fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE, + shadowboldoffset, shadowbold); + if (!fonts[3]) { + for (i = 0; i < 3; i++) + if (fonts[i]) + unifont_destroy(fonts[i]); + return dupprintf("%s: unable to load wide bold font \"%s\"", + fs->name); } } + /* + * Now we've got past all the possible error conditions, we can + * actually update our state. + */ + + for (i = 0; i < 4; i++) { + if (inst->fonts[i]) + unifont_destroy(inst->fonts[i]); + inst->fonts[i] = fonts[i]; + } + inst->font_width = inst->fonts[0]->width; inst->font_height = inst->fonts[0]->height; - inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage, - inst->cfg.utf8_override, + inst->direct_to_font = init_ucs(&inst->ucsdata, + conf_get_str(inst->conf, CONF_line_codepage), + conf_get_int(inst->conf, CONF_utf8_override), inst->fonts[0]->public_charset, - inst->cfg.vtmode); + conf_get_int(inst->conf, CONF_vtmode)); + + return NULL; } void set_geom_hints(struct gui_data *inst) { GdkGeometry geom; - geom.min_width = inst->font_width + 2*inst->cfg.window_border; - geom.min_height = inst->font_height + 2*inst->cfg.window_border; + geom.min_width = inst->font_width + 2*inst->window_border; + geom.min_height = inst->font_height + 2*inst->window_border; geom.max_width = geom.max_height = -1; - geom.base_width = 2*inst->cfg.window_border; - geom.base_height = 2*inst->cfg.window_border; + geom.base_width = 2*inst->window_border; + geom.base_height = 2*inst->window_border; geom.width_inc = inst->font_width; geom.height_inc = inst->font_height; geom.min_aspect = geom.max_aspect = 0; @@ -2831,16 +3002,16 @@ void event_log_menuitem(GtkMenuItem *item, gpointer data) void change_settings_menuitem(GtkMenuItem *item, gpointer data) { - /* This maps colour indices in inst->cfg to those used in inst->cols. */ + /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 }; struct gui_data *inst = (struct gui_data *)data; - char *title = dupcat(appname, " Reconfiguration", NULL); - Config cfg2, oldcfg; - int i, need_size; + char *title; + Conf *oldconf, *newconf; + int i, j, need_size; assert(lenof(ww) == NCFGCOLOURS); @@ -2849,50 +3020,58 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) else inst->reconfiguring = TRUE; - cfg2 = inst->cfg; /* structure copy */ + title = dupcat(appname, " Reconfiguration", NULL); - if (do_config_box(title, &cfg2, 1, - inst->back?inst->back->cfg_info(inst->backhandle):0)) { + oldconf = inst->conf; + newconf = conf_copy(inst->conf); - oldcfg = inst->cfg; /* structure copy */ - inst->cfg = cfg2; /* structure copy */ + if (do_config_box(title, newconf, 1, + inst->back?inst->back->cfg_info(inst->backhandle):0)) { + inst->conf = newconf; /* Pass new config data to the logging module */ - log_reconfig(inst->logctx, &cfg2); + log_reconfig(inst->logctx, inst->conf); /* * Flush the line discipline's edit buffer in the case * where local editing has just been disabled. */ - if (inst->ldisc) + if (inst->ldisc) { + ldisc_configure(inst->ldisc, inst->conf); ldisc_send(inst->ldisc, NULL, 0, 0); + } /* Pass new config data to the terminal */ - term_reconfig(inst->term, &cfg2); + term_reconfig(inst->term, inst->conf); /* Pass new config data to the back end */ if (inst->back) - inst->back->reconfig(inst->backhandle, &cfg2); + inst->back->reconfig(inst->backhandle, inst->conf); + + cache_conf_values(inst); /* - * Just setting inst->cfg is sufficient to cause colour + * Just setting inst->conf is sufficient to cause colour * setting changes to appear on the next ESC]R palette * reset. But we should also check whether any colour - * settings have been changed, and revert the ones that - * have to the new default, on the assumption that the user - * is most likely to want an immediate update. + * settings have been changed, and revert the ones that have + * to the new default, on the assumption that the user is + * most likely to want an immediate update. */ for (i = 0; i < NCFGCOLOURS; i++) { - if (oldcfg.colours[i][0] != cfg2.colours[i][0] || - oldcfg.colours[i][1] != cfg2.colours[i][1] || - oldcfg.colours[i][2] != cfg2.colours[i][2]) { - real_palette_set(inst, ww[i], cfg2.colours[i][0], - cfg2.colours[i][1], - cfg2.colours[i][2]); + for (j = 0; j < 3; j++) + if (conf_get_int_int(oldconf, CONF_colours, i*3+j) != + conf_get_int_int(newconf, CONF_colours, i*3+j)) + break; + if (j < 3) { + real_palette_set(inst, ww[i], + conf_get_int_int(newconf,CONF_colours,i*3+0), + conf_get_int_int(newconf,CONF_colours,i*3+1), + conf_get_int_int(newconf,CONF_colours,i*3+2)); /* * If the default background has changed, we must * repaint the space in between the window border * and the text area. */ - if (i == 258) { + if (ww[i] == 258) { set_window_background(inst); draw_backing_rect(inst); } @@ -2903,47 +3082,80 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * If the scrollbar needs to be shown, hidden, or moved * from one end to the other of the window, do so now. */ - if (oldcfg.scrollbar != cfg2.scrollbar) { - if (cfg2.scrollbar) + if (conf_get_int(oldconf, CONF_scrollbar) != + conf_get_int(newconf, CONF_scrollbar)) { + if (conf_get_int(newconf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); } - if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) { + if (conf_get_int(oldconf, CONF_scrollbar_on_left) != + conf_get_int(newconf, CONF_scrollbar_on_left)) { gtk_box_reorder_child(inst->hbox, inst->sbar, - cfg2.scrollbar_on_left ? 0 : 1); + conf_get_int(newconf, CONF_scrollbar_on_left) + ? 0 : 1); } /* * Change the window title, if required. */ - if (strcmp(oldcfg.wintitle, cfg2.wintitle)) - set_title(inst, cfg2.wintitle); + if (strcmp(conf_get_str(oldconf, CONF_wintitle), + conf_get_str(newconf, CONF_wintitle))) + set_title(inst, conf_get_str(newconf, CONF_wintitle)); set_window_titles(inst); /* * Redo the whole tangled fonts and Unicode mess if * necessary. */ - if (strcmp(oldcfg.font.name, cfg2.font.name) || - strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) || - strcmp(oldcfg.widefont.name, cfg2.widefont.name) || - strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) || - strcmp(oldcfg.line_codepage, cfg2.line_codepage) || - oldcfg.vtmode != cfg2.vtmode || - oldcfg.shadowbold != cfg2.shadowbold) { - setup_fonts_ucs(inst); - need_size = 1; - } else - need_size = 0; + need_size = FALSE; + if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name, + conf_get_fontspec(newconf, CONF_font)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name, + conf_get_fontspec(newconf, CONF_boldfont)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name, + conf_get_fontspec(newconf, CONF_widefont)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name, + conf_get_fontspec(newconf, CONF_wideboldfont)->name) || + strcmp(conf_get_str(oldconf, CONF_line_codepage), + conf_get_str(newconf, CONF_line_codepage)) || + conf_get_int(oldconf, CONF_utf8_override) != + conf_get_int(newconf, CONF_utf8_override) || + conf_get_int(oldconf, CONF_vtmode) != + conf_get_int(newconf, CONF_vtmode) || + conf_get_int(oldconf, CONF_shadowbold) != + conf_get_int(newconf, CONF_shadowbold) || + conf_get_int(oldconf, CONF_shadowboldoffset) != + conf_get_int(newconf, CONF_shadowboldoffset)) { + char *errmsg = setup_fonts_ucs(inst); + if (errmsg) { + char *msgboxtext = + dupprintf("Could not change fonts in terminal window: %s\n", + errmsg); + messagebox(inst->window, "Font setup error", msgboxtext, + string_width("Could not change fonts in terminal window:"), + "OK", 'o', +1, 1, + NULL); + sfree(msgboxtext); + sfree(errmsg); + } else { + need_size = TRUE; + } + } /* * Resize the window. */ - if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height || - oldcfg.window_border != cfg2.window_border || need_size) { + if (conf_get_int(oldconf, CONF_width) != + conf_get_int(newconf, CONF_width) || + conf_get_int(oldconf, CONF_height) != + conf_get_int(newconf, CONF_height) || + conf_get_int(oldconf, CONF_window_border) != + conf_get_int(newconf, CONF_window_border) || + need_size) { set_geom_hints(inst); - request_resize(inst, cfg2.width, cfg2.height); + request_resize(inst, conf_get_int(newconf, CONF_width), + conf_get_int(newconf, CONF_height)); } else { /* * The above will have caused a call to term_size() for @@ -2952,9 +3164,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * happened and we will need an explicit term_size() * here. */ - if (oldcfg.savelines != cfg2.savelines) + if (conf_get_int(oldconf, CONF_savelines) != + conf_get_int(newconf, CONF_savelines)) term_size(inst->term, inst->term->rows, inst->term->cols, - cfg2.savelines); + conf_get_int(newconf, CONF_savelines)); } term_invalidate(inst->term); @@ -2964,6 +3177,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * border has been redrawn as well as the text area. */ gtk_widget_queue_draw(inst->area); + + conf_free(oldconf); + } else { + conf_free(newconf); } sfree(title); inst->reconfiguring = FALSE; @@ -3014,6 +3231,7 @@ void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...) pid = fork(); if (pid < 0) { perror("fork"); + sfree(args); return; } @@ -3047,6 +3265,7 @@ void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...) } else { int status; + sfree(args); waitpid(pid, &status, 0); } @@ -3056,11 +3275,11 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { struct gui_data *inst = (struct gui_data *)gdata; /* - * For this feature we must marshal cfg and (possibly) pty_argv + * For this feature we must marshal conf and (possibly) pty_argv * into a byte stream, create a pipe, and send this byte stream * to the child through the pipe. */ - int i, ret, size; + int i, ret, sersize, size; char *data; char option[80]; int pipefd[2]; @@ -3070,16 +3289,16 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) return; } - size = sizeof(inst->cfg); + size = sersize = conf_serialised_size(inst->conf); if (use_pty_argv && pty_argv) { for (i = 0; pty_argv[i]; i++) size += strlen(pty_argv[i]) + 1; } data = snewn(size, char); - memcpy(data, &inst->cfg, sizeof(inst->cfg)); + conf_serialise(inst->conf, data); if (use_pty_argv && pty_argv) { - int p = sizeof(inst->cfg); + int p = sersize; for (i = 0; pty_argv[i]; i++) { strcpy(data + p, pty_argv[i]); p += strlen(pty_argv[i]) + 1; @@ -3088,7 +3307,7 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) } sprintf(option, "---[%d,%d]", pipefd[0], size); - fcntl(pipefd[0], F_SETFD, 0); + noncloexec(pipefd[0]); fork_and_exec_self(inst, pipefd[1], option, NULL); close(pipefd[0]); @@ -3101,9 +3320,9 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) sfree(data); } -int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) +int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg) { - int fd, i, ret, size; + int fd, i, ret, size, size_used; char *data; if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) { @@ -3124,10 +3343,10 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) exit(1); } - memcpy(cfg, data, sizeof(Config)); - if (use_pty_argv && size > sizeof(Config)) { + size_used = conf_deserialise(conf, data, size); + if (use_pty_argv && size > size_used) { int n = 0; - i = sizeof(Config); + i = size_used; while (i < size) { while (i < size && data[i]) i++; if (i >= size) { @@ -3141,7 +3360,7 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) pty_argv = snewn(n+1, char *); pty_argv[n] = NULL; n = 0; - i = sizeof(Config); + i = size_used; while (i < size) { char *p = data + i; while (i < size && data[i]) i++; @@ -3151,6 +3370,8 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) } } + sfree(data); + return 0; } @@ -3320,33 +3541,36 @@ void update_specials_menu(void *frontend) static void start_backend(struct gui_data *inst) { - extern Backend *select_backend(Config *cfg); + extern Backend *select_backend(Conf *conf); char *realhost; const char *error; + char *s; - inst->back = select_backend(&inst->cfg); + inst->back = select_backend(inst->conf); error = inst->back->init((void *)inst, &inst->backhandle, - &inst->cfg, inst->cfg.host, inst->cfg.port, - &realhost, inst->cfg.tcp_nodelay, - inst->cfg.tcp_keepalives); + inst->conf, + conf_get_str(inst->conf, CONF_host), + conf_get_int(inst->conf, CONF_port), + &realhost, + conf_get_int(inst->conf, CONF_tcp_nodelay), + conf_get_int(inst->conf, CONF_tcp_keepalives)); if (error) { char *msg = dupprintf("Unable to open connection to %s:\n%s", - inst->cfg.host, error); + conf_get_str(inst->conf, CONF_host), error); inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); exit(0); } - if (inst->cfg.wintitle[0]) { - set_title(inst, inst->cfg.wintitle); - set_icon(inst, inst->cfg.wintitle); + s = conf_get_str(inst->conf, CONF_wintitle); + if (s[0]) { + set_title_and_icon(inst, s, s); } else { char *title = make_default_wintitle(realhost); - set_title(inst, title); - set_icon(inst, title); + set_title_and_icon(inst, title, title); sfree(title); } sfree(realhost); @@ -3356,7 +3580,7 @@ static void start_backend(struct gui_data *inst) term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle); inst->ldisc = - ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle, + ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle, inst); gtk_widget_set_sensitive(inst->restartitem, FALSE); @@ -3364,9 +3588,11 @@ static void start_backend(struct gui_data *inst) int pt_main(int argc, char **argv) { - extern int cfgbox(Config *cfg); + extern int cfgbox(Conf *conf); struct gui_data *inst; + setlocale(LC_CTYPE, ""); + /* * Create an instance structure and initialise to zeroes */ @@ -3374,6 +3600,8 @@ int pt_main(int argc, char **argv) memset(inst, 0, sizeof(*inst)); inst->alt_keycode = -1; /* this one needs _not_ to be zero */ inst->busy_status = BUSY_NOT; + inst->conf = conf_new(); + inst->wintitle = inst->icontitle = NULL; /* defer any child exit handling until we're ready to deal with * it */ @@ -3395,27 +3623,27 @@ int pt_main(int argc, char **argv) } if (argc > 1 && !strncmp(argv[1], "---", 3)) { - read_dupsession_data(inst, &inst->cfg, argv[1]); + read_dupsession_data(inst, inst->conf, argv[1]); /* Splatter this argument so it doesn't clutter a ps listing */ - memset(argv[1], 0, strlen(argv[1])); + smemclr(argv[1], strlen(argv[1])); } else { /* 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)) + if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf)) exit(1); /* pre-defaults pass to get -class */ - do_defaults(NULL, &inst->cfg); - if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg)) + do_defaults(NULL, inst->conf); + if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf)) exit(1); /* post-defaults, do everything */ - cmdline_run_saved(&inst->cfg); + cmdline_run_saved(inst->conf); if (loaded_session) allow_launch = TRUE; - if ((!allow_launch || !cfg_launchable(&inst->cfg)) && - !cfgbox(&inst->cfg)) + if ((!allow_launch || !conf_launchable(inst->conf)) && + !cfgbox(inst->conf)) exit(0); /* config box hit Cancel */ } @@ -3426,25 +3654,39 @@ int pt_main(int argc, char **argv) inst->area = gtk_drawing_area_new(); - setup_fonts_ucs(inst); +#if GTK_CHECK_VERSION(2,0,0) + inst->imc = gtk_im_multicontext_new(); +#endif + + { + char *errmsg = setup_fonts_ucs(inst); + if (errmsg) { + fprintf(stderr, "%s: %s\n", appname, errmsg); + exit(1); + } + } init_cutbuffers(); inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - if (inst->cfg.winclass[0]) - gtk_window_set_wmclass(GTK_WINDOW(inst->window), - inst->cfg.winclass, inst->cfg.winclass); + { + const char *winclass = conf_get_str(inst->conf, CONF_winclass); + if (*winclass) + gtk_window_set_wmclass(GTK_WINDOW(inst->window), + winclass, winclass); + } /* * Set up the colour map. */ palette_reset(inst); - inst->width = inst->cfg.width; - inst->height = inst->cfg.height; + inst->width = conf_get_int(inst->conf, CONF_width); + inst->height = conf_get_int(inst->conf, CONF_height); + cache_conf_values(inst); gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), - inst->font_width * inst->cfg.width + 2*inst->cfg.window_border, - inst->font_height * inst->cfg.height + 2*inst->cfg.window_border); + inst->font_width * inst->width + 2*inst->window_border, + inst->font_height * inst->height + 2*inst->window_border); inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0)); inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust); inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); @@ -3453,10 +3695,10 @@ int pt_main(int argc, char **argv) * unwanted, so we can pop it up quickly if it suddenly becomes * desirable. */ - if (inst->cfg.scrollbar_on_left) + if (conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0); - if (!inst->cfg.scrollbar_on_left) + if (!conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox)); @@ -3464,7 +3706,7 @@ int pt_main(int argc, char **argv) set_geom_hints(inst); gtk_widget_show(inst->area); - if (inst->cfg.scrollbar) + if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); @@ -3512,7 +3754,11 @@ int pt_main(int argc, char **argv) GTK_SIGNAL_FUNC(selection_get), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event", GTK_SIGNAL_FUNC(selection_clear), inst); - if (inst->cfg.scrollbar) +#if GTK_CHECK_VERSION(2,0,0) + g_signal_connect(G_OBJECT(inst->imc), "commit", + G_CALLBACK(input_method_commit_event), inst); +#endif + if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed", GTK_SIGNAL_FUNC(scrollbar_moved), inst); gtk_widget_add_events(GTK_WIDGET(inst->area), @@ -3612,13 +3858,14 @@ int pt_main(int argc, char **argv) inst->eventlogstuff = eventlogstuff_new(); - inst->term = term_init(&inst->cfg, &inst->ucsdata, inst); - inst->logctx = log_init(inst, &inst->cfg); + inst->term = term_init(inst->conf, &inst->ucsdata, inst); + inst->logctx = log_init(inst, inst->conf); term_provide_logctx(inst->term, inst->logctx); uxsel_init(); - term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines); + term_size(inst->term, inst->height, inst->width, + conf_get_int(inst->conf, CONF_savelines)); start_backend(inst); diff --git a/contrib/putty/UNIX/MAKEFILE.AM b/contrib/putty/UNIX/MAKEFILE.AM new file mode 100644 index 0000000..99ef1ce --- /dev/null +++ b/contrib/putty/UNIX/MAKEFILE.AM @@ -0,0 +1,338 @@ +# Makefile.am for putty under Unix with Autoconf/Automake. +# +# This file was created by `mkfiles.pl' from the `Recipe' file. +# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. + +allsources = $(srcdir)/../be_all_s.c $(srcdir)/../be_none.c \ + $(srcdir)/../be_nos_s.c $(srcdir)/../be_ssh.c \ + $(srcdir)/../charset/charset.h $(srcdir)/../charset/enum.c \ + $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/internal.h \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdgen.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../config.c \ + $(srcdir)/../cproxy.c $(srcdir)/../dialog.c \ + $(srcdir)/../dialog.h $(srcdir)/../import.c \ + $(srcdir)/../int64.c $(srcdir)/../int64.h \ + $(srcdir)/../ldisc.c $(srcdir)/../ldisc.h \ + $(srcdir)/../ldiscucs.c $(srcdir)/../logging.c \ + $(srcdir)/../macosx/osx.h $(srcdir)/../macosx/osxclass.h \ + $(srcdir)/../macosx/osxctrls.m $(srcdir)/../macosx/osxdlg.m \ + $(srcdir)/../macosx/osxmain.m $(srcdir)/../macosx/osxsel.m \ + $(srcdir)/../macosx/osxwin.m $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../misc.h \ + $(srcdir)/../network.h $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../notiming.c \ + $(srcdir)/../pgssapi.c $(srcdir)/../pgssapi.h \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../proxy.h \ + $(srcdir)/../pscp.c $(srcdir)/../psftp.c \ + $(srcdir)/../psftp.h $(srcdir)/../putty.h \ + $(srcdir)/../puttymem.h $(srcdir)/../puttyps.h \ + $(srcdir)/../raw.c $(srcdir)/../rlogin.c \ + $(srcdir)/../sercfg.c $(srcdir)/../settings.c \ + $(srcdir)/../sftp.c $(srcdir)/../sftp.h $(srcdir)/../ssh.c \ + $(srcdir)/../ssh.h $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshdssg.c $(srcdir)/../sshgss.h \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshgssc.h \ + $(srcdir)/../sshmd5.c $(srcdir)/../sshprime.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshrsag.c \ + $(srcdir)/../sshsh256.c $(srcdir)/../sshsh512.c \ + $(srcdir)/../sshsha.c $(srcdir)/../sshzlib.c \ + $(srcdir)/../storage.h $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../terminal.h \ + $(srcdir)/../testback.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../tree234.h $(srcdir)/../unix/gtkcfg.c \ + $(srcdir)/../unix/gtkcols.c $(srcdir)/../unix/gtkcols.h \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkfont.h $(srcdir)/../unix/gtkwin.c \ + $(srcdir)/../unix/unix.h $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxcons.c $(srcdir)/../unix/uxgen.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxplink.c $(srcdir)/../unix/uxprint.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxpterm.c \ + $(srcdir)/../unix/uxpty.c $(srcdir)/../unix/uxputty.c \ + $(srcdir)/../unix/uxsel.c $(srcdir)/../unix/uxser.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmptcfg.c \ + $(srcdir)/../unix/xpmpterm.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../version.c \ + $(srcdir)/../wcwidth.c $(srcdir)/../wildcard.c \ + $(srcdir)/../windows/pageant.rc \ + $(srcdir)/../windows/plink.rc $(srcdir)/../windows/pscp.rc \ + $(srcdir)/../windows/psftp.rc $(srcdir)/../windows/putty.rc \ + $(srcdir)/../windows/puttygen.rc \ + $(srcdir)/../windows/puttytel.rc \ + $(srcdir)/../windows/rcstuff.h \ + $(srcdir)/../windows/sizetip.c \ + $(srcdir)/../windows/version.rc2 \ + $(srcdir)/../windows/win_res.h \ + $(srcdir)/../windows/win_res.rc2 \ + $(srcdir)/../windows/wincfg.c $(srcdir)/../windows/wincons.c \ + $(srcdir)/../windows/winctrls.c \ + $(srcdir)/../windows/windefs.c $(srcdir)/../windows/windlg.c \ + $(srcdir)/../windows/window.c $(srcdir)/../windows/wingss.c \ + $(srcdir)/../windows/winhandl.c \ + $(srcdir)/../windows/winhelp.c \ + $(srcdir)/../windows/winhelp.h \ + $(srcdir)/../windows/winjump.c \ + $(srcdir)/../windows/winmisc.c $(srcdir)/../windows/winnet.c \ + $(srcdir)/../windows/winnoise.c \ + $(srcdir)/../windows/winnojmp.c \ + $(srcdir)/../windows/winpgen.c \ + $(srcdir)/../windows/winpgnt.c \ + $(srcdir)/../windows/winpgntc.c \ + $(srcdir)/../windows/winplink.c \ + $(srcdir)/../windows/winprint.c \ + $(srcdir)/../windows/winproxy.c \ + $(srcdir)/../windows/winser.c $(srcdir)/../windows/winsftp.c \ + $(srcdir)/../windows/winstore.c \ + $(srcdir)/../windows/winstuff.h \ + $(srcdir)/../windows/wintime.c $(srcdir)/../windows/winucs.c \ + $(srcdir)/../windows/winutils.c \ + $(srcdir)/../windows/winx11.c $(srcdir)/../x11fwd.c + +if HAVE_GTK +bin_PROGRAMS = plink pscp psftp puttygen pterm putty puttytel +else +bin_PROGRAMS = plink pscp psftp puttygen +endif + +AM_CPPFLAGS = -I$(srcdir)/.././ -I$(srcdir)/../charset/ \ + -I$(srcdir)/../windows/ -I$(srcdir)/../unix/ \ + -I$(srcdir)/../macosx/ +if HAVE_GTK +AM_CFLAGS = $(GTK_CFLAGS) $(COMPAT) $(XFLAGS) $(WARNINGOPTS) +else +AM_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) +endif + +libversion_a_SOURCES = $(srcdir)/../version.c +libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) $(VER) \ + -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; \ + md5sum -c manifest >/dev/null 2>&1); then cat \ + $(srcdir)/../version.def; else echo "$(VER)"; fi` +noinst_LIBRARIES = libversion.a + +plink_SOURCES = $(srcdir)/../be_all_s.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../ldisc.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../raw.c \ + $(srcdir)/../rlogin.c $(srcdir)/../settings.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../telnet.c \ + $(srcdir)/../time.c $(srcdir)/../timing.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxplink.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxsel.c $(srcdir)/../unix/uxser.c \ + $(srcdir)/../unix/uxsignal.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +plink_LDADD = libversion.a + +pscp_SOURCES = $(srcdir)/../be_ssh.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../int64.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../pscp.c \ + $(srcdir)/../settings.c $(srcdir)/../sftp.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +pscp_LDADD = libversion.a + +psftp_SOURCES = $(srcdir)/../be_ssh.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../int64.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../psftp.c \ + $(srcdir)/../settings.c $(srcdir)/../sftp.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +psftp_LDADD = libversion.a + +if HAVE_GTK +pterm_SOURCES = $(srcdir)/../be_none.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../dialog.c \ + $(srcdir)/../ldisc.c $(srcdir)/../ldiscucs.c \ + $(srcdir)/../logging.c $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../sercfg.c \ + $(srcdir)/../settings.c $(srcdir)/../terminal.c \ + $(srcdir)/../time.c $(srcdir)/../timing.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/gtkcfg.c \ + $(srcdir)/../unix/gtkcols.c $(srcdir)/../unix/gtkdlg.c \ + $(srcdir)/../unix/gtkfont.c $(srcdir)/../unix/gtkwin.c \ + $(srcdir)/../unix/uxcfg.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxpterm.c \ + $(srcdir)/../unix/uxpty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsignal.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../unix/uxucs.c $(srcdir)/../unix/xkeysym.c \ + $(srcdir)/../unix/xpmptcfg.c $(srcdir)/../unix/xpmpterm.c \ + $(srcdir)/../wcwidth.c +pterm_LDADD = libversion.a $(GTK_LIBS) +endif + +if HAVE_GTK +putty_SOURCES = $(srcdir)/../be_all_s.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../cproxy.c \ + $(srcdir)/../dialog.c $(srcdir)/../ldisc.c \ + $(srcdir)/../ldiscucs.c $(srcdir)/../logging.c \ + $(srcdir)/../minibidi.c $(srcdir)/../misc.c \ + $(srcdir)/../pgssapi.c $(srcdir)/../pinger.c \ + $(srcdir)/../portfwd.c $(srcdir)/../proxy.c \ + $(srcdir)/../raw.c $(srcdir)/../rlogin.c \ + $(srcdir)/../sercfg.c $(srcdir)/../settings.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/gtkcfg.c $(srcdir)/../unix/gtkcols.c \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkwin.c $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxputty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxser.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../wcwidth.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +putty_LDADD = libversion.a $(GTK_LIBS) +endif + +puttygen_SOURCES = $(srcdir)/../cmdgen.c $(srcdir)/../conf.c \ + $(srcdir)/../import.c $(srcdir)/../misc.c \ + $(srcdir)/../notiming.c $(srcdir)/../sshaes.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdss.c $(srcdir)/../sshdssg.c \ + $(srcdir)/../sshmd5.c $(srcdir)/../sshprime.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshrsag.c \ + $(srcdir)/../sshsh256.c $(srcdir)/../sshsh512.c \ + $(srcdir)/../sshsha.c $(srcdir)/../time.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgen.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnoise.c $(srcdir)/../unix/uxstore.c +puttygen_LDADD = libversion.a + +if HAVE_GTK +puttytel_SOURCES = $(srcdir)/../be_nos_s.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../dialog.c \ + $(srcdir)/../ldisc.c $(srcdir)/../ldiscucs.c \ + $(srcdir)/../logging.c $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../pinger.c \ + $(srcdir)/../proxy.c $(srcdir)/../raw.c \ + $(srcdir)/../rlogin.c $(srcdir)/../sercfg.c \ + $(srcdir)/../settings.c $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/gtkcfg.c $(srcdir)/../unix/gtkcols.c \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkwin.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxmisc.c $(srcdir)/../unix/uxnet.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxputty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxser.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../wcwidth.c +puttytel_LDADD = libversion.a $(GTK_LIBS) +endif + +BUILT_SOURCES = empty.h +empty.h: $(allsources) + echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ + +man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \ + ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1 +if HAVE_SETID_CMD +install-exec-local: + @SETID_CMD@ $(bindir)/pterm + chmod @SETID_MODE@ $(bindir)/pterm +endif diff --git a/contrib/putty/UNIX/MAKEFILE.GTK b/contrib/putty/UNIX/MAKEFILE.GTK index 480e052..c49e8d2 100644 --- a/contrib/putty/UNIX/MAKEFILE.GTK +++ b/contrib/putty/UNIX/MAKEFILE.GTK @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -148,16 +153,7 @@ man1dir=$(mandir)/man1 all: plink pscp psftp pterm putty puttygen puttytel -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ @@ -165,9 +161,18 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -175,16 +180,17 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -192,46 +198,49 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o psftp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \ - gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \ - nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \ - xpmptcfg.o xpmpterm.o - $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ +pterm: be_none.o cmdline.o conf.o config.o dialog.o fromucs.o gtkcfg.o \ gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \ slookup.o terminal.o time.o timing.o toucs.o tree234.o \ utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \ uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) + xkeysym.o xpmptcfg.o xpmpterm.o + $(CC) -o $@ be_none.o cmdline.o conf.o config.o dialog.o fromucs.o \ + gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ + ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ + minibidi.o misc.o nocproxy.o nogss.o sbcs.o sbcsdat.o \ + sercfg.o settings.o slookup.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o uxcfg.o uxmisc.o uxprint.o \ + uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ + version.o wcwidth.o xenc.o xkeysym.o xpmptcfg.o xpmpterm.o \ + $(XLDFLAGS) -putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ - uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ - uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \ +putty: be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o fromucs.o \ + gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ + ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ + minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ + rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ + ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \ + uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \ + uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ + wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ + xpmputty.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o \ fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \ ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ @@ -246,18 +255,18 @@ putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ xpmputty.o $(XLDFLAGS) -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) + uxstore.o version.o + $(CC) -o $@ cmdgen.o conf.o import.o misc.o notiming.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ + uxnoise.o uxstore.o version.o $(ULDFLAGS) -puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ +puttytel: be_nos_s.o cmdline.o conf.o config.o dialog.o fromucs.o gtkcfg.o \ gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \ @@ -266,7 +275,7 @@ puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \ + $(CC) -o $@ be_nos_s.o cmdline.o conf.o config.o dialog.o fromucs.o \ gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \ @@ -306,6 +315,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -584,7 +598,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c diff --git a/contrib/putty/UNIX/MAKEFILE.IN b/contrib/putty/UNIX/MAKEFILE.IN deleted file mode 100644 index fc5d577..0000000 --- a/contrib/putty/UNIX/MAKEFILE.IN +++ /dev/null @@ -1,877 +0,0 @@ -# Makefile.in for putty under Unix with Autoconf. -# -# This file was created by `mkfiles.pl' from the `Recipe' file. -# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. -# -# Extra options you can set: -# -# - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234" -# Generates executables whose About box report them as being a -# development snapshot. SVN_REV is a Subversion revision number. -# -# - VER=-DRELEASE=0.43 -# Generates executables whose About box report them as being a -# release version. -# -# - COMPAT=-DAUTO_WINSOCK (Windows only) -# Causes PuTTY to assume that includes its own WinSock -# header file, so that it won't try to include . -# -# - COMPAT=-DWINSOCK_TWO (Windows only) -# Causes the PuTTY utilities to include instead of -# , except Plink which _needs_ WinSock 2 so it already -# does this. -# -# - COMPAT=-DNO_SECURITY (Windows only) -# Disables Pageant's use of , which is not available -# with some development environments (such as older versions of -# the Cygwin/mingw GNU toolchain). This means that Pageant -# won't care about the local user ID of processes accessing it; a -# version of Pageant built with this option will therefore refuse -# to run under NT-series OSes on security grounds (although it -# will run fine on Win95-series OSes where there is no access -# control anyway). -# -# - COMPAT=-DNO_MULTIMON (Windows only) -# Disables PuTTY's use of , which is not available -# with some development environments. This means that PuTTY's -# full-screen mode (configurable to work on Alt-Enter) will -# not behave usefully in a multi-monitor environment. -# -# Note that this definition is always enabled in the Cygwin -# build, since at the time of writing this is -# known not to be available in Cygwin. -# -# - COMPAT=-DNO_HTMLHELP (Windows only) -# Disables PuTTY's use of , which is not available -# with some development environments. The resulting binary -# will only look for an old-style WinHelp file (.HLP/.CNT), and -# will ignore any .CHM file. -# -# Note that this definition is always enabled in the Cygwin -# build, since at the time of writing this is -# 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 -# binaries. This may be necessary to build for 64-bit Windows; -# the manifests are only included to use the XP GUI style on -# Windows XP, and the architecture tags are a lie on 64-bit. -# -# - COMPAT=-DNO_IPV6 -# Disables PuTTY's ability to make IPv6 connections, enabling -# it to compile under development environments which do not -# support IPv6 in their header files. -# -# - COMPAT=-DNO_GSSAPI -# Disables PuTTY's ability to use GSSAPI functions for -# authentication and key exchange. -# -# - COMPAT=-DSTATIC_GSSAPI -# Causes PuTTY to try to link statically against the GSSAPI -# library instead of the default of doing it at run time. -# -# - COMPAT=-DMSVC4 (Windows only) -# - RCFL=-DMSVC4 -# Makes a couple of minor changes so that PuTTY compiles using -# MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. -# -# - RCFL=-DASCIICTLS (Windows only) -# Uses ASCII rather than Unicode to specify the tab control in -# the resource file. Probably most useful when compiling with -# Cygnus/mingw32, whose resource compiler may have less of a -# problem with it. -# -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# -# - XFLAGS=-DDEBUG -# Causes PuTTY to enable internal debugging. -# -# - XFLAGS=-DMALLOC_LOG -# Causes PuTTY to emit a file called putty_mem.log, logging every -# memory allocation and free, so you can track memory leaks. -# -# - XFLAGS=-DMINEFIELD (Windows only) -# Causes PuTTY to use a custom memory allocator, similar in -# concept to Electric Fence, in place of regular malloc(). Wastes -# huge amounts of RAM, but should cause heap-corruption bugs to -# show up as GPFs at the point of failure rather than appearing -# later on as second-level damage. -# - -CC = @CC@ - -CFLAGS = @CFLAGS@ @PUTTYCFLAGS@ @CPPFLAGS@ @DEFS@ @GTK_CFLAGS@ -I.././ \ - -I../charset/ -I../windows/ -I../unix/ -I../macosx/ -XLDFLAGS = @LDFLAGS@ @LIBS@ @GTK_LIBS@ -ULDFLAGS = @LDFLAGS@ @LIBS@ -INSTALL=@INSTALL@ -INSTALL_PROGRAM=$(INSTALL) -INSTALL_DATA=$(INSTALL) -prefix=@prefix@ -exec_prefix=@exec_prefix@ -bindir=@bindir@ -datarootdir=@datarootdir@ -mandir=@mandir@ -man1dir=$(mandir)/man1 - - -.SUFFIXES: - - -all: @all_targets@ -all-cli: plink pscp psftp puttygen -all-gtk: pterm putty puttytel - -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ - settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ - ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ - uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ - uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ - uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \ - gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \ - nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \ - xpmptcfg.o xpmpterm.o - $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \ - slookup.o terminal.o time.o timing.o toucs.o tree234.o \ - utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \ - uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) - -putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ - uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ - uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \ - fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \ - ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ - minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ - rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \ - tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ - wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ - xpmputty.o $(XLDFLAGS) - -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ - sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) - -puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o uxcfg.o uxmisc.o \ - uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ - uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \ - gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ - ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ - minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \ - rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \ - uxcfg.o uxmisc.o uxnet.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ - wcwidth.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) - -be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c -be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c -be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c -be_ssh.o: ../be_ssh.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_ssh.c -cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c -cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c -config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c -cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ - ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c -dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c -fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c -gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c -gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c -gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \ - ../storage.h ../dialog.h ../tree234.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c -gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c -gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \ - ../puttyps.h ../network.h ../misc.h ../tree234.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c -import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c -int64.o: ../int64.c ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c -ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ - ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c -ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ - ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c -localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c -logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c -macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c -mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c -minibidi.o: ../minibidi.c ../misc.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c -misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c -nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c -nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c -notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c -osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \ - ../tree234.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m -osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \ - ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m -osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m -osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m -osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \ - ../puttyps.h ../network.h ../misc.h ../tree234.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m -pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c -pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c -portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c -proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c -pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ - ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c -psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ - ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c -raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c -rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c -sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c -sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c -sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c -settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c -sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c -sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c -slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ - ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c -ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \ - ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c -sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c -ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c -sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c -sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c -sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c -sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c -sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c -sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c -sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c -sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c -sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \ - ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../tree234.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c -sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c -sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c -sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c -sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c -sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c -sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c -sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c -sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c -sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c -sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c -telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c -terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \ - ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c -testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c -time.o: ../time.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c -timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c -toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c -utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c -ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c -uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ - ../puttymem.h ../puttyps.h ../network.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c -uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c -uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c -uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c -uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c -uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c -uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c -uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c -uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c -uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c -uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ - ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c -uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c -uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c -uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c -uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c -uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c -uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c -uxsignal.o: ../unix/uxsignal.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c -uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c -uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ - ../misc.h ../puttyps.h ../network.h ../tree234.h \ - ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c -wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c -wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c -wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c -wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c -winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \ - ../puttyps.h ../network.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../tree234.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c -windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c -windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ - ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \ - ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c -window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ - ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c -wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ - ../sshgssc.h ../misc.h ../puttyps.h ../network.h \ - ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c -winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c -winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c -winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c -winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c -winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \ - ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c -winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c -winnojmp.o: ../windows/winnojmp.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c -winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c -winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ - ../puttyps.h ../network.h ../puttymem.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c -winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c -winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c -winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c -winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \ - ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c -winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c -winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c -winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c -wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c -winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \ - ../puttyps.h ../network.h ../tree234.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c -winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c -winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c -x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c -xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c -xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c -xpmptcfg.o: ../unix/xpmptcfg.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c -xpmpterm.o: ../unix/xpmpterm.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c -xpmpucfg.o: ../unix/xpmpucfg.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c -xpmputty.o: ../unix/xpmputty.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c - -version.o: FORCE - if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \ - $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \ - else \ - $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \ - fi -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 - $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm - if test -n "$(UTMP_GROUP)"; then \ - chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \ - chmod 2755 $(DESTDIR)$(bindir)/pterm; \ - elif test -n "$(UTMP_USER)"; then \ - chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \ - chmod 4755 $(DESTDIR)$(bindir)/pterm; \ - fi - $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty - $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen - $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel - $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1 - $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1 - $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1 - $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1 - $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1 - $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1 - $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1 - -install-strip: - $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" - -clean: - rm -f *.o plink pscp psftp pterm putty puttygen puttytel - -distclean: clean - rm -f config.status config.cache config.log configure.lineno \ - config.status.lineno Makefile - -FORCE: diff --git a/contrib/putty/UNIX/MAKEFILE.UX b/contrib/putty/UNIX/MAKEFILE.UX index 5b03b95..f4c584b 100644 --- a/contrib/putty/UNIX/MAKEFILE.UX +++ b/contrib/putty/UNIX/MAKEFILE.UX @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -129,16 +134,7 @@ man1dir=$(mandir)/man1 all: plink pscp psftp puttygen -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ @@ -146,9 +142,18 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -156,16 +161,17 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -173,25 +179,26 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o psftp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) + uxstore.o version.o + $(CC) -o $@ cmdgen.o conf.o import.o misc.o notiming.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ + uxnoise.o uxstore.o version.o $(ULDFLAGS) be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -223,6 +230,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -501,7 +513,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c diff --git a/contrib/putty/UNIX/UNIX.H b/contrib/putty/UNIX/UNIX.H index 198085f..89ef1e0 100644 --- a/contrib/putty/UNIX/UNIX.H +++ b/contrib/putty/UNIX/UNIX.H @@ -13,13 +13,14 @@ #include "charset.h" struct Filename { - char path[FILENAME_MAX]; + char *path; }; -FILE *f_open(struct Filename, char const *, int); +FILE *f_open(const struct Filename *, char const *, int); struct FontSpec { - char name[256]; + char *name; /* may be "" to indicate no selected font at all */ }; +struct FontSpec *fontspec_new(const char *name); typedef void *Context; /* FIXME: probably needs changing */ @@ -59,11 +60,6 @@ unsigned long getticks(void); /* based on gettimeofday(2) */ #define GETTICKCOUNT getticks #define TICKSPERSEC 1000 /* we choose to use milliseconds */ #define CURSORBLINK 450 /* no standard way to set this */ -/* getticks() works using gettimeofday(), so it's vulnerable to system clock - * changes causing chaos. Therefore, we provide a compensation mechanism. */ -#define TIMING_SYNC -#define TIMING_SYNC_ANOW -extern long tickcount_offset; #define WCHAR wchar_t #define BYTE unsigned char @@ -89,18 +85,23 @@ long get_windowid(void *frontend); void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ /* Things pterm.c needs from gtkdlg.c */ -int do_config_box(const char *title, Config *cfg, +int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo); void fatal_message_box(void *window, char *msg); +void nonfatal_message_box(void *window, char *msg); void about_box(void *window); void *eventlogstuff_new(void); void showeventlog(void *estuff, void *parentwin); void logevent_dlg(void *estuff, const char *string); int reallyclose(void *frontend); +#ifdef MAY_REFER_TO_GTK_IN_HEADERS +int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...); +int string_width(char *text); +#endif /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); -int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch); +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); @@ -155,7 +156,10 @@ void (*putty_signal(int sig, void (*func)(int)))(int); void block_signal(int sig, int block_it); /* uxmisc.c */ -int cloexec(int); +void cloexec(int); +void noncloexec(int); +int nonblock(int); +int no_nonblock(int); /* * Exports from unicode.c. @@ -170,6 +174,13 @@ int init_ucs(struct unicode_data *ucsdata, char *line_codepage, void *sk_getxdmdata(void *sock, int *lenp); /* + * Function provided by front ends, and called by uxnet.c to indicate + * that net_pending_errors() would like to be called back when the + * front end has a spare moment and isn't deep in any other recursion. + */ +void frontend_net_error_pending(void); + +/* * General helpful Unix stuff: more helpful version of the FD_SET * macro, which also handles maxfd. */ diff --git a/contrib/putty/UNIX/UXAGENTC.C b/contrib/putty/UNIX/UXAGENTC.C index 74cd9cd..1642a69 100644 --- a/contrib/putty/UNIX/UXAGENTC.C +++ b/contrib/putty/UNIX/UXAGENTC.C @@ -17,7 +17,8 @@ int agent_exists(void) { - if (getenv("SSH_AUTH_SOCK") != NULL) + const char *p = getenv("SSH_AUTH_SOCK"); + if (p && *p) return TRUE; return FALSE; } @@ -74,13 +75,12 @@ static int agent_select_result(int fd, int event) } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { - conn->retsize = GET_32BIT(conn->retbuf); + conn->retsize = toint(GET_32BIT(conn->retbuf) + 4); if (conn->retsize <= 0) { conn->retbuf = NULL; conn->retlen = 0; goto done; } - conn->retsize += 4; assert(conn->retbuf == conn->sizebuf); conn->retbuf = snewn(conn->retsize, char); memcpy(conn->retbuf, conn->sizebuf, 4); diff --git a/contrib/putty/UNIX/UXCFG.C b/contrib/putty/UNIX/UXCFG.C index 2e872cc..0e0bab2 100644 --- a/contrib/putty/UNIX/UXCFG.C +++ b/contrib/putty/UNIX/UXCFG.C @@ -16,11 +16,11 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) union control *c; /* - * The Config structure contains two Unix-specific elements - * which are not configured in here: stamp_utmp and - * login_shell. This is because pterm does not put up a - * configuration box right at the start, which is the only time - * when these elements would be useful to configure. + * The Conf structure contains two Unix-specific elements which + * are not configured in here: stamp_utmp and login_shell. This + * is because pterm does not put up a configuration box right at + * the start, which is the only time when these elements would + * be useful to configure. */ /* @@ -41,8 +41,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, proxy_type)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_proxy_type) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -58,9 +58,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && - c->generic.context.i == - offsetof(Config, proxy_telnet_command)) { - assert(c->generic.handler == dlg_stdeditbox_handler); + c->generic.context.i == CONF_proxy_telnet_command) { + assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); diff --git a/contrib/putty/UNIX/UXCONS.C b/contrib/putty/UNIX/UXCONS.C index 2e71df4..ee78a6d 100644 --- a/contrib/putty/UNIX/UXCONS.C +++ b/contrib/putty/UNIX/UXCONS.C @@ -7,8 +7,10 @@ #include #include #include + #include #include +#include #include "putty.h" #include "storage.h" @@ -68,7 +70,7 @@ void notify_remote_exit(void *frontend) { } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { } @@ -233,7 +235,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -254,11 +256,11 @@ int askappend(void *frontend, Filename filename, premsg(&cf); if (console_batch_mode) { - fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); fflush(stderr); return 0; } - fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); { @@ -319,38 +321,54 @@ void console_provide_logctx(void *logctx) void logevent(void *frontend, const char *string) { struct termios cf; - premsg(&cf); + if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) + premsg(&cf); if (console_logctx) log_eventlog(console_logctx, string); - postmsg(&cf); + if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) + postmsg(&cf); } /* - * Special function to print text to the console for password - * prompts and the like. Uses /dev/tty or stderr, in that order of - * preference; also sanitises escape sequences out of the text, on + * Special functions to read and print to the console for password + * prompts and the like. Uses /dev/tty or stdin/stderr, in that order + * of preference; also sanitises escape sequences out of the text, on * the basis that it might have been sent by a hostile SSH server * doing malicious keyboard-interactive. */ -static void console_prompt_text(FILE **confp, const char *data, int len) +static void console_open(FILE **outfp, int *infd) { - int i; + int fd; - if (!*confp) { - if ((*confp = fopen("/dev/tty", "w")) == NULL) - *confp = stderr; + if ((fd = open("/dev/tty", O_RDWR)) >= 0) { + *infd = fd; + *outfp = fdopen(*infd, "w"); + } else { + *infd = 0; + *outfp = stderr; } +} +static void console_close(FILE *outfp, int infd) +{ + if (outfp != stderr) + fclose(outfp); /* will automatically close infd too */ +} + +static void console_prompt_text(FILE *outfp, const char *data, int len) +{ + int i; for (i = 0; i < len; i++) if ((data[i] & 0x60) || (data[i] == '\n')) - fputc(data[i], *confp); - fflush(*confp); + fputc(data[i], outfp); + fflush(outfp); } int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { size_t curr_prompt; - FILE *confp = NULL; + FILE *outfp = NULL; + int infd; /* * Zero all the results, in case we abort half-way through. @@ -358,62 +376,80 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } if (p->n_prompts && console_batch_mode) return 0; + console_open(&outfp, &infd); + /* * Preamble. */ /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { size_t l = strlen(p->name); - console_prompt_text(&confp, p->name, l); + console_prompt_text(outfp, p->name, l); if (p->name[l-1] != '\n') - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); } /* ...but we always print any `instruction'. */ if (p->instruction) { size_t l = strlen(p->instruction); - console_prompt_text(&confp, p->instruction, l); + console_prompt_text(outfp, p->instruction, l); if (p->instruction[l-1] != '\n') - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; - int i; + int len; prompt_t *pr = p->prompts[curr_prompt]; - tcgetattr(0, &oldmode); + tcgetattr(infd, &oldmode); newmode = oldmode; newmode.c_lflag |= ISIG | ICANON; if (!pr->echo) newmode.c_lflag &= ~ECHO; else newmode.c_lflag |= ECHO; - tcsetattr(0, TCSANOW, &newmode); + tcsetattr(infd, TCSANOW, &newmode); - console_prompt_text(&confp, pr->prompt, strlen(pr->prompt)); + console_prompt_text(outfp, pr->prompt, strlen(pr->prompt)); - i = read(0, pr->result, pr->result_len - 1); + len = 0; + while (1) { + int ret; - tcsetattr(0, TCSANOW, &oldmode); + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + ret = read(infd, pr->result + len, pr->resultsize - len - 1); + if (ret <= 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + break; + } + } - if (i > 0 && pr->result[i-1] == '\n') - i--; - pr->result[i] = '\0'; + tcsetattr(infd, TCSANOW, &oldmode); if (!pr->echo) - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); + + if (len < 0) { + console_close(outfp, infd); + return 0; /* failure due to read error */ + } + pr->result[len] = '\0'; } - if (confp && confp != stderr) - fclose(confp); + console_close(outfp, infd); return 1; /* success */ } diff --git a/contrib/putty/UNIX/UXGEN.C b/contrib/putty/UNIX/UXGEN.C index b1b4d19..636d1cc 100644 --- a/contrib/putty/UNIX/UXGEN.C +++ b/contrib/putty/UNIX/UXGEN.C @@ -26,6 +26,7 @@ char *get_random_data(int len) ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); + sfree(buf); perror("puttygen: unable to read /dev/random"); return NULL; } diff --git a/contrib/putty/UNIX/UXGSS.C b/contrib/putty/UNIX/UXGSS.C index 1bb803c..61747ef 100644 --- a/contrib/putty/UNIX/UXGSS.C +++ b/contrib/putty/UNIX/UXGSS.C @@ -53,9 +53,10 @@ static void gss_init(struct ssh_gss_library *lib, void *dlhandle, } /* Dynamically load gssapi libs. */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { void *gsslib; + char *gsspath; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); list->libraries = snewn(4, struct ssh_gss_library); @@ -77,11 +78,11 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) 2, "Using GSSAPI from libgss.so.1"); /* User-specified GSSAPI library */ - if (cfg->ssh_gss_custom.path[0] && - (gsslib = dlopen(cfg->ssh_gss_custom.path, RTLD_LAZY)) != NULL) + gsspath = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*gsspath && (gsslib = dlopen(gsspath, RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 3, dupprintf("Using GSSAPI from user-specified" - " library '%s'", cfg->ssh_gss_custom.path)); + " library '%s'", gsspath)); return list; } @@ -129,7 +130,7 @@ const struct keyvalwhere gsslibkeywords[] = { #include /* Dynamically load gssapi libs. */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); diff --git a/contrib/putty/UNIX/UXMISC.C b/contrib/putty/UNIX/UXMISC.C index 4bda059..5e4075c 100644 --- a/contrib/putty/UNIX/UXMISC.C +++ b/contrib/putty/UNIX/UXMISC.C @@ -6,48 +6,92 @@ #include #include #include +#include #include +#include #include #include #include #include "putty.h" -long tickcount_offset = 0; - unsigned long getticks(void) { - struct timeval tv; - gettimeofday(&tv, NULL); /* - * We want to use milliseconds rather than microseconds, - * because we need a decent number of them to fit into a 32-bit - * word so it can be used for keepalives. + * We want to use milliseconds rather than the microseconds or + * nanoseconds given by the underlying clock functions, because we + * need a decent number of them to fit into a 32-bit word so it + * can be used for keepalives. */ - return tv.tv_sec * 1000 + tv.tv_usec / 1000 + tickcount_offset; +#if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC + { + /* Use CLOCK_MONOTONIC if available, so as to be unconfused if + * the system clock changes. */ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec * TICKSPERSEC + + ts.tv_nsec / (1000000000 / TICKSPERSEC); + } +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); + } } -Filename filename_from_str(const char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } +Filename *filename_copy(const Filename *fn) +{ + return filename_from_str(fn->path); +} + const char *filename_to_str(const Filename *fn) { return fn->path; } -int filename_equal(Filename f1, Filename f2) +int filename_equal(const Filename *f1, const Filename *f2) +{ + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) +{ + return !fn->path[0]; +} + +void filename_free(Filename *fn) { - return !strcmp(f1.path, f2.path); + sfree(fn->path); + sfree(fn); } -int filename_is_null(Filename fn) +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) { - return !*fn.path; + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); } #ifdef DEBUG @@ -125,28 +169,115 @@ void pgp_fingerprints(void) } /* - * Set FD_CLOEXEC on a file descriptor + * Set and clear fcntl options on a file descriptor. We don't + * realistically expect any of these operations to fail (the most + * plausible error condition is EBADF, but we always believe ourselves + * to be passing a valid fd so even that's an assertion-fail sort of + * response), so we don't make any effort to return sensible error + * codes to the caller - we just log to standard error and die + * unceremoniously. However, nonblock and no_nonblock do return the + * previous state of O_NONBLOCK. */ -int cloexec(int fd) { +void cloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); - if (fdflags == -1) return -1; - return fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } } +void noncloexec(int fd) { + int fdflags; -FILE *f_open(struct Filename filename, char const *mode, int is_private) + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +int nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} +int no_nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} + +FILE *f_open(const Filename *filename, char const *mode, int is_private) { if (!is_private) { - return fopen(filename.path, mode); + return fopen(filename->path, mode); } else { int fd; assert(mode[0] == 'w'); /* is_private is meaningless for read, and tricky for append */ - fd = open(filename.path, O_WRONLY | O_CREAT | O_TRUNC, - 0700); + fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) return NULL; return fdopen(fd, mode); } } + +FontSpec *fontspec_new(const char *name) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *data) +{ + int len = strlen(f->name); + if (data) + strcpy(data, f->name); + return len + 1; /* include trailing NUL */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + *used = end - data + 1; + return fontspec_new(data); +} diff --git a/contrib/putty/UNIX/UXNET.C b/contrib/putty/UNIX/UXNET.C index 0882638..76f87e0 100644 --- a/contrib/putty/UNIX/UXNET.C +++ b/contrib/putty/UNIX/UXNET.C @@ -80,13 +80,13 @@ struct Socket_tag { int connected; /* irrelevant for listening sockets */ int writable; int frozen; /* this causes readability notifications to be ignored */ - int frozen_readable; /* this means we missed at least one readability - * notification while we were frozen */ int localhost_only; /* for listening sockets */ char oobdata[1]; int sending_oob; int oobpending; /* is there OOB data available to read? */ int oobinline; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; + int incomingeof; int pending_error; /* in case send() returns error */ int listener; int nodelay, keepalive; /* for connect()-type sockets */ @@ -341,7 +341,7 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } } -int sk_hostname_is_local(char *name) +int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || @@ -388,6 +388,11 @@ int sk_address_is_local(SockAddr addr) } } +int sk_address_is_special_local(SockAddr addr) +{ + return addr->superfamily == UNIX; +} + int sk_addrtype(SockAddr addr) { SockAddrStep step; @@ -466,6 +471,7 @@ static void sk_tcp_flush(Socket s) static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); +static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_private_ptr(Socket s, void *ptr); static void *sk_tcp_get_private_ptr(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); @@ -476,6 +482,7 @@ static struct socket_function_table tcp_fn_table = { sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -498,10 +505,11 @@ Socket sk_register(OSSocket sockfd, Plug plug) ret->writable = 1; /* to start with */ ret->sending_oob = 0; ret->frozen = 1; - ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 0; ret->parent = ret->child = NULL; ret->addr = NULL; @@ -529,7 +537,7 @@ static int try_connect(Actual_Socket sock) const union sockaddr_union *sa; int err = 0; short localport; - int fl, salen, family; + int salen, family; /* * Remove the socket from the tree before we overwrite its @@ -561,17 +569,32 @@ static int try_connect(Actual_Socket sock) if (sock->oobinline) { int b = TRUE; - setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } if (sock->nodelay) { int b = TRUE; - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)); + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } if (sock->keepalive) { int b = TRUE; - setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)); + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } /* @@ -669,9 +692,7 @@ static int try_connect(Actual_Socket sock) exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ } - fl = fcntl(s, F_GETFL); - if (fl != -1) - fcntl(s, F_SETFL, fl | O_NONBLOCK); + nonblock(s); if ((connect(s, &(sa->sa), salen)) < 0) { if ( errno != EINPROGRESS ) { @@ -719,11 +740,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; - ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 0; ret->addr = addr; START_STEP(ret->addr, ret->step); @@ -771,11 +793,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; - ret->frozen_readable = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 1; ret->addr = NULL; @@ -820,7 +843,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->oobinline = 0; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const char *)&on, sizeof(on)) < 0) { + ret->error = strerror(errno); + close(s); + return (Socket) ret; + } retcode = -1; addr = NULL; addrlen = -1; /* placate optimiser */ @@ -1038,6 +1066,16 @@ void try_send(Actual_Socket s) * plug_closing()) at some suitable future moment. */ s->pending_error = err; + /* + * Immediately cease selecting on this socket, so that + * we don't tight-loop repeatedly trying to do + * whatever it was that went wrong. + */ + uxsel_tell(s); + /* + * Notify the front end that it might want to call us. + */ + frontend_net_error_pending(); return; } } else { @@ -1053,6 +1091,20 @@ void try_send(Actual_Socket s) } } } + + /* + * If we reach here, we've finished sending everything we might + * have needed to send. Send EOF, if we need to. + */ + if (s->outgoingeof == EOF_PENDING) { + shutdown(s->s, SHUT_WR); + s->outgoingeof = EOF_SENT; + } + + /* + * Also update the select status, because we don't need to select + * for writing any more. + */ uxsel_tell(s); } @@ -1060,6 +1112,8 @@ static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Add the data to the buffer list on the socket. */ @@ -1084,6 +1138,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Replace the buffer list on the socket with the data. */ @@ -1107,6 +1163,30 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) return s->sending_oob; } +static void sk_tcp_write_eof(Socket sock) +{ + Actual_Socket s = (Actual_Socket) sock; + + assert(s->outgoingeof == EOF_NO); + + /* + * Mark the socket as pending outgoing EOF. + */ + s->outgoingeof = EOF_PENDING; + + /* + * Now try sending from the start of the buffer list. + */ + if (s->writable) + try_send(s); + + /* + * Update the select() status to correctly reflect whether or + * not we should be selecting for write. + */ + uxsel_tell(s); +} + static int net_select_result(int fd, int event) { int ret; @@ -1168,7 +1248,6 @@ static int net_select_result(int fd, int event) union sockaddr_union su; socklen_t addrlen = sizeof(su); int t; /* socket of connection */ - int fl; memset(&su, 0, addrlen); t = accept(s->s, &su.sa, &addrlen); @@ -1176,9 +1255,7 @@ 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); + nonblock(t); if (s->localhost_only && !sockaddr_is_loopback(&su.sa)) { @@ -1195,10 +1272,8 @@ static int net_select_result(int fd, int event) */ /* In the case the socket is still frozen, we don't even bother */ - if (s->frozen) { - s->frozen_readable = 1; + if (s->frozen) break; - } /* * We have received data on the socket. For an oobinline @@ -1237,6 +1312,8 @@ static int net_select_result(int fd, int event) if (err != 0) return plug_closing(s->plug, strerror(err), err, 0); } else if (0 == ret) { + s->incomingeof = TRUE; /* stop trying to read now */ + uxsel_tell(s); return plug_closing(s->plug, NULL, 0, 0); } else { /* @@ -1349,26 +1426,23 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen) if (s->frozen == is_frozen) return; s->frozen = is_frozen; - if (!is_frozen && s->frozen_readable) { - char c; - recv(s->s, &c, 1, MSG_PEEK); - } - s->frozen_readable = 0; uxsel_tell(s); } static void uxsel_tell(Actual_Socket s) { int rwx = 0; - if (s->listener) { - rwx |= 1; /* read == accept */ - } else { - if (!s->connected) - rwx |= 2; /* write == connect */ - if (s->connected && !s->frozen) - rwx |= 1 | 4; /* read, except */ - if (bufchain_size(&s->output_data)) - rwx |= 2; /* write */ + if (!s->pending_error) { + if (s->listener) { + rwx |= 1; /* read == accept */ + } else { + if (!s->connected) + rwx |= 2; /* write == connect */ + if (s->connected && !s->frozen && !s->incomingeof) + rwx |= 1 | 4; /* read, except */ + if (bufchain_size(&s->output_data)) + rwx |= 2; /* write */ + } } uxsel_set(s->s, rwx, net_select_result); } diff --git a/contrib/putty/UNIX/UXPLINK.C b/contrib/putty/UNIX/UXPLINK.C index 90baa68..3addc3e 100644 --- a/contrib/putty/UNIX/UXPLINK.C +++ b/contrib/putty/UNIX/UXPLINK.C @@ -63,6 +63,22 @@ void modalfatalbox(char *p, ...) } cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + struct termios cf; + va_list ap; + premsg(&cf); + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + postmsg(&cf); + if (logctx) { + log_free(logctx); + logctx = NULL; + } +} void connection_fatal(void *frontend, char *p, ...) { struct termios cf; @@ -98,7 +114,7 @@ static int local_tty = FALSE; /* do we have a local tty? */ static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; /* * Default settings that are specific to pterm. @@ -116,30 +132,20 @@ char *platform_default_s(const char *name) int platform_default_i(const char *name, int def) { - if (!strcmp(name, "TermWidth") || - !strcmp(name, "TermHeight")) { - struct winsize size; - if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) - return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row); - } return def; } -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *x_get_default(const char *key) @@ -383,31 +389,33 @@ void cleanup_termios(void) } bufchain stdout_data, stderr_data; +enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int try_output(int is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); void *senddata; - int sendlen, ret, fl; - - if (bufchain_size(chain) == 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); + int sendlen, ret; + + if (bufchain_size(chain) > 0) { + int prev_nonblock = nonblock(fd); + 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 (!prev_nonblock) + no_nonblock(fd); + if (ret < 0 && errno != EAGAIN) { + perror(is_stderr ? "stderr: write" : "stdout: write"); + exit(1); + } + } + if (outgoingeof == EOF_PENDING && bufchain_size(&stdout_data) == 0) { + close(STDOUT_FILENO); + outgoingeof = EOF_SENT; } return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); } @@ -419,6 +427,7 @@ int from_backend(void *frontend_handle, int is_stderr, bufchain_add(&stderr_data, data, len); return try_output(TRUE); } else { + assert(outgoingeof == EOF_NO); bufchain_add(&stdout_data, data, len); return try_output(FALSE); } @@ -434,6 +443,14 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) return 0; /* not reached */ } +int from_backend_eof(void *frontend_handle) +{ + assert(outgoingeof == EOF_NO); + outgoingeof = EOF_PENDING; + try_output(FALSE); + return FALSE; /* do not respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; @@ -576,6 +593,8 @@ static void version(void) exit(1); } +void frontend_net_error_pending(void) {} + int main(int argc, char **argv) { int sending; @@ -588,7 +607,8 @@ int main(int argc, char **argv) int errors; int use_subsystem = 0; int got_host = FALSE; - long now; + unsigned long now; + struct winsize size; fdlist = NULL; fdcount = fdsize = 0; @@ -599,16 +619,21 @@ int main(int argc, char **argv) default_protocol = PROT_SSH; default_port = 22; + bufchain_init(&stdout_data); + bufchain_init(&stderr_data); + outgoingeof = EOF_NO; + flags = FLAG_STDERR | FLAG_STDERR_TTY; stderr_tty_init(); /* * Process the command line. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; - default_protocol = cfg.protocol; - default_port = cfg.port; + default_protocol = conf_get_int(conf, CONF_protocol); + default_port = conf_get_int(conf, CONF_port); errors = 0; { /* @@ -618,8 +643,10 @@ int main(int argc, char **argv) if (p) { const Backend *b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; - default_port = cfg.port = b->default_port; + default_protocol = b->protocol; + default_port = b->default_port; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); } } } @@ -627,7 +654,7 @@ int main(int argc, char **argv) char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, &cfg); + 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); @@ -639,10 +666,13 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { - /* Save status to write to cfg later. */ + /* Save status to write to conf later. */ use_subsystem = 1; - } else if (!strcmp(p, "-V")) { + } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); + } else if (!strcmp(p, "--help")) { + usage(); + exit(0); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); @@ -660,7 +690,7 @@ int main(int argc, char **argv) errors = 1; } } else if (*p) { - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* @@ -674,7 +704,7 @@ int main(int argc, char **argv) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg.protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -682,11 +712,10 @@ int main(int argc, char **argv) if (*p) *p++ = '\0'; if (c == ':') - cfg.port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; @@ -701,7 +730,9 @@ int main(int argc, char **argv) *r = '\0'; b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); portnumber = b->default_port; } p = r + 1; @@ -728,26 +759,24 @@ int main(int argc, char **argv) * same name as the hostname. */ { - Config cfg2; - do_defaults(host, &cfg2); - if (loaded_session || !cfg_launchable(&cfg2)) { + Conf *conf2 = conf_new(); + do_defaults(host, conf2); + if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = default_port; + conf_set_str(conf, CONF_host, host); + conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { - cfg = cfg2; + conf_copy_into(conf, conf2); loaded_session = TRUE; } + conf_free(conf2); } if (user) { /* Patch in specified username. */ - strncpy(cfg.username, user, - sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } } @@ -774,9 +803,9 @@ int main(int argc, char **argv) } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ - cfg.remote_cmd_ptr = command; - cfg.remote_cmd_ptr2 = NULL; - cfg.nopty = TRUE; /* command => no terminal */ + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } @@ -786,70 +815,78 @@ int main(int argc, char **argv) if (errors) return 1; - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; + + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } + + /* + * Trim off a colon suffix if it's there. + */ + host[strcspn(host, ":")] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) - cfg.ssh_subsys = TRUE; - - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; - - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; - } - cfg.host[p1] = '\0'; - } + conf_set_int(conf, CONF_ssh_subsys, TRUE); - if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host) + if (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); @@ -860,7 +897,13 @@ int main(int argc, char **argv) * Select port. */ if (portnumber != -1) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); + + /* + * Block SIGPIPE, so that we'll get EPIPE individually on + * particular network connections that go wrong. + */ + putty_signal(SIGPIPE, SIG_IGN); /* * Set up the pipe we'll use to tell us about SIGWINCH. @@ -871,6 +914,15 @@ int main(int argc, char **argv) } putty_signal(SIGWINCH, sigwinch); + /* + * Now that we've got the SIGWINCH handler installed, try to find + * out the initial terminal size. + */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) >= 0) { + conf_set_int(conf, CONF_width, size.ws_col); + conf_set_int(conf, CONF_height, size.ws_row); + } + sk_init(); uxsel_init(); @@ -879,28 +931,34 @@ int main(int argc, char **argv) * 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; + if (conf_get_int(conf, CONF_protocol) == PROT_SSH && + !conf_get_int(conf, CONF_x11_forward) && + !conf_get_int(conf, CONF_agentfwd) && + !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) + conf_set_int(conf, CONF_ssh_simple, TRUE); + /* * Start up the connection. */ - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); console_provide_logctx(logctx); { const char *error; char *realhost; /* nodelay is only useful if stdin is a terminal device */ - int nodelay = cfg.tcp_nodelay && isatty(0); + int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && isatty(0); - error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, - &realhost, nodelay, cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, nodelay, + conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s\n", error); return 1; } back->provide_logctx(backhandle, logctx); - ldisc_create(&cfg, NULL, back, backhandle, NULL); + ldisc_create(conf, NULL, back, backhandle, NULL); sfree(realhost); } connopen = 1; @@ -975,12 +1033,17 @@ int main(int argc, char **argv) } do { - long next, ticks; + unsigned long next, then; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; @@ -990,27 +1053,8 @@ int main(int argc, char **argv) ret = select(maxfd, &rset, &wset, &xset, ptv); if (ret == 0) now = next; - else { - long newnow = GETTICKCOUNT(); - /* - * Check to see whether the system clock has - * changed massively during the select. - */ - if (newnow - now < 0 || newnow - now > next - now) { - /* - * If so, look at the elapsed time in the - * select and use it to compute a new - * tickcount_offset. - */ - long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000; - /* So we'd like GETTICKCOUNT to have returned othernow, - * but instead it return newnow. Hence ... */ - tickcount_offset += othernow - newnow; - now = othernow; - } else { - now = newnow; - } - } + else + now = GETTICKCOUNT(); } while (ret < 0 && errno == EINTR); if (ret < 0) { @@ -1039,7 +1083,7 @@ int main(int argc, char **argv) if (read(signalpipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ - if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0) + if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) back->size(backhandle, size.ws_col, size.ws_row); } @@ -1072,6 +1116,8 @@ int main(int argc, char **argv) back->unthrottle(backhandle, try_output(TRUE)); } + net_pending_errors(); + if ((!connopen || !back->connected(backhandle)) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) diff --git a/contrib/putty/UNIX/UXPROXY.C b/contrib/putty/UNIX/UXPROXY.C index 147df6e..dc52607 100644 --- a/contrib/putty/UNIX/UXPROXY.C +++ b/contrib/putty/UNIX/UXPROXY.C @@ -29,6 +29,7 @@ struct Socket_localproxy_tag { bufchain pending_output_data; bufchain pending_input_data; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; void *privptr; }; @@ -95,12 +96,14 @@ static void sk_localproxy_close (Socket s) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; - del234(localproxy_by_fromfd, ps); - del234(localproxy_by_tofd, ps); + if (ps->to_cmd >= 0) { + del234(localproxy_by_tofd, ps); + uxsel_del(ps->to_cmd); + close(ps->to_cmd); + } - uxsel_del(ps->to_cmd); + del234(localproxy_by_fromfd, ps); uxsel_del(ps->from_cmd); - close(ps->to_cmd); close(ps->from_cmd); sfree(ps); @@ -129,6 +132,14 @@ static int localproxy_try_send(Local_Proxy_Socket ps) } } + if (ps->outgoingeof == EOF_PENDING) { + del234(localproxy_by_tofd, ps); + close(ps->to_cmd); + uxsel_del(ps->to_cmd); + ps->to_cmd = -1; + ps->outgoingeof = EOF_SENT; + } + if (bufchain_size(&ps->pending_output_data) == 0) uxsel_del(ps->to_cmd); else @@ -141,6 +152,8 @@ static int sk_localproxy_write (Socket s, const char *data, int len) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + assert(ps->outgoingeof == EOF_NO); + bufchain_add(&ps->pending_output_data, data, len); localproxy_try_send(ps); @@ -157,6 +170,16 @@ static int sk_localproxy_write_oob (Socket s, const char *data, int len) return sk_localproxy_write(s, data, len); } +static void sk_localproxy_write_eof (Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + assert(ps->outgoingeof == EOF_NO); + ps->outgoingeof = EOF_PENDING; + + localproxy_try_send(ps); +} + static void sk_localproxy_flush (Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ @@ -224,7 +247,7 @@ static int localproxy_select_result(int fd, int event) Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { char *cmd; @@ -233,6 +256,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, + sk_localproxy_write_eof, sk_localproxy_flush, sk_localproxy_set_private_ptr, sk_localproxy_get_private_ptr, @@ -243,15 +267,16 @@ Socket platform_new_connection(SockAddr addr, char *hostname, Local_Proxy_Socket ret; int to_cmd_pipe[2], from_cmd_pipe[2], pid; - if (cfg->proxy_type != PROXY_CMD) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; - cmd = format_telnet_command(addr, port, cfg); + cmd = format_telnet_command(addr, port, conf); ret = snew(struct Socket_localproxy_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; + ret->outgoingeof = EOF_NO; bufchain_init(&ret->pending_input_data); bufchain_init(&ret->pending_output_data); @@ -263,6 +288,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, if (pipe(to_cmd_pipe) < 0 || pipe(from_cmd_pipe) < 0) { ret->error = dupprintf("pipe: %s", strerror(errno)); + sfree(cmd); return (Socket)ret; } cloexec(to_cmd_pipe[1]); @@ -272,6 +298,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, if (pid < 0) { ret->error = dupprintf("fork: %s", strerror(errno)); + sfree(cmd); return (Socket)ret; } else if (pid == 0) { close(0); @@ -280,8 +307,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname, dup2(from_cmd_pipe[1], 1); close(to_cmd_pipe[0]); close(from_cmd_pipe[1]); - fcntl(0, F_SETFD, 0); - fcntl(1, F_SETFD, 0); + noncloexec(0); + noncloexec(1); execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); _exit(255); } diff --git a/contrib/putty/UNIX/UXPTERM.C b/contrib/putty/UNIX/UXPTERM.C index 73670e8..ef5254a 100644 --- a/contrib/putty/UNIX/UXPTERM.C +++ b/contrib/putty/UNIX/UXPTERM.C @@ -12,19 +12,29 @@ const int use_event_log = 0; /* pterm doesn't need it */ const int new_session = 0, saved_sessions = 0; /* or these */ const int use_pty_argv = TRUE; -Backend *select_backend(Config *cfg) +Backend *select_backend(Conf *conf) { return &pty_backend; } -int cfgbox(Config *cfg) +void net_pending_errors(void) +{ + /* + * Stub version of net_pending_errors(), because gtkwin.c has to + * be prepared to call it when linked into PuTTY and therefore we + * have to avoid a link failure when linking gtkwin.c in turn into + * a non-networked application. + */ +} + +int cfgbox(Conf *conf) { /* * This is a no-op in pterm, except that we'll ensure the * protocol is set to -1 to inhibit the useless Connection * panel in the config box. */ - cfg->protocol = -1; + conf_set_int(conf, CONF_protocol, -1); return 1; } @@ -33,7 +43,7 @@ void cleanup_exit(int code) exit(code); } -int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { return 0; /* pterm doesn't have any. */ } @@ -47,11 +57,14 @@ int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); extern void pty_pre_init(void); /* declared in pty.c */ + int ret; cmdline_tooltype = TOOLTYPE_NONNETWORK; default_protocol = -1; pty_pre_init(); - return pt_main(argc, argv); + ret = pt_main(argc, argv); + cleanup_exit(ret); + return ret; /* not reached, but placates optimisers */ } diff --git a/contrib/putty/UNIX/UXPTY.C b/contrib/putty/UNIX/UXPTY.C index b1e240e..8f0d9e3 100644 --- a/contrib/putty/UNIX/UXPTY.C +++ b/contrib/putty/UNIX/UXPTY.C @@ -76,7 +76,7 @@ typedef struct pty_tag *Pty; static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */ struct pty_tag { - Config cfg; + Conf *conf; int master_fd, slave_fd; void *frontend; char name[FILENAME_MAX]; @@ -167,8 +167,8 @@ static tree234 *ptys_by_pid = NULL; static Pty single_pty = NULL; #ifndef OMIT_UTMP -static pid_t pty_utmp_helper_pid; -static int pty_utmp_helper_pipe; +static pid_t pty_utmp_helper_pid = -1; +static int pty_utmp_helper_pipe = -1; static int pty_stamped_utmp; static struct utmpx utmp_entry; #endif @@ -270,7 +270,6 @@ static void fatal_sig_handler(int signum) { putty_signal(signum, SIG_DFL); cleanup_utmp(); - setuid(getuid()); raise(signum); } #endif @@ -335,12 +334,28 @@ static void pty_open_master(Pty pty) chown(pty->name, getuid(), gp ? gp->gr_gid : -1); chmod(pty->name, 0600); #else - pty->master_fd = open("/dev/ptmx", O_RDWR); + + const int flags = O_RDWR +#ifdef O_NOCTTY + | O_NOCTTY +#endif + ; + +#ifdef HAVE_POSIX_OPENPT + pty->master_fd = posix_openpt(flags); + + if (pty->master_fd < 0) { + perror("posix_openpt"); + exit(1); + } +#else + pty->master_fd = open("/dev/ptmx", flags); if (pty->master_fd < 0) { perror("/dev/ptmx: open"); exit(1); } +#endif if (grantpt(pty->master_fd) < 0) { perror("grantpt"); @@ -358,15 +373,7 @@ static void pty_open_master(Pty pty) strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1); #endif - { - /* - * Set the pty master into non-blocking mode. - */ - int fl; - fl = fcntl(pty->master_fd, F_GETFL); - if (fl != -1 && !(fl & O_NONBLOCK)) - fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK); - } + nonblock(pty->master_fd); if (!ptys_by_fd) ptys_by_fd = newtree234(pty_compare_by_fd); @@ -396,6 +403,7 @@ void pty_pre_init(void) #endif pty = single_pty = snew(struct pty_tag); + pty->conf = NULL; bufchain_init(&pty->output_data); /* set the child signal handler straight away; it needs to be set @@ -408,106 +416,106 @@ void pty_pre_init(void) if (geteuid() != getuid() || getegid() != getgid()) { pty_open_master(pty); - } #ifndef OMIT_UTMP - /* - * Fork off the utmp helper. - */ - if (pipe(pipefd) < 0) { - perror("pterm: pipe"); - exit(1); - } - cloexec(pipefd[0]); - cloexec(pipefd[1]); - pid = fork(); - if (pid < 0) { - perror("pterm: fork"); - exit(1); - } else if (pid == 0) { - char display[128], buffer[128]; - int dlen, ret; - - close(pipefd[1]); - /* - * Now sit here until we receive a display name from the - * other end of the pipe, and then stamp utmp. Unstamp utmp - * again, and exit, when the pipe closes. - */ + /* + * Fork off the utmp helper. + */ + if (pipe(pipefd) < 0) { + perror("pterm: pipe"); + exit(1); + } + cloexec(pipefd[0]); + cloexec(pipefd[1]); + pid = fork(); + if (pid < 0) { + perror("pterm: fork"); + exit(1); + } else if (pid == 0) { + char display[128], buffer[128]; + int dlen, ret; + + close(pipefd[1]); + /* + * Now sit here until we receive a display name from the + * other end of the pipe, and then stamp utmp. Unstamp utmp + * again, and exit, when the pipe closes. + */ - dlen = 0; - while (1) { + dlen = 0; + while (1) { - ret = read(pipefd[0], buffer, lenof(buffer)); - if (ret <= 0) { - cleanup_utmp(); - _exit(0); - } else if (!pty_stamped_utmp) { - if (dlen < lenof(display)) - memcpy(display+dlen, buffer, - min(ret, lenof(display)-dlen)); - if (buffer[ret-1] == '\0') { - /* - * Now we have a display name. NUL-terminate - * it, and stamp utmp. - */ - display[lenof(display)-1] = '\0'; - /* - * Trap as many fatal signals as we can in the - * hope of having the best possible chance to - * clean up utmp before termination. We are - * unfortunately unprotected against SIGKILL, - * but that's life. - */ - putty_signal(SIGHUP, fatal_sig_handler); - putty_signal(SIGINT, fatal_sig_handler); - putty_signal(SIGQUIT, fatal_sig_handler); - putty_signal(SIGILL, fatal_sig_handler); - putty_signal(SIGABRT, fatal_sig_handler); - putty_signal(SIGFPE, fatal_sig_handler); - putty_signal(SIGPIPE, fatal_sig_handler); - putty_signal(SIGALRM, fatal_sig_handler); - putty_signal(SIGTERM, fatal_sig_handler); - putty_signal(SIGSEGV, fatal_sig_handler); - putty_signal(SIGUSR1, fatal_sig_handler); - putty_signal(SIGUSR2, fatal_sig_handler); + ret = read(pipefd[0], buffer, lenof(buffer)); + if (ret <= 0) { + cleanup_utmp(); + _exit(0); + } else if (!pty_stamped_utmp) { + if (dlen < lenof(display)) + memcpy(display+dlen, buffer, + min(ret, lenof(display)-dlen)); + if (buffer[ret-1] == '\0') { + /* + * Now we have a display name. NUL-terminate + * it, and stamp utmp. + */ + display[lenof(display)-1] = '\0'; + /* + * Trap as many fatal signals as we can in the + * hope of having the best possible chance to + * clean up utmp before termination. We are + * unfortunately unprotected against SIGKILL, + * but that's life. + */ + putty_signal(SIGHUP, fatal_sig_handler); + putty_signal(SIGINT, fatal_sig_handler); + putty_signal(SIGQUIT, fatal_sig_handler); + putty_signal(SIGILL, fatal_sig_handler); + putty_signal(SIGABRT, fatal_sig_handler); + putty_signal(SIGFPE, fatal_sig_handler); + putty_signal(SIGPIPE, fatal_sig_handler); + putty_signal(SIGALRM, fatal_sig_handler); + putty_signal(SIGTERM, fatal_sig_handler); + putty_signal(SIGSEGV, fatal_sig_handler); + putty_signal(SIGUSR1, fatal_sig_handler); + putty_signal(SIGUSR2, fatal_sig_handler); #ifdef SIGBUS - putty_signal(SIGBUS, fatal_sig_handler); + putty_signal(SIGBUS, fatal_sig_handler); #endif #ifdef SIGPOLL - putty_signal(SIGPOLL, fatal_sig_handler); + putty_signal(SIGPOLL, fatal_sig_handler); #endif #ifdef SIGPROF - putty_signal(SIGPROF, fatal_sig_handler); + putty_signal(SIGPROF, fatal_sig_handler); #endif #ifdef SIGSYS - putty_signal(SIGSYS, fatal_sig_handler); + putty_signal(SIGSYS, fatal_sig_handler); #endif #ifdef SIGTRAP - putty_signal(SIGTRAP, fatal_sig_handler); + putty_signal(SIGTRAP, fatal_sig_handler); #endif #ifdef SIGVTALRM - putty_signal(SIGVTALRM, fatal_sig_handler); + putty_signal(SIGVTALRM, fatal_sig_handler); #endif #ifdef SIGXCPU - putty_signal(SIGXCPU, fatal_sig_handler); + putty_signal(SIGXCPU, fatal_sig_handler); #endif #ifdef SIGXFSZ - putty_signal(SIGXFSZ, fatal_sig_handler); + putty_signal(SIGXFSZ, fatal_sig_handler); #endif #ifdef SIGIO - putty_signal(SIGIO, fatal_sig_handler); + putty_signal(SIGIO, fatal_sig_handler); #endif - setup_utmp(pty->name, display); - } - } - } - } else { - close(pipefd[0]); - pty_utmp_helper_pid = pid; - pty_utmp_helper_pipe = pipefd[1]; - } + setup_utmp(pty->name, display); + } + } + } + } else { + close(pipefd[0]); + pty_utmp_helper_pid = pid; + pty_utmp_helper_pipe = pipefd[1]; + } #endif + } /* Drop privs. */ { @@ -515,11 +523,23 @@ void pty_pre_init(void) int gid = getgid(), uid = getuid(); int setresgid(gid_t, gid_t, gid_t); int setresuid(uid_t, uid_t, uid_t); - setresgid(gid, gid, gid); - setresuid(uid, uid, uid); + if (setresgid(gid, gid, gid) < 0) { + perror("setresgid"); + exit(1); + } + if (setresuid(uid, uid, uid) < 0) { + perror("setresuid"); + exit(1); + } #else - setgid(getgid()); - setuid(getuid()); + if (setgid(getgid()) < 0) { + perror("setgid"); + exit(1); + } + if (setuid(getuid()) < 0) { + perror("setuid"); + exit(1); + } #endif } } @@ -588,6 +608,8 @@ int pty_real_select_result(Pty pty, int event, int status) } if (finished && !pty->finished) { + int close_on_exit; + uxsel_del(pty->master_fd); pty_close(pty); pty->master_fd = -1; @@ -600,9 +622,11 @@ int pty_real_select_result(Pty pty, int event, int status) * Only On Clean and it wasn't a clean exit) do we output a * `terminated' message. */ - if (pty->cfg.close_on_exit == FORCE_OFF || - (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) { + close_on_exit = conf_get_int(pty->conf, CONF_close_on_exit); + if (close_on_exit == FORCE_OFF || + (close_on_exit == AUTO && pty->exit_code != 0)) { char message[512]; + message[0] = '\0'; if (WIFEXITED(pty->exit_code)) sprintf(message, "\r\n[pterm: process terminated with exit" " code %d]\r\n", WEXITSTATUS(pty->exit_code)); @@ -681,7 +705,7 @@ static void pty_uxsel_setup(Pty pty) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, +static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -694,6 +718,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, if (single_pty) { pty = single_pty; + assert(pty->conf == NULL); } else { pty = snew(struct pty_tag); pty->master_fd = pty->slave_fd = -1; @@ -705,9 +730,9 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, pty->frontend = frontend; *backend_handle = NULL; /* we can't sensibly use this, sadly */ - pty->cfg = *cfg; /* structure copy */ - pty->term_width = cfg->width; - pty->term_height = cfg->height; + pty->conf = conf_copy(conf); + pty->term_width = conf_get_int(conf, CONF_width); + pty->term_height = conf_get_int(conf, CONF_height); if (pty->master_fd < 0) pty_open_master(pty); @@ -719,7 +744,8 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, { struct termios attrs; tcgetattr(pty->master_fd, &attrs); - attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010'; + attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete) + ? '\177' : '\010'; tcsetattr(pty->master_fd, TCSANOW, &attrs); } @@ -728,21 +754,23 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, * Stamp utmp (that is, tell the utmp helper process to do so), * or not. */ - if (!cfg->stamp_utmp) { - close(pty_utmp_helper_pipe); /* just let the child process die */ - pty_utmp_helper_pipe = -1; - } else { - char *location = get_x_display(pty->frontend); - int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ - while (pos < len) { - int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); - if (ret < 0) { - perror("pterm: writing to utmp helper process"); - close(pty_utmp_helper_pipe); /* arrgh, just give up */ - pty_utmp_helper_pipe = -1; - break; - } - pos += ret; + if (pty_utmp_helper_pipe >= 0) { /* if it's < 0, we can't anyway */ + if (!conf_get_int(conf, CONF_stamp_utmp)) { + close(pty_utmp_helper_pipe); /* just let the child process die */ + pty_utmp_helper_pipe = -1; + } else { + char *location = get_x_display(pty->frontend); + int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ + while (pos < len) { + int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); + if (ret < 0) { + perror("pterm: writing to utmp helper process"); + close(pty_utmp_helper_pipe); /* arrgh, just give up */ + pty_utmp_helper_pipe = -1; + break; + } + pos += ret; + } } } #endif @@ -772,7 +800,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, } close(pty->master_fd); - fcntl(slavefd, F_SETFD, 0); /* don't close on exec */ + noncloexec(slavefd); dup2(slavefd, 0); dup2(slavefd, 1); dup2(slavefd, 2); @@ -784,10 +812,15 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, pgrp = getpid(); tcsetpgrp(0, pgrp); setpgid(pgrp, pgrp); - close(open(pty->name, O_WRONLY, 0)); + { + int ptyfd = open(pty->name, O_WRONLY, 0); + if (ptyfd >= 0) + close(ptyfd); + } setpgid(pgrp, pgrp); { - char *term_env_var = dupprintf("TERM=%s", cfg->termtype); + char *term_env_var = dupprintf("TERM=%s", + conf_get_str(conf, CONF_termtype)); putenv(term_env_var); /* We mustn't free term_env_var, as putenv links it into the * environment in place. @@ -803,18 +836,12 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, } #endif { - char *e = cfg->environmt; - char *var, *varend, *val, *varval; - while (*e) { - var = e; - while (*e && *e != '\t') e++; - varend = e; - if (*e == '\t') e++; - val = e; - while (*e) e++; - e++; - - varval = dupprintf("%.*s=%s", varend-var, var, val); + char *key, *val; + + for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { + char *varval = dupcat(key, "=", val, NULL); putenv(varval); /* * We must not free varval, since putenv links it @@ -836,12 +863,44 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, putty_signal(SIGQUIT, SIG_DFL); putty_signal(SIGPIPE, SIG_DFL); block_signal(SIGCHLD, 0); - if (pty_argv) + if (pty_argv) { + /* + * Exec the exact argument list we were given. + */ execvp(pty_argv[0], pty_argv); - else { + /* + * If that fails, and if we had exactly one argument, pass + * that argument to $SHELL -c. + * + * This arranges that we can _either_ follow 'pterm -e' + * with a list of argv elements to be fed directly to + * exec, _or_ with a single argument containing a command + * to be parsed by a shell (but, in cases of doubt, the + * former is more reliable). + * + * A quick survey of other terminal emulators' -e options + * (as of Debian squeeze) suggests that: + * + * - xterm supports both modes, more or less like this + * - gnome-terminal will only accept a one-string shell command + * - Eterm, kterm and rxvt will only accept a list of + * argv elements (as did older versions of pterm). + * + * It therefore seems important to support both usage + * modes in order to be a drop-in replacement for either + * xterm or gnome-terminal, and hence for anyone's + * plausible uses of the Debian-style alias + * 'x-terminal-emulator'... + */ + if (pty_argv[1] == NULL) { + char *shell = getenv("SHELL"); + if (shell) + execl(shell, shell, "-c", pty_argv[0], (void *)NULL); + } + } else { char *shell = getenv("SHELL"); char *shellname; - if (cfg->login_shell) { + if (conf_get_int(conf, CONF_login_shell)) { char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; @@ -884,7 +943,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, return NULL; } -static void pty_reconfig(void *handle, Config *cfg) +static void pty_reconfig(void *handle, Conf *conf) { Pty pty = (Pty)handle; /* @@ -892,7 +951,7 @@ static void pty_reconfig(void *handle, Config *cfg) * unfortunately we do need to pick up the setting of Close On * Exit so we know whether to give a `terminated' message. */ - pty->cfg = *cfg; /* structure copy */ + conf_copy_into(pty->conf, conf); } /* @@ -906,7 +965,17 @@ static void pty_free(void *handle) del234(ptys_by_pid, pty); del234(ptys_by_fd, pty); - sfree(pty); + conf_free(pty->conf); + pty->conf = NULL; + + if (pty == single_pty) { + /* + * Leave this structure around in case we need to Restart + * Session. + */ + } else { + sfree(pty); + } } static void pty_try_write(Pty pty) diff --git a/contrib/putty/UNIX/UXPUTTY.C b/contrib/putty/UNIX/UXPUTTY.C index 83eae33..457a4a0 100644 --- a/contrib/putty/UNIX/UXPUTTY.C +++ b/contrib/putty/UNIX/UXPUTTY.C @@ -31,17 +31,17 @@ void cleanup_exit(int code) exit(code); } -Backend *select_backend(Config *cfg) +Backend *select_backend(Conf *conf) { - Backend *back = backend_from_proto(cfg->protocol); + Backend *back = backend_from_proto(conf_get_int(conf, CONF_protocol)); assert(back != NULL); return back; } -int cfgbox(Config *cfg) +int cfgbox(Conf *conf) { char *title = dupcat(appname, " Configuration", NULL); - int ret = do_config_box(title, cfg, 0, 0); + int ret = do_config_box(title, conf, 0, 0); sfree(title); return ret; } @@ -50,7 +50,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 *allow_launch) +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { char *p, *q = arg; @@ -61,7 +61,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) * argument, so that it will be deferred until it's a good * moment to run it. */ - int ret = cmdline_process_param("-P", arg, 1, cfg); + int ret = cmdline_process_param("-P", arg, 1, conf); assert(ret == 2); } else if (!strncmp(q, "telnet:", 7)) { /* @@ -74,7 +74,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg->protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -82,11 +82,10 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) if (*p) *p++ = '\0'; if (c == ':') - cfg->port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg->port = -1; - strncpy(cfg->host, q, sizeof(cfg->host) - 1); - cfg->host[sizeof(cfg->host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = 1; } else { /* @@ -97,8 +96,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) p++; if (*p) *p++ = '\0'; - strncpy(cfg->host, q, sizeof(cfg->host) - 1); - cfg->host[sizeof(cfg->host) - 1] = '\0'; + conf_set_str(conf, CONF_host, q); got_host = 1; } if (got_host) @@ -127,6 +125,8 @@ char *platform_get_x_display(void) { int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); + int ret; + sk_init(); flags = FLAG_VERBOSE | FLAG_INTERACTIVE; default_protocol = be_default_protocol; @@ -137,5 +137,7 @@ int main(int argc, char **argv) if (b) default_port = b->default_port; } - return pt_main(argc, argv); + ret = pt_main(argc, argv); + cleanup_exit(ret); + return ret; /* not reached, but placates optimisers */ } diff --git a/contrib/putty/UNIX/UXSER.C b/contrib/putty/UNIX/UXSER.C index de47877..6425f96 100644 --- a/contrib/putty/UNIX/UXSER.C +++ b/contrib/putty/UNIX/UXSER.C @@ -60,10 +60,10 @@ static int serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial serial); static void serial_try_write(Serial serial); -static const char *serial_configure(Serial serial, Config *cfg) +static const char *serial_configure(Serial serial, Conf *conf) { struct termios options; - int bflag, bval; + int bflag, bval, speed, flow, parity; const char *str; char *msg; @@ -75,8 +75,9 @@ static const char *serial_configure(Serial serial, Config *cfg) /* * Find the appropriate baud rate flag. */ + speed = conf_get_int(conf, CONF_serspeed); #define SETBAUD(x) (bflag = B ## x, bval = x) -#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0) +#define CHECKBAUD(x) do { if (speed >= x) SETBAUD(x); } while (0) SETBAUD(50); #ifdef B75 CHECKBAUD(75); @@ -183,18 +184,19 @@ static const char *serial_configure(Serial serial, Config *cfg) sfree(msg); options.c_cflag &= ~CSIZE; - switch (cfg->serdatabits) { + switch (conf_get_int(conf, CONF_serdatabits)) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: return "Invalid number of data bits (need 5, 6, 7 or 8)"; } - msg = dupprintf("Configuring %d data bits", cfg->serdatabits); + msg = dupprintf("Configuring %d data bits", + conf_get_int(conf, CONF_serdatabits)); logevent(serial->frontend, msg); sfree(msg); - if (cfg->serstopbits >= 4) { + if (conf_get_int(conf, CONF_serstopbits) >= 4) { options.c_cflag |= CSTOPB; } else { options.c_cflag &= ~CSTOPB; @@ -211,10 +213,11 @@ static const char *serial_configure(Serial serial, Config *cfg) #ifdef CNEW_RTSCTS options.c_cflag &= ~CNEW_RTSCTS; #endif - if (cfg->serflow == SER_FLOW_XONXOFF) { + flow = conf_get_int(conf, CONF_serflow); + if (flow == SER_FLOW_XONXOFF) { options.c_iflag |= IXON | IXOFF; str = "XON/XOFF"; - } else if (cfg->serflow == SER_FLOW_RTSCTS) { + } else if (flow == SER_FLOW_RTSCTS) { #ifdef CRTSCTS options.c_cflag |= CRTSCTS; #endif @@ -229,11 +232,12 @@ static const char *serial_configure(Serial serial, Config *cfg) sfree(msg); /* Parity */ - if (cfg->serparity == SER_PAR_ODD) { + parity = conf_get_int(conf, CONF_serparity); + if (parity == SER_PAR_ODD) { options.c_cflag |= PARENB; options.c_cflag |= PARODD; str = "odd"; - } else if (cfg->serparity == SER_PAR_EVEN) { + } else if (parity == SER_PAR_EVEN) { options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; str = "even"; @@ -284,12 +288,13 @@ static const char *serial_configure(Serial serial, Config *cfg) * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { Serial serial; const char *err; + char *line; serial = snew(struct serial_backend_data); *backend_handle = serial; @@ -299,22 +304,24 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, serial->inbufsize = 0; bufchain_init(&serial->output_data); + line = conf_get_str(conf, CONF_serline); { - char *msg = dupprintf("Opening serial device %s", cfg->serline); + char *msg = dupprintf("Opening serial device %s", line); logevent(serial->frontend, msg); + sfree(msg); } - serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (serial->fd < 0) return "Unable to open serial port"; cloexec(serial->fd); - err = serial_configure(serial, cfg); + err = serial_configure(serial, conf); if (err) return err; - *realhost = dupstr(cfg->serline); + *realhost = dupstr(line); if (!serial_by_fd) serial_by_fd = newtree234(serial_compare_by_fd); @@ -349,14 +356,14 @@ static void serial_free(void *handle) sfree(serial); } -static void serial_reconfig(void *handle, Config *cfg) +static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; /* * FIXME: what should we do if this returns an error? */ - serial_configure(serial, cfg); + serial_configure(serial, conf); } static int serial_select_result(int fd, int event) diff --git a/contrib/putty/UNIX/UXSFTP.C b/contrib/putty/UNIX/UXSFTP.C index a0ae9fb..33e7648 100644 --- a/contrib/putty/UNIX/UXSFTP.C +++ b/contrib/putty/UNIX/UXSFTP.C @@ -34,7 +34,7 @@ char *x_get_default(const char *key) return NULL; /* this is a stub */ } -void platform_get_x11_auth(struct X11Display *display, const Config *cfg) +void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } @@ -53,21 +53,17 @@ int platform_default_i(const char *name, int def) return def; } -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *get_ttymode(void *frontend, const char *mode) { return NULL; } @@ -125,7 +121,8 @@ struct RFile { }; RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime) + unsigned long *mtime, unsigned long *atime, + long *perms) { int fd; RFile *ret; @@ -137,7 +134,7 @@ RFile *open_existing_file(char *name, uint64 *size, ret = snew(RFile); ret->fd = fd; - if (size || mtime || atime) { + if (size || mtime || atime || perms) { struct stat statbuf; if (fstat(fd, &statbuf) < 0) { fprintf(stderr, "%s: stat: %s\n", name, strerror(errno)); @@ -153,6 +150,9 @@ RFile *open_existing_file(char *name, uint64 *size, if (atime) *atime = statbuf.st_atime; + + if (perms) + *perms = statbuf.st_mode; } return ret; @@ -174,12 +174,13 @@ struct WFile { char *name; }; -WFile *open_new_file(char *name) +WFile *open_new_file(char *name, long perms) { int fd; WFile *ret; - fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666); + fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, + (mode_t)(perms ? perms : 0666)); if (fd < 0) return NULL; @@ -442,7 +443,7 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) fd_set rset, wset, xset; int i, fdcount, fdsize, *fdlist; int fd, fdstate, rwx, ret, maxfd; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(); fdlist = NULL; fdcount = fdsize = 0; @@ -488,13 +489,17 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) FD_SET_MAX(0, maxfd, rset); do { - long next, ticks; + unsigned long next, then; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks <= 0) - ticks = 1; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; @@ -504,27 +509,8 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) ret = select(maxfd, &rset, &wset, &xset, ptv); if (ret == 0) now = next; - else { - long newnow = GETTICKCOUNT(); - /* - * Check to see whether the system clock has - * changed massively during the select. - */ - if (newnow - now < 0 || newnow - now > next - now) { - /* - * If so, look at the elapsed time in the - * select and use it to compute a new - * tickcount_offset. - */ - long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000; - /* So we'd like GETTICKCOUNT to have returned othernow, - * but instead it return newnow. Hence ... */ - tickcount_offset += othernow - newnow; - now = othernow; - } else { - now = newnow; - } - } + else + now = GETTICKCOUNT(); } while (ret < 0 && errno != EINTR); } while (ret == 0); @@ -579,6 +565,7 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) ret = ssh_sftp_do_select(TRUE, no_fds_ok); if (ret < 0) { printf("connection died\n"); + sfree(buf); return NULL; /* woop woop */ } if (ret > 0) { @@ -589,10 +576,12 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) ret = read(0, buf+buflen, 1); if (ret < 0) { perror("read"); + sfree(buf); return NULL; } if (ret == 0) { /* eof on stdin; no error, but no answer either */ + sfree(buf); return NULL; } @@ -604,6 +593,8 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) } } +void frontend_net_error_pending(void) {} + /* * Main program: do platform-specific initialisation and then call * psftp_main(). diff --git a/contrib/putty/UNIX/UXSTORE.C b/contrib/putty/UNIX/UXSTORE.C index 29cfa34..b666afe 100644 --- a/contrib/putty/UNIX/UXSTORE.C +++ b/contrib/putty/UNIX/UXSTORE.C @@ -166,25 +166,31 @@ void *open_settings_w(const char *sessionname, char **errmsg) /* * Start by making sure the .putty directory and its sessions - * subdir actually exist. Ignore error returns from mkdir since - * they're perfectly likely to be `already exists', and any - * other error will trip us up later on so there's no real need - * to catch it now. + * subdir actually exist. */ + filename = make_filename(INDEX_DIR, NULL); + if (mkdir(filename, 0700) < 0 && errno != EEXIST) { + *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " + "returned '%s'", filename, strerror(errno)); + sfree(filename); + return NULL; + } + sfree(filename); + filename = make_filename(INDEX_SESSIONDIR, NULL); - if (mkdir(filename, 0700) != 0) { - char *filename2 = make_filename(INDEX_DIR, NULL); - mkdir(filename2, 0700); - sfree(filename2); - mkdir(filename, 0700); + if (mkdir(filename, 0700) < 0 && errno != EEXIST) { + *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " + "returned '%s'", filename, strerror(errno)); + sfree(filename); + return NULL; } sfree(filename); filename = make_filename(INDEX_SESSION, sessionname); fp = fopen(filename, "w"); if (!fp) { - *errmsg = dupprintf("Unable to create %s: %s", - filename, strerror(errno)); + *errmsg = dupprintf("Unable to save session: open(\"%s\") " + "returned '%s'", filename, strerror(errno)); sfree(filename); return NULL; /* can't open */ } @@ -299,8 +305,10 @@ void *open_settings_r(const char *sessionname) char *value = strchr(line, '='); struct skeyval *kv; - if (!value) + if (!value) { + sfree(line); continue; + } *value++ = '\0'; value[strcspn(value, "\r\n")] = '\0'; /* trim trailing NL */ @@ -317,7 +325,7 @@ void *open_settings_r(const char *sessionname) return ret; } -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) +char *read_setting_s(void *handle, const char *key) { tree234 *tree = (tree234 *)handle; const char *val; @@ -333,11 +341,8 @@ char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) if (!val) return NULL; - else { - strncpy(buffer, val, buflen); - buffer[buflen-1] = '\0'; - return buffer; - } + else + return dupstr(val); } int read_setting_i(void *handle, const char *key, int defvalue) @@ -360,7 +365,7 @@ int read_setting_i(void *handle, const char *key, int defvalue) return atoi(val); } -int read_setting_fontspec(void *handle, const char *name, FontSpec *result) +FontSpec *read_setting_fontspec(void *handle, const char *name) { /* * In GTK1-only PuTTY, we used to store font names simply as a @@ -375,29 +380,41 @@ int read_setting_fontspec(void *handle, const char *name, FontSpec *result) * ("FontName"). */ char *suffname = dupcat(name, "Name", NULL); - if (read_setting_s(handle, suffname, result->name, sizeof(result->name))) { + char *tmp; + + if ((tmp = read_setting_s(handle, suffname)) != NULL) { + FontSpec *fs = fontspec_new(tmp); sfree(suffname); - return TRUE; /* got new-style name */ + sfree(tmp); + return fs; /* got new-style name */ } sfree(suffname); /* Fall back to old-style name. */ - memcpy(result->name, "server:", 7); - if (!read_setting_s(handle, name, - result->name + 7, sizeof(result->name) - 7) || - !result->name[7]) { - result->name[0] = '\0'; - return FALSE; + tmp = read_setting_s(handle, name); + if (tmp && *tmp) { + char *tmp2 = dupcat("server:", tmp, NULL); + FontSpec *fs = fontspec_new(tmp2); + sfree(tmp2); + sfree(tmp); + return fs; } else { - return TRUE; + sfree(tmp); + return NULL; } } -int read_setting_filename(void *handle, const char *name, Filename *result) +Filename *read_setting_filename(void *handle, const char *name) { - return !!read_setting_s(handle, name, result->path, sizeof(result->path)); + char *tmp = read_setting_s(handle, name); + if (tmp) { + Filename *ret = filename_from_str(tmp); + sfree(tmp); + return ret; + } else + return NULL; } -void write_setting_fontspec(void *handle, const char *name, FontSpec result) +void write_setting_fontspec(void *handle, const char *name, FontSpec *fs) { /* * read_setting_fontspec had to handle two cases, but when @@ -405,12 +422,12 @@ void write_setting_fontspec(void *handle, const char *name, FontSpec result) * new-style name. */ char *suffname = dupcat(name, "Name", NULL); - write_setting_s(handle, suffname, result.name); + write_setting_s(handle, suffname, fs->name); sfree(suffname); } -void write_setting_filename(void *handle, const char *name, Filename result) +void write_setting_filename(void *handle, const char *name, Filename *result) { - write_setting_s(handle, name, result.path); + write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) @@ -580,30 +597,40 @@ void store_host_key(const char *hostname, int port, int headerlen; char *filename, *tmpfilename; - newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key); - headerlen = 1 + strcspn(newtext, " "); /* count the space too */ - /* * Open both the old file and a new file. */ tmpfilename = make_filename(INDEX_HOSTKEYS_TMP, NULL); wfp = fopen(tmpfilename, "w"); - if (!wfp) { + if (!wfp && errno == ENOENT) { char *dir; dir = make_filename(INDEX_DIR, NULL); - mkdir(dir, 0700); + if (mkdir(dir, 0700) < 0) { + char *msg = dupprintf("Unable to store host key: mkdir(\"%s\") " + "returned '%s'", dir, strerror(errno)); + nonfatal(msg); + sfree(dir); + sfree(tmpfilename); + return; + } sfree(dir); wfp = fopen(tmpfilename, "w"); } if (!wfp) { - sfree(tmpfilename); - return; + char *msg = dupprintf("Unable to store host key: open(\"%s\") " + "returned '%s'", tmpfilename, strerror(errno)); + nonfatal(msg); + sfree(tmpfilename); + return; } filename = make_filename(INDEX_HOSTKEYS, NULL); rfp = fopen(filename, "r"); + newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key); + headerlen = 1 + strcspn(newtext, " "); /* count the space too */ + /* * Copy all lines from the old file to the new one that _don't_ * involve the same host key identifier as the one we're adding. @@ -612,6 +639,7 @@ void store_host_key(const char *hostname, int port, while ( (line = fgetline(rfp)) ) { if (strncmp(line, newtext, headerlen)) fputs(line, wfp); + sfree(line); } fclose(rfp); } @@ -623,7 +651,12 @@ void store_host_key(const char *hostname, int port, fclose(wfp); - rename(tmpfilename, filename); + if (rename(tmpfilename, filename) < 0) { + char *msg = dupprintf("Unable to store host key: rename(\"%s\",\"%s\")" + " returned '%s'", tmpfilename, filename, + strerror(errno)); + nonfatal(msg); + } sfree(tmpfilename); sfree(filename); @@ -660,18 +693,48 @@ void write_random_seed(void *data, int len) */ fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { + if (errno != ENOENT) { + char *msg = dupprintf("Unable to write random seed: open(\"%s\") " + "returned '%s'", fname, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + return; + } char *dir; dir = make_filename(INDEX_DIR, NULL); - mkdir(dir, 0700); + if (mkdir(dir, 0700) < 0) { + char *msg = dupprintf("Unable to write random seed: mkdir(\"%s\") " + "returned '%s'", dir, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + sfree(dir); + return; + } sfree(dir); fd = open(fname, O_CREAT | O_WRONLY, 0600); + if (fd < 0) { + char *msg = dupprintf("Unable to write random seed: open(\"%s\") " + "returned '%s'", fname, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + return; + } } while (len > 0) { int ret = write(fd, data, len); - if (ret <= 0) break; + if (ret < 0) { + char *msg = dupprintf("Unable to write random seed: write " + "returned '%s'", strerror(errno)); + nonfatal(msg); + sfree(msg); + break; + } len -= ret; data = (char *)data + len; } diff --git a/contrib/putty/UNIX/UXUCS.C b/contrib/putty/UNIX/UXUCS.C index d2d6522..20023b9 100644 --- a/contrib/putty/UNIX/UXUCS.C +++ b/contrib/putty/UNIX/UXUCS.C @@ -21,7 +21,7 @@ int is_dbcs_leadbyte(int codepage, char byte) return 0; /* we don't do DBCS */ } -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { if (codepage == DEFAULT_CODEPAGE) { @@ -29,7 +29,6 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, mbstate_t state; memset(&state, 0, sizeof state); - setlocale(LC_CTYPE, ""); while (mblen > 0) { size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state); @@ -40,8 +39,6 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, mblen -= i; } - setlocale(LC_CTYPE, "C"); - return n; } else if (codepage == CS_NONE) { int n = 0; @@ -59,7 +56,7 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, NULL, NULL, 0); } -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { @@ -73,7 +70,6 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, int n = 0; memset(&state, 0, sizeof state); - setlocale(LC_CTYPE, ""); while (wclen > 0) { int i = wcrtomb(output, wcstr[0], &state); @@ -85,8 +81,6 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, wclen--; } - setlocale(LC_CTYPE, "C"); - return n; } else if (codepage == CS_NONE) { int n = 0; @@ -139,7 +133,7 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, /* * Failing that, line_codepage should be decoded from the - * specification in cfg. + * specification in conf. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = decode_codepage(linecharset); @@ -162,7 +156,8 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, * in the line codepage into Unicode. */ for (i = 0; i < 256; i++) { - char c[1], *p; + char c[1]; + const char *p; wchar_t wc[1]; int len; c[0] = i; @@ -216,7 +211,8 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, * simply CP437. */ for (i = 0; i < 256; i++) { - char c[1], *p; + char c[1]; + const char *p; wchar_t wc[1]; int len; c[0] = i; @@ -257,17 +253,19 @@ const char *cp_name(int codepage) const char *cp_enumerate(int index) { int charset; - if (index == 0) - return "Use font encoding"; - charset = charset_localenc_nth(index-1); - if (charset == CS_NONE) + charset = charset_localenc_nth(index); + if (charset == CS_NONE) { + /* "Use font encoding" comes after all the named charsets */ + if (charset_localenc_nth(index-1) != CS_NONE) + return "Use font encoding"; return NULL; + } return charset_to_localenc(charset); } int decode_codepage(char *cp_name) { - if (!*cp_name) - return CS_NONE; /* use font encoding */ + if (!cp_name || !*cp_name) + return CS_UTF8; return charset_from_localenc(cp_name); } diff --git a/contrib/putty/UNIX/UX_X11.C b/contrib/putty/UNIX/UX_X11.C index 7dd17df..46f9957 100644 --- a/contrib/putty/UNIX/UX_X11.C +++ b/contrib/putty/UNIX/UX_X11.C @@ -12,7 +12,7 @@ #include "ssh.h" #include "network.h" -void platform_get_x11_auth(struct X11Display *disp, const Config *cfg) +void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { char *xauthfile; int needs_free; diff --git a/contrib/putty/VERSION.C b/contrib/putty/VERSION.C index 918a920..16347ca 100644 --- a/contrib/putty/VERSION.C +++ b/contrib/putty/VERSION.C @@ -5,6 +5,22 @@ #define STR1(x) #x #define STR(x) STR1(x) +#ifdef INCLUDE_EMPTY_H +/* + * Horrible hack to force version.o to be rebuilt unconditionally in + * the automake world: empty.h is an empty header file, created by the + * makefile and forcibly updated every time make is run. Including it + * here causes automake to track it as a dependency, which will cause + * version.o to be rebuilt too. + * + * The space between # and include causes mkfiles.pl's dependency + * scanner (for all other makefile types) to ignore this include, + * which is correct because only the automake makefile passes + * -DINCLUDE_EMPTY_H to enable it. + */ +# include "empty.h" +#endif + #if defined SNAPSHOT #if defined SVN_REV diff --git a/contrib/putty/WCWIDTH.C b/contrib/putty/WCWIDTH.C index bcd153d..0a3e2d0 100644 --- a/contrib/putty/WCWIDTH.C +++ b/contrib/putty/WCWIDTH.C @@ -50,7 +50,7 @@ * * http://www.unicode.org/unicode/reports/tr11/ * - * Markus Kuhn -- 2003-05-20 (Unicode 4.0) + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author @@ -64,12 +64,12 @@ #include "putty.h" /* for prototypes */ struct interval { - int first; - int last; + unsigned int first; + unsigned int last; }; /* auxiliary function for binary search in interval table */ -static int bisearch(wchar_t ucs, const struct interval *table, int max) { +static int bisearch(unsigned int ucs, const struct interval *table, int max) { int min = 0; int mid; @@ -121,32 +121,32 @@ static int bisearch(wchar_t ucs, const struct interval *table, int max) { * in ISO 10646. */ -int mk_wcwidth(wchar_t ucs) +int mk_wcwidth(unsigned int ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { - { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 }, - { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, - { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, - { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, - { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, - { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, - { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, - { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, - { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, - { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, - { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, - { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, - { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, - { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, - { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, - { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, - { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, - { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, - { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, - { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, @@ -155,18 +155,25 @@ int mk_wcwidth(wchar_t ucs) { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 }, - { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, - { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, - { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, - { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, - { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F }, - { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, - { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, - { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, - { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ @@ -190,6 +197,7 @@ int mk_wcwidth(wchar_t ucs) ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || @@ -198,7 +206,7 @@ int mk_wcwidth(wchar_t ucs) } -int mk_wcswidth(const wchar_t *pwcs, size_t n) +int mk_wcswidth(const unsigned int *pwcs, size_t n) { int w, width = 0; @@ -214,14 +222,14 @@ int mk_wcswidth(const wchar_t *pwcs, size_t n) /* * The following functions are the same as mk_wcwidth() and - * mk_wcwidth_cjk(), except that spacing characters in the East Asian + * mk_wcswidth(), except that spacing characters in the East Asian * Ambiguous (A) category as defined in Unicode Technical Report #11 * have a column width of 2. This variant might be useful for users of * CJK legacy encodings who want to migrate to UCS without changing * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ -int mk_wcwidth_cjk(wchar_t ucs) +int mk_wcwidth_cjk(unsigned int ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ @@ -289,7 +297,7 @@ int mk_wcwidth_cjk(wchar_t ucs) } -int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n) { int w, width = 0; diff --git a/contrib/putty/WILDCARD.C b/contrib/putty/WILDCARD.C index 75a7573..de5c3bc 100644 --- a/contrib/putty/WILDCARD.C +++ b/contrib/putty/WILDCARD.C @@ -326,7 +326,8 @@ int wc_unescape(char *output, const char *wildcard) wildcard++; } } - *output = '\0'; + if (output) + *output = '\0'; return 1; /* it's clean */ } diff --git a/contrib/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV b/contrib/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV index 215755a..f5b9647 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=35 +UnitCount=36 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pageant_private.rc @@ -33,7 +33,7 @@ CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] -FileName=..\..\..\misc.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -43,7 +43,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit2] -FileName=..\..\..\sshaes.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\windows\winpgnt.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winpgnt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,6 +213,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit20] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -222,7 +232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit20] +[Unit21] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -232,7 +242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit21] +[Unit22] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -242,7 +252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit22] +[Unit23] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -252,7 +262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit23] +[Unit24] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -262,7 +272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit24] +[Unit25] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -272,7 +282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit25] +[Unit26] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -282,7 +292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit26] +[Unit27] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -292,7 +302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit27] +[Unit28] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -302,7 +312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit28] +[Unit29] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -312,7 +322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit29] +[Unit30] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -322,7 +332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit30] +[Unit31] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -332,7 +342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit31] +[Unit32] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -342,7 +352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit32] +[Unit33] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -352,7 +362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit33] +[Unit34] FileName=..\..\..\windows\pageant.ico Folder=Resource Files Compile=0 @@ -362,7 +372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit35] FileName=..\..\..\windows\pageant.rc Folder=Resource Files Compile=1 @@ -372,7 +382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit36] FileName=..\..\..\windows\pageants.ico Folder=Resource Files Compile=0 diff --git a/contrib/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV b/contrib/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV index a7f5a5b..40d4018 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=76 +UnitCount=77 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=plink_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\ldisc.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\settings.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\timing.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winplink.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winplink.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,6 +563,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -762,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit75] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -772,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit75] +[Unit76] FileName=..\..\..\windows\plink.rc Folder=Resource Files Compile=1 @@ -782,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit76] +[Unit77] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 diff --git a/contrib/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV b/contrib/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV index 785f535..99e1327 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=73 +UnitCount=74 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pscp_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\int64.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\int64.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\pscp.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\settings.c +FileName=..\..\..\pscp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sftp.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\timing.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,6 +533,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\pscp.rc Folder=Resource Files Compile=1 diff --git a/contrib/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV b/contrib/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV index d9608f6..44e02d0 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=73 +UnitCount=74 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=psftp_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\int64.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\int64.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\psftp.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\settings.c +FileName=..\..\..\psftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sftp.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\timing.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,6 +533,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\psftp.rc Folder=Resource Files Compile=1 diff --git a/contrib/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV b/contrib/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV index a8cad26..146044b 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=93 +UnitCount=94 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=putty_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\config.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\cproxy.c +FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\dialog.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\ldisc.c +FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\ldiscucs.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\logging.c +FileName=..\..\..\ldiscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\minibidi.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\misc.c +FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sercfg.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\settings.c +FileName=..\..\..\sercfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\ssh.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\terminal.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\timing.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\wildcard.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,6 +703,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit69] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -762,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit75] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -772,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit75] +[Unit76] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -782,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit76] +[Unit77] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -792,7 +802,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit77] +[Unit78] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -802,7 +812,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit78] +[Unit79] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -812,7 +822,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit79] +[Unit80] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -822,7 +832,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit80] +[Unit81] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -832,7 +842,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit81] +[Unit82] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -842,7 +852,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit82] +[Unit83] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -852,7 +862,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit83] +[Unit84] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -862,7 +872,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit84] +[Unit85] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -872,7 +882,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit85] +[Unit86] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -882,7 +892,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit86] +[Unit87] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -892,7 +902,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit87] +[Unit88] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -902,7 +912,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit88] +[Unit89] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -912,7 +922,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit89] +[Unit90] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -922,7 +932,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit90] +[Unit91] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -932,7 +942,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit91] +[Unit92] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -942,7 +952,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit92] +[Unit93] FileName=..\..\..\windows\putty.rc Folder=Resource Files Compile=1 @@ -952,7 +962,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit93] +[Unit94] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 diff --git a/contrib/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV b/contrib/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV index 7d1ec3c..39ce582 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=46 +UnitCount=47 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttygen_private.rc @@ -33,7 +33,7 @@ CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] -FileName=..\..\..\import.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -43,7 +43,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit2] -FileName=..\..\..\misc.c +FileName=..\..\..\import.c Folder=Source Files Compile=1 CompileCpp=0 @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\notiming.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\sshaes.c +FileName=..\..\..\notiming.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\sshdssg.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshprime.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\sshrsag.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsag.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\windows\winpgen.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winpgen.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,6 +313,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit30] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -322,7 +332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit30] +[Unit31] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -332,7 +342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit31] +[Unit32] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -342,7 +352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit32] +[Unit33] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -352,7 +362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit33] +[Unit34] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -362,7 +372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit35] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -372,7 +382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit36] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -382,7 +392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit36] +[Unit37] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -392,7 +402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit37] +[Unit38] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -402,7 +412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit38] +[Unit39] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -412,7 +422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit39] +[Unit40] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -422,7 +432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit40] +[Unit41] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -432,7 +442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit41] +[Unit42] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -442,7 +452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit42] +[Unit43] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -452,7 +462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit44] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -462,7 +472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit45] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -472,7 +482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit46] FileName=..\..\..\windows\puttygen.ico Folder=Resource Files Compile=0 @@ -482,7 +492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit47] FileName=..\..\..\windows\puttygen.rc Folder=Resource Files Compile=1 diff --git a/contrib/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV b/contrib/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV index 6da9f45..538cf8e 100644 --- a/contrib/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV +++ b/contrib/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=64 +UnitCount=65 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttytel_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\config.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\dialog.c +FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\ldisc.c +FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\ldiscucs.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\logging.c +FileName=..\..\..\ldiscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\minibidi.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\misc.c +FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\nocproxy.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\nogss.c +FileName=..\..\..\nocproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\pinger.c +FileName=..\..\..\nogss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sercfg.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\settings.c +FileName=..\..\..\sercfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\telnet.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\terminal.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\timing.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,6 +443,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -452,7 +462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit44] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -462,7 +472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit45] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -472,7 +482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit46] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -482,7 +492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit47] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -492,7 +502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit47] +[Unit48] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -502,7 +512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit48] +[Unit49] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -512,7 +522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit49] +[Unit50] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -522,7 +532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit50] +[Unit51] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -532,7 +542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit51] +[Unit52] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\windows\puttytel.rc Folder=Resource Files Compile=1 diff --git a/contrib/putty/WINDOWS/MAKEFILE.BOR b/contrib/putty/WINDOWS/MAKEFILE.BOR index edb5ee5..2166f77 100644 --- a/contrib/putty/WINDOWS/MAKEFILE.BOR +++ b/contrib/putty/WINDOWS/MAKEFILE.BOR @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -130,29 +135,30 @@ RCFLAGS = $(RCFLAGS) $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj pageant.rsp +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj \ + pageant.rsp ilink32 -aa -Gn -L$(BCB)\lib @pageant.rsp -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \ - plink.rsp +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj plink.rsp ilink32 -ap -Gn -L$(BCB)\lib @plink.rsp -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -163,9 +169,9 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj pscp.rsp ilink32 -ap -Gn -L$(BCB)\lib @pscp.rsp -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -176,46 +182,46 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj psftp.rsp ilink32 -ap -Gn -L$(BCB)\lib @psftp.rsp -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \ putty.rsp ilink32 -aa -Gn -L$(BCB)\lib @putty.rsp -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj puttygen.rsp +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ + winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ + wintime.obj winutils.obj puttygen.rsp ilink32 -aa -Gn -L$(BCB)\lib @puttygen.rsp -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj puttytel.rsp ilink32 -aa -Gn -L$(BCB)\lib @puttytel.rsp pageant.rsp: $(MAKEFILE) echo c0w32 + > pageant.rsp - echo misc.obj sshaes.obj sshbn.obj sshdes.obj + >> pageant.rsp + echo conf.obj misc.obj sshaes.obj sshbn.obj sshdes.obj + >> pageant.rsp echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj + >> pageant.rsp echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj + >> pageant.rsp echo version.obj winhelp.obj winmisc.obj winpgnt.obj + >> pageant.rsp @@ -226,79 +232,79 @@ pageant.rsp: $(MAKEFILE) plink.rsp: $(MAKEFILE) echo c0x32 + > plink.rsp - echo be_all_s.obj cmdline.obj cproxy.obj ldisc.obj + >> plink.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> plink.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> plink.rsp - echo settings.obj ssh.obj sshaes.obj ssharcf.obj + >> plink.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> plink.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> plink.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> plink.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> plink.rsp - echo telnet.obj timing.obj tree234.obj version.obj + >> plink.rsp - echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> plink.rsp - echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> plink.rsp - echo winnojmp.obj winpgntc.obj winplink.obj + >> plink.rsp - echo winproxy.obj winser.obj winstore.obj wintime.obj + >> plink.rsp - echo winx11.obj x11fwd.obj >> plink.rsp + echo be_all_s.obj cmdline.obj conf.obj cproxy.obj + >> plink.rsp + echo ldisc.obj logging.obj misc.obj pgssapi.obj + >> plink.rsp + echo pinger.obj portfwd.obj proxy.obj raw.obj + >> plink.rsp + echo rlogin.obj settings.obj ssh.obj sshaes.obj + >> plink.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> plink.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> plink.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> plink.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> plink.rsp + echo sshzlib.obj telnet.obj timing.obj tree234.obj + >> plink.rsp + echo version.obj wildcard.obj wincons.obj windefs.obj + >> plink.rsp + echo wingss.obj winhandl.obj winmisc.obj winnet.obj + >> plink.rsp + echo winnoise.obj winnojmp.obj winpgntc.obj + >> plink.rsp + echo winplink.obj winproxy.obj winser.obj winstore.obj + >> plink.rsp + echo wintime.obj winx11.obj x11fwd.obj >> plink.rsp echo plink.exe >> plink.rsp echo nul,cw32 import32 ole32, >> plink.rsp echo plink.res >> plink.rsp pscp.rsp: $(MAKEFILE) echo c0x32 + > pscp.rsp - echo be_ssh.obj cmdline.obj cproxy.obj int64.obj + >> pscp.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> pscp.rsp - echo portfwd.obj proxy.obj pscp.obj settings.obj + >> pscp.rsp - echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> pscp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> pscp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> pscp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> pscp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> pscp.rsp - echo timing.obj tree234.obj version.obj wildcard.obj + >> pscp.rsp - echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> pscp.rsp - echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> pscp.rsp - echo winpgntc.obj winproxy.obj winsftp.obj + >> pscp.rsp - echo winstore.obj wintime.obj x11fwd.obj >> pscp.rsp + echo be_ssh.obj cmdline.obj conf.obj cproxy.obj + >> pscp.rsp + echo int64.obj logging.obj misc.obj pgssapi.obj + >> pscp.rsp + echo pinger.obj portfwd.obj proxy.obj pscp.obj + >> pscp.rsp + echo settings.obj sftp.obj ssh.obj sshaes.obj + >> pscp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> pscp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> pscp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> pscp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> pscp.rsp + echo sshzlib.obj timing.obj tree234.obj version.obj + >> pscp.rsp + echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> pscp.rsp + echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> pscp.rsp + echo winnojmp.obj winpgntc.obj winproxy.obj + >> pscp.rsp + echo winsftp.obj winstore.obj wintime.obj x11fwd.obj >> pscp.rsp echo pscp.exe >> pscp.rsp echo nul,cw32 import32 ole32, >> pscp.rsp echo pscp.res >> pscp.rsp psftp.rsp: $(MAKEFILE) echo c0x32 + > psftp.rsp - echo be_ssh.obj cmdline.obj cproxy.obj int64.obj + >> psftp.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> psftp.rsp - echo portfwd.obj proxy.obj psftp.obj settings.obj + >> psftp.rsp - echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> psftp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> psftp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> psftp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> psftp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> psftp.rsp - echo timing.obj tree234.obj version.obj wildcard.obj + >> psftp.rsp - echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> psftp.rsp - echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> psftp.rsp - echo winpgntc.obj winproxy.obj winsftp.obj + >> psftp.rsp - echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp + echo be_ssh.obj cmdline.obj conf.obj cproxy.obj + >> psftp.rsp + echo int64.obj logging.obj misc.obj pgssapi.obj + >> psftp.rsp + echo pinger.obj portfwd.obj proxy.obj psftp.obj + >> psftp.rsp + echo settings.obj sftp.obj ssh.obj sshaes.obj + >> psftp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> psftp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> psftp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> psftp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> psftp.rsp + echo sshzlib.obj timing.obj tree234.obj version.obj + >> psftp.rsp + echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> psftp.rsp + echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> psftp.rsp + echo winnojmp.obj winpgntc.obj winproxy.obj + >> psftp.rsp + echo winsftp.obj winstore.obj wintime.obj x11fwd.obj >> psftp.rsp echo psftp.exe >> psftp.rsp echo nul,cw32 import32 ole32, >> psftp.rsp echo psftp.res >> psftp.rsp putty.rsp: $(MAKEFILE) echo c0w32 + > putty.rsp - echo be_all_s.obj cmdline.obj config.obj cproxy.obj + >> putty.rsp - echo dialog.obj ldisc.obj ldiscucs.obj logging.obj + >> putty.rsp - echo minibidi.obj misc.obj pgssapi.obj pinger.obj + >> putty.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> putty.rsp - echo sercfg.obj settings.obj sizetip.obj ssh.obj + >> putty.rsp - echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj + >> putty.rsp - echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj + >> putty.rsp - echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj + >> putty.rsp - echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj + >> putty.rsp - echo sshsha.obj sshzlib.obj telnet.obj terminal.obj + >> putty.rsp - echo timing.obj tree234.obj version.obj wcwidth.obj + >> putty.rsp - echo wildcard.obj wincfg.obj winctrls.obj windefs.obj + >> putty.rsp - echo windlg.obj window.obj wingss.obj winhandl.obj + >> putty.rsp - echo winhelp.obj winjump.obj winmisc.obj winnet.obj + >> putty.rsp - echo winnoise.obj winpgntc.obj winprint.obj + >> putty.rsp + echo be_all_s.obj cmdline.obj conf.obj config.obj + >> putty.rsp + echo cproxy.obj dialog.obj ldisc.obj ldiscucs.obj + >> putty.rsp + echo logging.obj minibidi.obj misc.obj pgssapi.obj + >> putty.rsp + echo pinger.obj portfwd.obj proxy.obj raw.obj + >> putty.rsp + echo rlogin.obj sercfg.obj settings.obj sizetip.obj + >> putty.rsp + echo ssh.obj sshaes.obj ssharcf.obj sshblowf.obj + >> putty.rsp + echo sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj + >> putty.rsp + echo sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj + >> putty.rsp + echo sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj + >> putty.rsp + echo sshsh512.obj sshsha.obj sshzlib.obj telnet.obj + >> putty.rsp + echo terminal.obj timing.obj tree234.obj version.obj + >> putty.rsp + echo wcwidth.obj wildcard.obj wincfg.obj winctrls.obj + >> putty.rsp + echo windefs.obj windlg.obj window.obj wingss.obj + >> putty.rsp + echo winhandl.obj winhelp.obj winjump.obj winmisc.obj + >> putty.rsp + echo winnet.obj winnoise.obj winpgntc.obj winprint.obj + >> putty.rsp echo winproxy.obj winser.obj winstore.obj wintime.obj + >> putty.rsp echo winucs.obj winutils.obj winx11.obj x11fwd.obj >> putty.rsp echo putty.exe >> putty.rsp @@ -307,30 +313,31 @@ putty.rsp: $(MAKEFILE) puttygen.rsp: $(MAKEFILE) echo c0w32 + > puttygen.rsp - echo import.obj misc.obj notiming.obj sshaes.obj + >> puttygen.rsp - echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj + >> puttygen.rsp - echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj + >> puttygen.rsp - echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj + >> puttygen.rsp - echo sshsha.obj tree234.obj version.obj winctrls.obj + >> puttygen.rsp - echo winhelp.obj winmisc.obj winnoise.obj winnojmp.obj + >> puttygen.rsp - echo winpgen.obj winstore.obj wintime.obj winutils.obj >> puttygen.rsp + echo conf.obj import.obj misc.obj notiming.obj + >> puttygen.rsp + echo sshaes.obj sshbn.obj sshdes.obj sshdss.obj + >> puttygen.rsp + echo sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj + >> puttygen.rsp + echo sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj + >> puttygen.rsp + echo sshsh512.obj sshsha.obj tree234.obj version.obj + >> puttygen.rsp + echo winctrls.obj winhelp.obj winmisc.obj winnoise.obj + >> puttygen.rsp + echo winnojmp.obj winpgen.obj winstore.obj wintime.obj + >> puttygen.rsp + echo winutils.obj >> puttygen.rsp echo puttygen.exe >> puttygen.rsp echo nul,cw32 import32 ole32, >> puttygen.rsp echo puttygen.res >> puttygen.rsp puttytel.rsp: $(MAKEFILE) echo c0w32 + > puttytel.rsp - echo be_nos_s.obj cmdline.obj config.obj dialog.obj + >> puttytel.rsp - echo ldisc.obj ldiscucs.obj logging.obj minibidi.obj + >> puttytel.rsp - echo misc.obj nocproxy.obj nogss.obj pinger.obj + >> puttytel.rsp - echo proxy.obj raw.obj rlogin.obj sercfg.obj + >> puttytel.rsp - echo settings.obj sizetip.obj telnet.obj terminal.obj + >> puttytel.rsp - echo timing.obj tree234.obj version.obj wcwidth.obj + >> puttytel.rsp - echo wincfg.obj winctrls.obj windefs.obj windlg.obj + >> puttytel.rsp - echo window.obj winhandl.obj winhelp.obj winjump.obj + >> puttytel.rsp - echo winmisc.obj winnet.obj winprint.obj winproxy.obj + >> puttytel.rsp - echo winser.obj winstore.obj wintime.obj winucs.obj + >> puttytel.rsp - echo winutils.obj >> puttytel.rsp + echo be_nos_s.obj cmdline.obj conf.obj config.obj + >> puttytel.rsp + echo dialog.obj ldisc.obj ldiscucs.obj logging.obj + >> puttytel.rsp + echo minibidi.obj misc.obj nocproxy.obj nogss.obj + >> puttytel.rsp + echo pinger.obj proxy.obj raw.obj rlogin.obj + >> puttytel.rsp + echo sercfg.obj settings.obj sizetip.obj telnet.obj + >> puttytel.rsp + echo terminal.obj timing.obj tree234.obj version.obj + >> puttytel.rsp + echo wcwidth.obj wincfg.obj winctrls.obj windefs.obj + >> puttytel.rsp + echo windlg.obj window.obj winhandl.obj winhelp.obj + >> puttytel.rsp + echo winjump.obj winmisc.obj winnet.obj winprint.obj + >> puttytel.rsp + echo winproxy.obj winser.obj winstore.obj wintime.obj + >> puttytel.rsp + echo winucs.obj winutils.obj >> puttytel.rsp echo puttytel.exe >> puttytel.rsp echo nul,cw32 import32 ole32, >> puttytel.rsp echo puttytel.res >> puttytel.rsp @@ -359,6 +366,10 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -577,7 +588,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\puttyps.h \ ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \ diff --git a/contrib/putty/WINDOWS/MAKEFILE.CYG b/contrib/putty/WINDOWS/MAKEFILE.CYG index 18caabc..162f7ff 100644 --- a/contrib/putty/WINDOWS/MAKEFILE.CYG +++ b/contrib/putty/WINDOWS/MAKEFILE.CYG @@ -1,4 +1,4 @@ -# Makefile for putty under cygwin. +# Makefile for putty under Cygwin, MinGW, or Winelib. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -115,11 +120,13 @@ RC = $(TOOLPATH)windres # RCINC = --include-dir c:\cygwin\include\ CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT \ - -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -I.././ \ - -I../charset/ -I../windows/ -I../unix/ -I../macosx/ + -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP \ + -DNO_SECUREZEROMEMORY -I.././ -I../charset/ -I../windows/ \ + -I../unix/ -I../macosx/ LDFLAGS = -mno-cygwin -s RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400 +CFLAGS += -DSECURITY_WIN32 # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) # _WIN32_IE is required to expose identifiers that only make sense on @@ -133,28 +140,29 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.o pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o \ +pageant.exe: conf.o misc.o pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o \ sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ tree234.o version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \ winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map misc.o \ + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map conf.o misc.o \ pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o sshmd5.o \ sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o tree234.o \ version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \ winutils.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 -lwinmm -lwinspool -plink.exe: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o plink.res.o portfwd.o proxy.o raw.o rlogin.o \ - settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o timing.o tree234.o version.o \ - wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ - winnet.o winnoise.o winnojmp.o winpgntc.o winplink.o \ - winproxy.o winser.o winstore.o wintime.o winx11.o x11fwd.o +plink.exe: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ + pgssapi.o pinger.o plink.res.o portfwd.o proxy.o raw.o \ + rlogin.o settings.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o telnet.o timing.o tree234.o \ + version.o wildcard.o wincons.o windefs.o wingss.o winhandl.o \ + winmisc.o winnet.o winnoise.o winnojmp.o winpgntc.o \ + winplink.o winproxy.o winser.o winstore.o wintime.o winx11.o \ + x11fwd.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map be_all_s.o cmdline.o \ - cproxy.o ldisc.o logging.o misc.o pgssapi.o pinger.o \ + conf.o cproxy.o ldisc.o logging.o misc.o pgssapi.o pinger.o \ plink.res.o portfwd.o proxy.o raw.o rlogin.o settings.o \ ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ @@ -166,37 +174,37 @@ plink.exe: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ -luser32 -lwinmm -lwinspool -pscp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o pscp.o pscp.res.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \ - windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \ - winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \ - wintime.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map be_ssh.o cmdline.o cproxy.o \ - int64.o logging.o misc.o pgssapi.o pinger.o portfwd.o \ - proxy.o pscp.o pscp.res.o settings.o sftp.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o timing.o \ - tree234.o version.o wildcard.o wincons.o windefs.o wingss.o \ - winhandl.o winmisc.o winnet.o winnoise.o winnojmp.o \ +pscp.exe: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o pscp.o pscp.res.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o timing.o tree234.o version.o \ + wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ + winnet.o winnoise.o winnojmp.o winpgntc.o winproxy.o \ + winsftp.o winstore.o wintime.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map be_ssh.o cmdline.o conf.o \ + cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \ + portfwd.o proxy.o pscp.o pscp.res.o settings.o sftp.o ssh.o \ + sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ + sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ + sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ + timing.o tree234.o version.o wildcard.o wincons.o windefs.o \ + wingss.o winhandl.o winmisc.o winnet.o winnoise.o winnojmp.o \ winpgntc.o winproxy.o winsftp.o winstore.o wintime.o \ x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 -lwinmm -lwinspool -psftp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o psftp.o psftp.res.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \ - windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \ - winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \ - wintime.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map be_ssh.o cmdline.o \ +psftp.exe: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftp.res.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o timing.o tree234.o version.o \ + wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ + winnet.o winnoise.o winnojmp.o winpgntc.o winproxy.o \ + winsftp.o winstore.o wintime.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map be_ssh.o cmdline.o conf.o \ cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \ portfwd.o proxy.o psftp.o psftp.res.o settings.o sftp.o \ ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ @@ -208,7 +216,7 @@ psftp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ wintime.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \ -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool -putty.exe: be_all_s.o cmdline.o config.o cproxy.o dialog.o ldisc.o \ +putty.exe: be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o ldisc.o \ ldiscucs.o logging.o minibidi.o misc.o pgssapi.o pinger.o \ portfwd.o proxy.o putty.res.o raw.o rlogin.o sercfg.o \ settings.o sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o \ @@ -221,52 +229,54 @@ putty.exe: be_all_s.o cmdline.o config.o cproxy.o dialog.o ldisc.o \ winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ winutils.o winx11.o x11fwd.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,putty.map be_all_s.o \ - cmdline.o config.o cproxy.o dialog.o ldisc.o ldiscucs.o \ - logging.o minibidi.o misc.o pgssapi.o pinger.o portfwd.o \ - proxy.o putty.res.o raw.o rlogin.o sercfg.o settings.o \ - sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o terminal.o timing.o tree234.o \ - version.o wcwidth.o wildcard.o wincfg.o winctrls.o windefs.o \ - windlg.o window.o wingss.o winhandl.o winhelp.o winjump.o \ - winmisc.o winnet.o winnoise.o winpgntc.o winprint.o \ - winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o \ - winx11.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \ - -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool - -puttygen.exe: import.o misc.o notiming.o puttygen.res.o sshaes.o sshbn.o \ + cmdline.o conf.o config.o cproxy.o dialog.o ldisc.o \ + ldiscucs.o logging.o minibidi.o misc.o pgssapi.o pinger.o \ + portfwd.o proxy.o putty.res.o raw.o rlogin.o sercfg.o \ + settings.o sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o timing.o \ + tree234.o version.o wcwidth.o wildcard.o wincfg.o winctrls.o \ + windefs.o windlg.o window.o wingss.o winhandl.o winhelp.o \ + winjump.o winmisc.o winnet.o winnoise.o winpgntc.o \ + winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ + winutils.o winx11.o x11fwd.o -ladvapi32 -lcomctl32 \ + -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 \ + -lwinmm -lwinspool + +puttygen.exe: conf.o import.o misc.o notiming.o puttygen.res.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o tree234.o version.o winctrls.o winhelp.o winmisc.o \ + winnoise.o winnojmp.o winpgen.o winstore.o wintime.o \ + winutils.o + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map conf.o \ + import.o misc.o notiming.o puttygen.res.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ tree234.o version.o winctrls.o winhelp.o winmisc.o \ winnoise.o winnojmp.o winpgen.o winstore.o wintime.o \ - winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map import.o \ - misc.o notiming.o puttygen.res.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o tree234.o \ - version.o winctrls.o winhelp.o winmisc.o winnoise.o \ - winnojmp.o winpgen.o winstore.o wintime.o winutils.o \ - -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 \ - -lshell32 -luser32 -lwinmm -lwinspool + winutils.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ + -lole32 -lshell32 -luser32 -lwinmm -lwinspool -puttytel.exe: be_nos_s.o cmdline.o config.o dialog.o ldisc.o ldiscucs.o \ +puttytel.exe: be_nos_s.o cmdline.o conf.o config.o dialog.o ldisc.o \ + ldiscucs.o logging.o minibidi.o misc.o nocproxy.o nogss.o \ + pinger.o proxy.o puttytel.res.o raw.o rlogin.o sercfg.o \ + settings.o sizetip.o telnet.o terminal.o timing.o tree234.o \ + version.o wcwidth.o wincfg.o winctrls.o windefs.o windlg.o \ + window.o winhandl.o winhelp.o winjump.o winmisc.o winnet.o \ + winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ + winutils.o + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_nos_s.o \ + cmdline.o conf.o config.o dialog.o ldisc.o ldiscucs.o \ logging.o minibidi.o misc.o nocproxy.o nogss.o pinger.o \ proxy.o puttytel.res.o raw.o rlogin.o sercfg.o settings.o \ sizetip.o telnet.o terminal.o timing.o tree234.o version.o \ wcwidth.o wincfg.o winctrls.o windefs.o windlg.o window.o \ winhandl.o winhelp.o winjump.o winmisc.o winnet.o winprint.o \ - winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_nos_s.o \ - cmdline.o config.o dialog.o ldisc.o ldiscucs.o logging.o \ - minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o \ - puttytel.res.o raw.o rlogin.o sercfg.o settings.o sizetip.o \ - telnet.o terminal.o timing.o tree234.o version.o wcwidth.o \ - wincfg.o winctrls.o windefs.o windlg.o window.o winhandl.o \ - winhelp.o winjump.o winmisc.o winnet.o winprint.o winproxy.o \ - winser.o winstore.o wintime.o winucs.o winutils.o -ladvapi32 \ - -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ - -luser32 -lwinmm -lwinspool + winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o \ + -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 \ + -lshell32 -luser32 -lwinmm -lwinspool be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -304,6 +314,12 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c + config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -448,7 +464,7 @@ osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m pageant.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc pageant.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc -o pageant.res.o pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \ ../misc.h ../windows/winstuff.h ../macosx/osx.h \ @@ -463,7 +479,7 @@ pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c plink.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc plink.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc -o plink.res.o portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ ../misc.h ../puttymem.h ../tree234.h ../int64.h \ @@ -484,7 +500,7 @@ pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c pscp.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc pscp.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc -o pscp.res.o psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ @@ -493,16 +509,16 @@ psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c psftp.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc psftp.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc -o psftp.res.o putty.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc putty.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc -o putty.res.o puttygen.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc puttygen.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc -o puttygen.res.o puttytel.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc puttytel.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc -o puttytel.res.o raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -673,7 +689,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h @@ -1007,6 +1023,6 @@ version.o: FORCE $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c clean: - rm -f *.o *.exe *.res.o *.map + rm -f *.o *.exe *.res.o *.so *.map FORCE: diff --git a/contrib/putty/WINDOWS/MAKEFILE.LCC b/contrib/putty/WINDOWS/MAKEFILE.LCC index 2f30ede..5137b04 100644 --- a/contrib/putty/WINDOWS/MAKEFILE.LCC +++ b/contrib/putty/WINDOWS/MAKEFILE.LCC @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -119,29 +124,30 @@ RCFLAGS += $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj - lcclnk -subsystem windows -o pageant.exe misc.obj pageant.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshmd5.obj sshpubk.obj \ - sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ - version.obj winhelp.obj winmisc.obj winpgnt.obj winpgntc.obj \ - winutils.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ - winmm.lib imm32.lib +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj + lcclnk -subsystem windows -o pageant.exe conf.obj misc.obj pageant.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshmd5.obj \ + sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ + tree234.obj version.obj winhelp.obj winmisc.obj winpgnt.obj \ + winpgntc.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ + winspool.lib winmm.lib imm32.lib -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj - lcclnk -o plink.exe be_all_s.obj cmdline.obj cproxy.obj ldisc.obj \ +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj + lcclnk -o plink.exe be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ @@ -155,18 +161,7 @@ plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ winmm.lib imm32.lib -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \ - wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \ - winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ - winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ - wintime.obj x11fwd.obj - lcclnk -o pscp.exe be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ @@ -176,12 +171,23 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \ winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ - wintime.obj x11fwd.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + wintime.obj x11fwd.obj + lcclnk -o pscp.exe be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ + proxy.obj pscp.obj pscp.res settings.obj sftp.obj ssh.obj \ + sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \ + sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshgssc.obj \ + sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj sshzlib.obj timing.obj tree234.obj \ + version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ + winhandl.obj winmisc.obj winnet.obj winnoise.obj \ + winnojmp.obj winpgntc.obj winproxy.obj winsftp.obj \ + winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \ + ws2_32.lib winspool.lib winmm.lib imm32.lib -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -190,7 +196,7 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ wintime.obj x11fwd.obj - lcclnk -o psftp.exe be_ssh.obj cmdline.obj cproxy.obj int64.obj \ + lcclnk -o psftp.exe be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj \ logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ proxy.obj psftp.obj psftp.res settings.obj sftp.obj ssh.obj \ sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \ @@ -203,73 +209,73 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \ ws2_32.lib winspool.lib winmm.lib imm32.lib -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj - lcclnk -subsystem windows -o putty.exe be_all_s.obj cmdline.obj config.obj \ - cproxy.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \ - minibidi.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ - proxy.obj putty.res raw.obj rlogin.obj sercfg.obj \ - settings.obj sizetip.obj ssh.obj sshaes.obj ssharcf.obj \ - sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj \ - sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj \ - sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ - sshzlib.obj telnet.obj terminal.obj timing.obj tree234.obj \ - version.obj wcwidth.obj wildcard.obj wincfg.obj winctrls.obj \ - windefs.obj windlg.obj window.obj wingss.obj winhandl.obj \ - winhelp.obj winjump.obj winmisc.obj winnet.obj winnoise.obj \ - winpgntc.obj winprint.obj winproxy.obj winser.obj \ - winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ - x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ - winmm.lib imm32.lib + lcclnk -subsystem windows -o putty.exe be_all_s.obj cmdline.obj conf.obj \ + config.obj cproxy.obj dialog.obj ldisc.obj ldiscucs.obj \ + logging.obj minibidi.obj misc.obj pgssapi.obj pinger.obj \ + portfwd.obj proxy.obj putty.res raw.obj rlogin.obj \ + sercfg.obj settings.obj sizetip.obj ssh.obj sshaes.obj \ + ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ + sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ + sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ + sshsha.obj sshzlib.obj telnet.obj terminal.obj timing.obj \ + tree234.obj version.obj wcwidth.obj wildcard.obj wincfg.obj \ + winctrls.obj windefs.obj windlg.obj window.obj wingss.obj \ + winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ + winnoise.obj winpgntc.obj winprint.obj winproxy.obj \ + winser.obj winstore.obj wintime.obj winucs.obj winutils.obj \ + winx11.obj x11fwd.obj shell32.lib wsock32.lib ws2_32.lib \ + winspool.lib winmm.lib imm32.lib -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj - lcclnk -subsystem windows -o puttygen.exe import.obj misc.obj notiming.obj \ - puttygen.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj \ - sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj \ - tree234.obj version.obj winctrls.obj winhelp.obj winmisc.obj \ +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ - wintime.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + wintime.obj winutils.obj + lcclnk -subsystem windows -o puttygen.exe conf.obj import.obj misc.obj \ + notiming.obj puttygen.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj \ + sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj \ + sshsha.obj tree234.obj version.obj winctrls.obj winhelp.obj \ + winmisc.obj winnoise.obj winnojmp.obj winpgen.obj \ + winstore.obj wintime.obj winutils.obj shell32.lib \ + wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj lcclnk -subsystem windows -o puttytel.exe be_nos_s.obj cmdline.obj \ - config.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \ - minibidi.obj misc.obj nocproxy.obj nogss.obj pinger.obj \ - proxy.obj puttytel.res raw.obj rlogin.obj sercfg.obj \ - settings.obj sizetip.obj telnet.obj terminal.obj timing.obj \ - tree234.obj version.obj wcwidth.obj wincfg.obj winctrls.obj \ - windefs.obj windlg.obj window.obj winhandl.obj winhelp.obj \ - winjump.obj winmisc.obj winnet.obj winprint.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winucs.obj winutils.obj \ - shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ - imm32.lib + conf.obj config.obj dialog.obj ldisc.obj ldiscucs.obj \ + logging.obj minibidi.obj misc.obj nocproxy.obj nogss.obj \ + pinger.obj proxy.obj puttytel.res raw.obj rlogin.obj \ + sercfg.obj settings.obj sizetip.obj telnet.obj terminal.obj \ + timing.obj tree234.obj version.obj wcwidth.obj wincfg.obj \ + winctrls.obj windefs.obj windlg.obj window.obj winhandl.obj \ + winhelp.obj winjump.obj winmisc.obj winnet.obj winprint.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winucs.obj \ + winutils.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ + winmm.lib imm32.lib be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ @@ -301,6 +307,11 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdline.c +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\conf.c config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -596,7 +607,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\timing.c toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\toucs.c -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\utf8.c diff --git a/contrib/putty/WINDOWS/MAKEFILE.VC b/contrib/putty/WINDOWS/MAKEFILE.VC index 59c3c43..0ad1f17 100644 --- a/contrib/putty/WINDOWS/MAKEFILE.VC +++ b/contrib/putty/WINDOWS/MAKEFILE.VC @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -119,29 +124,30 @@ RCFLAGS = $(RCFLAGS) $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj pageant.rsp +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj \ + pageant.rsp link $(LFLAGS) $(XLFLAGS) -out:pageant.exe -map:pageant.map @pageant.rsp -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \ - plink.rsp +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj plink.rsp link $(LFLAGS) $(XLFLAGS) -out:plink.exe -map:plink.map @plink.rsp -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -152,9 +158,9 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj pscp.rsp link $(LFLAGS) $(XLFLAGS) -out:pscp.exe -map:pscp.map @pscp.rsp -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -165,81 +171,81 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj psftp.rsp link $(LFLAGS) $(XLFLAGS) -out:psftp.exe -map:psftp.map @psftp.rsp -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \ putty.rsp link $(LFLAGS) $(XLFLAGS) -out:putty.exe -map:putty.map @putty.rsp -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj puttygen.rsp +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ + winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ + wintime.obj winutils.obj puttygen.rsp link $(LFLAGS) $(XLFLAGS) -out:puttygen.exe -map:puttygen.map @puttygen.rsp -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj puttytel.rsp link $(LFLAGS) $(XLFLAGS) -out:puttytel.exe -map:puttytel.map @puttytel.rsp pageant.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > pageant.rsp - echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> pageant.rsp - echo imm32.lib misc.obj ole32.lib pageant.res >> pageant.rsp - echo shell32.lib sshaes.obj sshbn.obj sshdes.obj >> pageant.rsp - echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj >> pageant.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj >> pageant.rsp - echo user32.lib version.obj winhelp.obj winmisc.obj >> pageant.rsp - echo winmm.lib winpgnt.obj winpgntc.obj winspool.lib >> pageant.rsp - echo winutils.obj >> pageant.rsp + echo advapi32.lib comctl32.lib comdlg32.lib conf.obj >> pageant.rsp + echo gdi32.lib imm32.lib misc.obj ole32.lib >> pageant.rsp + echo pageant.res shell32.lib sshaes.obj sshbn.obj >> pageant.rsp + echo sshdes.obj sshdss.obj sshmd5.obj sshpubk.obj >> pageant.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> pageant.rsp + echo tree234.obj user32.lib version.obj winhelp.obj >> pageant.rsp + echo winmisc.obj winmm.lib winpgnt.obj winpgntc.obj >> pageant.rsp + echo winspool.lib winutils.obj >> pageant.rsp plink.rsp: $(MAKEFILE) echo /nologo /subsystem:console > plink.rsp echo advapi32.lib be_all_s.obj cmdline.obj >> plink.rsp - echo comctl32.lib comdlg32.lib cproxy.obj gdi32.lib >> plink.rsp - echo imm32.lib ldisc.obj logging.obj misc.obj >> plink.rsp - echo ole32.lib pgssapi.obj pinger.obj plink.res >> plink.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj >> plink.rsp - echo settings.obj shell32.lib ssh.obj sshaes.obj >> plink.rsp - echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj >> plink.rsp - echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj >> plink.rsp - echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj >> plink.rsp - echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> plink.rsp - echo sshzlib.obj telnet.obj timing.obj tree234.obj >> plink.rsp - echo user32.lib version.obj wildcard.obj wincons.obj >> plink.rsp - echo windefs.obj wingss.obj winhandl.obj winmisc.obj >> plink.rsp - echo winmm.lib winnet.obj winnoise.obj winnojmp.obj >> plink.rsp - echo winpgntc.obj winplink.obj winproxy.obj winser.obj >> plink.rsp - echo winspool.lib winstore.obj wintime.obj winx11.obj >> plink.rsp - echo x11fwd.obj >> plink.rsp + echo comctl32.lib comdlg32.lib conf.obj cproxy.obj >> plink.rsp + echo gdi32.lib imm32.lib ldisc.obj logging.obj >> plink.rsp + echo misc.obj ole32.lib pgssapi.obj pinger.obj >> plink.rsp + echo plink.res portfwd.obj proxy.obj raw.obj >> plink.rsp + echo rlogin.obj settings.obj shell32.lib ssh.obj >> plink.rsp + echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj >> plink.rsp + echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj >> plink.rsp + echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj >> plink.rsp + echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj >> plink.rsp + echo sshsha.obj sshzlib.obj telnet.obj timing.obj >> plink.rsp + echo tree234.obj user32.lib version.obj wildcard.obj >> plink.rsp + echo wincons.obj windefs.obj wingss.obj winhandl.obj >> plink.rsp + echo winmisc.obj winmm.lib winnet.obj winnoise.obj >> plink.rsp + echo winnojmp.obj winpgntc.obj winplink.obj >> plink.rsp + echo winproxy.obj winser.obj winspool.lib winstore.obj >> plink.rsp + echo wintime.obj winx11.obj x11fwd.obj >> plink.rsp pscp.rsp: $(MAKEFILE) echo /nologo /subsystem:console > pscp.rsp echo advapi32.lib be_ssh.obj cmdline.obj comctl32.lib >> pscp.rsp - echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> pscp.rsp - echo int64.obj logging.obj misc.obj ole32.lib >> pscp.rsp - echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> pscp.rsp - echo pscp.obj pscp.res settings.obj sftp.obj >> pscp.rsp + echo comdlg32.lib conf.obj cproxy.obj gdi32.lib >> pscp.rsp + echo imm32.lib int64.obj logging.obj misc.obj >> pscp.rsp + echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> pscp.rsp + echo proxy.obj pscp.obj pscp.res settings.obj sftp.obj >> pscp.rsp echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> pscp.rsp echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> pscp.rsp echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> pscp.rsp @@ -255,75 +261,76 @@ pscp.rsp: $(MAKEFILE) psftp.rsp: $(MAKEFILE) echo /nologo /subsystem:console > psftp.rsp echo advapi32.lib be_ssh.obj cmdline.obj comctl32.lib >> psftp.rsp - echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> psftp.rsp - echo int64.obj logging.obj misc.obj ole32.lib >> psftp.rsp - echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> psftp.rsp - echo psftp.obj psftp.res settings.obj sftp.obj >> psftp.rsp - echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> psftp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> psftp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> psftp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> psftp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> psftp.rsp - echo timing.obj tree234.obj user32.lib version.obj >> psftp.rsp - echo wildcard.obj wincons.obj windefs.obj wingss.obj >> psftp.rsp - echo winhandl.obj winmisc.obj winmm.lib winnet.obj >> psftp.rsp - echo winnoise.obj winnojmp.obj winpgntc.obj >> psftp.rsp + echo comdlg32.lib conf.obj cproxy.obj gdi32.lib >> psftp.rsp + echo imm32.lib int64.obj logging.obj misc.obj >> psftp.rsp + echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> psftp.rsp + echo proxy.obj psftp.obj psftp.res settings.obj >> psftp.rsp + echo sftp.obj shell32.lib ssh.obj sshaes.obj >> psftp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj >> psftp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj >> psftp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj >> psftp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> psftp.rsp + echo sshzlib.obj timing.obj tree234.obj user32.lib >> psftp.rsp + echo version.obj wildcard.obj wincons.obj windefs.obj >> psftp.rsp + echo wingss.obj winhandl.obj winmisc.obj winmm.lib >> psftp.rsp + echo winnet.obj winnoise.obj winnojmp.obj winpgntc.obj >> psftp.rsp echo winproxy.obj winsftp.obj winspool.lib >> psftp.rsp echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp putty.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > putty.rsp echo advapi32.lib be_all_s.obj cmdline.obj >> putty.rsp - echo comctl32.lib comdlg32.lib config.obj cproxy.obj >> putty.rsp - echo dialog.obj gdi32.lib imm32.lib ldisc.obj >> putty.rsp - echo ldiscucs.obj logging.obj minibidi.obj misc.obj >> putty.rsp - echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> putty.rsp - echo proxy.obj putty.res raw.obj rlogin.obj sercfg.obj >> putty.rsp - echo settings.obj shell32.lib sizetip.obj ssh.obj >> putty.rsp - echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj >> putty.rsp - echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj >> putty.rsp - echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj >> putty.rsp - echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj >> putty.rsp - echo sshsha.obj sshzlib.obj telnet.obj terminal.obj >> putty.rsp - echo timing.obj tree234.obj user32.lib version.obj >> putty.rsp - echo wcwidth.obj wildcard.obj wincfg.obj winctrls.obj >> putty.rsp - echo windefs.obj windlg.obj window.obj wingss.obj >> putty.rsp - echo winhandl.obj winhelp.obj winjump.obj winmisc.obj >> putty.rsp - echo winmm.lib winnet.obj winnoise.obj winpgntc.obj >> putty.rsp - echo winprint.obj winproxy.obj winser.obj winspool.lib >> putty.rsp - echo winstore.obj wintime.obj winucs.obj winutils.obj >> putty.rsp - echo winx11.obj x11fwd.obj >> putty.rsp + echo comctl32.lib comdlg32.lib conf.obj config.obj >> putty.rsp + echo cproxy.obj dialog.obj gdi32.lib imm32.lib >> putty.rsp + echo ldisc.obj ldiscucs.obj logging.obj minibidi.obj >> putty.rsp + echo misc.obj ole32.lib pgssapi.obj pinger.obj >> putty.rsp + echo portfwd.obj proxy.obj putty.res raw.obj >> putty.rsp + echo rlogin.obj sercfg.obj settings.obj shell32.lib >> putty.rsp + echo sizetip.obj ssh.obj sshaes.obj ssharcf.obj >> putty.rsp + echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> putty.rsp + echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> putty.rsp + echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> putty.rsp + echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> putty.rsp + echo telnet.obj terminal.obj timing.obj tree234.obj >> putty.rsp + echo user32.lib version.obj wcwidth.obj wildcard.obj >> putty.rsp + echo wincfg.obj winctrls.obj windefs.obj windlg.obj >> putty.rsp + echo window.obj wingss.obj winhandl.obj winhelp.obj >> putty.rsp + echo winjump.obj winmisc.obj winmm.lib winnet.obj >> putty.rsp + echo winnoise.obj winpgntc.obj winprint.obj >> putty.rsp + echo winproxy.obj winser.obj winspool.lib winstore.obj >> putty.rsp + echo wintime.obj winucs.obj winutils.obj winx11.obj >> putty.rsp + echo x11fwd.obj >> putty.rsp puttygen.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > puttygen.rsp - echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> puttygen.rsp - echo imm32.lib import.obj misc.obj notiming.obj >> puttygen.rsp - echo ole32.lib puttygen.res shell32.lib sshaes.obj >> puttygen.rsp - echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj >> puttygen.rsp - echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj >> puttygen.rsp - echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj >> puttygen.rsp - echo sshsha.obj tree234.obj user32.lib version.obj >> puttygen.rsp - echo winctrls.obj winhelp.obj winmisc.obj winmm.lib >> puttygen.rsp - echo winnoise.obj winnojmp.obj winpgen.obj >> puttygen.rsp + echo advapi32.lib comctl32.lib comdlg32.lib conf.obj >> puttygen.rsp + echo gdi32.lib imm32.lib import.obj misc.obj >> puttygen.rsp + echo notiming.obj ole32.lib puttygen.res shell32.lib >> puttygen.rsp + echo sshaes.obj sshbn.obj sshdes.obj sshdss.obj >> puttygen.rsp + echo sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj >> puttygen.rsp + echo sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj >> puttygen.rsp + echo sshsh512.obj sshsha.obj tree234.obj user32.lib >> puttygen.rsp + echo version.obj winctrls.obj winhelp.obj winmisc.obj >> puttygen.rsp + echo winmm.lib winnoise.obj winnojmp.obj winpgen.obj >> puttygen.rsp echo winspool.lib winstore.obj wintime.obj >> puttygen.rsp echo winutils.obj >> puttygen.rsp puttytel.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > puttytel.rsp echo advapi32.lib be_nos_s.obj cmdline.obj >> puttytel.rsp - echo comctl32.lib comdlg32.lib config.obj dialog.obj >> puttytel.rsp - echo gdi32.lib imm32.lib ldisc.obj ldiscucs.obj >> puttytel.rsp - echo logging.obj minibidi.obj misc.obj nocproxy.obj >> puttytel.rsp - echo nogss.obj ole32.lib pinger.obj proxy.obj >> puttytel.rsp - echo puttytel.res raw.obj rlogin.obj sercfg.obj >> puttytel.rsp - echo settings.obj shell32.lib sizetip.obj telnet.obj >> puttytel.rsp - echo terminal.obj timing.obj tree234.obj user32.lib >> puttytel.rsp - echo version.obj wcwidth.obj wincfg.obj winctrls.obj >> puttytel.rsp - echo windefs.obj windlg.obj window.obj winhandl.obj >> puttytel.rsp - echo winhelp.obj winjump.obj winmisc.obj winmm.lib >> puttytel.rsp - echo winnet.obj winprint.obj winproxy.obj winser.obj >> puttytel.rsp - echo winspool.lib winstore.obj wintime.obj winucs.obj >> puttytel.rsp - echo winutils.obj >> puttytel.rsp + echo comctl32.lib comdlg32.lib conf.obj config.obj >> puttytel.rsp + echo dialog.obj gdi32.lib imm32.lib ldisc.obj >> puttytel.rsp + echo ldiscucs.obj logging.obj minibidi.obj misc.obj >> puttytel.rsp + echo nocproxy.obj nogss.obj ole32.lib pinger.obj >> puttytel.rsp + echo proxy.obj puttytel.res raw.obj rlogin.obj >> puttytel.rsp + echo sercfg.obj settings.obj shell32.lib sizetip.obj >> puttytel.rsp + echo telnet.obj terminal.obj timing.obj tree234.obj >> puttytel.rsp + echo user32.lib version.obj wcwidth.obj wincfg.obj >> puttytel.rsp + echo winctrls.obj windefs.obj windlg.obj window.obj >> puttytel.rsp + echo winhandl.obj winhelp.obj winjump.obj winmisc.obj >> puttytel.rsp + echo winmm.lib winnet.obj winprint.obj winproxy.obj >> puttytel.rsp + echo winser.obj winspool.lib winstore.obj wintime.obj >> puttytel.rsp + echo winucs.obj winutils.obj >> puttytel.rsp be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ @@ -361,6 +368,12 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\charset\charset.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\cmdline.c +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h + cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\conf.c + config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -746,7 +759,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\toucs.c -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h diff --git a/contrib/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP b/contrib/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP index 935cedd..09ec866 100644 --- a/contrib/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP +++ b/contrib/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP @@ -94,6 +94,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\misc.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PLINK/PLINK.DSP b/contrib/putty/WINDOWS/MSVC/PLINK/PLINK.DSP index fc3d6eb..0cb6c6f 100644 --- a/contrib/putty/WINDOWS/MSVC/PLINK/PLINK.DSP +++ b/contrib/putty/WINDOWS/MSVC/PLINK/PLINK.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PSCP/PSCP.DSP b/contrib/putty/WINDOWS/MSVC/PSCP/PSCP.DSP index ae52b8c..26169e0 100644 --- a/contrib/putty/WINDOWS/MSVC/PSCP/PSCP.DSP +++ b/contrib/putty/WINDOWS/MSVC/PSCP/PSCP.DSP @@ -111,6 +111,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP b/contrib/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP index 65d5bb7..7385f35 100644 --- a/contrib/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP +++ b/contrib/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP @@ -111,6 +111,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP b/contrib/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP index 69a2480..a9b80ac 100644 --- a/contrib/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP +++ b/contrib/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\config.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP b/contrib/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP index 6ee999c..3dbf1fb 100644 --- a/contrib/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP +++ b/contrib/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP @@ -94,6 +94,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\import.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP b/contrib/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP index cd74ae6..3aa53d8 100644 --- a/contrib/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP +++ b/contrib/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\config.c # End Source File # Begin Source File diff --git a/contrib/putty/WINDOWS/PAGEANT.RC b/contrib/putty/WINDOWS/PAGEANT.RC index f6422f3..5ff0cf2 100644 --- a/contrib/putty/WINDOWS/PAGEANT.RC +++ b/contrib/putty/WINDOWS/PAGEANT.RC @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/contrib/putty/WINDOWS/PUTTY.ISS b/contrib/putty/WINDOWS/PUTTY.ISS index 3299c1f..6aada2a 100644 --- a/contrib/putty/WINDOWS/PUTTY.ISS +++ b/contrib/putty/WINDOWS/PUTTY.ISS @@ -1,5 +1,5 @@ ; -*- no -*- -; $Id: putty.iss 9366 2011-12-10 12:08:09Z simon $ +; $Id: putty.iss 9998 2013-08-06 17:09:07Z simon $ ; ; -- Inno Setup installer script for PuTTY and its related tools. ; Last tested with Inno Setup 5.0.8. @@ -14,10 +14,10 @@ [Setup] AppName=PuTTY -AppVerName=PuTTY version 0.62 -VersionInfoTextVersion=Release 0.62 -AppVersion=0.62 -VersionInfoVersion=0.62.0.0 +AppVerName=PuTTY version 0.63 +VersionInfoTextVersion=Release 0.63 +AppVersion=0.63 +VersionInfoVersion=0.63.0.0 AppPublisher=Simon Tatham AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/ AppReadmeFile={app}\README.txt diff --git a/contrib/putty/WINDOWS/PUTTYGEN.RC b/contrib/putty/WINDOWS/PUTTYGEN.RC index 1cea261..53ec369 100644 --- a/contrib/putty/WINDOWS/PUTTYGEN.RC +++ b/contrib/putty/WINDOWS/PUTTYGEN.RC @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/contrib/putty/WINDOWS/VERSION.RC2 b/contrib/putty/WINDOWS/VERSION.RC2 index 1c76927..96e5e84 100644 --- a/contrib/putty/WINDOWS/VERSION.RC2 +++ b/contrib/putty/WINDOWS/VERSION.RC2 @@ -39,7 +39,7 @@ /* 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,62 +#define BASE_VERSION 0,63 #if defined SNAPSHOT @@ -120,7 +120,7 @@ BEGIN VALUE "OriginalFilename", APPNAME VALUE "FileVersion", VERSION_TEXT VALUE "ProductVersion", VERSION_TEXT - VALUE "LegalCopyright", "Copyright \251 1997-2011 Simon Tatham." + VALUE "LegalCopyright", "Copyright \251 1997-2013 Simon Tatham." #if (!defined SNAPSHOT) && (!defined RELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", VERSION_TEXT /* NBI */ diff --git a/contrib/putty/WINDOWS/WINCFG.C b/contrib/putty/WINDOWS/WINCFG.C index 60694fc..3a9d98d 100644 --- a/contrib/putty/WINDOWS/WINCFG.C +++ b/contrib/putty/WINDOWS/WINCFG.C @@ -70,8 +70,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Control the scrollback in the window"); ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, - I(offsetof(Config,scrollbar_in_fullscreen))); + conf_checkbox_handler, + I(CONF_scrollbar_in_fullscreen)); /* * Really this wants to go just after `Display scrollbar'. See * if we can find that control, and do some shuffling. @@ -81,7 +81,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && - c->generic.context.i == offsetof(Config,scrollbar)) { + c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-in-FS one. @@ -105,10 +105,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Enable extra keyboard features:"); ctrl_checkbox(s, "AltGr acts as Compose key", 't', HELPCTX(keyboard_compose), - dlg_stdcheckbox_handler, I(offsetof(Config,compose_key))); + conf_checkbox_handler, I(CONF_compose_key)); ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd', HELPCTX(keyboard_ctrlalt), - dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys))); + conf_checkbox_handler, I(CONF_ctrlaltkeys)); /* * Windows allows an arbitrary .WAV to be played as a bell, and @@ -133,8 +133,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, beep)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_beep) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 2; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -159,7 +159,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, FILTER_WAVE_FILES, FALSE, "Select bell sound file", HELPCTX(bell_style), - dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile))); + conf_filesel_handler, I(CONF_bell_wavefile)); /* * While we've got this box open, taskbar flashing on a bell is @@ -167,8 +167,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, */ ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3, HELPCTX(bell_taskbar), - dlg_stdradiobutton_handler, - I(offsetof(Config, beep_ind)), + conf_radiobutton_handler, + I(CONF_beep_ind), "Disabled", I(B_IND_DISABLED), "Flashing", I(B_IND_FLASH), "Steady", I(B_IND_STEADY), NULL); @@ -180,7 +180,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Adjust the window border"); ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's', HELPCTX(appearance_border), - dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge))); + conf_checkbox_handler, I(CONF_sunken_edge)); /* * Configurable font quality settings for Windows. @@ -191,8 +191,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, HELPCTX(appearance_font), variable_pitch_handler, I(0)); ctrl_radiobuttons(s, "Font quality:", 'q', 2, HELPCTX(appearance_font), - dlg_stdradiobutton_handler, - I(offsetof(Config, font_quality)), + conf_radiobutton_handler, + I(CONF_font_quality), "Antialiased", I(FQ_ANTIALIASED), "Non-Antialiased", I(FQ_NONANTIALIASED), "ClearType", I(FQ_CLEARTYPE), @@ -206,8 +206,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's', HELPCTX(translation_cyrillic), - dlg_stdcheckbox_handler, - I(offsetof(Config,xlat_capslockcyr))); + conf_checkbox_handler, + I(CONF_xlat_capslockcyr)); /* * On Windows we can use but not enumerate translation tables @@ -232,8 +232,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, vtmode)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_vtmode) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 3; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -272,7 +272,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Formatting of pasted characters"); ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f', HELPCTX(selection_rtf), - dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste))); + conf_checkbox_handler, I(CONF_rtf_paste)); /* * Windows often has no middle button, so we supply a selection @@ -283,8 +283,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Control use of mouse"); ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1, HELPCTX(selection_buttons), - dlg_stdradiobutton_handler, - I(offsetof(Config, mouse_is_xterm)), + conf_radiobutton_handler, + I(CONF_mouse_is_xterm), "Windows (Middle extends, Right brings up menu)", I(2), "Compromise (Middle extends, Right pastes)", I(0), "xterm (Right extends, Middle pastes)", I(1), NULL); @@ -304,10 +304,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "General options for colour usage"); ctrl_checkbox(s, "Attempt to use logical palettes", 'l', HELPCTX(colours_logpal), - dlg_stdcheckbox_handler, I(offsetof(Config,try_palette))); + conf_checkbox_handler, I(CONF_try_palette)); ctrl_checkbox(s, "Use system colours", 's', HELPCTX(colours_system), - dlg_stdcheckbox_handler, I(offsetof(Config,system_colour))); + conf_checkbox_handler, I(CONF_system_colour)); /* @@ -316,8 +316,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window", "size", "Set the size of the window"); ctrl_radiobuttons(s, "When window is resized:", 'z', 1, HELPCTX(window_resize), - dlg_stdradiobutton_handler, - I(offsetof(Config, resize_action)), + conf_radiobutton_handler, + I(CONF_resize_action), "Change the number of rows and columns", I(RESIZE_TERM), "Change the size of the font", I(RESIZE_FONT), "Change font size only when maximised", I(RESIZE_EITHER), @@ -331,20 +331,20 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Window closes on ALT-F4", '4', HELPCTX(behaviour_altf4), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4))); + conf_checkbox_handler, I(CONF_alt_f4)); ctrl_checkbox(s, "System menu appears on ALT-Space", 'y', HELPCTX(behaviour_altspace), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_space))); + conf_checkbox_handler, I(CONF_alt_space)); ctrl_checkbox(s, "System menu appears on ALT alone", 'l', HELPCTX(behaviour_altonly), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_only))); + conf_checkbox_handler, I(CONF_alt_only)); ctrl_checkbox(s, "Ensure window is always on top", 'e', HELPCTX(behaviour_alwaysontop), - dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop))); + conf_checkbox_handler, I(CONF_alwaysontop)); ctrl_checkbox(s, "Full screen on Alt-Enter", 'f', HELPCTX(behaviour_altenter), - dlg_stdcheckbox_handler, - I(offsetof(Config,fullscreenonaltenter))); + conf_checkbox_handler, + I(CONF_fullscreenonaltenter)); /* * Windows supports a local-command proxy. This also means we @@ -356,8 +356,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, proxy_type)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_proxy_type) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -373,9 +373,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && - c->generic.context.i == - offsetof(Config, proxy_telnet_command)) { - assert(c->generic.handler == dlg_stdeditbox_handler); + c->generic.context.i == CONF_proxy_telnet_command) { + assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); @@ -399,6 +398,6 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, ctrl_filesel(s, "X authority file for local display", 't', NULL, FALSE, "Select X authority file", HELPCTX(ssh_tunnels_xauthority), - dlg_stdfilesel_handler, I(offsetof(Config, xauthfile))); + conf_filesel_handler, I(CONF_xauthfile)); } } diff --git a/contrib/putty/WINDOWS/WINCONS.C b/contrib/putty/WINDOWS/WINCONS.C index 4f984d9..f1c8cbc 100644 --- a/contrib/putty/WINDOWS/WINCONS.C +++ b/contrib/putty/WINDOWS/WINCONS.C @@ -41,7 +41,7 @@ void notify_remote_exit(void *frontend) { } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { } @@ -201,7 +201,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; @@ -223,11 +223,11 @@ int askappend(void *frontend, Filename filename, char line[32]; if (console_batch_mode) { - fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); fflush(stderr); return 0; } - fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); hin = GetStdHandle(STD_INPUT_HANDLE); @@ -315,7 +315,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } /* @@ -365,9 +365,9 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { - DWORD savemode, newmode, i = 0; + DWORD savemode, newmode; + int len; prompt_t *pr = p->prompts[curr_prompt]; - BOOL r; GetConsoleMode(hin, &savemode); newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; @@ -379,25 +379,44 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) console_data_untrusted(hout, pr->prompt, strlen(pr->prompt)); - r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL); + len = 0; + while (1) { + DWORD ret = 0; + BOOL r; + + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + + r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1, + &ret, NULL); + + if (!r || ret == 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + if (pr->result[len - 1] == '\r') + len--; + break; + } + } SetConsoleMode(hin, savemode); - if ((int) i > pr->result_len) - i = pr->result_len - 1; - else - i = i - 2; - pr->result[i] = '\0'; - if (!pr->echo) { DWORD dummy; WriteFile(hout, "\r\n", 2, &dummy, NULL); } + if (len < 0) { + return 0; /* failure due to read error */ + } + + pr->result[len] = '\0'; } return 1; /* success */ - } void frontend_keypress(void *handle) diff --git a/contrib/putty/WINDOWS/WINCTRLS.C b/contrib/putty/WINDOWS/WINCTRLS.C index 159c925..8673ef9 100644 --- a/contrib/putty/WINDOWS/WINCTRLS.C +++ b/contrib/putty/WINDOWS/WINCTRLS.C @@ -448,6 +448,8 @@ char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines) if (lines) *lines = nlines; + sfree(pwidths); + return ret; } @@ -1639,8 +1641,8 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, shortcuts[nshortcuts++] = ctrl->fontselect.shortcut; statictext(&pos, escaped, 1, base_id); staticbtn(&pos, "", base_id+1, "Change...", base_id+2); + data = fontspec_new("", 0, 0, 0); sfree(escaped); - data = snew(FontSpec); break; default: assert(!"Can't happen"); @@ -1665,7 +1667,9 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, winctrl_add_shortcuts(dp, c); if (actual_base_id == base_id) base_id += num_ids; - } + } else { + sfree(data); + } if (colstart >= 0) { /* @@ -1934,21 +1938,21 @@ int winctrl_handle_command(struct dlgparam *dp, UINT msg, CHOOSEFONT cf; LOGFONT lf; HDC hdc; - FontSpec fs = *(FontSpec *)c->data; - + FontSpec *fs = (FontSpec *)c->data; + hdc = GetDC(0); - lf.lfHeight = -MulDiv(fs.height, + lf.lfHeight = -MulDiv(fs->height, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(0, hdc); lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0; lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0; - lf.lfWeight = (fs.isbold ? FW_BOLD : 0); - lf.lfCharSet = fs.charset; + lf.lfWeight = (fs->isbold ? FW_BOLD : 0); + lf.lfCharSet = fs->charset; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; - strncpy(lf.lfFaceName, fs.name, + strncpy(lf.lfFaceName, fs->name, sizeof(lf.lfFaceName) - 1); lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0'; @@ -1959,13 +1963,11 @@ int winctrl_handle_command(struct dlgparam *dp, UINT msg, CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; if (ChooseFont(&cf)) { - strncpy(fs.name, lf.lfFaceName, - sizeof(fs.name) - 1); - fs.name[sizeof(fs.name) - 1] = '\0'; - fs.isbold = (lf.lfWeight == FW_BOLD); - fs.charset = lf.lfCharSet; - fs.height = cf.iPointSize / 10; + fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD), + cf.iPointSize / 10, lf.lfCharSet); dlg_fontsel_set(ctrl, dp, fs); + fontspec_free(fs); + ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } } @@ -2100,13 +2102,12 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) SetDlgItemText(dp->hwnd, c->base_id+1, text); } -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) +char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_EDITBOX); - GetDlgItemText(dp->hwnd, c->base_id+1, buffer, length); - buffer[length-1] = '\0'; + return GetDlgItemText_alloc(dp->hwnd, c->base_id+1); } /* The `listbox' functions can also apply to combo boxes. */ @@ -2286,51 +2287,56 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text) } } -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - SetDlgItemText(dp->hwnd, c->base_id+1, fn.path); + SetDlgItemText(dp->hwnd, c->base_id+1, fn->path); } -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn) +Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); + char *tmp; + Filename *ret; assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - GetDlgItemText(dp->hwnd, c->base_id+1, fn->path, lenof(fn->path)); - fn->path[lenof(fn->path)-1] = '\0'; + tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); + ret = filename_from_str(tmp); + sfree(tmp); + return ret; } -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs) +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { char *buf, *boldstr; struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - *(FontSpec *)c->data = fs; /* structure copy */ + fontspec_free((FontSpec *)c->data); + c->data = fontspec_copy(fs); - boldstr = (fs.isbold ? "bold, " : ""); - if (fs.height == 0) - buf = dupprintf("Font: %s, %sdefault height", fs.name, boldstr); + boldstr = (fs->isbold ? "bold, " : ""); + if (fs->height == 0) + buf = dupprintf("Font: %s, %sdefault height", fs->name, boldstr); else - buf = dupprintf("Font: %s, %s%d-%s", fs.name, boldstr, - (fs.height < 0 ? -fs.height : fs.height), - (fs.height < 0 ? "pixel" : "point")); + buf = dupprintf("Font: %s, %s%d-%s", fs->name, boldstr, + (fs->height < 0 ? -fs->height : fs->height), + (fs->height < 0 ? "pixel" : "point")); SetDlgItemText(dp->hwnd, c->base_id+1, buf); sfree(buf); dlg_auto_set_fixed_pitch_flag(dp); } -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs) +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - *fs = *(FontSpec *)c->data; /* structure copy */ + return fontspec_copy((FontSpec *)c->data); } /* @@ -2364,6 +2370,8 @@ void dlg_set_focus(union control *ctrl, void *dlg) struct winctrl *c = dlg_findbyctrl(dp, ctrl); int id; HWND ctl; + if (!c) + return; switch (ctrl->generic.type) { case CTRL_EDITBOX: id = c->base_id + 1; break; case CTRL_RADIO: @@ -2471,8 +2479,10 @@ int dlg_coloursel_results(union control *ctrl, void *dlg, void dlg_auto_set_fixed_pitch_flag(void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; - Config *cfg = (Config *)dp->data; - HFONT font; + Conf *conf = (Conf *)dp->data; + FontSpec *fs; + int quality; + HFONT hfont; HDC hdc; TEXTMETRIC tm; int is_var; @@ -2483,16 +2493,19 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg) * dialog box as false. * * We assume here that any client of the dlg_* mechanism which is - * using font selectors at all is also using a normal 'Config *' + * using font selectors at all is also using a normal 'Conf *' * as dp->data. */ - font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg->font_quality), - FIXED_PITCH | FF_DONTCARE, cfg->font.name); + quality = conf_get_int(conf, CONF_font_quality); + fs = conf_get_fontspec(conf, CONF_font); + + hfont = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), + FIXED_PITCH | FF_DONTCARE, fs->name); hdc = GetDC(NULL); - if (font && hdc && SelectObject(hdc, font) && GetTextMetrics(hdc, &tm)) { + if (hdc && SelectObject(hdc, hfont) && GetTextMetrics(hdc, &tm)) { /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH); } else { @@ -2500,8 +2513,8 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg) } if (hdc) ReleaseDC(NULL, hdc); - if (font) - DeleteObject(font); + if (hfont) + DeleteObject(hfont); if (is_var) dp->fixed_pitch_fonts = FALSE; diff --git a/contrib/putty/WINDOWS/WINDEFS.C b/contrib/putty/WINDOWS/WINDEFS.C index de01daf..e2f04ac 100644 --- a/contrib/putty/WINDOWS/WINDEFS.C +++ b/contrib/putty/WINDOWS/WINDEFS.C @@ -6,28 +6,20 @@ #include -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - if (!strcmp(name, "Font")) { - strcpy(ret.name, "Courier New"); - ret.isbold = 0; - ret.charset = ANSI_CHARSET; - ret.height = 10; - } else { - ret.name[0] = '\0'; - } - return ret; + if (!strcmp(name, "Font")) + return fontspec_new("Courier New", 0, 10, ANSI_CHARSET); + else + return fontspec_new("", 0, 0, 0); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *platform_default_s(const char *name) diff --git a/contrib/putty/WINDOWS/WINDLG.C b/contrib/putty/WINDOWS/WINDLG.C index debc510..d2b2a5f 100644 --- a/contrib/putty/WINDOWS/WINDLG.C +++ b/contrib/putty/WINDOWS/WINDLG.C @@ -44,7 +44,7 @@ static struct dlgparam dp; static char **events = NULL; static int nevents = 0, negsize = 0; -extern Config cfg; /* defined in window.c */ +extern Conf *conf; /* defined in window.c */ #define PRINTER_DISABLED_STRING "None (printing disabled)" @@ -648,7 +648,7 @@ int do_config(void) dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Configuration", appname); dp.errtitle = dupprintf("%s Error", appname); - dp.data = &cfg; + dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ @@ -666,15 +666,15 @@ int do_config(void) int do_reconfig(HWND hwnd, int protcfginfo) { - Config backup_cfg; - int ret; + Conf *backup_conf; + int ret, protocol; - backup_cfg = cfg; /* structure copy */ + backup_conf = conf_copy(conf); ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo); - win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, - cfg.protocol); + protocol = conf_get_int(conf, CONF_protocol); + setup_config_box(ctrlbox, TRUE, protocol, protcfginfo); + win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol); dp_init(&dp); winctrl_init(&ctrls_base); winctrl_init(&ctrls_panel); @@ -682,7 +682,7 @@ int do_reconfig(HWND hwnd, int protcfginfo) dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Reconfiguration", appname); dp.errtitle = dupprintf("%s Error", appname); - dp.data = &cfg; + dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ @@ -695,7 +695,9 @@ int do_reconfig(HWND hwnd, int protcfginfo) dp_cleanup(&dp); if (!ret) - cfg = backup_cfg; /* structure copy */ + conf_copy_into(conf, backup_conf); + + conf_free(backup_conf); return ret; } @@ -856,7 +858,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -870,7 +872,7 @@ int askappend(void *frontend, Filename filename, char *mbtitle; int mbret; - message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = MessageBox(NULL, message, mbtitle, diff --git a/contrib/putty/WINDOWS/WINDOW.C b/contrib/putty/WINDOWS/WINDOW.C index c533542..e087a2b 100644 --- a/contrib/putty/WINDOWS/WINDOW.C +++ b/contrib/putty/WINDOWS/WINDOW.C @@ -80,7 +80,7 @@ static Mouse_Button translate_button(Mouse_Button button); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); -static void cfgtopalette(void); +static void conftopalette(void); static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); @@ -140,7 +140,11 @@ static struct { enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; -Config cfg; /* exported to windlg.c */ +Conf *conf; /* exported to windlg.c */ + +static void conf_cache_data(void); +int cursor_type; +int vtmode; static struct sesslist sesslist; /* for saved-session menu */ @@ -164,14 +168,15 @@ struct agent_callback { #define FONT_OEMUND 0x22 #define FONT_OEMBOLDUND 0x23 -#define FONT_MAXNO 0x2F +#define FONT_MAXNO 0x40 #define FONT_SHIFT 5 static HFONT fonts[FONT_MAXNO]; static LOGFONT lfont; static int fontflag[FONT_MAXNO]; static enum { - BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT -} bold_mode; + BOLD_NONE, BOLD_SHADOW, BOLD_FONT +} bold_font_mode; +static int bold_colours; static enum { UND_LINE, UND_FONT } und_mode; @@ -202,6 +207,12 @@ static int compose_state = 0; static UINT wm_mousewheel = WM_MOUSEWHEEL; +#define IS_HIGH_VARSEL(wch1, wch2) \ + ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF)) +#define IS_LOW_VARSEL(wch) \ + (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \ + ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */ + /* Dummy routine, only required in plink. */ void ldisc_update(void *frontend, int echo, int edit) { @@ -223,7 +234,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 = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", @@ -232,22 +243,24 @@ static void start_backend(void) cleanup_exit(1); } - error = back->init(NULL, &backhandle, &cfg, - cfg.host, cfg.port, &realhost, cfg.tcp_nodelay, - cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, + conf_get_int(conf, CONF_tcp_nodelay), + conf_get_int(conf, CONF_tcp_keepalives)); back->provide_logctx(backhandle, logctx); if (error) { char *str = dupprintf("%s Error", appname); sprintf(msg, "Unable to open connection to\n" - "%.800s\n" "%s", cfg_dest(&cfg), error); + "%.800s\n" "%s", conf_dest(conf), error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); exit(0); } window_name = icon_name = NULL; - if (*cfg.wintitle) { - title = cfg.wintitle; - } else { + title = conf_get_str(conf, CONF_wintitle); + if (!*title) { sprintf(msg, "%s - %s", realhost, appname); title = msg; } @@ -263,7 +276,7 @@ static void start_backend(void) /* * Set up a line discipline. */ - ldisc = ldisc_create(&cfg, term, back, backhandle, NULL); + ldisc = ldisc_create(conf, term, back, backhandle, NULL); /* * Destroy the Restart Session menu item. (This will return @@ -364,6 +377,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) init_flashwindow(); + conf = conf_new(); + /* * Initialize COM. */ @@ -395,9 +410,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (b) default_port = b->default_port; } - cfg.logtype = LGTYP_NONE; + conf_set_int(conf, CONF_logtype, LGTYP_NONE); - do_defaults(NULL, &cfg); + do_defaults(NULL, conf); p = cmdline; @@ -422,8 +437,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) while (i > 1 && isspace(p[i - 1])) i--; p[i] = '\0'; - do_defaults(p + 1, &cfg); - if (!cfg_launchable(&cfg) && !do_config()) { + do_defaults(p + 1, conf); + if (!conf_launchable(conf) && !do_config()) { cleanup_exit(0); } allow_launch = TRUE; /* allow it to be launched directly */ @@ -431,15 +446,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * An initial & means we've been given a command line * containing the hex value of a HANDLE for a file - * mapping object, which we must then extract as a - * config. + * mapping object, which we must then interpret as a + * serialised Conf. */ HANDLE filemap; - Config *cp; - if (sscanf(p + 1, "%p", &filemap) == 1 && + void *cp; + unsigned cpsize; + if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 && (cp = MapViewOfFile(filemap, FILE_MAP_READ, - 0, 0, sizeof(Config))) != NULL) { - cfg = *cp; + 0, 0, cpsize)) != NULL) { + conf_deserialise(conf, cp, cpsize); UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config()) { @@ -461,7 +477,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) int ret; ret = cmdline_process_param(p, i+1isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1409,7 +1440,7 @@ static void init_fonts(int pick_width, int pick_height) if (pick_height) font_height = pick_height; else { - font_height = cfg.font.height; + font_height = font->height; if (font_height > 0) { font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); @@ -1417,13 +1448,14 @@ static void init_fonts(int pick_width, int pick_height) } font_width = pick_width; + quality = conf_get_int(conf, CONF_font_quality); #define f(i,c,w,u) \ fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \ c, OUT_DEFAULT_PRECIS, \ - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \ - FIXED_PITCH | FF_DONTCARE, cfg.font.name) + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \ + FIXED_PITCH | FF_DONTCARE, font->name) - f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE); + f(FONT_NORMAL, font->charset, fw_dontcare, FALSE); SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); @@ -1466,7 +1498,7 @@ static void init_fonts(int pick_width, int pick_height) ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } - f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE); + f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE); /* * Some fonts, e.g. 9-pt Courier, draw their underlines @@ -1516,8 +1548,8 @@ static void init_fonts(int pick_width, int pick_height) } } - if (bold_mode == BOLD_FONT) { - f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE); + if (bold_font_mode == BOLD_FONT) { + f(FONT_BOLD, font->charset, fw_bold, FALSE); } #undef f @@ -1543,23 +1575,24 @@ static void init_fonts(int pick_width, int pick_height) fonts[FONT_UNDERLINE] = 0; } - if (bold_mode == BOLD_FONT && + if (bold_font_mode == BOLD_FONT && fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) { - bold_mode = BOLD_SHADOW; + bold_font_mode = BOLD_SHADOW; DeleteObject(fonts[FONT_BOLD]); fonts[FONT_BOLD] = 0; } fontflag[0] = fontflag[1] = fontflag[2] = 1; - init_ucs(&cfg, &ucsdata); + init_ucs(conf, &ucsdata); } static void another_font(int fontno) { int basefont; - int fw_dontcare, fw_bold; + int fw_dontcare, fw_bold, quality; int c, u, w, x; char *s; + FontSpec *font; if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno]) return; @@ -1568,7 +1601,9 @@ static void another_font(int fontno) if (basefont != fontno && !fontflag[basefont]) another_font(basefont); - if (cfg.font.isbold) { + font = conf_get_fontspec(conf, CONF_font); + + if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1576,10 +1611,10 @@ static void another_font(int fontno) fw_bold = FW_BOLD; } - c = cfg.font.charset; + c = font->charset; w = fw_dontcare; u = FALSE; - s = cfg.font.name; + s = font->name; x = font_width; if (fontno & FONT_WIDE) @@ -1593,10 +1628,12 @@ static void another_font(int fontno) if (fontno & FONT_UNDERLINE) u = TRUE; + quality = conf_get_int(conf, CONF_font_quality); + fonts[fontno] = CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w, FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), DEFAULT_PITCH | FF_DONTCARE, s); fontflag[fontno] = 1; @@ -1619,11 +1656,11 @@ void request_resize(void *frontend, int w, int h) /* If the window is maximized supress resizing attempts */ if (IsZoomed(hwnd)) { - if (cfg.resize_action == RESIZE_TERM) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } - if (cfg.resize_action == RESIZE_DISABLED) return; + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ @@ -1654,9 +1691,10 @@ void request_resize(void *frontend, int w, int h) } } - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); - if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) { + if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && + !IsZoomed(hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; @@ -1677,7 +1715,7 @@ static void reset_window(int reinit) { * This function doesn't like to change the terminal size but if the * font size is locked that may be it's only soluion. */ - int win_width, win_height; + int win_width, win_height, resize_action, window_border; RECT cr, wr; #ifdef RDB_DEBUG_PATCH @@ -1691,7 +1729,11 @@ static void reset_window(int reinit) { win_width = cr.right - cr.left; win_height = cr.bottom - cr.top; - if (cfg.resize_action == RESIZE_DISABLED) reinit = 2; + resize_action = conf_get_int(conf, CONF_resize_action); + window_border = conf_get_int(conf, CONF_window_border); + + if (resize_action == RESIZE_DISABLED) + reinit = 2; /* Are we being forced to reload the fonts ? */ if (reinit>1) { @@ -1726,9 +1768,9 @@ static void reset_window(int reinit) { extra_width = wr.right - wr.left - cr.right + cr.left; extra_height = wr.bottom - wr.top - cr.bottom + cr.top; - if (cfg.resize_action != RESIZE_TERM) { - if ( font_width != win_width/term->cols || - font_height != win_height/term->rows) { + if (resize_action != RESIZE_TERM) { + if (font_width != win_width/term->cols || + font_height != win_height/term->rows) { deinit_fonts(); init_fonts(win_width/term->cols, win_height/term->rows); offset_width = (win_width-font_width*term->cols)/2; @@ -1740,13 +1782,13 @@ static void reset_window(int reinit) { #endif } } else { - if ( font_width * term->cols != win_width || - font_height * term->rows != win_height) { + if (font_width * term->cols != win_width || + font_height * term->rows != win_height) { /* Our only choice at this point is to change the * size of the terminal; Oh well. */ term_size(term, win_height/font_height, win_width/font_width, - cfg.savelines); + conf_get_int(conf, CONF_savelines)); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; InvalidateRect(hwnd, NULL, TRUE); @@ -1766,7 +1808,7 @@ static void reset_window(int reinit) { debug((27, "reset_window() -> Forced re-init")); #endif - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1791,10 +1833,10 @@ static void reset_window(int reinit) { * window. But that may be too big for the screen which forces us * to change the terminal. */ - if ((cfg.resize_action == RESIZE_TERM && reinit<=0) || - (cfg.resize_action == RESIZE_EITHER && reinit<0) || + if ((resize_action == RESIZE_TERM && reinit<=0) || + (resize_action == RESIZE_EITHER && reinit<0) || reinit>0) { - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1811,7 +1853,7 @@ static void reset_window(int reinit) { /* Grrr too big */ if ( term->rows > height || term->cols > width ) { - if (cfg.resize_action == RESIZE_EITHER) { + if (resize_action == RESIZE_EITHER) { /* Make the font the biggest we can */ if (term->cols > width) font_width = (ss.right - ss.left - extra_width) @@ -1828,7 +1870,8 @@ static void reset_window(int reinit) { } else { if ( height > term->rows ) height = term->rows; if ( width > term->cols ) width = term->cols; - term_size(term, height, width, cfg.savelines); + term_size(term, height, width, + conf_get_int(conf, CONF_savelines)); #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> term resize to (%d,%d)", height, width)); @@ -1853,12 +1896,12 @@ static void reset_window(int reinit) { /* We're allowed to or must change the font but do we want to ? */ - if (font_width != (win_width-cfg.window_border*2)/term->cols || - font_height != (win_height-cfg.window_border*2)/term->rows) { + if (font_width != (win_width-window_border*2)/term->cols || + font_height != (win_height-window_border*2)/term->rows) { deinit_fonts(); - init_fonts((win_width-cfg.window_border*2)/term->cols, - (win_height-cfg.window_border*2)/term->rows); + init_fonts((win_width-window_border*2)/term->cols, + (win_height-window_border*2)/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; @@ -1887,7 +1930,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) { int thistime = GetMessageTime(); - if (send_raw_mouse && !(cfg.mouse_override && shift)) { + if (send_raw_mouse && + !(shift && conf_get_int(conf, CONF_mouse_override))) { lastbtn = MBT_NOTHING; term_mouse(term, b, translate_button(b), MA_CLICK, x, y, shift, ctrl, alt); @@ -1917,9 +1961,11 @@ static Mouse_Button translate_button(Mouse_Button button) if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) - return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) - return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } @@ -1928,8 +1974,8 @@ static void show_mouseptr(int show) /* NB that the counter in ShowCursor() is also frobbed by * update_mouse_pointer() */ static int cursor_visible = 1; - if (!cfg.hide_mouseptr) /* override if this feature disabled */ - show = 1; + if (!conf_get_int(conf, CONF_hide_mouseptr)) + show = 1; /* override if this feature disabled */ if (cursor_visible && !show) ShowCursor(FALSE); else if (!cursor_visible && show) @@ -1954,14 +2000,15 @@ static int resizing; void notify_remote_exit(void *fe) { - int exitcode; + int exitcode, close_on_exit; if (!session_closed && (exitcode = back->exitcode(backhandle)) >= 0) { + close_on_exit = conf_get_int(conf, CONF_close_on_exit); /* Abnormal exits will already have set session_closed and taken * appropriate action. */ - if (cfg.close_on_exit == FORCE_ON || - (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) { + if (close_on_exit == FORCE_ON || + (close_on_exit == AUTO && exitcode != INT_MAX)) { PostQuitMessage(0); } else { must_close_session = TRUE; @@ -1976,15 +2023,26 @@ void notify_remote_exit(void *fe) } } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { - long ticks = next - GETTICKCOUNT(); - if (ticks <= 0) ticks = 1; /* just in case */ + unsigned long now = GETTICKCOUNT(); + long ticks; + if (now - next < INT_MAX) + ticks = 0; + else + ticks = next - now; KillTimer(hwnd, TIMING_TIMER_ID); SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL); timing_next_time = next; } +static void conf_cache_data(void) +{ + /* Cache some items from conf to speed lookups in very hot code */ + cursor_type = conf_get_int(conf, CONF_cursor_type); + vtmode = conf_get_int(conf, CONF_vtmode); +} + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1994,11 +2052,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static int fullscr_on_max = FALSE; static int processed_resize = FALSE; static UINT last_mousemove = 0; + int resize_action; switch (message) { case WM_TIMER: if ((UINT_PTR)wParam == TIMING_TIMER_ID) { - long next; + unsigned long next; KillTimer(hwnd, TIMING_TIMER_ID); if (run_timers(timing_next_time, &next)) { @@ -2014,7 +2073,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, char *str; show_mouseptr(1); str = dupprintf("%s Exit Confirmation", appname); - if (!cfg.warn_on_close || session_closed || + if (session_closed || !conf_get_int(conf, CONF_warn_on_close) || MessageBox(hwnd, "Are you sure you want to close this session?", str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) @@ -2061,7 +2120,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * config structure. */ SECURITY_ATTRIBUTES sa; - Config *p; + void *p; + int size; + + size = conf_serialised_size(conf); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; @@ -2069,18 +2131,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, filemap = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, - 0, sizeof(Config), NULL); + 0, size, NULL); if (filemap && filemap != INVALID_HANDLE_VALUE) { - p = (Config *) MapViewOfFile(filemap, - FILE_MAP_WRITE, - 0, 0, sizeof(Config)); + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); if (p) { - *p = cfg; /* structure copy */ + conf_serialise(conf, p); UnmapViewOfFile(p); } } inherit_handles = TRUE; - sprintf(c, "putty &%p", filemap); + sprintf(c, "putty &%p:%u", filemap, (unsigned)size); cl = c; } else if (wParam == IDM_SAVEDSESS) { unsigned int sessno = ((lParam - IDM_SAVED_MIN) @@ -2107,6 +2167,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, si.lpReserved2 = NULL; CreateProcess(b, cl, NULL, NULL, inherit_handles, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); if (filemap) CloseHandle(filemap); @@ -2124,7 +2186,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case IDM_RECONF: { - Config prev_cfg; + Conf *prev_conf; int init_lvl = 1; int reconfig_result; @@ -2133,62 +2195,78 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else reconfiguring = TRUE; - GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle)); - prev_cfg = cfg; + /* + * Copy the current window title into the stored + * previous configuration, so that doing nothing to + * the window title field in the config box doesn't + * reset the title to its startup state. + */ + conf_set_str(conf, CONF_wintitle, window_name); + + prev_conf = conf_copy(conf); reconfig_result = do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0); reconfiguring = FALSE; - if (!reconfig_result) + if (!reconfig_result) { + conf_free(prev_conf); break; + } + conf_cache_data(); + + resize_action = conf_get_int(conf, CONF_resize_action); { /* Disable full-screen if resizing forbidden */ int i; for (i = 0; i < lenof(popup_menus); i++) EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_BYCOMMAND | - (cfg.resize_action == RESIZE_DISABLED) + (resize_action == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED); /* Gracefully unzoom if necessary */ - if (IsZoomed(hwnd) && - (cfg.resize_action == RESIZE_DISABLED)) { + if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) ShowWindow(hwnd, SW_RESTORE); - } } /* Pass new config data to the logging module */ - log_reconfig(logctx, &cfg); + log_reconfig(logctx, conf); sfree(logpal); /* * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ + ldisc_configure(ldisc, conf); if (ldisc) ldisc_send(ldisc, NULL, 0, 0); if (pal) DeleteObject(pal); logpal = NULL; pal = NULL; - cfgtopalette(); + conftopalette(); init_palette(); /* Pass new config data to the terminal */ - term_reconfig(term, &cfg); + term_reconfig(term, conf); /* Pass new config data to the back end */ if (back) - back->reconfig(backhandle, &cfg); + back->reconfig(backhandle, conf); /* Screen size changed ? */ - if (cfg.height != prev_cfg.height || - cfg.width != prev_cfg.width || - cfg.savelines != prev_cfg.savelines || - cfg.resize_action == RESIZE_FONT || - (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || - cfg.resize_action == RESIZE_DISABLED) - term_size(term, cfg.height, cfg.width, cfg.savelines); + if (conf_get_int(conf, CONF_height) != + conf_get_int(prev_conf, CONF_height) || + conf_get_int(conf, CONF_width) != + conf_get_int(prev_conf, CONF_width) || + conf_get_int(conf, CONF_savelines) != + conf_get_int(prev_conf, CONF_savelines) || + resize_action == RESIZE_FONT || + (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || + resize_action == RESIZE_DISABLED) + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); /* Enable or disable the scroll bar, etc */ { @@ -2197,8 +2275,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, GetWindowLongPtr(hwnd, GWL_EXSTYLE); nexflag = exflag; - if (cfg.alwaysontop != prev_cfg.alwaysontop) { - if (cfg.alwaysontop) { + if (conf_get_int(conf, CONF_alwaysontop) != + conf_get_int(prev_conf, CONF_alwaysontop)) { + if (conf_get_int(conf, CONF_alwaysontop)) { nexflag |= WS_EX_TOPMOST; SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -2208,25 +2287,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE); } } - if (cfg.sunken_edge) + if (conf_get_int(conf, CONF_sunken_edge)) nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; - if (is_full_screen() ? - cfg.scrollbar_in_fullscreen : cfg.scrollbar) + if (conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : + CONF_scrollbar)) nflg |= WS_VSCROLL; else nflg &= ~WS_VSCROLL; - if (cfg.resize_action == RESIZE_DISABLED || + if (resize_action == RESIZE_DISABLED || is_full_screen()) nflg &= ~WS_THICKFRAME; else nflg |= WS_THICKFRAME; - if (cfg.resize_action == RESIZE_DISABLED) + if (resize_action == RESIZE_DISABLED) nflg &= ~WS_MAXIMIZEBOX; else nflg |= WS_MAXIMIZEBOX; @@ -2247,34 +2327,47 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } /* Oops */ - if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { + if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { force_normal(hwnd); init_lvl = 2; } - set_title(NULL, cfg.wintitle); + set_title(NULL, conf_get_str(conf, CONF_wintitle)); if (IsIconic(hwnd)) { SetWindowText(hwnd, - cfg.win_name_always ? window_name : - icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); } - if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 || - strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 || - cfg.font.isbold != prev_cfg.font.isbold || - cfg.font.height != prev_cfg.font.height || - cfg.font.charset != prev_cfg.font.charset || - cfg.font_quality != prev_cfg.font_quality || - cfg.vtmode != prev_cfg.vtmode || - cfg.bold_colour != prev_cfg.bold_colour || - cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_EITHER || - (cfg.resize_action != prev_cfg.resize_action)) - init_lvl = 2; + { + FontSpec *font = conf_get_fontspec(conf, CONF_font); + FontSpec *prev_font = conf_get_fontspec(prev_conf, + CONF_font); + + if (!strcmp(font->name, prev_font->name) || + !strcmp(conf_get_str(conf, CONF_line_codepage), + conf_get_str(prev_conf, CONF_line_codepage)) || + font->isbold != prev_font->isbold || + font->height != prev_font->height || + font->charset != prev_font->charset || + conf_get_int(conf, CONF_font_quality) != + conf_get_int(prev_conf, CONF_font_quality) || + conf_get_int(conf, CONF_vtmode) != + conf_get_int(prev_conf, CONF_vtmode) || + conf_get_int(conf, CONF_bold_style) != + conf_get_int(prev_conf, CONF_bold_style) || + resize_action == RESIZE_DISABLED || + resize_action == RESIZE_EITHER || + resize_action != conf_get_int(prev_conf, + CONF_resize_action)) + init_lvl = 2; + } InvalidateRect(hwnd, NULL, TRUE); reset_window(init_lvl); net_pending_errors(); + + conf_free(prev_conf); } break; case IDM_COPYALL: @@ -2352,7 +2445,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_MBUTTONUP: case WM_RBUTTONUP: if (message == WM_RBUTTONDOWN && - ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) { + ((wParam & MK_CONTROL) || + (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) { POINT cursorpos; show_mouseptr(1); /* make sure pointer is visible */ @@ -2659,7 +2753,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, debug((27, "WM_EXITSIZEMOVE")); #endif if (need_backend_resize) { - term_size(term, cfg.height, cfg.width, cfg.savelines); + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); InvalidateRect(hwnd, NULL, TRUE); } break; @@ -2669,13 +2765,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * 1) Keep the sizetip uptodate * 2) Make sure the window size is _stepped_ in units of the font size. */ - if (cfg.resize_action == RESIZE_TERM || - (cfg.resize_action == RESIZE_EITHER && !is_alt_pressed())) { + resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && !is_alt_pressed())) { int width, height, w, h, ew, eh; LPRECT r = (LPRECT) lParam; - if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER && - (cfg.height != term->rows || cfg.width != term->cols )) { + if (!need_backend_resize && resize_action == RESIZE_EITHER && + (conf_get_int(conf, CONF_height) != term->rows || + conf_get_int(conf, CONF_width) != term->cols)) { /* * Great! It seems that both the terminal size and the * font size have been changed and the user is now dragging. @@ -2684,11 +2782,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * font size! * * This would be easier but it seems to be too confusing. - - term_size(term, cfg.height, cfg.width, cfg.savelines); - reset_window(2); */ - cfg.height=term->rows; cfg.width=term->cols; + conf_set_int(conf, CONF_height, term->rows); + conf_set_int(conf, CONF_width, term->cols); InvalidateRect(hwnd, NULL, TRUE); need_backend_resize = TRUE; @@ -2725,8 +2821,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return 0; } else { int width, height, w, h, rv = 0; - int ex_width = extra_width + (cfg.window_border - offset_width) * 2; - int ex_height = extra_height + (cfg.window_border - offset_height) * 2; + int window_border = conf_get_int(conf, CONF_window_border); + int ex_width = extra_width + (window_border - offset_width) * 2; + int ex_height = extra_height + (window_border - offset_height) * 2; LPRECT r = (LPRECT) lParam; width = r->right - r->left - ex_width; @@ -2762,6 +2859,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, sys_cursor_update(); break; case WM_SIZE: + resize_action = conf_get_int(conf, CONF_resize_action); #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED": @@ -2773,7 +2871,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #endif if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, - cfg.win_name_always ? window_name : icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) SetWindowText(hwnd, window_name); if (wParam == SIZE_RESTORED) { @@ -2806,12 +2905,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, processed_resize = TRUE; - if (cfg.resize_action == RESIZE_DISABLED) { + if (resize_action == RESIZE_DISABLED) { /* A resize, well it better be a minimize. */ reset_window(-1); } else { int width, height, w, h; + int window_border = conf_get_int(conf, CONF_window_border); width = LOWORD(lParam); height = HIWORD(lParam); @@ -2820,36 +2920,50 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, was_zoomed = 1; prev_rows = term->rows; prev_cols = term->cols; - if (cfg.resize_action == RESIZE_TERM) { + if (resize_action == RESIZE_TERM) { w = width / font_width; if (w < 1) w = 1; h = height / font_height; if (h < 1) h = 1; - term_size(term, h, w, cfg.savelines); + if (resizing) { + /* + * As below, if we're in the middle of an + * interactive resize we don't call + * back->size. In Windows 7, this case can + * arise in maximisation as well via the Aero + * snap UI. + */ + need_backend_resize = TRUE; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); + } else { + term_size(term, h, w, + conf_get_int(conf, CONF_savelines)); + } } reset_window(0); } else if (wParam == SIZE_RESTORED && was_zoomed) { was_zoomed = 0; - if (cfg.resize_action == RESIZE_TERM) { - w = (width-cfg.window_border*2) / font_width; + if (resize_action == RESIZE_TERM) { + w = (width-window_border*2) / font_width; if (w < 1) w = 1; - h = (height-cfg.window_border*2) / font_height; + h = (height-window_border*2) / font_height; if (h < 1) h = 1; - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); reset_window(2); - } else if (cfg.resize_action != RESIZE_FONT) + } else if (resize_action != RESIZE_FONT) reset_window(2); else reset_window(0); } else if (wParam == SIZE_MINIMIZED) { /* do nothing */ - } else if (cfg.resize_action == RESIZE_TERM || - (cfg.resize_action == RESIZE_EITHER && + } else if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && !is_alt_pressed())) { - w = (width-cfg.window_border*2) / font_width; + w = (width-window_border*2) / font_width; if (w < 1) w = 1; - h = (height-cfg.window_border*2) / font_height; + h = (height-window_border*2) / font_height; if (h < 1) h = 1; if (resizing) { @@ -2860,10 +2974,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * opaque drag.) */ need_backend_resize = TRUE; - cfg.height = h; - cfg.width = w; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); } else { - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); } } else { reset_window(0); @@ -2893,7 +3007,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case SB_THUMBPOSITION: case SB_THUMBTRACK: - term_scroll(term, 1, HIWORD(wParam)); + /* + * Use GetScrollInfo instead of HIWORD(wParam) to get + * 32-bit scroll position. + */ + { + SCROLLINFO si; + + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) + si.nTrackPos = HIWORD(wParam); + term_scroll(term, 1, si.nTrackPos); + } break; } break; @@ -3018,9 +3144,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * instead we luni_send the characters one by one. */ term_seen_key_event(term); - for (i = 0; i < n; i += 2) { - if (ldisc) + /* don't divide SURROGATE PAIR */ + if (ldisc) { + for (i = 0; i < n; i += 2) { + WCHAR hs = *(unsigned short *)(buff+i); + if (IS_HIGH_SURROGATE(hs) && i+2 < n) { + WCHAR ls = *(unsigned short *)(buff+i+2); + if (IS_LOW_SURROGATE(ls)) { + luni_send(ldisc, (unsigned short *)(buff+i), 2, 1); + i += 2; + continue; + } + } luni_send(ldisc, (unsigned short *)(buff+i), 1, 1); + } } free(buff); } @@ -3060,7 +3197,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } return 0; case WM_SYSCOLORCHANGE: - if (cfg.system_colour) { + if (conf_get_int(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ /* XXX actually this zaps the entire palette. */ systopalette(); @@ -3112,7 +3249,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; if (send_raw_mouse && - !(cfg.mouse_override && shift_pressed)) { + !(conf_get_int(conf, CONF_mouse_override) && + shift_pressed)) { /* Mouse wheel position is in screen coordinates for * some reason */ POINT p; @@ -3219,9 +3357,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, int text_adjust = 0; int xoffset = 0; int maxlen, remaining, opaque; + int is_cursor = FALSE; static int *lpDx = NULL; static int lpDx_len = 0; int *lpDx_maybe; + int len2; /* for SURROGATE PAIR */ lattr &= LATTR_MODE; @@ -3239,17 +3379,15 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, x += offset_width; y += offset_height; - if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) { + if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) { attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS); - if (bold_mode == BOLD_COLOURS) - attr &= ~ATTR_BOLD; - /* cursor fg and bg */ attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT); + is_cursor = TRUE; } nfont = 0; - if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) { + if (vtmode == VT_POORMAN && lattr != LATTR_NORM) { /* Assume a poorman font is borken in other ways too. */ lattr = LATTR_WIDE; } else @@ -3266,6 +3404,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, if (attr & ATTR_NARROW) nfont |= FONT_NARROW; +#ifdef USES_VTLINE_HACK /* Special hack for the VT100 linedraw glyphs. */ if (text[0] >= 0x23BA && text[0] <= 0x23BD) { switch ((unsigned char) (text[0])) { @@ -3290,9 +3429,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, force_manual_underline = 1; } } +#endif /* Anything left as an original character set is unprintable. */ - if (DIRECT_CHAR(text[0])) { + if (DIRECT_CHAR(text[0]) && + (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) { int i; for (i = 0; i < len; i++) text[i] = 0xFFFD; @@ -3304,7 +3445,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); - if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD)) + if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD)) nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) nfont |= FONT_UNDERLINE; @@ -3324,11 +3465,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = nbg; nbg = t; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) { + if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) { + if (bold_colours && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -3345,6 +3486,24 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, line_box.top = y; line_box.right = x + char_width * len; line_box.bottom = y + font_height; + /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */ + { + int i; + int rc_width = 0; + for (i = 0; i < len ; i++) { + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + i++; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) { + rc_width += char_width; + i++; + } else if (IS_LOW_VARSEL(text[i])) { + /* do nothing */ + } else { + rc_width += char_width; + } + } + line_box.right = line_box.left + rc_width; + } /* Only want the left half of double width lines */ if (line_box.right > font_width*term->cols+offset_width) @@ -3375,19 +3534,47 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, opaque = TRUE; /* start by erasing the rectangle */ for (remaining = len; remaining > 0; - text += len, remaining -= len, x += char_width * len) { + text += len, remaining -= len, x += char_width * len2) { len = (maxlen < remaining ? maxlen : remaining); - - if (len > lpDx_len) { - if (len > lpDx_len) { - lpDx_len = len * 9 / 8 + 16; - lpDx = sresize(lpDx, lpDx_len, int); - } + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + len2 = len; + if (maxlen == 1) { + if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) + len++; + if (remaining-len >= 1 && IS_LOW_VARSEL(text[len])) + len++; + else if (remaining-len >= 2 && + IS_HIGH_VARSEL(text[len], text[len+1])) + len += 2; } + + if (len > lpDx_len) { + lpDx_len = len * 9 / 8 + 16; + lpDx = sresize(lpDx, lpDx_len, int); + + if (lpDx_maybe) lpDx_maybe = lpDx; + } + { int i; - for (i = 0; i < len; i++) + /* only last char has dx width in SURROGATE PAIR and + * VARIATION sequence */ + for (i = 0; i < len; i++) { lpDx[i] = char_width; + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) { + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (IS_LOW_VARSEL(text[i])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = char_width; + } + } } /* We're using a private area for direct to font. (512 chars.) */ @@ -3432,7 +3619,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, uni_buf, nlen, lpDx_maybe); - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == @@ -3457,7 +3644,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, directbuf, len, lpDx_maybe); - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); /* GRR: This draws the character outside its box and @@ -3496,7 +3683,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, opaque && !(attr & TATTR_COMBINING)); /* And the shadow bold hack. */ - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == @@ -3536,9 +3723,35 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len, { if (attr & TATTR_COMBINING) { unsigned long a = 0; - attr &= ~TATTR_COMBINING; + int len0 = 1; + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1])) + len0 = 2; + if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+1, attr, lattr); + text += len0+1; + len -= len0+1; + a = TATTR_COMBINING; + } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+2, attr, lattr); + text += len0+2; + len -= len0+2; + a = TATTR_COMBINING; + } else { + attr &= ~TATTR_COMBINING; + } + while (len--) { - do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) { + do_text_internal(ctx, x, y, text, 2, attr | a, lattr); + len--; + text++; + } else { + do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + } + text++; a = TATTR_COMBINING; } @@ -3553,7 +3766,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, int fnt_width; int char_width; HDC hdc = ctx; - int ctype = cfg.cursor_type; + int ctype = cursor_type; lattr &= LATTR_MODE; @@ -3698,13 +3911,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, int r, i, code; unsigned char *p = output; static int alt_sum = 0; + int funky_type = conf_get_int(conf, CONF_funky_type); + int no_applic_k = conf_get_int(conf, CONF_no_applic_k); + int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys); + int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad); HKL kbd_layout = GetKeyboardLayout(0); - /* keys is for ToAsciiEx. There's some ick here, see below. */ - static WORD keys[3]; + static wchar_t keys_unicode[3]; static int compose_char = 0; - static WPARAM compose_key = 0; + static WPARAM compose_keycode = 0; r = GetKeyboardState(keystate); if (!r) @@ -3754,12 +3970,12 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else if (ch) debug((", $%02x", ch)); - if (keys[0]) - debug((", KB0=%02x", keys[0])); - if (keys[1]) - debug((", KB1=%02x", keys[1])); - if (keys[2]) - debug((", KB2=%02x", keys[2])); + if (keys_unicode[0]) + debug((", KB0=%04x", keys_unicode[0])); + if (keys_unicode[1]) + debug((", KB1=%04x", keys_unicode[1])); + if (keys_unicode[2]) + debug((", KB2=%04x", keys_unicode[2])); if ((keystate[VK_SHIFT] & 0x80) != 0) debug((", S")); @@ -3791,9 +4007,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ - if ((cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys && - !cfg.no_applic_k)) + if ((funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && term->app_keypad_keys && + !no_applic_k)) && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) { wParam = VK_EXECUTE; @@ -3819,7 +4035,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */ if (left_alt && (keystate[VK_CONTROL] & 0x80)) { - if (cfg.ctrlaltkeys) + if (ctrlaltkeys) keystate[VK_MENU] = 0; else { keystate[VK_RMENU] = 0x80; @@ -3833,16 +4049,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Note if AltGr was pressed and if it was used as a compose key */ if (!compose_state) { - compose_key = 0x100; - if (cfg.compose_key) { + compose_keycode = 0x100; + if (conf_get_int(conf, CONF_compose_key)) { if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) - compose_key = wParam; + compose_keycode = wParam; } if (wParam == VK_APPS) - compose_key = wParam; + compose_keycode = wParam; } - if (wParam == compose_key) { + if (wParam == compose_keycode) { if (compose_state == 0 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state = 1; @@ -3857,9 +4073,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, compose_state = 0; /* Sanitize the number pad if not using a PC NumPad */ - if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k - && cfg.funky_type != FUNKY_XTERM) - || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) { + if (left_alt || (term->app_keypad_keys && !no_applic_k + && funky_type != FUNKY_XTERM) + || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) { if ((HIWORD(lParam) & KF_EXTENDED) == 0) { int nParam = 0; switch (wParam) { @@ -3936,15 +4152,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, request_paste(NULL); return 0; } - if (left_alt && wParam == VK_F4 && cfg.alt_f4) { + if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) { return -1; } - if (left_alt && wParam == VK_SPACE && cfg.alt_space) { + if (left_alt && wParam == VK_SPACE && conf_get_int(conf, + CONF_alt_space)) { SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } - if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter && - (cfg.resize_action != RESIZE_DISABLED)) { + if (left_alt && wParam == VK_RETURN && + conf_get_int(conf, CONF_fullscreenonaltenter) && + (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) { if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) flip_full_screen(); return -1; @@ -3956,7 +4174,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } /* Nethack keypad */ - if (cfg.nethack_keypad && !left_alt) { + if (nethack_keypad && !left_alt) { switch (wParam) { case VK_NUMPAD1: *p++ = "bB\002\002"[shift_state & 3]; @@ -3992,9 +4210,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (!left_alt) { int xkey = 0; - if (cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && - term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) { + if (funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && + term->app_keypad_keys && !no_applic_k)) switch (wParam) { case VK_EXECUTE: xkey = 'P'; break; @@ -4008,7 +4226,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'S'; break; } - if (term->app_keypad_keys && !cfg.no_applic_k) + if (term->app_keypad_keys && !no_applic_k) switch (wParam) { case VK_NUMPAD0: xkey = 'p'; @@ -4045,7 +4263,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'n'; break; case VK_ADD: - if (cfg.funky_type == FUNKY_XTERM) { + if (funky_type == FUNKY_XTERM) { if (shift_state) xkey = 'l'; else @@ -4057,15 +4275,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; case VK_DIVIDE: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'o'; break; case VK_MULTIPLY: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'j'; break; case VK_SUBTRACT: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'm'; break; @@ -4087,13 +4305,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ - *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08); *p++ = 0; return -2; } if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */ /* We do the opposite of what is configured */ - *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F); *p++ = 0; return -2; } @@ -4237,7 +4455,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; } /* Reorder edit keys to physical order */ - if (cfg.funky_type == FUNKY_VT400 && code <= 6) + if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (term->vt52_mode && code > 0 && code <= 6) { @@ -4245,8 +4463,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */ - code >= 11 && code <= 34) { + if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) { + /* SCO function keys */ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; switch (wParam) { @@ -4268,7 +4486,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p += sprintf((char *) p, "\x1B[%c", codes[index]); return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { @@ -4278,7 +4496,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } return p - output; } - if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { + if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) offt++; @@ -4291,18 +4509,19 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt); return p - output; } - if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11); return p - output; } - if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", code + 'P' - 11); else p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11); return p - output; } - if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + if ((code == 1 || code == 4) && + conf_get_int(conf, CONF_rxvt_homeend)) { p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw"); return p - output; } @@ -4362,7 +4581,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, BOOL capsOn=0; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ - if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) { + if(keystate[VK_CAPITAL] != 0 && + conf_get_int(conf, CONF_xlat_capslockcyr)) { capsOn= !left_alt; keystate[VK_CAPITAL] = 0; } @@ -4371,6 +4591,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * be is? There's indication on MS' website of an Inquire/InquireEx * functioning returning a KBINFO structure which tells us. */ if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) { + r = ToUnicodeEx(wParam, scan, keystate, keys_unicode, + lenof(keys_unicode), 0, kbd_layout); + } else { /* XXX 'keys' parameter is declared in MSDN documentation as * 'LPWORD lpChar'. * The experience of a French user indicates that on @@ -4381,12 +4604,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * Win9x/NT split, but I suspect it's worse than that. * See wishlist item `win-dead-keys' for more horrible detail * and speculations. */ - BYTE keybs[3]; int i; - r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout); - for (i=0; i<3; i++) keys[i] = keybs[i]; - } else { + static WORD keys[3]; + static BYTE keysb[3]; r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); + if (r > 0) { + for (i = 0; i < r; i++) { + keysb[i] = (BYTE)keys[i]; + } + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r, + keys_unicode, lenof(keys_unicode)); + } } #ifdef SHOW_TOASCII_RESULT if (r == 1 && !key_down) { @@ -4396,13 +4624,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else debug((", LCH(%d)", alt_sum)); } else { - debug((", ACH(%d)", keys[0])); + debug((", ACH(%d)", keys_unicode[0])); } } else if (r > 0) { int r1; debug((", ASC(")); for (r1 = 0; r1 < r; r1++) { - debug(("%s%d", r1 ? "," : "", keys[r1])); + debug(("%s%d", r1 ? "," : "", keys_unicode[r1])); } debug((")")); } @@ -4419,18 +4647,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p = output; for (i = 0; i < r; i++) { - unsigned char ch = (unsigned char) keys[i]; + wchar_t wch = keys_unicode[i]; - if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') { - compose_char = ch; + if (compose_state == 2 && wch >= ' ' && wch < 0x80) { + compose_char = wch; compose_state++; continue; } - if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') { + if (compose_state == 3 && wch >= ' ' && wch < 0x80) { int nc; compose_state = 0; - if ((nc = check_compose(compose_char, ch)) == -1) { + if ((nc = check_compose(compose_char, wch)) == -1) { MessageBeep(MB_ICONHAND); return 0; } @@ -4451,7 +4679,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (ldisc) luni_send(ldisc, &keybuf, 1, 1); } else { - ch = (char) alt_sum; + char ch = (char) alt_sum; /* * We need not bother about stdin * backlogs here, because in GUI PuTTY @@ -4469,40 +4697,39 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } else { term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, &ch, 1, 1); + luni_send(ldisc, &wch, 1, 1); } } else { - if(capsOn && ch < 0x80) { + if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; - cbuf[1] = xlat_uskbd2cyrllic(ch); + cbuf[1] = xlat_uskbd2cyrllic(wch); term_seen_key_event(term); if (ldisc) luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1); } else { - char cbuf[2]; + WCHAR cbuf[2]; cbuf[0] = '\033'; - cbuf[1] = ch; + cbuf[1] = wch; term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, - cbuf+!left_alt, 1+!!left_alt, 1); + luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1); } } show_mouseptr(0); } /* This is so the ALT-Numpad and dead keys work correctly. */ - keys[0] = 0; + keys_unicode[0] = 0; return p - output; } /* If we're definitly not building up an ALT-54321 then clear it */ if (!left_alt) - keys[0] = 0; + keys_unicode[0] = 0; /* If we will be using alt_sum fix the 256s */ - else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) - keys[0] = 10; + else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) + keys_unicode[0] = 10; } /* @@ -4512,7 +4739,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * we return -1, which means Windows will give the keystroke * its default handling (i.e. bring up the System menu). */ - if (wParam == VK_MENU && !cfg.alt_only) + if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only)) return 0; return -1; @@ -4523,7 +4750,7 @@ void set_title(void *frontend, char *title) sfree(window_name); window_name = snewn(1 + strlen(title), char); strcpy(window_name, title); - if (cfg.win_name_always || !IsIconic(hwnd)) + if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4532,7 +4759,7 @@ void set_icon(void *frontend, char *title) sfree(icon_name); icon_name = snewn(1 + strlen(title), char); strcpy(icon_name, title); - if (!cfg.win_name_always && IsIconic(hwnd)) + if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4540,7 +4767,8 @@ void set_sbar(void *frontend, int total, int start, int page) { SCROLLINFO si; - if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar) + if (!conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : CONF_scrollbar)) return; si.cbSize = sizeof(si); @@ -4588,7 +4816,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) { if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; real_palette_set(n, r, g, b); if (pal) { @@ -4688,15 +4916,22 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des GlobalFree(clipdata2); return; } - if (!(lock = GlobalLock(clipdata))) + if (!(lock = GlobalLock(clipdata))) { + GlobalFree(clipdata); + GlobalFree(clipdata2); return; - if (!(lock2 = GlobalLock(clipdata2))) + } + if (!(lock2 = GlobalLock(clipdata2))) { + GlobalUnlock(clipdata); + GlobalFree(clipdata); + GlobalFree(clipdata2); return; + } memcpy(lock, data, len * sizeof(wchar_t)); WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL); - if (cfg.rtf_paste) { + if (conf_get_int(conf, CONF_rtf_paste)) { wchar_t unitab[256]; char *rtf = NULL; unsigned char *tdata = (unsigned char *)lock2; @@ -4711,13 +4946,14 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des int attrUnder, lastAttrUnder = 0; int palette[NALLCOLOURS]; int numcolours; + FontSpec *font = conf_get_fontspec(conf, CONF_font); get_unitab(CP_ACP, unitab, 0); - rtfsize = 100 + strlen(cfg.font.name); + rtfsize = 100 + strlen(font->name); rtf = snewn(rtfsize, char); rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d", - cfg.font.name, cfg.font.height*2); + font->name, font->height*2); /* * Add colour palette @@ -4740,7 +4976,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) { + if (bold_colours && (attr[i] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4831,7 +5067,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) { + if (bold_colours && (attr[tindex] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4848,7 +5084,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des /* * Collect other attributes */ - if (bold_mode != BOLD_COLOURS) + if (bold_font_mode != BOLD_NONE) attrBold = attr[tindex] & ATTR_BOLD; else attrBold = 0; @@ -4867,7 +5103,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = -1; /* No coloring */ if (fgcolour >= 256) { /* Default colour */ - if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1) + if (bold_colours && (fgcolour & 1) && bgcolour == -1) attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */ fgcolour = -1; /* No coloring */ @@ -5132,6 +5368,22 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +/* + * Print a message box and don't close the connection. + */ +void nonfatal(char *fmt, ...) +{ + va_list ap; + char *stuff, morestuff[100]; + + va_start(ap, fmt); + stuff = dupvprintf(fmt, ap); + va_end(ap); + sprintf(morestuff, "%.70s Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); +} + DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO)); static void init_flashwindow(void) @@ -5163,9 +5415,9 @@ static int flashing = 0; * Timer for platforms where we must maintain window flashing manually * (e.g., Win95). */ -static void flash_window_timer(void *ctx, long now) +static void flash_window_timer(void *ctx, unsigned long now) { - if (flashing && now - next_flash >= 0) { + if (flashing && now == next_flash) { flash_window(1); } } @@ -5176,7 +5428,8 @@ static void flash_window_timer(void *ctx, long now) */ static void flash_window(int mode) { - if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) { + int beep_ind = conf_get_int(conf, CONF_beep_ind); + if ((mode == 0) || (beep_ind == B_IND_DISABLED)) { /* stop */ if (flashing) { flashing = 0; @@ -5198,7 +5451,7 @@ static void flash_window(int mode) * "flashing" mode, although I haven't seen this * documented. */ flash_window_ex(FLASHW_ALL | FLASHW_TIMER, - (cfg.beep_ind == B_IND_FLASH ? 0 : 2), + (beep_ind == B_IND_FLASH ? 0 : 2), 0 /* system cursor blink rate */); /* No need to schedule timer */ } else { @@ -5207,7 +5460,7 @@ static void flash_window(int mode) } } - } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) { + } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ if (flashing && !p_FlashWindowEx) { FlashWindow(hwnd, TRUE); /* toggle */ @@ -5241,16 +5494,17 @@ void do_beep(void *frontend, int mode) */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { - if (!PlaySound(cfg.bell_wavefile.path, NULL, + Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile); + if (!PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { - char buf[sizeof(cfg.bell_wavefile.path) + 80]; + char buf[sizeof(bell_wavefile->path) + 80]; char otherbuf[100]; sprintf(buf, "Unable to play sound file\n%s\n" - "Using default sound instead", cfg.bell_wavefile.path); + "Using default sound instead", bell_wavefile->path); sprintf(otherbuf, "%.70s Sound Error", appname); MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); - cfg.beep = BELL_DEFAULT; + conf_set_int(conf, CONF_beep, BELL_DEFAULT); } } else if (mode == BELL_PCSPEAKER) { static long lastbeep = 0; @@ -5296,8 +5550,9 @@ void set_iconic(void *frontend, int iconic) */ void move_window(void *frontend, int x, int y) { - if (cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_FONT || + int resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_DISABLED || + resize_action == RESIZE_FONT || IsZoomed(hwnd)) return; @@ -5310,7 +5565,7 @@ void move_window(void *frontend, int x, int y) */ void set_zorder(void *frontend, int top) { - if (cfg.alwaysontop) + if (conf_get_int(conf, CONF_alwaysontop)) return; /* ignore */ SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -5432,7 +5687,7 @@ static void make_full_screen() /* Remove the window furniture. */ style = GetWindowLongPtr(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); - if (cfg.scrollbar_in_fullscreen) + if (conf_get_int(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5467,11 +5722,11 @@ static void clear_full_screen() /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; - if (cfg.resize_action == RESIZE_DISABLED) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) style &= ~WS_THICKFRAME; else style |= WS_THICKFRAME; - if (cfg.scrollbar) + if (conf_get_int(conf, CONF_scrollbar)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5526,6 +5781,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len) return term_data_untrusted(term, data, len); } +int from_backend_eof(void *frontend) +{ + return TRUE; /* do respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; diff --git a/contrib/putty/WINDOWS/WINGSS.C b/contrib/putty/WINDOWS/WINGSS.C index a2076c5..91d2d45 100644 --- a/contrib/putty/WINDOWS/WINGSS.C +++ b/contrib/putty/WINDOWS/WINGSS.C @@ -65,11 +65,12 @@ const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { HMODULE module; HKEY regkey; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); + char *path; list->libraries = snewn(3, struct ssh_gss_library); list->nlibraries = 0; @@ -148,8 +149,9 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) * Custom GSSAPI DLL. */ module = NULL; - if (cfg->ssh_gss_custom.path[0]) { - module = LoadLibrary(cfg->ssh_gss_custom.path); + path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*path) { + module = LoadLibrary(path); } if (module) { struct ssh_gss_library *lib = @@ -157,7 +159,7 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) lib->id = 2; lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" - " library '%s'", cfg->ssh_gss_custom.path); + " library '%s'", path); lib->handle = (void *)module; #define BIND_GSS_FN(name) \ diff --git a/contrib/putty/WINDOWS/WINHANDL.C b/contrib/putty/WINDOWS/WINHANDL.C index dbcab2b..06c2a6a 100644 --- a/contrib/putty/WINDOWS/WINHANDL.C +++ b/contrib/putty/WINDOWS/WINHANDL.C @@ -250,6 +250,7 @@ struct handle_output { * Data only ever read or written by the main thread. */ bufchain queued_data; /* data still waiting to be written */ + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; /* * Callback function called when the backlog in the bufchain @@ -320,6 +321,11 @@ static void handle_try_output(struct handle_output *ctx) ctx->len = sendlen; SetEvent(ctx->ev_from_main); ctx->busy = TRUE; + } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 && + ctx->outgoingeof == EOF_PENDING) { + CloseHandle(ctx->h); + ctx->h = INVALID_HANDLE_VALUE; + ctx->outgoingeof = EOF_SENT; } } @@ -408,6 +414,7 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, h->u.o.done = FALSE; h->u.o.privdata = privdata; bufchain_init(&h->u.o.queued_data); + h->u.o.outgoingeof = EOF_NO; h->u.o.sentdata = sentdata; h->u.o.flags = flags; @@ -424,11 +431,28 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, int handle_write(struct handle *h, const void *data, int len) { assert(h->output); + assert(h->u.o.outgoingeof == EOF_NO); bufchain_add(&h->u.o.queued_data, data, len); handle_try_output(&h->u.o); return bufchain_size(&h->u.o.queued_data); } +void handle_write_eof(struct handle *h) +{ + /* + * This function is called when we want to proactively send an + * end-of-file notification on the handle. We can only do this by + * actually closing the handle - so never call this on a + * bidirectional handle if we're still interested in its incoming + * direction! + */ + assert(h->output); + if (!h->u.o.outgoingeof == EOF_NO) { + h->u.o.outgoingeof = EOF_PENDING; + handle_try_output(&h->u.o); + } +} + HANDLE *handle_get_events(int *nevents) { HANDLE *ret; diff --git a/contrib/putty/WINDOWS/WINHELP.C b/contrib/putty/WINDOWS/WINHELP.C index c072e05..4924cf1 100644 --- a/contrib/putty/WINDOWS/WINHELP.C +++ b/contrib/putty/WINDOWS/WINHELP.C @@ -81,7 +81,7 @@ int has_help(void) * unrealistic, since even Vista will have it if the user * specifically downloads it. */ - return (help_path + return (help_path != NULL #ifndef NO_HTMLHELP || chm_path #endif /* NO_HTMLHELP */ diff --git a/contrib/putty/WINDOWS/WINHELP.H b/contrib/putty/WINDOWS/WINHELP.H index d670c0c..ba78d67 100644 --- a/contrib/putty/WINDOWS/WINHELP.H +++ b/contrib/putty/WINDOWS/WINHELP.H @@ -144,6 +144,7 @@ #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_ssh_bugs_winadj "ssh.bugs.winadj:config-ssh-bug-winadj" #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" diff --git a/contrib/putty/WINDOWS/WINJUMP.C b/contrib/putty/WINDOWS/WINJUMP.C index 3455a8a..9ae6dbd 100644 --- a/contrib/putty/WINDOWS/WINJUMP.C +++ b/contrib/putty/WINDOWS/WINJUMP.C @@ -353,12 +353,12 @@ static const PROPERTYKEY PKEY_Title = { 0x00000002 }; -/* Type-checking macro to provide arguments for CoCreateInstance() etc. - * The pointer arithmetic is a compile-time pointer type check that 'obj' - * really is a 'type **', but is intended to have no effect at runtime. */ +/* Type-checking macro to provide arguments for CoCreateInstance() + * etc, ensuring that 'obj' really is a 'type **'. */ +#define typecheck(checkexpr, result) \ + (sizeof(checkexpr) ? (result) : (result)) #define COMPTR(type, obj) &IID_##type, \ - (void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \ - - (sizeof((obj)-(type **)(obj)))) + typecheck((obj)-(type **)(obj), (void **)(void *)(obj)) static char putty_path[2048]; @@ -410,16 +410,20 @@ static IShellLink *make_shell_link(const char *appname, /* Check if this is a valid session, otherwise don't add. */ if (sessionname) { psettings_tmp = open_settings_r(sessionname); - if (!psettings_tmp) + if (!psettings_tmp) { + sfree(app_path); return NULL; + } close_settings_r(psettings_tmp); } /* Create the new item. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, - COMPTR(IShellLink, &ret)))) + COMPTR(IShellLink, &ret)))) { + sfree(app_path); return NULL; + } /* Set path, parameters, icon and description. */ ret->lpVtbl->SetPath(ret, app_path); diff --git a/contrib/putty/WINDOWS/WINMISC.C b/contrib/putty/WINDOWS/WINMISC.C index e70e77e..7e3b24d 100644 --- a/contrib/putty/WINDOWS/WINMISC.C +++ b/contrib/putty/WINDOWS/WINMISC.C @@ -14,28 +14,69 @@ char *platform_get_x_display(void) { return dupstr(getenv("DISPLAY")); } -Filename filename_from_str(const char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } +Filename *filename_copy(const Filename *fn) +{ + return filename_from_str(fn->path); +} + const char *filename_to_str(const Filename *fn) { return fn->path; } -int filename_equal(Filename f1, Filename f2) +int filename_equal(const Filename *f1, const Filename *f2) +{ + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) { - return !strcmp(f1.path, f2.path); + return !*fn->path; } -int filename_is_null(Filename fn) +void filename_free(Filename *fn) +{ + sfree(fn->path); + sfree(fn); +} + +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) { - return !*fn.path; + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); +} + +#ifndef NO_SECUREZEROMEMORY +/* + * Windows implementation of smemclr (see misc.c) using SecureZeroMemory. + */ +void smemclr(void *b, size_t n) { + if (b && n > 0) + SecureZeroMemory(b, n); } +#endif char *get_username(void) { @@ -132,6 +173,69 @@ HMODULE load_system32_dll(const char *libname) return ret; } +/* + * A tree234 containing mappings from system error codes to strings. + */ + +struct errstring { + int error; + char *text; +}; + +static int errstring_find(void *av, void *bv) +{ + int *a = (int *)av; + struct errstring *b = (struct errstring *)bv; + if (*a < b->error) + return -1; + if (*a > b->error) + return +1; + return 0; +} +static int errstring_compare(void *av, void *bv) +{ + struct errstring *a = (struct errstring *)av; + return errstring_find(&a->error, bv); +} + +static tree234 *errstrings = NULL; + +const char *win_strerror(int error) +{ + struct errstring *es; + + if (!errstrings) + errstrings = newtree234(errstring_compare); + + es = find234(errstrings, &error, errstring_find); + + if (!es) { + int bufsize; + + es = snew(struct errstring); + es->error = error; + /* maximum size for FormatMessage is 64K */ + bufsize = 65535; + es->text = snewn(bufsize, char); + if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + es->text, bufsize, NULL)) { + sprintf(es->text, + "Windows error code %d (and FormatMessage returned %d)", + error, GetLastError()); + } else { + int len = strlen(es->text); + if (len > 0 && es->text[len-1] == '\n') + es->text[len-1] = '\0'; + } + es->text = sresize(es->text, strlen(es->text) + 1, char); + add234(errstrings, es); + } + + return es->text; +} + #ifdef DEBUG static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; @@ -379,3 +483,51 @@ void *minefield_c_realloc(void *p, size_t size) } #endif /* MINEFIELD */ + +FontSpec *fontspec_new(const char *name, + int bold, int height, int charset) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + f->isbold = bold; + f->height = height; + f->charset = charset; + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name, f->isbold, f->height, f->charset); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->name) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->name); + PUT_32BIT_MSB_FIRST(data + len, f->isbold); + PUT_32BIT_MSB_FIRST(data + len + 4, f->height); + PUT_32BIT_MSB_FIRST(data + len + 8, f->charset); + } + return len + 12; /* also include three 4-byte ints */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end; + if (maxsize < 13) + return NULL; + end = memchr(data, '\0', maxsize-12); + if (!end) + return NULL; + end++; + *used = end - data + 12; + return fontspec_new(data, + GET_32BIT_MSB_FIRST(end), + GET_32BIT_MSB_FIRST(end + 4), + GET_32BIT_MSB_FIRST(end + 8)); +} diff --git a/contrib/putty/WINDOWS/WINNET.C b/contrib/putty/WINDOWS/WINNET.C index c5445c5..60e77ed 100644 --- a/contrib/putty/WINDOWS/WINNET.C +++ b/contrib/putty/WINDOWS/WINNET.C @@ -64,6 +64,7 @@ struct Socket_tag { char oobdata[1]; int sending_oob; int oobinline, nodelay, keepalive, privport; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; SockAddr addr; SockAddrStep step; int port; @@ -167,6 +168,7 @@ DECL_WINDOWS_FUNCTION(static, int, setsockopt, DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int)); DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); +DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, ioctlsocket, (SOCKET, long, u_long FAR *)); DECL_WINDOWS_FUNCTION(static, SOCKET, accept, @@ -291,6 +293,7 @@ void sk_init(void) GET_WINDOWS_FUNCTION(winsock_module, socket); GET_WINDOWS_FUNCTION(winsock_module, listen); GET_WINDOWS_FUNCTION(winsock_module, send); + GET_WINDOWS_FUNCTION(winsock_module, shutdown); GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket); GET_WINDOWS_FUNCTION(winsock_module, accept); GET_WINDOWS_FUNCTION(winsock_module, recv); @@ -329,8 +332,38 @@ void sk_cleanup(void) #endif } +struct errstring { + int error; + char *text; +}; + +static int errstring_find(void *av, void *bv) +{ + int *a = (int *)av; + struct errstring *b = (struct errstring *)bv; + if (*a < b->error) + return -1; + if (*a > b->error) + return +1; + return 0; +} +static int errstring_compare(void *av, void *bv) +{ + struct errstring *a = (struct errstring *)av; + return errstring_find(&a->error, bv); +} + +static tree234 *errstrings = NULL; + char *winsock_error_string(int error) { + const char prefix[] = "Network error: "; + struct errstring *es; + + /* + * Error codes we know about and have historically had reasonably + * sensible error messages for. + */ switch (error) { case WSAEACCES: return "Network error: Permission denied"; @@ -403,9 +436,53 @@ char *winsock_error_string(int error) return "Network error: Resource temporarily unavailable"; case WSAEDISCON: return "Network error: Graceful shutdown in progress"; - default: - return "Unknown network error"; } + + /* + * Generic code to handle any other error. + * + * Slightly nasty hack here: we want to return a static string + * which the caller will never have to worry about freeing, but on + * the other hand if we call FormatMessage to get it then it will + * want to either allocate a buffer or write into one we own. + * + * So what we do is to maintain a tree234 of error strings we've + * already used. New ones are allocated from the heap, but then + * put in this tree and kept forever. + */ + + if (!errstrings) + errstrings = newtree234(errstring_compare); + + es = find234(errstrings, &error, errstring_find); + + if (!es) { + int bufsize, bufused; + + es = snew(struct errstring); + es->error = error; + /* maximum size for FormatMessage is 64K */ + bufsize = 65535 + sizeof(prefix); + es->text = snewn(bufsize, char); + strcpy(es->text, prefix); + bufused = strlen(es->text); + if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + es->text + bufused, bufsize - bufused, NULL)) { + sprintf(es->text + bufused, + "Windows error code %d (and FormatMessage returned %d)", + error, GetLastError()); + } else { + int len = strlen(es->text); + if (len > 0 && es->text[len-1] == '\n') + es->text[len-1] = '\0'; + } + es->text = sresize(es->text, strlen(es->text) + 1, char); + add234(errstrings, es); + } + + return es->text; } SockAddr sk_namelookup(const char *host, char **canonicalname, @@ -595,7 +672,7 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } } -int sk_hostname_is_local(char *name) +int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || @@ -642,7 +719,7 @@ int sk_address_is_local(SockAddr addr) #ifndef NO_IPV6 if (family == AF_INET6) { - return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)step.ai->ai_addr); + return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr); } else #endif if (family == AF_INET) { @@ -664,6 +741,11 @@ int sk_address_is_local(SockAddr addr) } } +int sk_address_is_special_local(SockAddr addr) +{ + return 0; /* no Unix-domain socket analogue here */ +} + int sk_addrtype(SockAddr addr) { SockAddrStep step; @@ -745,6 +827,7 @@ static void sk_tcp_flush(Socket s) static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); +static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_private_ptr(Socket s, void *ptr); static void *sk_tcp_get_private_ptr(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); @@ -759,6 +842,7 @@ Socket sk_register(void *sock, Plug plug) sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -780,6 +864,7 @@ Socket sk_register(void *sock, Plug plug) bufchain_init(&ret->output_data); ret->writable = 1; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 1; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ @@ -1007,6 +1092,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -1028,6 +1114,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->connected = 0; /* to start with */ ret->writable = 0; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ @@ -1058,6 +1145,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -1089,6 +1177,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = local_host_only; @@ -1200,7 +1289,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { p_closesocket(s); - ret->error = winsock_error_string(err); + ret->error = winsock_error_string(p_WSAGetLastError()); return (Socket) ret; } @@ -1325,12 +1414,23 @@ void try_send(Actual_Socket s) } } } + + /* + * If we reach here, we've finished sending everything we might + * have needed to send. Send EOF, if we need to. + */ + if (s->outgoingeof == EOF_PENDING) { + p_shutdown(s->s, SD_SEND); + s->outgoingeof = EOF_SENT; + } } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Add the data to the buffer list on the socket. */ @@ -1349,6 +1449,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Replace the buffer list on the socket with the data. */ @@ -1366,6 +1468,24 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) return s->sending_oob; } +static void sk_tcp_write_eof(Socket sock) +{ + Actual_Socket s = (Actual_Socket) sock; + + assert(s->outgoingeof == EOF_NO); + + /* + * Mark the socket as pending outgoing EOF. + */ + s->outgoingeof = EOF_PENDING; + + /* + * Now try sending from the start of the buffer list. + */ + if (s->writable) + try_send(s); +} + int select_result(WPARAM wParam, LPARAM lParam) { int ret, open; diff --git a/contrib/putty/WINDOWS/WINNOISE.C b/contrib/putty/WINDOWS/WINNOISE.C index bdf8697..ce390db 100644 --- a/contrib/putty/WINDOWS/WINNOISE.C +++ b/contrib/putty/WINDOWS/WINNOISE.C @@ -9,10 +9,18 @@ #include "ssh.h" #include "storage.h" +#include + +DECL_WINDOWS_FUNCTION(static, BOOL, CryptAcquireContextA, + (HCRYPTPROV *, LPCTSTR, LPCTSTR, DWORD, DWORD)); +DECL_WINDOWS_FUNCTION(static, BOOL, CryptGenRandom, + (HCRYPTPROV, DWORD, BYTE *)); +DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext, + (HCRYPTPROV, DWORD)); +static HMODULE wincrypt_module = NULL; + /* - * This function is called once, at PuTTY startup, and will do some - * seriously silly things like listing directories and getting disk - * free space and a process snapshot. + * This function is called once, at PuTTY startup. */ void noise_get_heavy(void (*func) (void *, int)) @@ -20,6 +28,7 @@ void noise_get_heavy(void (*func) (void *, int)) HANDLE srch; WIN32_FIND_DATA finddata; DWORD pid; + HCRYPTPROV crypt_provider; char winpath[MAX_PATH + 3]; GetWindowsDirectory(winpath, sizeof(winpath)); @@ -35,6 +44,24 @@ void noise_get_heavy(void (*func) (void *, int)) pid = GetCurrentProcessId(); func(&pid, sizeof(pid)); + if (!wincrypt_module) { + wincrypt_module = load_system32_dll("advapi32.dll"); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext); + } + + if (wincrypt_module && p_CryptAcquireContextA && + p_CryptGenRandom && p_CryptReleaseContext && + p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + BYTE buf[32]; + if (p_CryptGenRandom(crypt_provider, 32, buf)) { + func(buf, sizeof(buf)); + } + p_CryptReleaseContext(crypt_provider, 0); + } + read_random_seed(func); /* Update the seed immediately, in case another instance uses it. */ random_save_seed(); diff --git a/contrib/putty/WINDOWS/WINPGEN.C b/contrib/putty/WINDOWS/WINPGEN.C index 13dfac7..37d0ae0 100644 --- a/contrib/putty/WINDOWS/WINPGEN.C +++ b/contrib/putty/WINDOWS/WINPGEN.C @@ -5,6 +5,7 @@ #include #include #include +#include #define PUTTY_DO_GLOBALS @@ -19,7 +20,7 @@ #define WM_DONEKEY (WM_APP + 1) -#define DEFAULT_KEYSIZE 1024 +#define DEFAULT_KEYSIZE 2048 static char *cmdline_keyfile = NULL; @@ -40,6 +41,22 @@ void modalfatalbox(char *fmt, ...) exit(1); } +/* + * Print a non-fatal message box and do not exit. + */ +void nonfatal(char *fmt, ...) +{ + va_list ap; + char *stuff; + + va_start(ap, fmt); + stuff = dupvprintf(fmt, ap); + va_end(ap); + MessageBox(NULL, stuff, "PuTTYgen Error", + MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + sfree(stuff); +} + /* ---------------------------------------------------------------------- * Progress report code. This is really horrible :-) */ @@ -116,10 +133,8 @@ static void progress_update(void *param, int action, int phase, int iprogress) extern char ver[]; -#define PASSPHRASE_MAXLEN 512 - struct PassphraseProcStruct { - char *passphrase; + char **passphrase; char *comment; }; @@ -129,7 +144,7 @@ struct PassphraseProcStruct { static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static char *passphrase = NULL; + static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { @@ -157,8 +172,9 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); - *passphrase = 0; - SetDlgItemText(hwnd, 102, passphrase); + burnstr(*passphrase); + *passphrase = dupstr(""); + SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -173,9 +189,8 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - GetDlgItemText(hwnd, 102, passphrase, - PASSPHRASE_MAXLEN - 1); - passphrase[PASSPHRASE_MAXLEN - 1] = '\0'; + burnstr(*passphrase); + *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } @@ -405,11 +420,11 @@ static int save_ssh1_pubkey(char *filename, struct RSAKey *key) char *dec1, *dec2; FILE *fp; - dec1 = bignum_decimal(key->exponent); - dec2 = bignum_decimal(key->modulus); fp = fopen(filename, "wb"); if (!fp) return 0; + dec1 = bignum_decimal(key->exponent); + dec2 = bignum_decimal(key->modulus); fprintf(fp, "%d %s %s %s\n", bignum_bitcount(key->modulus), dec1, dec2, key->comment); fclose(fp); @@ -615,19 +630,18 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) } void load_key_file(HWND hwnd, struct MainDlgState *state, - Filename filename, int was_import_cmd) + Filename *filename, int was_import_cmd) { - char passphrase[PASSPHRASE_MAXLEN]; + char *passphrase; int needs_pass; int type, realtype; int ret; const char *errmsg = NULL; char *comment; - struct PassphraseProcStruct pps; struct RSAKey newkey1; struct ssh2_userkey *newkey2 = NULL; - type = realtype = key_type(&filename); + type = realtype = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2 && !import_possible(type)) { @@ -646,19 +660,22 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, } comment = NULL; + passphrase = NULL; if (realtype == SSH_KEYTYPE_SSH1) - needs_pass = rsakey_encrypted(&filename, &comment); + needs_pass = rsakey_encrypted(filename, &comment); else if (realtype == SSH_KEYTYPE_SSH2) - needs_pass = - ssh2_userkey_encrypted(&filename, &comment); + needs_pass = ssh2_userkey_encrypted(filename, &comment); else - needs_pass = import_encrypted(&filename, realtype, - &comment); - pps.passphrase = passphrase; - pps.comment = comment; + needs_pass = import_encrypted(filename, realtype, &comment); do { + burnstr(passphrase); + passphrase = NULL; + if (needs_pass) { int dlgret; + struct PassphraseProcStruct pps; + pps.passphrase = &passphrase; + pps.comment = comment; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, @@ -667,22 +684,20 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, ret = -2; break; } + assert(passphrase != NULL); } else - *passphrase = '\0'; + passphrase = dupstr(""); if (type == SSH_KEYTYPE_SSH1) { if (realtype == type) - ret = loadrsakey(&filename, &newkey1, - passphrase, &errmsg); + ret = loadrsakey(filename, &newkey1, passphrase, &errmsg); else - ret = import_ssh1(&filename, realtype, - &newkey1, passphrase, &errmsg); + ret = import_ssh1(filename, realtype, &newkey1, + passphrase, &errmsg); } else { if (realtype == type) - newkey2 = ssh2_load_userkey(&filename, - passphrase, &errmsg); + newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg); else - newkey2 = import_ssh2(&filename, realtype, - passphrase, &errmsg); + newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg); if (newkey2 == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!newkey2) @@ -784,6 +799,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, MB_OK | MB_ICONINFORMATION); } } + burnstr(passphrase); } /* @@ -938,8 +954,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, /* * Load a key file if one was provided on the command line. */ - if (cmdline_keyfile) - load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0); + if (cmdline_keyfile) { + Filename *fn = filename_from_str(cmdline_keyfile); + load_key_file(hwnd, state, fn, 0); + filename_free(fn); + } return 1; case WM_MOUSEMOVE: @@ -958,7 +977,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, * Seed the entropy pool */ random_add_heavynoise(state->entropy, state->entropy_size); - memset(state->entropy, 0, state->entropy_size); + smemclr(state->entropy, state->entropy_size); sfree(state->entropy); state->collecting_entropy = FALSE; @@ -1102,8 +1121,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; - char passphrase[PASSPHRASE_MAXLEN]; - char passphrase2[PASSPHRASE_MAXLEN]; + char *passphrase, *passphrase2; int type, realtype; if (state->ssh2) @@ -1129,16 +1147,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, break; } - GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, - passphrase, sizeof(passphrase)); - GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, - passphrase2, sizeof(passphrase2)); + passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT); + passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT); if (strcmp(passphrase, passphrase2)) { MessageBox(hwnd, "The two passphrases given do not match.", "PuTTYgen Error", MB_OK | MB_ICONERROR); + burnstr(passphrase); + burnstr(passphrase2); break; } + burnstr(passphrase2); if (!*passphrase) { int ret; ret = MessageBox(hwnd, @@ -1146,8 +1165,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, "without a passphrase to protect it?", "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); - if (ret != IDYES) - break; + if (ret != IDYES) { + burnstr(passphrase); + break; + } } if (prompt_keyfile(hwnd, "Save private key as:", filename, 1, (type == realtype))) { @@ -1161,33 +1182,38 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); sfree(buffer); - if (ret != IDYES) + if (ret != IDYES) { + burnstr(passphrase); break; + } } if (state->ssh2) { - Filename fn = filename_from_str(filename); + Filename *fn = filename_from_str(filename); if (type != realtype) - ret = export_ssh2(&fn, type, &state->ssh2key, + ret = export_ssh2(fn, type, &state->ssh2key, *passphrase ? passphrase : NULL); else - ret = ssh2_save_userkey(&fn, &state->ssh2key, + ret = ssh2_save_userkey(fn, &state->ssh2key, *passphrase ? passphrase : NULL); + filename_free(fn); } else { - Filename fn = filename_from_str(filename); + Filename *fn = filename_from_str(filename); if (type != realtype) - ret = export_ssh1(&fn, type, &state->key, + ret = export_ssh1(fn, type, &state->key, *passphrase ? passphrase : NULL); else - ret = saversakey(&fn, &state->key, + ret = saversakey(fn, &state->key, *passphrase ? passphrase : NULL); + filename_free(fn); } if (ret <= 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } + burnstr(passphrase); } break; case IDC_SAVEPUB: @@ -1233,9 +1259,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, if (!state->generation_thread_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Load private key:", - filename, 0, LOWORD(wParam)==IDC_LOAD)) - load_key_file(hwnd, state, filename_from_str(filename), - LOWORD(wParam) != IDC_LOAD); + filename, 0, LOWORD(wParam)==IDC_LOAD)) { + Filename *fn = filename_from_str(filename); + load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD); + filename_free(fn); + } } break; } diff --git a/contrib/putty/WINDOWS/WINPGNT.C b/contrib/putty/WINDOWS/WINPGNT.C index ba55162..3b1b43e 100644 --- a/contrib/putty/WINDOWS/WINPGNT.C +++ b/contrib/putty/WINDOWS/WINPGNT.C @@ -159,10 +159,8 @@ struct blob { }; static int cmpkeys_ssh2_asymm(void *av, void *bv); -#define PASSPHRASE_MAXLEN 512 - struct PassphraseProcStruct { - char *passphrase; + char **passphrase; char *comment; }; @@ -176,7 +174,7 @@ static void forget_passphrases(void) { while (count234(passphrases) > 0) { char *pp = index234(passphrases, 0); - memset(pp, 0, strlen(pp)); + smemclr(pp, strlen(pp)); delpos234(passphrases, 0); free(pp); } @@ -247,7 +245,7 @@ static HWND passphrase_box; static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static char *passphrase = NULL; + static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { @@ -275,8 +273,9 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); - *passphrase = 0; - SetDlgItemText(hwnd, 102, passphrase); + burnstr(*passphrase); + *passphrase = dupstr(""); + SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -291,9 +290,8 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - GetDlgItemText(hwnd, 102, passphrase, - PASSPHRASE_MAXLEN - 1); - passphrase[PASSPHRASE_MAXLEN - 1] = '\0'; + burnstr(*passphrase); + *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } @@ -355,28 +353,27 @@ static void keylist_update(void) 0, (LPARAM) listentry); } for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) { - char listentry[512], *p; - int len; + char *listentry, *p; + int fp_len; /* * Replace two spaces in the fingerprint with tabs, for * nice alignment in the box. */ p = skey->alg->fingerprint(skey->data); - strncpy(listentry, p, sizeof(listentry)); + listentry = dupprintf("%s\t%s", p, skey->comment); + fp_len = strlen(listentry); + sfree(p); + p = strchr(listentry, ' '); - if (p) + if (p && p < listentry + fp_len) *p = '\t'; p = strchr(listentry, ' '); - if (p) + if (p && p < listentry + fp_len) *p = '\t'; - len = strlen(listentry); - if (len < sizeof(listentry) - 2) { - listentry[len] = '\t'; - strncpy(listentry + len + 1, skey->comment, - sizeof(listentry) - len - 1); - } + SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); + sfree(listentry); } SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0); } @@ -385,9 +382,9 @@ static void keylist_update(void) /* * This function loads a key from a file and adds it. */ -static void add_keyfile(Filename filename) +static void add_keyfile(Filename *filename) { - char passphrase[PASSPHRASE_MAXLEN]; + char *passphrase; struct RSAKey *rkey = NULL; struct ssh2_userkey *skey = NULL; int needs_pass; @@ -395,11 +392,10 @@ static void add_keyfile(Filename filename) int attempts; char *comment; const char *error = NULL; - struct PassphraseProcStruct pps; int type; int original_pass; - type = key_type(&filename); + type = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { char *msg = dupprintf("Couldn't load this key (%s)", key_type_to_str(type)); @@ -419,7 +415,7 @@ static void add_keyfile(Filename filename) int i, nkeys, bloblen, keylistlen; if (type == SSH_KEYTYPE_SSH1) { - if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL, &error)) { + if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { char *msg = dupprintf("Couldn't load private key (%s)", error); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); @@ -429,7 +425,7 @@ static void add_keyfile(Filename filename) keylist = get_keylist1(&keylistlen); } else { unsigned char *blob2; - blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, + blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, NULL, &error); if (!blob) { char *msg = dupprintf("Couldn't load private key (%s)", error); @@ -453,7 +449,12 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - nkeys = GET_32BIT(keylist); + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + MessageBox(NULL, "Received broken key list?!", APPNAME, + MB_OK | MB_ICONERROR); + return; + } p = keylist + 4; keylistlen -= 4; @@ -481,8 +482,8 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - n = 4 + GET_32BIT(p); - if (keylistlen < n) { + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; @@ -498,8 +499,8 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - n = 4 + GET_32BIT(p); - if (keylistlen < n) { + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; @@ -517,23 +518,30 @@ static void add_keyfile(Filename filename) error = NULL; if (type == SSH_KEYTYPE_SSH1) - needs_pass = rsakey_encrypted(&filename, &comment); + needs_pass = rsakey_encrypted(filename, &comment); else - needs_pass = ssh2_userkey_encrypted(&filename, &comment); + needs_pass = ssh2_userkey_encrypted(filename, &comment); attempts = 0; if (type == SSH_KEYTYPE_SSH1) rkey = snew(struct RSAKey); - pps.passphrase = passphrase; - pps.comment = comment; + passphrase = NULL; original_pass = 0; do { + burnstr(passphrase); + passphrase = NULL; + if (needs_pass) { /* try all the remembered passphrases first */ char *pp = index234(passphrases, attempts); if(pp) { - strcpy(passphrase, pp); + passphrase = dupstr(pp); } else { int dlgret; + struct PassphraseProcStruct pps; + + pps.passphrase = &passphrase; + pps.comment = comment; + original_pass = 1; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, (LPARAM) &pps); @@ -545,13 +553,16 @@ static void add_keyfile(Filename filename) sfree(rkey); return; /* operation cancelled */ } + + assert(passphrase != NULL); } } else - *passphrase = '\0'; + passphrase = dupstr(""); + if (type == SSH_KEYTYPE_SSH1) - ret = loadrsakey(&filename, rkey, passphrase, &error); + ret = loadrsakey(filename, rkey, passphrase, &error); else { - skey = ssh2_load_userkey(&filename, passphrase, &error); + skey = ssh2_load_userkey(filename, passphrase, &error); if (skey == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!skey) @@ -562,11 +573,14 @@ static void add_keyfile(Filename filename) attempts++; } while (ret == -1); - /* if they typed in an ok passphrase, remember it */ if(original_pass && ret) { - char *pp = dupstr(passphrase); - addpos234(passphrases, pp, 0); + /* If they typed in an ok passphrase, remember it */ + addpos234(passphrases, passphrase, 0); + } else { + /* Otherwise, destroy it */ + burnstr(passphrase); } + passphrase = NULL; if (comment) sfree(comment); @@ -797,8 +811,10 @@ static void *get_keylist1(int *length) retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; - if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) + if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { + sfree(response); return NULL; + } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); @@ -832,8 +848,10 @@ static void *get_keylist2(int *length) retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; - if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) + if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { + sfree(response); return NULL; + } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); @@ -928,12 +946,17 @@ static void answer_msg(void *msg) goto failure; p += i; i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus); - if (i < 0) + if (i < 0) { + freebn(reqkey.exponent); goto failure; + } p += i; i = ssh1_read_bignum(p, msgend - p, &challenge); - if (i < 0) + if (i < 0) { + freebn(reqkey.exponent); + freebn(reqkey.modulus); goto failure; + } p += i; if (msgend < p+16) { freebn(reqkey.exponent); @@ -958,7 +981,7 @@ static void answer_msg(void *msg) MD5Init(&md5c); MD5Update(&md5c, response_source, 48); MD5Final(response_md5, &md5c); - memset(response_source, 0, 48); /* burn the evidence */ + smemclr(response_source, 48); /* burn the evidence */ freebn(response); /* and that evidence */ freebn(challenge); /* yes, and that evidence */ freebn(reqkey.exponent); /* and free some memory ... */ @@ -988,17 +1011,17 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - b.len = GET_32BIT(p); + b.len = toint(GET_32BIT(p)); + if (b.len < 0 || b.len > msgend - (p+4)) + goto failure; p += 4; - if (msgend < p+b.len) - goto failure; b.blob = p; p += b.len; if (msgend < p+4) goto failure; - datalen = GET_32BIT(p); + datalen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+datalen) + if (datalen < 0 || datalen > msgend - p) goto failure; data = p; key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm); @@ -1071,9 +1094,9 @@ static void answer_msg(void *msg) sfree(key); goto failure; } - commentlen = GET_32BIT(p); + commentlen = toint(GET_32BIT(p)); - if (msgend < p+commentlen) { + if (commentlen < 0 || commentlen > msgend - p) { freersakey(key); sfree(key); goto failure; @@ -1110,9 +1133,9 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - alglen = GET_32BIT(p); + alglen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+alglen) + if (alglen < 0 || alglen > msgend - p) goto failure; alg = p; p += alglen; @@ -1146,10 +1169,10 @@ static void answer_msg(void *msg) sfree(key); goto failure; } - commlen = GET_32BIT(p); + commlen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+commlen) { + if (commlen < 0 || commlen > msgend - p) { key->alg->freekey(key->data); sfree(key); goto failure; @@ -1213,10 +1236,10 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - b.len = GET_32BIT(p); + b.len = toint(GET_32BIT(p)); p += 4; - if (msgend < p+b.len) + if (b.len < 0 || b.len > msgend - p) goto failure; b.blob = p; p += b.len; @@ -1423,10 +1446,12 @@ static void prompt_add_keyfile(void) of.lpstrTitle = "Select Private Key File"; of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; if (request_file(keypath, &of, TRUE, FALSE)) { - if(strlen(filelist) > of.nFileOffset) + if(strlen(filelist) > of.nFileOffset) { /* Only one filename returned? */ - add_keyfile(filename_from_str(filelist)); - else { + Filename *fn = filename_from_str(filelist); + add_keyfile(fn); + filename_free(fn); + } else { /* we are returned a bunch of strings, end to * end. first string is the directory, the * rest the filenames. terminated with an @@ -1436,7 +1461,9 @@ static void prompt_add_keyfile(void) char *filewalker = filelist + strlen(dir) + 1; while (*filewalker != '\0') { char *filename = dupcat(dir, "\\", filewalker, NULL); - add_keyfile(filename_from_str(filename)); + Filename *fn = filename_from_str(filename); + add_keyfile(fn); + filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; } @@ -1894,6 +1921,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #ifdef DEBUG_IPC debug(("couldn't get user SID\n")); #endif + CloseHandle(filemap); return 0; } @@ -1901,6 +1929,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #ifdef DEBUG_IPC debug(("couldn't get default SID\n")); #endif + CloseHandle(filemap); + sfree(ourself); return 0; } @@ -1912,6 +1942,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, debug(("couldn't get owner info for filemap: %d\n", rc)); #endif + CloseHandle(filemap); + sfree(ourself); + sfree(ourself2); return 0; } #ifdef DEBUG_IPC @@ -1930,6 +1963,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (!EqualSid(mapowner, ourself) && !EqualSid(mapowner, ourself2)) { CloseHandle(filemap); + LocalFree(psd); + sfree(ourself); + sfree(ourself2); return 0; /* security ID mismatch! */ } #ifdef DEBUG_IPC @@ -2003,7 +2039,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; - HMODULE advapi; char *command = NULL; int added_keys = 0; int argc, i; @@ -2044,8 +2079,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) "Pageant Fatal Error", MB_ICONERROR | MB_OK); return 1; #endif - } else - advapi = NULL; + } /* * See if we can find our Help file. @@ -2098,8 +2132,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-pgpfp")) { pgp_fingerprints(); - if (advapi) - FreeLibrary(advapi); return 1; } else if (!strcmp(argv[i], "-c")) { /* @@ -2113,7 +2145,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) command = ""; break; } else { - add_keyfile(filename_from_str(argv[i])); + Filename *fn = filename_from_str(argv[i]); + add_keyfile(fn); + filename_free(fn); added_keys = TRUE; } } @@ -2147,8 +2181,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); } - if (advapi) - FreeLibrary(advapi); return 0; } @@ -2228,9 +2260,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (keypath) filereq_free(keypath); - if (advapi) - FreeLibrary(advapi); - cleanup_exit(msg.wParam); return msg.wParam; /* just in case optimiser complains */ } diff --git a/contrib/putty/WINDOWS/WINPGNTC.C b/contrib/putty/WINDOWS/WINPGNTC.C index 0dabe71..c019d20 100644 --- a/contrib/putty/WINDOWS/WINPGNTC.C +++ b/contrib/putty/WINDOWS/WINPGNTC.C @@ -173,6 +173,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, return 1; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); + psa = NULL; #ifndef NO_SECURITY if (advapi_initialised || init_advapi()) { /* @@ -186,7 +187,6 @@ int agent_query(void *in, int inlen, void **out, int *outlen, */ usersid = get_user_sid(); - psa = NULL; if (usersid) { psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); @@ -209,8 +209,10 @@ int agent_query(void *in, int inlen, void **out, int *outlen, filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); - if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) + if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { + sfree(mapname); return 1; /* *out == NULL, so failure */ + } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); memcpy(p, in, inlen); cds.dwData = AGENT_COPYDATA_ID; @@ -237,6 +239,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, data->hwnd = hwnd; if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) return 0; + sfree(mapname); sfree(data); } #endif @@ -258,6 +261,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, } UnmapViewOfFile(p); CloseHandle(filemap); + sfree(mapname); if (psd) LocalFree(psd); sfree(usersid); diff --git a/contrib/putty/WINDOWS/WINPLINK.C b/contrib/putty/WINDOWS/WINPLINK.C index 7af9e1b..fc3e5b0 100644 --- a/contrib/putty/WINDOWS/WINPLINK.C +++ b/contrib/putty/WINDOWS/WINPLINK.C @@ -49,6 +49,19 @@ void modalfatalbox(char *p, ...) } cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + if (logctx) { + log_free(logctx); + logctx = NULL; + } +} void connection_fatal(void *frontend, char *p, ...) { va_list ap; @@ -83,7 +96,7 @@ WSAEVENT netevent; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; int term_ldisc(Terminal *term, int mode) { @@ -130,6 +143,12 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) return 0; /* not reached */ } +int from_backend_eof(void *frontend_handle) +{ + handle_write_eof(stdout_handle); + return FALSE; /* do not respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; @@ -283,7 +302,7 @@ int main(int argc, char **argv) int errors; int got_host = FALSE; int use_subsystem = 0; - long now, next; + unsigned long now, next, then; sklist = NULL; skcount = sksize = 0; @@ -298,10 +317,11 @@ int main(int argc, char **argv) /* * Process the command line. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; - default_protocol = cfg.protocol; - default_port = cfg.port; + default_protocol = conf_get_int(conf, CONF_protocol); + default_port = conf_get_int(conf, CONF_port); errors = 0; { /* @@ -311,8 +331,10 @@ int main(int argc, char **argv) if (p) { const Backend *b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; - default_port = cfg.port = b->default_port; + default_protocol = b->protocol; + default_port = b->default_port; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); } } } @@ -320,7 +342,7 @@ int main(int argc, char **argv) char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, &cfg); + 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); @@ -332,10 +354,12 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { - /* Save status to write to cfg later. */ + /* Save status to write to conf later. */ use_subsystem = 1; - } else if (!strcmp(p, "-V")) { + } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); + } else if (!strcmp(p, "--help")) { + usage(); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); @@ -344,7 +368,7 @@ int main(int argc, char **argv) errors = 1; } } else if (*p) { - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* * If the hostname starts with "telnet:", set the @@ -357,7 +381,7 @@ int main(int argc, char **argv) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg.protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -365,11 +389,10 @@ int main(int argc, char **argv) if (*p) *p++ = '\0'; if (c == ':') - cfg.port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; @@ -384,7 +407,9 @@ int main(int argc, char **argv) *r = '\0'; b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); portnumber = b->default_port; } p = r + 1; @@ -411,26 +436,24 @@ int main(int argc, char **argv) * same name as the hostname. */ { - Config cfg2; - do_defaults(host, &cfg2); - if (loaded_session || !cfg_launchable(&cfg2)) { + Conf *conf2 = conf_new(); + do_defaults(host, conf2); + if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = default_port; + conf_set_str(conf, CONF_host, host); + conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { - cfg = cfg2; + conf_copy_into(conf, conf2); loaded_session = TRUE; } + conf_free(conf2); } if (user) { /* Patch in specified username. */ - strncpy(cfg.username, user, - sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } } @@ -457,9 +480,9 @@ int main(int argc, char **argv) } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ - cfg.remote_cmd_ptr = command; - cfg.remote_cmd_ptr2 = NULL; - cfg.nopty = TRUE; /* command => no terminal */ + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } @@ -469,70 +492,78 @@ int main(int argc, char **argv) if (errors) return 1; - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; - /* See if host is of the form user@host */ - if (cfg_launchable(&cfg)) { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); + + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } + + /* + * Trim off a colon suffix if it's there. + */ + host[strcspn(host, ":")] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) - cfg.ssh_subsys = TRUE; + conf_set_int(conf, CONF_ssh_subsys, TRUE); - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; - - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; - } - cfg.host[p1] = '\0'; - } - - if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host) + if (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); @@ -543,7 +574,7 @@ int main(int argc, char **argv) * Select port. */ if (portnumber != -1) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); sk_init(); if (p_WSAEventSelect == NULL) { @@ -551,7 +582,7 @@ int main(int argc, char **argv) return 1; } - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); console_provide_logctx(logctx); /* @@ -562,11 +593,14 @@ int main(int argc, char **argv) const char *error; char *realhost; /* nodelay is only useful if stdin is a character device (console) */ - int nodelay = cfg.tcp_nodelay && + int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); - error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, - &realhost, nodelay, cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, nodelay, + conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); return 1; @@ -615,8 +649,12 @@ int main(int argc, char **argv) } if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; } else { ticks = INFINITE; } diff --git a/contrib/putty/WINDOWS/WINPRINT.C b/contrib/putty/WINDOWS/WINPRINT.C index 4098978..0bb7fc8 100644 --- a/contrib/putty/WINDOWS/WINPRINT.C +++ b/contrib/putty/WINDOWS/WINPRINT.C @@ -18,39 +18,39 @@ struct printer_job_tag { HANDLE hprinter; }; -static char *printer_add_enum(int param, DWORD level, char *buffer, - int offset, int *nprinters_ptr) +static int printer_add_enum(int param, DWORD level, char **buffer, + int offset, int *nprinters_ptr) { DWORD needed = 0, nprinters = 0; - buffer = sresize(buffer, offset+512, char); + *buffer = sresize(*buffer, offset+512, char); /* * Exploratory call to EnumPrinters to determine how much space * we'll need for the output. Discard the return value since it * will almost certainly be a failure due to lack of space. */ - EnumPrinters(param, NULL, level, buffer+offset, 512, + EnumPrinters(param, NULL, level, (*buffer)+offset, 512, &needed, &nprinters); if (needed < 512) needed = 512; - buffer = sresize(buffer, offset+needed, char); + *buffer = sresize(*buffer, offset+needed, char); - if (EnumPrinters(param, NULL, level, buffer+offset, + if (EnumPrinters(param, NULL, level, (*buffer)+offset, needed, &needed, &nprinters) == 0) - return NULL; + return FALSE; *nprinters_ptr += nprinters; - return buffer; + return TRUE; } printer_enum *printer_start_enum(int *nprinters_ptr) { printer_enum *ret = snew(printer_enum); - char *buffer = NULL, *retval; + char *buffer = NULL; *nprinters_ptr = 0; /* default return value */ buffer = snewn(512, char); @@ -71,12 +71,9 @@ printer_enum *printer_start_enum(int *nprinters_ptr) ret->enum_level = 4; } - retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, - ret->enum_level, buffer, 0, nprinters_ptr); - if (!retval) + if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, + ret->enum_level, &buffer, 0, nprinters_ptr)) goto error; - else - buffer = retval; switch (ret->enum_level) { case 4: diff --git a/contrib/putty/WINDOWS/WINPROXY.C b/contrib/putty/WINDOWS/WINPROXY.C index 4da4d2e..b1c5f0e 100644 --- a/contrib/putty/WINDOWS/WINPROXY.C +++ b/contrib/putty/WINDOWS/WINPROXY.C @@ -87,6 +87,13 @@ static int sk_localproxy_write_oob(Socket s, const char *data, int len) return sk_localproxy_write(s, data, len); } +static void sk_localproxy_write_eof(Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + handle_write_eof(ps->to_cmd_h); +} + static void sk_localproxy_flush(Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ @@ -123,7 +130,7 @@ static const char *sk_localproxy_socket_error(Socket s) Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { char *cmd; @@ -132,6 +139,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, + sk_localproxy_write_eof, sk_localproxy_flush, sk_localproxy_set_private_ptr, sk_localproxy_get_private_ptr, @@ -145,10 +153,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname, STARTUPINFO si; PROCESS_INFORMATION pi; - if (cfg->proxy_type != PROXY_CMD) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; - cmd = format_telnet_command(addr, port, cfg); + cmd = format_telnet_command(addr, port, conf); { char *msg = dupprintf("Starting local proxy command: %s", cmd); @@ -172,6 +180,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sa.bInheritHandle = TRUE; if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) { ret->error = dupprintf("Unable to create pipes for proxy command"); + sfree(cmd); return (Socket)ret; } @@ -179,6 +188,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); ret->error = dupprintf("Unable to create pipes for proxy command"); + sfree(cmd); return (Socket)ret; } @@ -198,6 +208,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname, CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); sfree(cmd); diff --git a/contrib/putty/WINDOWS/WINSER.C b/contrib/putty/WINDOWS/WINSER.C index 1928324..2e3a907 100644 --- a/contrib/putty/WINDOWS/WINSER.C +++ b/contrib/putty/WINDOWS/WINSER.C @@ -87,7 +87,7 @@ static void serial_sentdata(struct handle *h, int new_backlog) } } -static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) +static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf) { DCB dcb; COMMTIMEOUTS timeouts; @@ -121,17 +121,17 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) /* * Configurable parameters. */ - dcb.BaudRate = cfg->serspeed; - msg = dupprintf("Configuring baud rate %d", cfg->serspeed); + dcb.BaudRate = conf_get_int(conf, CONF_serspeed); + msg = dupprintf("Configuring baud rate %d", dcb.BaudRate); logevent(serial->frontend, msg); sfree(msg); - dcb.ByteSize = cfg->serdatabits; - msg = dupprintf("Configuring %d data bits", cfg->serdatabits); + dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); + msg = dupprintf("Configuring %d data bits", dcb.ByteSize); logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serstopbits) { + switch (conf_get_int(conf, CONF_serstopbits)) { case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break; case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break; case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break; @@ -141,7 +141,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serparity) { + switch (conf_get_int(conf, CONF_serparity)) { case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break; case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break; @@ -152,7 +152,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serflow) { + switch (conf_get_int(conf, CONF_serflow)) { case SER_FLOW_NONE: str = "no"; break; @@ -199,13 +199,13 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive) + Conf *conf, char *host, int port, + char **realhost, int nodelay, int keepalive) { Serial serial; HANDLE serport; const char *err; + char *serline; serial = snew(struct serial_backend_data); serial->port = INVALID_HANDLE_VALUE; @@ -216,8 +216,9 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, serial->frontend = frontend_handle; + serline = conf_get_str(conf, CONF_serline); { - char *msg = dupprintf("Opening serial device %s", cfg->serline); + char *msg = dupprintf("Opening serial device %s", serline); logevent(serial->frontend, msg); } @@ -246,9 +247,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, * existing configurations using \\.\ continue working.) */ char *serfilename = - dupprintf("%s%s", - strchr(cfg->serline, '\\') ? "" : "\\\\.\\", - cfg->serline); + dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); sfree(serfilename); @@ -257,7 +256,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; - err = serial_configure(serial, serport, cfg); + err = serial_configure(serial, serport, conf); if (err) return err; @@ -269,7 +268,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, HANDLE_FLAG_IGNOREEOF | HANDLE_FLAG_UNITBUFFER); - *realhost = dupstr(cfg->serline); + *realhost = dupstr(serline); /* * Specials are always available. @@ -288,12 +287,12 @@ static void serial_free(void *handle) sfree(serial); } -static void serial_reconfig(void *handle, Config *cfg) +static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; const char *err; - err = serial_configure(serial, serial->port, cfg); + err = serial_configure(serial, serial->port, conf); /* * FIXME: what should we do if err returns something? @@ -332,11 +331,11 @@ static void serial_size(void *handle, int width, int height) return; } -static void serbreak_timer(void *ctx, long now) +static void serbreak_timer(void *ctx, unsigned long now) { Serial serial = (Serial)ctx; - if (now >= serial->clearbreak_time && serial->port) { + if (now == serial->clearbreak_time && serial->port) { ClearCommBreak(serial->port); serial->break_in_progress = FALSE; logevent(serial->frontend, "Finished serial break"); diff --git a/contrib/putty/WINDOWS/WINSFTP.C b/contrib/putty/WINDOWS/WINSFTP.C index 8a36dcb..7039af8 100644 --- a/contrib/putty/WINDOWS/WINSFTP.C +++ b/contrib/putty/WINDOWS/WINSFTP.C @@ -20,7 +20,7 @@ int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) return ret; } -void platform_get_x11_auth(struct X11Display *display, const Config *cfg) +void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } @@ -88,7 +88,8 @@ struct RFile { }; RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime) + unsigned long *mtime, unsigned long *atime, + long *perms) { HANDLE h; RFile *ret; @@ -113,6 +114,9 @@ RFile *open_existing_file(char *name, uint64 *size, TIME_WIN_TO_POSIX(wrtime, *mtime); } + if (perms) + *perms = -1; + return ret; } @@ -137,7 +141,7 @@ struct WFile { HANDLE h; }; -WFile *open_new_file(char *name) +WFile *open_new_file(char *name, long perms) { HANDLE h; WFile *ret; @@ -482,15 +486,20 @@ extern int select_result(WPARAM, LPARAM); int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; - long next, ticks; + unsigned long next, then; + long ticks; HANDLE *handles; SOCKET *sklist; int skcount; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(); if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; } else { ticks = INFINITE; } @@ -602,7 +611,7 @@ int ssh_sftp_loop_iteration(void) if (p_WSAEventSelect == NULL) { fd_set readfds; int ret; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(), then; if (sftp_ssh_socket == INVALID_SOCKET) return -1; /* doom */ @@ -611,13 +620,17 @@ int ssh_sftp_loop_iteration(void) select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE); do { - long next, ticks; + unsigned long next; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks <= 0) - ticks = 1; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; diff --git a/contrib/putty/WINDOWS/WINSTORE.C b/contrib/putty/WINDOWS/WINSTORE.C index 13ee184..77437b4 100644 --- a/contrib/putty/WINDOWS/WINSTORE.C +++ b/contrib/putty/WINDOWS/WINSTORE.C @@ -150,17 +150,29 @@ void *open_settings_r(const char *sessionname) return (void *) sesskey; } -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) +char *read_setting_s(void *handle, const char *key) { DWORD type, size; - size = buflen; + char *ret; - if (!handle || - RegQueryValueEx((HKEY) handle, key, 0, - &type, buffer, &size) != ERROR_SUCCESS || - type != REG_SZ) return NULL; - else - return buffer; + if (!handle) + return NULL; + + /* Find out the type and size of the data. */ + if (RegQueryValueEx((HKEY) handle, key, 0, + &type, NULL, &size) != ERROR_SUCCESS || + type != REG_SZ) + return NULL; + + ret = snewn(size+1, char); + if (RegQueryValueEx((HKEY) handle, key, 0, + &type, ret, &size) != ERROR_SUCCESS || + type != REG_SZ) { + sfree(ret); + return NULL; + } + + return ret; } int read_setting_i(void *handle, const char *key, int defvalue) @@ -177,53 +189,76 @@ int read_setting_i(void *handle, const char *key, int defvalue) return val; } -int read_setting_fontspec(void *handle, const char *name, FontSpec *result) +FontSpec *read_setting_fontspec(void *handle, const char *name) { char *settingname; - FontSpec ret; + char *fontname; + FontSpec *ret; + int isbold, height, charset; + + fontname = read_setting_s(handle, name); + if (!fontname) + return NULL; - if (!read_setting_s(handle, name, ret.name, sizeof(ret.name))) - return 0; settingname = dupcat(name, "IsBold", NULL); - ret.isbold = read_setting_i(handle, settingname, -1); + isbold = read_setting_i(handle, settingname, -1); sfree(settingname); - if (ret.isbold == -1) return 0; + if (isbold == -1) { + sfree(fontname); + return NULL; + } + settingname = dupcat(name, "CharSet", NULL); - ret.charset = read_setting_i(handle, settingname, -1); + charset = read_setting_i(handle, settingname, -1); sfree(settingname); - if (ret.charset == -1) return 0; + if (charset == -1) { + sfree(fontname); + return NULL; + } + settingname = dupcat(name, "Height", NULL); - ret.height = read_setting_i(handle, settingname, INT_MIN); + height = read_setting_i(handle, settingname, INT_MIN); sfree(settingname); - if (ret.height == INT_MIN) return 0; - *result = ret; - return 1; + if (height == INT_MIN) { + sfree(fontname); + return NULL; + } + + ret = fontspec_new(fontname, isbold, height, charset); + sfree(fontname); + return ret; } -void write_setting_fontspec(void *handle, const char *name, FontSpec font) +void write_setting_fontspec(void *handle, const char *name, FontSpec *font) { char *settingname; - write_setting_s(handle, name, font.name); + write_setting_s(handle, name, font->name); settingname = dupcat(name, "IsBold", NULL); - write_setting_i(handle, settingname, font.isbold); + write_setting_i(handle, settingname, font->isbold); sfree(settingname); settingname = dupcat(name, "CharSet", NULL); - write_setting_i(handle, settingname, font.charset); + write_setting_i(handle, settingname, font->charset); sfree(settingname); settingname = dupcat(name, "Height", NULL); - write_setting_i(handle, settingname, font.height); + write_setting_i(handle, settingname, font->height); sfree(settingname); } -int read_setting_filename(void *handle, const char *name, Filename *result) +Filename *read_setting_filename(void *handle, const char *name) { - return !!read_setting_s(handle, name, result->path, sizeof(result->path)); + char *tmp = read_setting_s(handle, name); + if (tmp) { + Filename *ret = filename_from_str(tmp); + sfree(tmp); + return ret; + } else + return NULL; } -void write_setting_filename(void *handle, const char *name, Filename result) +void write_setting_filename(void *handle, const char *name, Filename *result) { - write_setting_s(handle, name, result.path); + write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) @@ -320,16 +355,18 @@ int verify_host_key(const char *hostname, int port, * Now read a saved key in from the registry and see what it * says. */ - otherstr = snewn(len, char); regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char); hostkey_regname(regname, hostname, port, keytype); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", - &rkey) != ERROR_SUCCESS) + &rkey) != ERROR_SUCCESS) { + sfree(regname); return 1; /* key does not exist in registry */ + } readlen = len; + otherstr = snewn(len, char); ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && @@ -392,6 +429,8 @@ int verify_host_key(const char *hostname, int port, RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr, strlen(otherstr) + 1); } + + sfree(oldstyle); } RegCloseKey(rkey); @@ -436,7 +475,10 @@ enum { DEL, OPEN_R, OPEN_W }; static int try_random_seed(char const *path, int action, HANDLE *ret) { if (action == DEL) { - remove(path); + if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) { + nonfatal("Unable to delete '%s': %s", path, + win_strerror(GetLastError())); + } *ret = INVALID_HANDLE_VALUE; return FALSE; /* so we'll do the next ones too */ } @@ -707,7 +749,7 @@ static int transform_jumplist_registry /* * Either return or free the result. */ - if (out) + if (out && ret == ERROR_SUCCESS) *out = old_value; else sfree(old_value); @@ -740,7 +782,7 @@ char *get_jumplist_registry_entries (void) { char *list_value; - if (transform_jumplist_registry(NULL,NULL,&list_value) != ERROR_SUCCESS) { + if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) { list_value = snewn(2, char); *list_value = '\0'; *(list_value + 1) = '\0'; diff --git a/contrib/putty/WINDOWS/WINSTUFF.H b/contrib/putty/WINDOWS/WINSTUFF.H index 81890a8..53906b3 100644 --- a/contrib/putty/WINDOWS/WINSTUFF.H +++ b/contrib/putty/WINDOWS/WINSTUFF.H @@ -16,16 +16,18 @@ #include "winhelp.h" struct Filename { - char path[FILENAME_MAX]; + char *path; }; -#define f_open(filename, mode, isprivate) ( fopen((filename).path, (mode)) ) +#define f_open(filename, mode, isprivate) ( fopen((filename)->path, (mode)) ) struct FontSpec { - char name[64]; + char *name; int isbold; int height; int charset; }; +struct FontSpec *fontspec_new(const char *name, + int bold, int height, int charset); #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 @@ -73,6 +75,10 @@ struct FontSpec { #define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) #define DF_END 0x0001 +#ifndef NO_SECUREZEROMEMORY +#define PLATFORM_HAS_SMEMCLR /* inhibit cross-platform one in misc.c */ +#endif + /* * Dynamically linked functions. These come in two flavours: * @@ -115,7 +121,7 @@ struct FontSpec { #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -143,6 +149,7 @@ typedef struct terminal_tag Terminal; #define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ #define DEFAULT_CODEPAGE CP_ACP +#define USES_VTLINE_HACK typedef HDC Context; @@ -234,15 +241,6 @@ GLOBAL void *logctx; "All Files (*.*)\0*\0\0\0") /* - * On some versions of Windows, it has been known for WM_TIMER to - * occasionally get its callback time simply wrong, and call us - * back several minutes early. Defining these symbols enables - * compensation code in timing.c. - */ -#define TIMING_SYNC -#define TIMING_SYNC_TICKCOUNT - -/* * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on * what it can get, which means any WinSock routines used outside * that module must be exported from it as function pointers. So @@ -285,6 +283,7 @@ BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save); filereq *filereq_new(void); void filereq_free(filereq *state); int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); +char *GetDlgItemText_alloc(HWND hwnd, int id); void split_into_argv(char *, int *, char ***, char ***); /* @@ -462,6 +461,7 @@ void show_help(HWND hwnd); extern OSVERSIONINFO osVersion; BOOL init_winver(void); HMODULE load_system32_dll(const char *libname); +const char *win_strerror(int error); /* * Exports from sizetip.c. @@ -473,7 +473,7 @@ void EnableSizeTip(int bEnable); * Exports from unicode.c. */ struct unicode_data; -void init_ucs(Config *, struct unicode_data *); +void init_ucs(Conf *, struct unicode_data *); /* * Exports from winhandl.c. @@ -489,6 +489,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags); int handle_write(struct handle *h, const void *data, int len); +void handle_write_eof(struct handle *h); HANDLE *handle_get_events(int *nevents); void handle_free(struct handle *h); void handle_got_event(HANDLE event); diff --git a/contrib/putty/WINDOWS/WINUCS.C b/contrib/putty/WINDOWS/WINUCS.C index 1b72147..024e369 100644 --- a/contrib/putty/WINDOWS/WINUCS.C +++ b/contrib/putty/WINDOWS/WINUCS.C @@ -390,6 +390,8 @@ struct cp_list_item { }; static const struct cp_list_item cp_list[] = { + {"UTF-8", CP_UTF8}, + {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1}, {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2}, {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3}, @@ -406,8 +408,6 @@ static const struct cp_list_item cp_list[] = { {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15}, {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16}, - {"UTF-8", CP_UTF8}, - {"KOI8-U", 0, 128, koi8_u}, {"KOI8-R", 20866}, {"HP-ROMAN8", 0, 96, roman8}, @@ -427,6 +427,7 @@ static const struct cp_list_item cp_list[] = { {"CP437", 437}, {"CP620 (Mazovia)", 0, 128, mazovia}, {"CP819", 28591}, + {"CP852", 852}, {"CP878", 20866}, {"Use font encoding", -1}, @@ -436,24 +437,27 @@ static const struct cp_list_item cp_list[] = { static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr); -void init_ucs(Config *cfg, struct unicode_data *ucsdata) +void init_ucs(Conf *conf, struct unicode_data *ucsdata) { int i, j; int used_dtf = 0; char tbuf[256]; + int vtmode; for (i = 0; i < 256; i++) tbuf[i] = i; /* Decide on the Line and Font codepages */ - ucsdata->line_codepage = decode_codepage(cfg->line_codepage); + ucsdata->line_codepage = decode_codepage(conf_get_str(conf, + CONF_line_codepage)); if (ucsdata->font_codepage <= 0) { ucsdata->font_codepage=0; ucsdata->dbcs_screenfont=0; } - if (cfg->vtmode == VT_OEMONLY) { + vtmode = conf_get_int(conf, CONF_vtmode); + if (vtmode == VT_OEMONLY) { ucsdata->font_codepage = 437; ucsdata->dbcs_screenfont = 0; if (ucsdata->line_codepage <= 0) @@ -473,7 +477,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) if (ucsdata->font_codepage == 437) ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF; } - if (cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_font + 1, unitab_xterm_std, sizeof(unitab_xterm_std)); @@ -481,7 +485,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1); /* Collect CP437 ucs table for SCO acs */ - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, sizeof(ucsdata->unitab_scoacs)); else @@ -490,7 +494,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) /* Collect line set ucs table */ if (ucsdata->line_codepage == ucsdata->font_codepage && (ucsdata->dbcs_screenfont || - cfg->vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { + vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { /* For DBCS and POOR fonts force direct to font */ used_dtf = 1; @@ -560,14 +564,14 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) ucsdata->unitab_ctrl[i] = 0xFF; /* Generate line->screen direct conversion links. */ - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP); - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) { + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) { link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP); } @@ -581,7 +585,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) } /* Last chance, if !unicode then try poorman links. */ - if (cfg->vtmode != VT_UNICODE) { + if (vtmode != VT_UNICODE) { static const char poorman_scoacs[] = "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* "; static const char poorman_latin1[] = @@ -1012,95 +1016,54 @@ int decode_codepage(char *cp_name) int codepage = -1; CPINFO cpinfo; - if (!*cp_name) { - /* - * Here we select a plausible default code page based on - * the locale the user is in. We wish to select an ISO code - * page or appropriate local default _rather_ than go with - * the Win125* series, because it's more important to have - * CSI and friends enabled by default than the ghastly - * Windows extra quote characters, and because it's more - * likely the user is connecting to a remote server that - * does something Unixy or VMSy and hence standards- - * compliant than that they're connecting back to a Windows - * box using horrible nonstandard charsets. - * - * Accordingly, Robert de Bath suggests a method for - * picking a default character set that runs as follows: - * first call GetACP to get the system's ANSI code page - * identifier, and translate as follows: - * - * 1250 -> ISO 8859-2 - * 1251 -> KOI8-U - * 1252 -> ISO 8859-1 - * 1253 -> ISO 8859-7 - * 1254 -> ISO 8859-9 - * 1255 -> ISO 8859-8 - * 1256 -> ISO 8859-6 - * 1257 -> ISO 8859-13 (changed from 8859-4 on advice of a Lithuanian) - * - * and for anything else, choose direct-to-font. - */ - int cp = GetACP(); - switch (cp) { - case 1250: cp_name = "ISO-8859-2"; break; - case 1251: cp_name = "KOI8-U"; break; - case 1252: cp_name = "ISO-8859-1"; break; - case 1253: cp_name = "ISO-8859-7"; break; - case 1254: cp_name = "ISO-8859-9"; break; - case 1255: cp_name = "ISO-8859-8"; break; - case 1256: cp_name = "ISO-8859-6"; break; - case 1257: cp_name = "ISO-8859-13"; break; - /* default: leave it blank, which will select -1, direct->font */ - } + if (!cp_name || !*cp_name) + return CP_UTF8; /* default */ + + for (cpi = cp_list; cpi->name; cpi++) { + s = cp_name; + d = cpi->name; + for (;;) { + while (*s && !isalnum(*s) && *s != ':') + s++; + while (*d && !isalnum(*d) && *d != ':') + d++; + if (*s == 0) { + codepage = cpi->codepage; + if (codepage == CP_UTF8) + goto break_break; + if (codepage == -1) + return codepage; + if (codepage == 0) { + codepage = 65536 + (cpi - cp_list); + goto break_break; + } + + if (GetCPInfo(codepage, &cpinfo) != 0) + goto break_break; + } + if (tolower((unsigned char)*s++) != tolower((unsigned char)*d++)) + break; + } } - if (cp_name && *cp_name) - for (cpi = cp_list; cpi->name; cpi++) { - s = cp_name; - d = cpi->name; - for (;;) { - while (*s && !isalnum(*s) && *s != ':') - s++; - while (*d && !isalnum(*d) && *d != ':') - d++; - if (*s == 0) { - codepage = cpi->codepage; - if (codepage == CP_UTF8) - goto break_break; - if (codepage == -1) - return codepage; - if (codepage == 0) { - codepage = 65536 + (cpi - cp_list); - goto break_break; - } - - if (GetCPInfo(codepage, &cpinfo) != 0) - goto break_break; - } - if (tolower(*s++) != tolower(*d++)) - break; - } - } + d = cp_name; + if (tolower((unsigned char)d[0]) == 'c' && + tolower((unsigned char)d[1]) == 'p') + d += 2; + if (tolower((unsigned char)d[0]) == 'i' && + tolower((unsigned char)d[1]) == 'b' && + tolower((unsigned char)d[2]) == 'm') + d += 3; + for (s = d; *s >= '0' && *s <= '9'; s++); + if (*s == 0 && s != d) + codepage = atoi(d); /* CP999 or IBM999 */ - if (cp_name && *cp_name) { - d = cp_name; - if (tolower(d[0]) == 'c' && tolower(d[1]) == 'p') - d += 2; - if (tolower(d[0]) == 'i' && tolower(d[1]) == 'b' - && tolower(d[2]) == 'm') - d += 3; - for (s = d; *s >= '0' && *s <= '9'; s++); - if (*s == 0 && s != d) - codepage = atoi(d); /* CP999 or IBM999 */ - - if (codepage == CP_ACP) - codepage = GetACP(); - if (codepage == CP_OEMCP) - codepage = GetOEMCP(); - if (codepage > 65535) - codepage = -2; - } + if (codepage == CP_ACP) + codepage = GetACP(); + if (codepage == CP_OEMCP) + codepage = GetOEMCP(); + if (codepage > 65535) + codepage = -2; break_break:; if (codepage != -1) { @@ -1201,7 +1164,7 @@ void get_unitab(int codepage, wchar_t * unitab, int ftype) } } -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { @@ -1239,7 +1202,7 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, mbstr, mblen, defchr, defused); } -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { return MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen); diff --git a/contrib/putty/WINDOWS/WINUTILS.C b/contrib/putty/WINDOWS/WINUTILS.C index 2daf1eb..29e65bc 100644 --- a/contrib/putty/WINDOWS/WINUTILS.C +++ b/contrib/putty/WINDOWS/WINUTILS.C @@ -151,12 +151,31 @@ void pgp_fingerprints(void) } /* - * Split a complete command line into argc/argv, attempting to do - * it exactly the same way Windows itself would do it (so that - * console utilities, which receive argc and argv from Windows, - * will have their command lines processed in the same way as GUI - * utilities which get a whole command line and must break it - * themselves). + * Handy wrapper around GetDlgItemText which doesn't make you invent + * an arbitrary length limit on the output string. Returned string is + * dynamically allocated; caller must free. + */ +char *GetDlgItemText_alloc(HWND hwnd, int id) +{ + char *ret = NULL; + int size = 0; + + do { + size = size * 4 / 3 + 512; + ret = sresize(ret, size, char); + GetDlgItemText(hwnd, id, ret, size); + } while (!memchr(ret, '\0', size-1)); + + return ret; +} + +/* + * Split a complete command line into argc/argv, attempting to do it + * exactly the same way the Visual Studio C library would do it (so + * that our console utilities, which receive argc and argv already + * broken apart by the C library, will have their command lines + * processed in the same way as the GUI utilities which get a whole + * command line and must call this function). * * Does not modify the input command line. * @@ -177,7 +196,17 @@ void split_into_argv(char *cmdline, int *argc, char ***argv, int outputargc; /* - * At first glance the rules appeared to be: + * These argument-breaking rules apply to Visual Studio 7, which + * is currently the compiler expected to be used for PuTTY. Visual + * Studio 10 has different rules, lacking the curious mod 3 + * behaviour of consecutive quotes described below; I presume they + * fixed a bug. As and when we migrate to a newer compiler, we'll + * have to adjust this to match; however, for the moment we + * faithfully imitate in our GUI utilities what our CLI utilities + * can't be prevented from doing. + * + * When I investigated this, at first glance the rules appeared to + * be: * * - Single quotes are not special characters. * diff --git a/contrib/putty/WINDOWS/WINX11.C b/contrib/putty/WINDOWS/WINX11.C index c8951b0..630fac7 100644 --- a/contrib/putty/WINDOWS/WINX11.C +++ b/contrib/putty/WINDOWS/WINX11.C @@ -9,10 +9,11 @@ #include "putty.h" #include "ssh.h" -void platform_get_x11_auth(struct X11Display *disp, const Config *cfg) +void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { - if (cfg->xauthfile.path[0]) - x11_get_auth_from_authfile(disp, cfg->xauthfile.path); + char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path; + if (xauthpath[0]) + x11_get_auth_from_authfile(disp, xauthpath); } const int platform_uses_x11_unix_by_default = FALSE; diff --git a/contrib/putty/WINDOWS/WIN_RES.RC2 b/contrib/putty/WINDOWS/WIN_RES.RC2 index e159c56..4cfaa92 100644 --- a/contrib/putty/WINDOWS/WIN_RES.RC2 +++ b/contrib/putty/WINDOWS/WIN_RES.RC2 @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/contrib/putty/X11FWD.C b/contrib/putty/X11FWD.C index 9f22a23..70f426a 100644 --- a/contrib/putty/X11FWD.C +++ b/contrib/putty/X11FWD.C @@ -68,8 +68,7 @@ static const struct plug_function_table dummy_plug = { dummy_plug_sent, dummy_plug_accepting }; -struct X11Display *x11_setup_display(char *display, int authtype, - const Config *cfg) +struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf) { struct X11Display *disp = snew(struct X11Display); char *localcopy; @@ -166,12 +165,13 @@ struct X11Display *x11_setup_display(char *display, int authtype, disp->port = 6000 + disp->displaynum; disp->addr = name_lookup(disp->hostname, disp->port, - &disp->realhost, cfg, ADDRTYPE_UNSPEC); + &disp->realhost, conf, ADDRTYPE_UNSPEC); if ((err = sk_addr_error(disp->addr)) != NULL) { sk_addr_free(disp->addr); sfree(disp->hostname); sfree(disp->unixsocketpath); + sfree(disp); return NULL; /* FIXME: report an error */ } } @@ -249,7 +249,7 @@ struct X11Display *x11_setup_display(char *display, int authtype, disp->localauthproto = X11_NO_AUTH; disp->localauthdata = NULL; disp->localauthdatalen = 0; - platform_get_x11_auth(disp, cfg); + platform_get_x11_auth(disp, conf); return disp; } @@ -265,10 +265,10 @@ void x11_free_display(struct X11Display *disp) sfree(disp->hostname); sfree(disp->unixsocketpath); if (disp->localauthdata) - memset(disp->localauthdata, 0, disp->localauthdatalen); + smemclr(disp->localauthdata, disp->localauthdatalen); sfree(disp->localauthdata); if (disp->remoteauthdata) - memset(disp->remoteauthdata, 0, disp->remoteauthdatalen); + smemclr(disp->remoteauthdata, disp->remoteauthdatalen); sfree(disp->remoteauthdata); sfree(disp->remoteauthprotoname); sfree(disp->remoteauthdatastring); @@ -344,7 +344,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp, int len[4]; int family, protocol; int ideal_match = FALSE; - char *ourhostname = get_hostname(); + char *ourhostname; /* * Normally we should look for precisely the details specified in @@ -373,6 +373,8 @@ void x11_get_auth_from_authfile(struct X11Display *disp, if (!authfp) return; + ourhostname = get_hostname(); + /* Records in .Xauthority contain four strings of up to 64K each */ buf = snewn(65537 * 4, char); @@ -488,7 +490,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp, done: fclose(authfp); - memset(buf, 0, 65537 * 4); + smemclr(buf, 65537 * 4); sfree(buf); sfree(ourhostname); } @@ -504,13 +506,20 @@ static int x11_closing(Plug plug, const char *error_msg, int error_code, { struct X11Private *pr = (struct X11Private *) plug; - /* - * We have no way to communicate down the forwarded connection, - * so if an error occurred on the socket, we just ignore it - * and treat it like a proper close. - */ - sshfwd_close(pr->c); - x11_close(pr->s); + if (error_msg) { + /* + * Socket error. Slam the connection instantly shut. + */ + sshfwd_unclean_close(pr->c); + } else { + /* + * Ordinary EOF received on socket. Send an EOF on the SSH + * channel. + */ + if (pr->c) + sshfwd_write_eof(pr->c); + } + return 1; } @@ -558,8 +567,7 @@ int x11_get_screen_number(char *display) * also, fills the SocketsStructure */ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c, - const char *peeraddr, int peerport, - const Config *cfg) + const char *peeraddr, int peerport, Conf *conf) { static const struct plug_function_table fn_table = { x11_log, @@ -586,7 +594,7 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c, pr->s = *s = new_connection(sk_addr_dup(disp->addr), disp->realhost, disp->port, - 0, 1, 0, 0, (Plug) pr, cfg); + 0, 1, 0, 0, (Plug) pr, conf); if ((err = sk_socket_error(*s)) != NULL) { sfree(pr); return err; @@ -723,8 +731,7 @@ int x11_send(Socket s, char *data, int len) memset(reply + 8, 0, msgsize); memcpy(reply + 8, message, msglen); sshfwd_write(pr->c, (char *)reply, 8 + msgsize); - sshfwd_close(pr->c); - x11_close(s); + sshfwd_write_eof(pr->c); sfree(reply); sfree(message); return 0; @@ -789,3 +796,8 @@ int x11_send(Socket s, char *data, int len) return sk_write(s, data, len); } + +void x11_send_eof(Socket s) +{ + sk_write_eof(s); +} diff --git a/putty/BUILDSCR.CV b/putty/BUILDSCR.CV new file mode 100644 index 0000000..71fb5c4 --- /dev/null +++ b/putty/BUILDSCR.CV @@ -0,0 +1,39 @@ +# -*- sh -*- + +# Build script to scan PuTTY with the downloadable Coverity scanner +# and generate a tar file to upload to their open-source scanning +# service. + +module putty + +# Preparations. +in putty do ./mkfiles.pl +in putty do ./mkauto.sh +in putty/doc do make + +# Scan the Unix build, on a 64-bit system to differentiate as much as +# possible from the other scan of the cross-platform files. +delegate covscan64 + in putty do ./configure + in putty do cov-build --dir cov-int make + in putty do tar czvf cov-int.tar.gz cov-int + return putty/cov-int.tar.gz +enddelegate + +# Scan the Windows build, by means of building with Winelib (since as +# of 2013-07-22, the Coverity Scan website doesn't offer a 32-bit +# Windows scanner for download). +delegate covscan32wine + in putty do tar xzvf cov-int.tar.gz + in putty/windows do cov-build --dir ../cov-int make -f Makefile.cyg CC=winegcc RC=wrc + in putty do tar czvf cov-int.tar.gz cov-int + return putty/cov-int.tar.gz +enddelegate + +# Provide the revision number as one of the build outputs, to make it +# easy to construct a curl upload command which will annotate it +# appropriately when uploaded. +in putty do echo $(revision) > revision.txt + +deliver putty/revision.txt $@ +deliver putty/cov-int.tar.gz $@ diff --git a/putty/CHARSET/CHARSET.H b/putty/CHARSET/CHARSET.H index 3f7eb34..85572c6 100644 --- a/putty/CHARSET/CHARSET.H +++ b/putty/CHARSET/CHARSET.H @@ -32,6 +32,7 @@ typedef enum { CS_ISO8859_16, CS_CP437, CS_CP850, + CS_CP852, CS_CP866, CS_CP1250, CS_CP1251, @@ -98,7 +99,8 @@ typedef struct { * U+FFFD (REPLACEMENT CHARACTER). */ -int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, +int charset_to_unicode(const char **input, int *inlen, + wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen); @@ -121,7 +123,8 @@ int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, * output charset). */ -int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen, +int charset_from_unicode(const wchar_t **input, int *inlen, + char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen); diff --git a/putty/CHARSET/FROMUCS.C b/putty/CHARSET/FROMUCS.C index ce69cd7..cdd1b6b 100644 --- a/putty/CHARSET/FROMUCS.C +++ b/putty/CHARSET/FROMUCS.C @@ -40,7 +40,8 @@ static void charset_emit(void *ctx, long int output) } } -int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen, +int charset_from_unicode(const wchar_t **input, int *inlen, + char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen) { diff --git a/putty/CHARSET/LOCALENC.C b/putty/CHARSET/LOCALENC.C index 9e51f72..99c93d6 100644 --- a/putty/CHARSET/LOCALENC.C +++ b/putty/CHARSET/LOCALENC.C @@ -21,6 +21,7 @@ static const struct { int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "", CS_NONE, 0 }, + { "UTF-8", CS_UTF8, 1 }, { "ISO-8859-1", CS_ISO8859_1, 1 }, { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 }, { "ISO-8859-2", CS_ISO8859_2, 1 }, @@ -39,6 +40,7 @@ static const struct { { "ISO-8859-16", CS_ISO8859_16, 1 }, { "CP437", CS_CP437, 1 }, { "CP850", CS_CP850, 1 }, + { "CP852", CS_CP852, 1 }, { "CP866", CS_CP866, 1 }, { "CP1250", CS_CP1250, 1 }, { "CP1251", CS_CP1251, 1 }, @@ -74,7 +76,6 @@ static const struct { { "VISCII", CS_VISCII, 1 }, { "HP ROMAN8", CS_HP_ROMAN8, 1 }, { "DEC MCS", CS_DEC_MCS, 1 }, - { "UTF-8", CS_UTF8, 1 }, }; const char *charset_to_localenc(int charset) diff --git a/putty/CHARSET/MIMEENC.C b/putty/CHARSET/MIMEENC.C index 27b860a..788d8ee 100644 --- a/putty/CHARSET/MIMEENC.C +++ b/putty/CHARSET/MIMEENC.C @@ -135,6 +135,11 @@ static const struct { { "850", CS_CP850 }, { "csPC850Multilingual", CS_CP850 }, + { "IBM852", CS_CP852 }, + { "cp852", CS_CP852 }, + { "852", CS_CP852 }, + { "csIBM852", CS_CP852 }, + { "IBM866", CS_CP866 }, { "cp866", CS_CP866 }, { "866", CS_CP866 }, diff --git a/putty/CHARSET/SBCS.DAT b/putty/CHARSET/SBCS.DAT index 2b919a4..4bf300f 100644 --- a/putty/CHARSET/SBCS.DAT +++ b/putty/CHARSET/SBCS.DAT @@ -394,6 +394,28 @@ charset CS_CP866 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0 + Another old DOS code page, submitted by a user and checked against + the translation table at + http://msdn.microsoft.com/en-us/goglobal/cc305161.aspx . + +charset CS_CP852 +0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f +0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f +0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f +0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f +0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f +0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f +0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f +0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f +00c7 00fc 00e9 00e2 00e4 016f 0107 00e7 0142 00eb 0150 0151 00ee 0179 00c4 0106 +00c9 0139 013a 00f4 00f6 013d 013e 015a 015b 00d6 00dc 0164 0165 0141 00d7 010d +00e1 00ed 00f3 00fa 0104 0105 017d 017e 0118 0119 00ac 017a 010c 015f 00ab 00bb +2591 2592 2593 2502 2524 00c1 00c2 011a 015e 2563 2551 2557 255d 017b 017c 2510 +2514 2534 252c 251c 2500 253c 0102 0103 255a 2554 2569 2566 2560 2550 256c 00a4 +0111 0110 010e 00cb 010f 0147 00cd 00ce 011b 2518 250c 2588 2584 0162 016e 2580 +00d3 00df 00d4 0143 0144 0148 0160 0161 0154 00da 0155 0170 00fd 00dd 0163 00b4 +00ad 02dd 02db 02c7 02d8 00a7 00f7 00b8 00b0 00a8 02d9 0171 0158 0159 25a0 00a0 + Here are some Windows code pages, generated by this piece of Bourne shell: diff --git a/putty/CHARSET/SBCSDAT.C b/putty/CHARSET/SBCSDAT.C index 664bcd5..d1510de 100644 --- a/putty/CHARSET/SBCSDAT.C +++ b/putty/CHARSET/SBCSDAT.C @@ -1425,6 +1425,81 @@ const charset_spec charset_CS_CP866 = { CS_CP866, read_sbcs, write_sbcs, &data_CS_CP866 }; +static const sbcs_data data_CS_CP852 = { + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x016f, 0x0107, 0x00e7, + 0x0142, 0x00eb, 0x0150, 0x0151, 0x00ee, 0x0179, 0x00c4, 0x0106, + 0x00c9, 0x0139, 0x013a, 0x00f4, 0x00f6, 0x013d, 0x013e, 0x015a, + 0x015b, 0x00d6, 0x00dc, 0x0164, 0x0165, 0x0141, 0x00d7, 0x010d, + 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x0104, 0x0105, 0x017d, 0x017e, + 0x0118, 0x0119, 0x00ac, 0x017a, 0x010c, 0x015f, 0x00ab, 0x00bb, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x011a, + 0x015e, 0x2563, 0x2551, 0x2557, 0x255d, 0x017b, 0x017c, 0x2510, + 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x0102, 0x0103, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, + 0x0111, 0x0110, 0x010e, 0x00cb, 0x010f, 0x0147, 0x00cd, 0x00ce, + 0x011b, 0x2518, 0x250c, 0x2588, 0x2584, 0x0162, 0x016e, 0x2580, + 0x00d3, 0x00df, 0x00d4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00da, 0x0155, 0x0170, 0x00fd, 0x00dd, 0x0163, 0x00b4, + 0x00ad, 0x02dd, 0x02db, 0x02c7, 0x02d8, 0x00a7, 0x00f7, 0x00b8, + 0x00b0, 0x00a8, 0x02d9, 0x0171, 0x0158, 0x0159, 0x25a0, 0x00a0 + }, + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0xff, 0xcf, 0xf5, 0xf9, 0xae, 0xaa, 0xf0, 0xf8, + 0xef, 0xf7, 0xaf, 0xb5, 0xb6, 0x8e, 0x80, 0x90, + 0xd3, 0xd6, 0xd7, 0xe0, 0xe2, 0x99, 0x9e, 0xe9, + 0x9a, 0xed, 0xe1, 0xa0, 0x83, 0x84, 0x87, 0x82, + 0x89, 0xa1, 0x8c, 0xa2, 0x93, 0x94, 0xf6, 0xa3, + 0x81, 0xec, 0xc6, 0xc7, 0xa4, 0xa5, 0x8f, 0x86, + 0xac, 0x9f, 0xd2, 0xd4, 0xd1, 0xd0, 0xa8, 0xa9, + 0xb7, 0xd8, 0x91, 0x92, 0x95, 0x96, 0x9d, 0x88, + 0xe3, 0xe4, 0xd5, 0xe5, 0x8a, 0x8b, 0xe8, 0xea, + 0xfc, 0xfd, 0x97, 0x98, 0xb8, 0xad, 0xe6, 0xe7, + 0xdd, 0xee, 0x9b, 0x9c, 0xde, 0x85, 0xeb, 0xfb, + 0x8d, 0xab, 0xbd, 0xbe, 0xa6, 0xa7, 0xf3, 0xf4, + 0xfa, 0xf2, 0xf1, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, + 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, + 0xc9, 0xbb, 0xc8, 0xbc, 0xcc, 0xb9, 0xcb, 0xca, + 0xce, 0xdf, 0xdc, 0xdb, 0xb0, 0xb1, 0xb2, 0xfe + }, + 256 +}; +const charset_spec charset_CS_CP852 = { + CS_CP852, read_sbcs, write_sbcs, &data_CS_CP852 +}; + static const sbcs_data data_CS_CP1250 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, @@ -3980,6 +4055,7 @@ ENUM_CHARSET(CS_ISO8859_1_X11) ENUM_CHARSET(CS_CP437) ENUM_CHARSET(CS_CP850) ENUM_CHARSET(CS_CP866) +ENUM_CHARSET(CS_CP852) ENUM_CHARSET(CS_CP1250) ENUM_CHARSET(CS_CP1251) ENUM_CHARSET(CS_CP1252) diff --git a/putty/CHARSET/TOUCS.C b/putty/CHARSET/TOUCS.C index 658eb73..e3608c0 100644 --- a/putty/CHARSET/TOUCS.C +++ b/putty/CHARSET/TOUCS.C @@ -46,7 +46,8 @@ static void unicode_emit(void *ctx, long int output) } } -int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen, +int charset_to_unicode(const char **input, int *inlen, + wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen) { diff --git a/putty/CHARSET/XENC.C b/putty/CHARSET/XENC.C index 2832f31..53e8a5e 100644 --- a/putty/CHARSET/XENC.C +++ b/putty/CHARSET/XENC.C @@ -46,6 +46,7 @@ static const struct { { "koi8-u", CS_KOI8_U }, { "ibm-cp437", CS_CP437 }, { "ibm-cp850", CS_CP850 }, + { "ibm-cp852", CS_CP852 }, { "ibm-cp866", CS_CP866 }, { "microsoft-cp1250", CS_CP1250 }, { "microsoft-cp1251", CS_CP1251 }, diff --git a/putty/CMDGEN.C b/putty/CMDGEN.C index 5d9efcf..e254c03 100644 --- a/putty/CMDGEN.C +++ b/putty/CMDGEN.C @@ -102,6 +102,16 @@ void modalfatalbox(char *p, ...) cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); +} + /* * Stubs to let everything else link sensibly. */ @@ -118,10 +128,7 @@ void sk_cleanup(void) void showversion(void) { - char *verstr = dupstr(ver); - verstr[0] = tolower((unsigned char)verstr[0]); - printf("PuTTYgen %s\n", verstr); - sfree(verstr); + printf("puttygen: %s\n", ver); } void usage(int standalone) @@ -257,12 +264,11 @@ static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen) int main(int argc, char **argv) { char *infile = NULL; - Filename infilename; + Filename *infilename = NULL, *outfilename = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; - Filename outfilename; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE; - int bits = 1024; + int bits = 2048; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; @@ -536,7 +542,7 @@ int main(int argc, char **argv) if (infile) { infilename = filename_from_str(infile); - intype = key_type(&infilename); + intype = key_type(infilename); switch (intype) { /* @@ -668,7 +674,7 @@ int main(int argc, char **argv) return 1; } random_add_heavynoise(entropy, bits / 8); - memset(entropy, 0, bits/8); + smemclr(entropy, bits/8); sfree(entropy); if (keytype == DSA) { @@ -707,11 +713,11 @@ int main(int argc, char **argv) * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) - encrypted = rsakey_encrypted(&infilename, &origcomment); + encrypted = rsakey_encrypted(infilename, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) - encrypted = ssh2_userkey_encrypted(&infilename, &origcomment); + encrypted = ssh2_userkey_encrypted(infilename, &origcomment); else - encrypted = import_encrypted(&infilename, intype, &origcomment); + encrypted = import_encrypted(infilename, intype, &origcomment); /* * If so, ask for a passphrase. @@ -721,7 +727,7 @@ int main(int argc, char **argv) int ret; p->to_server = FALSE; p->name = dupstr("SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { @@ -746,7 +752,7 @@ int main(int argc, char **argv) unsigned char *blob; int n, l, bloblen; - ret = rsakey_pubblob(&infilename, &vblob, &bloblen, + ret = rsakey_pubblob(infilename, &vblob, &bloblen, &origcomment, &error); blob = (unsigned char *)vblob; @@ -767,8 +773,11 @@ int main(int argc, char **argv) } ssh1key->comment = dupstr(origcomment); ssh1key->private_exponent = NULL; + ssh1key->p = NULL; + ssh1key->q = NULL; + ssh1key->iqmp = NULL; } else { - ret = loadrsakey(&infilename, ssh1key, passphrase, &error); + ret = loadrsakey(infilename, ssh1key, passphrase, &error); } if (ret > 0) error = NULL; @@ -778,15 +787,17 @@ int main(int argc, char **argv) case SSH_KEYTYPE_SSH2: if (!load_encrypted) { - ssh2blob = ssh2_userkey_loadpub(&infilename, &ssh2alg, + ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg, &ssh2bloblen, NULL, &error); - ssh2algf = find_pubkey_alg(ssh2alg); - if (ssh2algf) - bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); - else - bits = -1; + if (ssh2blob) { + ssh2algf = find_pubkey_alg(ssh2alg); + if (ssh2algf) + bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); + else + bits = -1; + } } else { - ssh2key = ssh2_load_userkey(&infilename, passphrase, &error); + ssh2key = ssh2_load_userkey(infilename, passphrase, &error); } if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; @@ -800,7 +811,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: - ssh2key = import_ssh2(&infilename, intype, passphrase, &error); + ssh2key = import_ssh2(infilename, intype, passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; @@ -846,8 +857,8 @@ int main(int argc, char **argv) p->to_server = FALSE; p->name = dupstr("New SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE, 512); - add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE); + add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { @@ -861,7 +872,7 @@ int main(int argc, char **argv) return 1; } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } passphrase = dupstr(p->prompts[0]->result); @@ -892,14 +903,14 @@ int main(int argc, char **argv) case PRIVATE: if (sshver == 1) { assert(ssh1key); - ret = saversakey(&outfilename, ssh1key, passphrase); + ret = saversakey(outfilename, ssh1key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-1 private key\n"); return 1; } } else { assert(ssh2key); - ret = ssh2_save_userkey(&outfilename, ssh2key, passphrase); + ret = ssh2_save_userkey(outfilename, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); return 1; @@ -1023,7 +1034,7 @@ int main(int argc, char **argv) case SSHCOM: assert(sshver == 2); assert(ssh2key); - ret = export_ssh2(&outfilename, outtype, ssh2key, passphrase); + ret = export_ssh2(outfilename, outtype, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); return 1; @@ -1036,7 +1047,7 @@ int main(int argc, char **argv) } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } diff --git a/putty/CMDLINE.C b/putty/CMDLINE.C index aa376a0..4a33500 100644 --- a/putty/CMDLINE.C +++ b/putty/CMDLINE.C @@ -63,7 +63,7 @@ void cmdline_cleanup(void) int pri; if (cmdline_password) { - memset(cmdline_password, 0, strlen(cmdline_password)); + smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; } @@ -105,15 +105,12 @@ int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) { if (tried_once) return 0; - strncpy(p->prompts[0]->result, cmdline_password, - p->prompts[0]->result_len); - p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0'; - memset(cmdline_password, 0, strlen(cmdline_password)); + prompt_set_result(p->prompts[0], cmdline_password); + smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; tried_once = 1; return 1; - } /* @@ -162,7 +159,7 @@ static int cmdline_check_unavailable(int flag, char *p) if (need_save < 0) return x; \ } while (0) -int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) +int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) { int ret = 0; @@ -170,7 +167,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(2); /* This parameter must be processed immediately rather than being * saved. */ - do_defaults(value, cfg); + do_defaults(value, conf); loaded_session = TRUE; cmdline_session_name = dupstr(value); return 2; @@ -179,41 +176,49 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_SSH; - default_port = cfg->port = 22; + default_protocol = PROT_SSH; + default_port = 22; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-telnet")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_TELNET; - default_port = cfg->port = 23; + default_protocol = PROT_TELNET; + default_port = 23; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-rlogin")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_RLOGIN; - default_port = cfg->port = 513; + default_protocol = PROT_RLOGIN; + default_port = 513; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); return 1; } if (!strcmp(p, "-raw")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_RAW; + default_protocol = PROT_RAW; + conf_set_int(conf, CONF_protocol, default_protocol); } if (!strcmp(p, "-serial")) { RETURN(1); /* Serial is not NONNETWORK in an odd sense of the word */ UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - default_protocol = cfg->protocol = PROT_SERIAL; - /* The host parameter will already be loaded into cfg->host, so copy it across */ - strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1); - cfg->serline[sizeof(cfg->serline) - 1] = '\0'; + default_protocol = PROT_SERIAL; + conf_set_int(conf, CONF_protocol, default_protocol); + /* The host parameter will already be loaded into CONF_host, + * so copy it across */ + conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host)); } if (!strcmp(p, "-v")) { RETURN(1); @@ -223,41 +228,23 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - strncpy(cfg->username, value, sizeof(cfg->username)); - cfg->username[sizeof(cfg->username) - 1] = '\0'; + conf_set_str(conf, CONF_username, value); } if (!strcmp(p, "-loghost")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - strncpy(cfg->loghost, value, sizeof(cfg->loghost)); - cfg->loghost[sizeof(cfg->loghost) - 1] = '\0'; + conf_set_str(conf, CONF_loghost, value); } if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { - char *fwd, *ptr, *q, *qq; - int dynamic, i=0; + char type, *q, *qq, *key, *val; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - dynamic = !strcmp(p, "-D"); - fwd = value; - ptr = cfg->portfwd; - /* if existing forwards, find end of list */ - while (*ptr) { - while (*ptr) - ptr++; - ptr++; - } - i = ptr - cfg->portfwd; - ptr[0] = p[1]; /* insert a 'L', 'R' or 'D' at the start */ - ptr++; - if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) { - cmdline_error("out of space for port forwardings"); - return ret; - } - strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2); - if (!dynamic) { + if (strcmp(p, "-D")) { /* + * For -L or -R forwarding types: + * * We expect _at least_ two colons in this string. The * possible formats are `sourceport:desthost:destport', * or `sourceip:sourceport:desthost:destport' if you're @@ -265,19 +252,47 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) * replace the one between source and dest with a \t; * this means we must find the second-to-last colon in * the string. + * + * (This looks like a foolish way of doing it given the + * existence of strrchr, but it's more efficient than + * two strrchrs - not to mention that the second strrchr + * would require us to modify the input string!) */ - q = qq = strchr(ptr, ':'); + + type = p[1]; /* 'L' or 'R' */ + + q = qq = strchr(value, ':'); while (qq) { char *qqq = strchr(qq+1, ':'); if (qqq) q = qq; qq = qqq; } - if (q) *q = '\t'; /* replace second-last colon with \t */ + + if (!q) { + cmdline_error("-%c expects at least two colons in its" + " argument", type); + return ret; + } + + key = dupprintf("%c%.*s", type, q - value, value); + val = dupstr(q+1); + } else { + /* + * Dynamic port forwardings are entered under the same key + * as if they were local (because they occupy the same + * port space - a local and a dynamic forwarding on the + * same local port are mutually exclusive), with the + * special value "D" (which can be distinguished from + * anything in the ordinary -L case by containing no + * colon). + */ + key = dupprintf("L%s", value); + val = dupstr("D"); } - cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0'; - cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0'; - ptr[strlen(ptr)+1] = '\000'; /* append 2nd '\000' */ + conf_set_str_str(conf, CONF_portfwd, key, val); + sfree(key); + sfree(val); } if ((!strcmp(p, "-nc"))) { char *host, *portp; @@ -286,20 +301,16 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - host = portp = value; - while (*portp && *portp != ':') - portp++; - if (*portp) { - unsigned len = portp - host; - if (len >= sizeof(cfg->ssh_nc_host)) - len = sizeof(cfg->ssh_nc_host) - 1; - memcpy(cfg->ssh_nc_host, value, len); - cfg->ssh_nc_host[len] = '\0'; - cfg->ssh_nc_port = atoi(portp+1); - } else { + portp = strchr(value, ':'); + if (!portp) { cmdline_error("-nc expects argument of form 'host:port'"); return ret; } + + host = dupprintf("%.*s", portp - value, value); + conf_set_str(conf, CONF_ssh_nc_host, host); + conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1)); + sfree(host); } if (!strcmp(p, "-m")) { char *filename, *command; @@ -317,8 +328,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) command = NULL; fp = fopen(filename, "r"); if (!fp) { - cmdline_error("unable to open command " - "file \"%s\"", filename); + cmdline_error("unable to open command file \"%s\"", filename); return ret; } do { @@ -332,16 +342,17 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) } command[cmdlen++] = d; } while (c != EOF); - cfg->remote_cmd_ptr = command; - cfg->remote_cmd_ptr2 = NULL; - cfg->nopty = TRUE; /* command => no terminal */ fclose(fp); + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no terminal */ + sfree(command); } if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -ssh,-telnet */ - cfg->port = atoi(value); + conf_set_int(conf, CONF_port, atoi(value)); } if (!strcmp(p, "-pw")) { RETURN(2); @@ -349,7 +360,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) 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) + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) cmdline_error("the -pw option can only be used with the " "SSH protocol"); else { @@ -357,7 +368,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) /* 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)); + smemclr(value, strlen(value)); } } @@ -366,105 +377,108 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->tryagent = TRUE; + conf_set_int(conf, CONF_tryagent, TRUE); } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->tryagent = FALSE; + conf_set_int(conf, CONF_tryagent, FALSE); } if (!strcmp(p, "-A")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->agentfwd = 1; + conf_set_int(conf, CONF_agentfwd, 1); } if (!strcmp(p, "-a")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->agentfwd = 0; + conf_set_int(conf, CONF_agentfwd, 0); } if (!strcmp(p, "-X")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->x11_forward = 1; + conf_set_int(conf, CONF_x11_forward, 1); } if (!strcmp(p, "-x")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->x11_forward = 0; + conf_set_int(conf, CONF_x11_forward, 0); } if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -m */ - cfg->nopty = 0; + conf_set_int(conf, CONF_nopty, 0); } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); - cfg->nopty = 1; + conf_set_int(conf, CONF_nopty, 1); } if (!strcmp(p, "-N")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->ssh_no_shell = 1; + conf_set_int(conf, CONF_ssh_no_shell, 1); } if (!strcmp(p, "-C")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->compression = 1; + conf_set_int(conf, CONF_compression, 1); } if (!strcmp(p, "-1")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->sshprot = 0; /* ssh protocol 1 only */ + conf_set_int(conf, CONF_sshprot, 0); /* ssh protocol 1 only */ } if (!strcmp(p, "-2")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->sshprot = 3; /* ssh protocol 2 only */ + conf_set_int(conf, CONF_sshprot, 3); /* ssh protocol 2 only */ } if (!strcmp(p, "-i")) { + Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - cfg->keyfile = filename_from_str(value); + fn = filename_from_str(value); + conf_set_filename(conf, CONF_keyfile, fn); + filename_free(fn); } if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) { RETURN(1); SAVEABLE(1); - cfg->addressfamily = ADDRTYPE_IPV4; + conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4); } if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) { RETURN(1); SAVEABLE(1); - cfg->addressfamily = ADDRTYPE_IPV6; + conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6); } if (!strcmp(p, "-sercfg")) { char* nextitem; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); - if (cfg->protocol != PROT_SERIAL) + if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL) cmdline_error("the -sercfg option can only be used with the " "serial protocol"); /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */ @@ -483,55 +497,45 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) if (length == 1) { switch (*nextitem) { case '1': - cfg->serstopbits = 2; - break; case '2': - cfg->serstopbits = 4; + conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0')); break; case '5': - cfg->serdatabits = 5; - break; case '6': - cfg->serdatabits = 6; - break; case '7': - cfg->serdatabits = 7; - break; case '8': - cfg->serdatabits = 8; - break; case '9': - cfg->serdatabits = 9; + conf_set_int(conf, CONF_serdatabits, *nextitem-'0'); break; case 'n': - cfg->serparity = SER_PAR_NONE; + conf_set_int(conf, CONF_serparity, SER_PAR_NONE); break; case 'o': - cfg->serparity = SER_PAR_ODD; + conf_set_int(conf, CONF_serparity, SER_PAR_ODD); break; case 'e': - cfg->serparity = SER_PAR_EVEN; + conf_set_int(conf, CONF_serparity, SER_PAR_EVEN); break; case 'm': - cfg->serparity = SER_PAR_MARK; + conf_set_int(conf, CONF_serparity, SER_PAR_MARK); break; case 's': - cfg->serparity = SER_PAR_SPACE; + conf_set_int(conf, CONF_serparity, SER_PAR_SPACE); break; case 'N': - cfg->serflow = SER_FLOW_NONE; + conf_set_int(conf, CONF_serflow, SER_FLOW_NONE); break; case 'X': - cfg->serflow = SER_FLOW_XONXOFF; + conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF); break; case 'R': - cfg->serflow = SER_FLOW_RTSCTS; + conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS); break; case 'D': - cfg->serflow = SER_FLOW_DSRDTR; + conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR); break; default: @@ -540,11 +544,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) } } else if (length == 3 && !strncmp(nextitem,"1.5",3)) { /* Messy special case */ - cfg->serstopbits = 3; + conf_set_int(conf, CONF_serstopbits, 3); } else { int serspeed = atoi(nextitem); if (serspeed != 0) { - cfg->serspeed = serspeed; + conf_set_int(conf, CONF_serspeed, serspeed); } else { cmdline_error("Unrecognised suboption \"-sercfg %s\"", nextitem); @@ -556,11 +560,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) return ret; /* unrecognised */ } -void cmdline_run_saved(Config *cfg) +void cmdline_run_saved(Conf *conf) { int pri, i; for (pri = 0; pri < NPRIORITIES; pri++) for (i = 0; i < saves[pri].nsaved; i++) cmdline_process_param(saves[pri].params[i].p, - saves[pri].params[i].value, 0, cfg); + saves[pri].params[i].value, 0, conf); } diff --git a/putty/CONF.C b/putty/CONF.C new file mode 100644 index 0000000..3f27205 --- /dev/null +++ b/putty/CONF.C @@ -0,0 +1,613 @@ +/* + * conf.c: implementation of the internal storage format used for + * the configuration of a PuTTY session. + */ + +#include +#include +#include + +#include "tree234.h" +#include "putty.h" + +/* + * Enumeration of types used in keys and values. + */ +typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type; + +/* + * Arrays which allow us to look up the subkey and value types for a + * given primary key id. + */ +#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype, +static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) }; +#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype, +static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) }; + +/* + * Configuration keys are primarily integers (big enum of all the + * different configurable options); some keys have string-designated + * subkeys, such as the list of environment variables (subkeys + * defined by the variable names); some have integer-designated + * subkeys (wordness, colours, preference lists). + */ +struct key { + int primary; + union { + int i; + char *s; + } secondary; +}; + +struct value { + union { + int intval; + char *stringval; + Filename *fileval; + FontSpec *fontval; + } u; +}; + +struct conf_entry { + struct key key; + struct value value; +}; + +struct conf_tag { + tree234 *tree; +}; + +/* + * Because 'struct key' is the first element in 'struct conf_entry', + * it's safe (guaranteed by the C standard) to cast arbitrarily back + * and forth between the two types. Therefore, we only need one + * comparison function, which can double as a main sort function for + * the tree (comparing two conf_entry structures with each other) + * and a search function (looking up an externally supplied key). + */ +static int conf_cmp(void *av, void *bv) +{ + struct key *a = (struct key *)av; + struct key *b = (struct key *)bv; + + if (a->primary < b->primary) + return -1; + else if (a->primary > b->primary) + return +1; + switch (subkeytypes[a->primary]) { + case TYPE_INT: + if (a->secondary.i < b->secondary.i) + return -1; + else if (a->secondary.i > b->secondary.i) + return +1; + return 0; + case TYPE_STR: + return strcmp(a->secondary.s, b->secondary.s); + default: + return 0; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct key'. We + * don't free the structure itself, since it's probably part of a + * larger allocated block. + */ +static void free_key(struct key *key) +{ + if (subkeytypes[key->primary] == TYPE_STR) + sfree(key->secondary.s); +} + +/* + * Copy a 'struct key' into another one, copying its dynamic data + * if necessary. + */ +static void copy_key(struct key *to, struct key *from) +{ + to->primary = from->primary; + switch (subkeytypes[to->primary]) { + case TYPE_INT: + to->secondary.i = from->secondary.i; + break; + case TYPE_STR: + to->secondary.s = dupstr(from->secondary.s); + break; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct value'. We + * don't free the value itself, since it's probably part of a larger + * allocated block. + */ +static void free_value(struct value *val, int type) +{ + if (type == TYPE_STR) + sfree(val->u.stringval); + else if (type == TYPE_FILENAME) + filename_free(val->u.fileval); + else if (type == TYPE_FONT) + fontspec_free(val->u.fontval); +} + +/* + * Copy a 'struct value' into another one, copying its dynamic data + * if necessary. + */ +static void copy_value(struct value *to, struct value *from, int type) +{ + switch (type) { + case TYPE_INT: + to->u.intval = from->u.intval; + break; + case TYPE_STR: + to->u.stringval = dupstr(from->u.stringval); + break; + case TYPE_FILENAME: + to->u.fileval = filename_copy(from->u.fileval); + break; + case TYPE_FONT: + to->u.fontval = fontspec_copy(from->u.fontval); + break; + } +} + +/* + * Free an entire 'struct conf_entry' and its dynamic data. + */ +static void free_entry(struct conf_entry *entry) +{ + free_key(&entry->key); + free_value(&entry->value, valuetypes[entry->key.primary]); + sfree(entry); +} + +Conf *conf_new(void) +{ + Conf *conf = snew(struct conf_tag); + + conf->tree = newtree234(conf_cmp); + + return conf; +} + +static void conf_clear(Conf *conf) +{ + struct conf_entry *entry; + + while ((entry = delpos234(conf->tree, 0)) != NULL) + free_entry(entry); +} + +void conf_free(Conf *conf) +{ + conf_clear(conf); + freetree234(conf->tree); + sfree(conf); +} + +static void conf_insert(Conf *conf, struct conf_entry *entry) +{ + struct conf_entry *oldentry = add234(conf->tree, entry); + if (oldentry && oldentry != entry) { + del234(conf->tree, oldentry); + free_entry(oldentry); + oldentry = add234(conf->tree, entry); + assert(oldentry == entry); + } +} + +void conf_copy_into(Conf *newconf, Conf *oldconf) +{ + struct conf_entry *entry, *entry2; + int i; + + conf_clear(newconf); + + for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) { + entry2 = snew(struct conf_entry); + copy_key(&entry2->key, &entry->key); + copy_value(&entry2->value, &entry->value, + valuetypes[entry->key.primary]); + add234(newconf->tree, entry2); + } +} + +Conf *conf_copy(Conf *oldconf) +{ + Conf *newconf = conf_new(); + + conf_copy_into(newconf, oldconf); + + return newconf; +} + +int conf_get_int(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +int conf_get_int_int(Conf *conf, int primary, int secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + key.secondary.i = secondary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +char *conf_get_str(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.stringval; +} + +char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + return entry ? entry->value.u.stringval : NULL; +} + +char *conf_get_str_str(Conf *conf, int primary, const char *secondary) +{ + char *ret = conf_get_str_str_opt(conf, primary, secondary); + assert(ret); + return ret; +} + +char *conf_get_str_strs(Conf *conf, int primary, + char *subkeyin, char **subkeyout) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + if (subkeyin) { + key.secondary.s = subkeyin; + entry = findrel234(conf->tree, &key, NULL, REL234_GT); + } else { + key.secondary.s = ""; + entry = findrel234(conf->tree, &key, NULL, REL234_GE); + } + if (!entry || entry->key.primary != primary) + return NULL; + *subkeyout = entry->key.secondary.s; + return entry->value.u.stringval; +} + +char *conf_get_str_nthstrkey(Conf *conf, int primary, int n) +{ + struct key key; + struct conf_entry *entry; + int index; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = ""; + entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index); + if (!entry || entry->key.primary != primary) + return NULL; + entry = index234(conf->tree, index + n); + if (!entry || entry->key.primary != primary) + return NULL; + return entry->key.secondary.s; +} + +Filename *conf_get_filename(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.fileval; +} + +FontSpec *conf_get_fontspec(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.fontval; +} + +void conf_set_int(Conf *conf, int primary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_int_int(Conf *conf, int primary, int secondary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->key.secondary.i = secondary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_str(Conf *conf, int primary, const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_set_str_str(Conf *conf, int primary, const char *secondary, + const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->key.secondary.s = dupstr(secondary); + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_del_str_str(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + if (entry) { + del234(conf->tree, entry); + free_entry(entry); + } + } + +void conf_set_filename(Conf *conf, int primary, const Filename *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + entry->key.primary = primary; + entry->value.u.fileval = filename_copy(value); + conf_insert(conf, entry); +} + +void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + entry->key.primary = primary; + entry->value.u.fontval = fontspec_copy(value); + conf_insert(conf, entry); +} + +int conf_serialised_size(Conf *conf) +{ + int i; + struct conf_entry *entry; + int size = 0; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + size += 4; /* primary key */ + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->key.secondary.s); + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->value.u.stringval); + break; + case TYPE_FILENAME: + size += filename_serialise(entry->value.u.fileval, NULL); + break; + case TYPE_FONT: + size += fontspec_serialise(entry->value.u.fontval, NULL); + break; + } + } + + size += 4; /* terminator value */ + + return size; +} + +void conf_serialise(Conf *conf, void *vdata) +{ + unsigned char *data = (unsigned char *)vdata; + int i, len; + struct conf_entry *entry; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + PUT_32BIT_MSB_FIRST(data, entry->key.primary); + data += 4; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->key.secondary.s); + memcpy(data, entry->key.secondary.s, len); + data += len; + *data++ = 0; + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->value.u.intval); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->value.u.stringval); + memcpy(data, entry->value.u.stringval, len); + data += len; + *data++ = 0; + break; + case TYPE_FILENAME: + data += filename_serialise(entry->value.u.fileval, data); + break; + case TYPE_FONT: + data += fontspec_serialise(entry->value.u.fontval, data); + break; + } + } + + PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU); +} + +int conf_deserialise(Conf *conf, void *vdata, int maxsize) +{ + unsigned char *data = (unsigned char *)vdata; + unsigned char *start = data; + struct conf_entry *entry; + unsigned primary; + int used; + unsigned char *zero; + + while (maxsize >= 4) { + primary = GET_32BIT_MSB_FIRST(data); + data += 4, maxsize -= 4; + + if (primary >= N_CONFIG_OPTIONS) + break; + + entry = snew(struct conf_entry); + entry->key.primary = primary; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + sfree(entry); + goto done; + } + entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data)); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + sfree(entry); + goto done; + } + entry->key.secondary.s = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + } + + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data)); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.stringval = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + case TYPE_FILENAME: + entry->value.u.fileval = + filename_deserialise(data, maxsize, &used); + if (!entry->value.u.fileval) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + data += used; + maxsize -= used; + break; + case TYPE_FONT: + entry->value.u.fontval = + fontspec_deserialise(data, maxsize, &used); + if (!entry->value.u.fontval) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + data += used; + maxsize -= used; + break; + } + conf_insert(conf, entry); + } + + done: + return (int)(data - start); +} diff --git a/putty/CONFIG.C b/putty/CONFIG.C index 38e47b6..e822eec 100644 --- a/putty/CONFIG.C +++ b/putty/CONFIG.C @@ -15,10 +15,149 @@ #define HOST_BOX_TITLE "Host Name (or IP address)" #define PORT_BOX_TITLE "Port" +void conf_radiobutton_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int button; + Conf *conf = (Conf *)data; + + /* + * For a standard radio button set, the context parameter gives + * the primary key (CONF_foo), and the extra data per button + * gives the value the target field should take if that button + * is the one selected. + */ + if (event == EVENT_REFRESH) { + int val = conf_get_int(conf, ctrl->radio.context.i); + for (button = 0; button < ctrl->radio.nbuttons; button++) + if (val == ctrl->radio.buttondata[button].i) + break; + /* We expected that `break' to happen, in all circumstances. */ + assert(button < ctrl->radio.nbuttons); + dlg_radiobutton_set(ctrl, dlg, button); + } else if (event == EVENT_VALCHANGE) { + button = dlg_radiobutton_get(ctrl, dlg); + assert(button >= 0 && button < ctrl->radio.nbuttons); + conf_set_int(conf, ctrl->radio.context.i, + ctrl->radio.buttondata[button].i); + } +} + +#define CHECKBOX_INVERT (1<<30) +void conf_checkbox_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key, invert; + Conf *conf = (Conf *)data; + + /* + * For a standard checkbox, the context parameter gives the + * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT. + */ + key = ctrl->checkbox.context.i; + if (key & CHECKBOX_INVERT) { + key &= ~CHECKBOX_INVERT; + invert = 1; + } else + invert = 0; + + /* + * C lacks a logical XOR, so the following code uses the idiom + * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1 + * iff exactly one of a and b is nonzero, otherwise 0.) + */ + + if (event == EVENT_REFRESH) { + int val = conf_get_int(conf, key); + dlg_checkbox_set(ctrl, dlg, (!val ^ !invert)); + } else if (event == EVENT_VALCHANGE) { + conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert); + } +} + +void conf_editbox_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + /* + * The standard edit-box handler expects the main `context' + * field to contain the primary key. The secondary `context2' + * field indicates the type of this field: + * + * - if context2 > 0, the field is a string. + * - if context2 == -1, the field is an int and the edit box + * is numeric. + * - if context2 < -1, the field is an int and the edit box is + * _floating_, and (-context2) gives the scale. (E.g. if + * context2 == -1000, then typing 1.2 into the box will set + * the field to 1200.) + */ + int key = ctrl->editbox.context.i; + int length = ctrl->editbox.context2.i; + Conf *conf = (Conf *)data; + + if (length > 0) { + if (event == EVENT_REFRESH) { + char *field = conf_get_str(conf, key); + dlg_editbox_set(ctrl, dlg, field); + } else if (event == EVENT_VALCHANGE) { + char *field = dlg_editbox_get(ctrl, dlg); + conf_set_str(conf, key, field); + sfree(field); + } + } else if (length < 0) { + if (event == EVENT_REFRESH) { + char str[80]; + int value = conf_get_int(conf, key); + if (length == -1) + sprintf(str, "%d", value); + else + sprintf(str, "%g", (double)value / (double)(-length)); + dlg_editbox_set(ctrl, dlg, str); + } else if (event == EVENT_VALCHANGE) { + char *str = dlg_editbox_get(ctrl, dlg); + if (length == -1) + conf_set_int(conf, key, atoi(str)); + else + conf_set_int(conf, key, (int)((-length) * atof(str))); + sfree(str); + } + } +} + +void conf_filesel_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key = ctrl->fileselect.context.i; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key)); + } else if (event == EVENT_VALCHANGE) { + Filename *filename = dlg_filesel_get(ctrl, dlg); + conf_set_filename(conf, key, filename); + filename_free(filename); + } +} + +void conf_fontsel_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + int key = ctrl->fontselect.context.i; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key)); + } else if (event == EVENT_VALCHANGE) { + FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg); + conf_set_fontspec(conf, key, fontspec); + fontspec_free(fontspec); + } +} + static void config_host_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* * This function works just like the standard edit box handler, @@ -26,29 +165,31 @@ static void config_host_handler(union control *ctrl, void *dlg, * different places depending on the protocol. */ if (event == EVENT_REFRESH) { - if (cfg->protocol == PROT_SERIAL) { + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain an n, * since that's the shortcut for the host name control. */ dlg_label_change(ctrl, dlg, "Serial line"); - dlg_editbox_set(ctrl, dlg, cfg->serline); + dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline)); } else { dlg_label_change(ctrl, dlg, HOST_BOX_TITLE); - dlg_editbox_set(ctrl, dlg, cfg->host); + dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host)); } } else if (event == EVENT_VALCHANGE) { - if (cfg->protocol == PROT_SERIAL) - dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline)); + char *s = dlg_editbox_get(ctrl, dlg); + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + conf_set_str(conf, CONF_serline, s); else - dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host)); + conf_set_str(conf, CONF_host, s); + sfree(s); } } static void config_port_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; char buf[80]; /* @@ -57,28 +198,31 @@ static void config_port_handler(union control *ctrl, void *dlg, * different places depending on the protocol. */ if (event == EVENT_REFRESH) { - if (cfg->protocol == PROT_SERIAL) { + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) { /* * This label text is carefully chosen to contain a p, * since that's the shortcut for the port control. */ dlg_label_change(ctrl, dlg, "Speed"); - sprintf(buf, "%d", cfg->serspeed); + sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed)); } else { dlg_label_change(ctrl, dlg, PORT_BOX_TITLE); - if (cfg->port != 0) - sprintf(buf, "%d", cfg->port); + if (conf_get_int(conf, CONF_port) != 0) + sprintf(buf, "%d", conf_get_int(conf, CONF_port)); else /* Display an (invalid) port of 0 as blank */ buf[0] = '\0'; } dlg_editbox_set(ctrl, dlg, buf); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, buf, lenof(buf)); - if (cfg->protocol == PROT_SERIAL) - cfg->serspeed = atoi(buf); + char *s = dlg_editbox_get(ctrl, dlg); + int i = atoi(s); + sfree(s); + + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + conf_set_int(conf, CONF_serspeed, i); else - cfg->port = atoi(buf); + conf_set_int(conf, CONF_port, i); } } @@ -95,7 +239,7 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct hostport *hp = (struct hostport *)ctrl->radio.context.p; /* @@ -106,20 +250,25 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, * structure giving the `union control's for both. */ if (event == EVENT_REFRESH) { + int protocol = conf_get_int(conf, CONF_protocol); for (button = 0; button < ctrl->radio.nbuttons; button++) - if (cfg->protocol == ctrl->radio.buttondata[button].i) + if (protocol == ctrl->radio.buttondata[button].i) break; /* We expected that `break' to happen, in all circumstances. */ assert(button < ctrl->radio.nbuttons); dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { - int oldproto = cfg->protocol; + int oldproto = conf_get_int(conf, CONF_protocol); + int newproto, port; + button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - cfg->protocol = ctrl->radio.buttondata[button].i; - if (oldproto != cfg->protocol) { + newproto = ctrl->radio.buttondata[button].i; + conf_set_int(conf, CONF_protocol, newproto); + + if (oldproto != newproto) { Backend *ob = backend_from_proto(oldproto); - Backend *nb = backend_from_proto(cfg->protocol); + Backend *nb = backend_from_proto(newproto); assert(ob); assert(nb); /* Iff the user hasn't changed the port from the old protocol's @@ -131,8 +280,9 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg, * controls in order and setting a non-default port before * getting to the protocol; we want that non-default port * to be preserved. */ - if (cfg->port == ob->default_port) - cfg->port = nb->default_port; + port = conf_get_int(conf, CONF_port); + if (port == ob->default_port) + conf_set_int(conf, CONF_port, nb->default_port); } dlg_refresh(hp->host, dlg); dlg_refresh(hp->port, dlg); @@ -143,26 +293,28 @@ static void loggingbuttons_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* This function works just like the standard radio-button handler, * but it has to fall back to "no logging" in situations where the * configured logging type isn't applicable. */ if (event == EVENT_REFRESH) { + int logtype = conf_get_int(conf, CONF_logtype); + for (button = 0; button < ctrl->radio.nbuttons; button++) - if (cfg->logtype == ctrl->radio.buttondata[button].i) + if (logtype == ctrl->radio.buttondata[button].i) break; - - /* We fell off the end, so we lack the configured logging type */ - if (button == ctrl->radio.nbuttons) { - button=0; - cfg->logtype=LGTYP_NONE; - } - dlg_radiobutton_set(ctrl, dlg, button); + + /* We fell off the end, so we lack the configured logging type */ + if (button == ctrl->radio.nbuttons) { + button = 0; + conf_set_int(conf, CONF_logtype, LGTYP_NONE); + } + dlg_radiobutton_set(ctrl, dlg, button); } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - cfg->logtype = ctrl->radio.buttondata[button].i; + conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i); } } @@ -170,15 +322,15 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, void *data, int event) { int button; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; /* * This function works much like the standard radio button - * handler, but it has to handle two fields in Config. + * handler, but it has to handle two fields in Conf. */ if (event == EVENT_REFRESH) { - if (cfg->nethack_keypad) + if (conf_get_int(conf, CONF_nethack_keypad)) button = 2; - else if (cfg->app_keypad) + else if (conf_get_int(conf, CONF_app_keypad)) button = 1; else button = 0; @@ -188,11 +340,11 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); if (button == 2) { - cfg->app_keypad = FALSE; - cfg->nethack_keypad = TRUE; + conf_set_int(conf, CONF_app_keypad, FALSE); + conf_set_int(conf, CONF_nethack_keypad, TRUE); } else { - cfg->app_keypad = (button != 0); - cfg->nethack_keypad = FALSE; + conf_set_int(conf, CONF_app_keypad, (button != 0)); + conf_set_int(conf, CONF_nethack_keypad, FALSE); } } } @@ -200,7 +352,7 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg, static void cipherlist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; @@ -218,7 +370,7 @@ static void cipherlist_handler(union control *ctrl, void *dlg, dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < CIPHER_MAX; i++) { - int c = cfg->ssh_cipherlist[i]; + int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i); int j; char *cstr = NULL; for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) { @@ -236,8 +388,8 @@ static void cipherlist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < CIPHER_MAX; i++) - cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i); - + conf_set_int_int(conf, CONF_ssh_cipherlist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } @@ -245,14 +397,14 @@ static void cipherlist_handler(union control *ctrl, void *dlg, static void gsslist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < ngsslibs; i++) { - int id = cfg->ssh_gsslist[i]; + int id = conf_get_int_int(conf, CONF_ssh_gsslist, i); assert(id >= 0 && id < ngsslibs); dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id); } @@ -263,7 +415,8 @@ static void gsslist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < ngsslibs; i++) - cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int_int(conf, CONF_ssh_gsslist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } #endif @@ -271,7 +424,7 @@ static void gsslist_handler(union control *ctrl, void *dlg, static void kexlist_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; @@ -288,7 +441,7 @@ static void kexlist_handler(union control *ctrl, void *dlg, dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < KEX_MAX; i++) { - int k = cfg->ssh_kexlist[i]; + int k = conf_get_int_int(conf, CONF_ssh_kexlist, i); int j; char *kstr = NULL; for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) { @@ -306,18 +459,19 @@ static void kexlist_handler(union control *ctrl, void *dlg, /* Update array to match the list box. */ for (i=0; i < KEX_MAX; i++) - cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i); - + conf_set_int_int(conf, CONF_ssh_kexlist, i, + dlg_listbox_getid(ctrl, dlg, i)); } } static void printerbox_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int nprinters, i; printer_enum *pe; + char *printer; dlg_update_start(ctrl, dlg); /* @@ -332,50 +486,62 @@ static void printerbox_handler(union control *ctrl, void *dlg, dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i)); printer_finish_enum(pe); } - dlg_editbox_set(ctrl, dlg, - (*cfg->printer ? cfg->printer : - PRINTER_DISABLED_STRING)); + printer = conf_get_str(conf, CONF_printer); + if (!printer) + printer = PRINTER_DISABLED_STRING; + dlg_editbox_set(ctrl, dlg, printer); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer)); - if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING)) - *cfg->printer = '\0'; + char *printer = dlg_editbox_get(ctrl, dlg); + if (!strcmp(printer, PRINTER_DISABLED_STRING)) + printer[0] = '\0'; + conf_set_str(conf, CONF_printer, printer); + sfree(printer); } } static void codepage_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; const char *cp, *thiscp; dlg_update_start(ctrl, dlg); - thiscp = cp_name(decode_codepage(cfg->line_codepage)); + thiscp = cp_name(decode_codepage(conf_get_str(conf, + CONF_line_codepage))); dlg_listbox_clear(ctrl, dlg); for (i = 0; (cp = cp_enumerate(i)) != NULL; i++) dlg_listbox_add(ctrl, dlg, cp); dlg_editbox_set(ctrl, dlg, thiscp); - strcpy(cfg->line_codepage, thiscp); + conf_set_str(conf, CONF_line_codepage, thiscp); dlg_update_done(ctrl, dlg); } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, cfg->line_codepage, - sizeof(cfg->line_codepage)); - strcpy(cfg->line_codepage, - cp_name(decode_codepage(cfg->line_codepage))); + char *codepage = dlg_editbox_get(ctrl, dlg); + conf_set_str(conf, CONF_line_codepage, + cp_name(decode_codepage(codepage))); + sfree(codepage); } } static void sshbug_handler(union control *ctrl, void *dlg, void *data, int event) { + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { + /* + * We must fetch the previously configured value from the Conf + * before we start modifying the drop-down list, otherwise the + * spurious SELCHANGE we trigger in the process will overwrite + * the value we wanted to keep. + */ + int oldconf = conf_get_int(conf, ctrl->listbox.context.i); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO); dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF); dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON); - switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) { + switch (oldconf) { case AUTO: dlg_listbox_select(ctrl, dlg, 0); break; case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break; case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break; @@ -387,27 +553,32 @@ static void sshbug_handler(union control *ctrl, void *dlg, i = AUTO; else i = dlg_listbox_getid(ctrl, dlg, i); - *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i; + conf_set_int(conf, ctrl->listbox.context.i, i); } } -#define SAVEDSESSION_LEN 2048 - struct sessionsaver_data { union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton; union control *okbutton, *cancelbutton; struct sesslist sesslist; int midsession; + char *savedsession; /* the current contents of ssd->editbox */ }; +static void sessionsaver_data_free(void *ssdv) +{ + struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv; + sfree(ssd->savedsession); + sfree(ssd); +} + /* * Helper function to load the session selected in the list box, if * any, as this is done in more than one place below. Returns 0 for * failure. */ static int load_selected_session(struct sessionsaver_data *ssd, - char *savedsession, - void *dlg, Config *cfg, int *maybe_launch) + void *dlg, Conf *conf, int *maybe_launch) { int i = dlg_listbox_index(ssd->listbox, dlg); int isdef; @@ -416,18 +587,11 @@ 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], 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; - } + load_settings(ssd->sesslist.sessions[i], conf); + sfree(ssd->savedsession); + ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]); + if (maybe_launch) + *maybe_launch = !isdef; dlg_refresh(NULL, dlg); /* Restore the selection, which might have been clobbered by * changing the value of the edit box. */ @@ -438,30 +602,13 @@ static int load_selected_session(struct sessionsaver_data *ssd, static void sessionsaver_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct sessionsaver_data *ssd = (struct sessionsaver_data *)ctrl->generic.context.p; - char *savedsession; - - /* - * The first time we're called in a new dialog, we must - * allocate space to store the current contents of the saved - * session edit box (since it must persist even when we switch - * panels, but is not part of the Config). - */ - if (!ssd->editbox) { - savedsession = NULL; - } else if (!dlg_get_privdata(ssd->editbox, dlg)) { - savedsession = (char *) - dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN); - savedsession[0] = '\0'; - } else { - savedsession = dlg_get_privdata(ssd->editbox, dlg); - } if (event == EVENT_REFRESH) { if (ctrl == ssd->editbox) { - dlg_editbox_set(ctrl, dlg, savedsession); + dlg_editbox_set(ctrl, dlg, ssd->savedsession); } else if (ctrl == ssd->listbox) { int i; dlg_update_start(ctrl, dlg); @@ -473,13 +620,13 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, } else if (event == EVENT_VALCHANGE) { int top, bottom, halfway, i; if (ctrl == ssd->editbox) { - dlg_editbox_get(ctrl, dlg, savedsession, - SAVEDSESSION_LEN); + sfree(ssd->savedsession); + ssd->savedsession = dlg_editbox_get(ctrl, dlg); top = ssd->sesslist.nsessions; bottom = -1; while (top-bottom > 1) { halfway = (top+bottom)/2; - i = strcmp(savedsession, ssd->sesslist.sessions[halfway]); + i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]); if (i <= 0 ) { top = halfway; } else { @@ -503,29 +650,25 @@ 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, &mbl) && - (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) { + if (load_selected_session(ssd, dlg, conf, &mbl) && + (mbl && ctrl == ssd->listbox && conf_launchable(conf))) { dlg_end(dlg, 1); /* it's all over, and succeeded */ } } else if (ctrl == ssd->savebutton) { - int isdef = !strcmp(savedsession, "Default Settings"); - if (!savedsession[0]) { + int isdef = !strcmp(ssd->savedsession, "Default Settings"); + if (!ssd->savedsession[0]) { int i = dlg_listbox_index(ssd->listbox, dlg); if (i < 0) { dlg_beep(dlg); return; } isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings"); - if (!isdef) { - strncpy(savedsession, ssd->sesslist.sessions[i], - SAVEDSESSION_LEN); - savedsession[SAVEDSESSION_LEN-1] = '\0'; - } else { - savedsession[0] = '\0'; - } + sfree(ssd->savedsession); + ssd->savedsession = dupstr(isdef ? "" : + ssd->sesslist.sessions[i]); } { - char *errmsg = save_settings(savedsession, cfg); + char *errmsg = save_settings(ssd->savedsession, conf); if (errmsg) { dlg_error_msg(dlg, errmsg); sfree(errmsg); @@ -560,21 +703,22 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, * valid host name in it, then load it and go. */ if (dlg_last_focused(ctrl, dlg) == ssd->listbox && - !cfg_launchable(cfg)) { - Config cfg2; + !conf_launchable(conf)) { + Conf *conf2 = conf_new(); int mbl = FALSE; - if (!load_selected_session(ssd, savedsession, dlg, - &cfg2, &mbl)) { + if (!load_selected_session(ssd, dlg, conf2, &mbl)) { dlg_beep(dlg); + conf_free(conf2); return; } /* If at this point we have a valid session, go! */ - if (mbl && cfg_launchable(&cfg2)) { - *cfg = cfg2; /* structure copy */ - cfg->remote_cmd_ptr = NULL; + if (mbl && conf_launchable(conf2)) { + conf_copy_into(conf, conf2); dlg_end(dlg, 1); } else dlg_beep(dlg); + + conf_free(conf2); return; } @@ -582,7 +726,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg, * Otherwise, do the normal thing: if we have a valid * session, get going. */ - if (cfg_launchable(cfg)) { + if (conf_launchable(conf)) { dlg_end(dlg, 1); } else dlg_beep(dlg); @@ -599,7 +743,7 @@ struct charclass_data { static void charclass_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct charclass_data *ccd = (struct charclass_data *)ctrl->generic.context.p; @@ -611,20 +755,22 @@ static void charclass_handler(union control *ctrl, void *dlg, for (i = 0; i < 128; i++) { char str[100]; sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, - (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]); + (i >= 0x21 && i != 0x7F) ? i : ' ', + conf_get_int_int(conf, CONF_wordness, i)); dlg_listbox_add(ctrl, dlg, str); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ccd->button) { - char str[100]; + char *str; int i, n; - dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str)); + str = dlg_editbox_get(ccd->editbox, dlg); n = atoi(str); + sfree(str); for (i = 0; i < 128; i++) { if (dlg_listbox_issel(ccd->listbox, dlg, i)) - cfg->wordness[i] = n; + conf_set_int_int(conf, CONF_wordness, i, n); } dlg_refresh(ccd->listbox, dlg); } @@ -652,7 +798,7 @@ static const char *const colours[] = { static void colour_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct colour_data *cd = (struct colour_data *)ctrl->generic.context.p; int update = FALSE, clear = FALSE, r, g, b; @@ -676,31 +822,32 @@ static void colour_handler(union control *ctrl, void *dlg, clear = TRUE; } else { clear = FALSE; - r = cfg->colours[i][0]; - g = cfg->colours[i][1]; - b = cfg->colours[i][2]; + r = conf_get_int_int(conf, CONF_colours, i*3+0); + g = conf_get_int_int(conf, CONF_colours, i*3+1); + b = conf_get_int_int(conf, CONF_colours, i*3+2); } update = TRUE; } } else if (event == EVENT_VALCHANGE) { if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) { /* The user has changed the colour using the edit boxes. */ - char buf[80]; + char *str; int i, cval; - dlg_editbox_get(ctrl, dlg, buf, lenof(buf)); - cval = atoi(buf); + str = dlg_editbox_get(ctrl, dlg); + cval = atoi(str); + sfree(str); if (cval > 255) cval = 255; if (cval < 0) cval = 0; i = dlg_listbox_index(cd->listbox, dlg); if (i >= 0) { if (ctrl == cd->redit) - cfg->colours[i][0] = cval; + conf_set_int_int(conf, CONF_colours, i*3+0, cval); else if (ctrl == cd->gedit) - cfg->colours[i][1] = cval; + conf_set_int_int(conf, CONF_colours, i*3+1, cval); else if (ctrl == cd->bedit) - cfg->colours[i][2] = cval; + conf_set_int_int(conf, CONF_colours, i*3+2, cval); } } } else if (event == EVENT_ACTION) { @@ -716,9 +863,9 @@ static void colour_handler(union control *ctrl, void *dlg, * pick up the results. */ dlg_coloursel_start(ctrl, dlg, - cfg->colours[i][0], - cfg->colours[i][1], - cfg->colours[i][2]); + conf_get_int_int(conf, CONF_colours, i*3+0), + conf_get_int_int(conf, CONF_colours, i*3+1), + conf_get_int_int(conf, CONF_colours, i*3+2)); } } else if (event == EVENT_CALLBACK) { if (ctrl == cd->button) { @@ -729,9 +876,9 @@ static void colour_handler(union control *ctrl, void *dlg, * selector did nothing (user hit Cancel, for example). */ if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) { - cfg->colours[i][0] = r; - cfg->colours[i][1] = g; - cfg->colours[i][2] = b; + conf_set_int_int(conf, CONF_colours, i*3+0, r); + conf_set_int_int(conf, CONF_colours, i*3+1, g); + conf_set_int_int(conf, CONF_colours, i*3+2, b); clear = FALSE; update = TRUE; } @@ -760,22 +907,21 @@ struct ttymodes_data { static void ttymodes_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct ttymodes_data *td = (struct ttymodes_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == td->listbox) { - char *p = cfg->ttymodes; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { - int tabpos = strchr(p, '\t') - p; - char *disp = dupprintf("%.*s\t%s", tabpos, p, - (p[tabpos+1] == 'A') ? "(auto)" : - p+tabpos+2); + for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { + char *disp = dupprintf("%s\t%s", key, + (val[0] == 'A') ? "(auto)" : val+1); dlg_listbox_add(ctrl, dlg, disp); - p += strlen(p) + 1; sfree(disp); } dlg_update_done(ctrl, dlg); @@ -795,73 +941,47 @@ static void ttymodes_handler(union control *ctrl, void *dlg, int ind = dlg_listbox_index(td->modelist, dlg); if (ind >= 0) { char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A'; - int slen, left; - char *p, str[lenof(cfg->ttymodes)]; + const char *key; + char *str, *val; /* Construct new entry */ - memset(str, 0, lenof(str)); - strncpy(str, ttymodes[ind], lenof(str)-3); - slen = strlen(str); - str[slen] = '\t'; - str[slen+1] = type; - slen += 2; - if (type == 'V') { - dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen); - } - /* Find end of list, deleting any existing instance */ - p = cfg->ttymodes; - left = lenof(cfg->ttymodes); - while (*p) { - int t = strchr(p, '\t') - p; - if (t == strlen(ttymodes[ind]) && - strncmp(p, ttymodes[ind], t) == 0) { - memmove(p, p+strlen(p)+1, left - (strlen(p)+1)); - continue; - } - left -= strlen(p) + 1; - p += strlen(p) + 1; - } - /* Append new entry */ - memset(p, 0, left); - strncpy(p, str, left - 2); + key = ttymodes[ind]; + str = dlg_editbox_get(td->valbox, dlg); + val = dupprintf("%c%s", type, str); + sfree(str); + conf_set_str_str(conf, CONF_ttymodes, key, val); + sfree(val); dlg_refresh(td->listbox, dlg); } else dlg_beep(dlg); } else if (ctrl == td->rembutton) { - char *p = cfg->ttymodes; - int i = 0, len = lenof(cfg->ttymodes); - while (*p) { - int multisel = dlg_listbox_index(td->listbox, dlg) < 0; + int i = 0; + char *key, *val; + int multisel = dlg_listbox_index(td->listbox, dlg) < 0; + for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) { 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); + int ind = 0; + val++; + while (ttymodes[ind]) { + if (!strcmp(ttymodes[ind], key)) + 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; + conf_del_str_str(conf, CONF_ttymodes, key); } - len -= strlen(p) + 1; - p += strlen(p) + 1; i++; } - memset(p, 0, lenof(cfg->ttymodes) - len); dlg_refresh(td->listbox, dlg); } } @@ -874,96 +994,67 @@ struct environ_data { static void environ_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct environ_data *ed = (struct environ_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == ed->listbox) { - char *p = cfg->environmt; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { + for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { + char *p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); - p += strlen(p) + 1; + sfree(p); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == ed->addbutton) { - char str[sizeof(cfg->environmt)]; - char *p; - dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1); - if (!*str) { + char *key, *val, *str; + key = dlg_editbox_get(ed->varbox, dlg); + if (!*key) { + sfree(key); dlg_beep(dlg); return; } - p = str + strlen(str); - *p++ = '\t'; - dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str)); - if (!*p) { + val = dlg_editbox_get(ed->valbox, dlg); + if (!*val) { + sfree(key); + sfree(val); dlg_beep(dlg); return; } - p = cfg->environmt; - while (*p) { - while (*p) - p++; - p++; - } - if ((p - cfg->environmt) + strlen(str) + 2 < - sizeof(cfg->environmt)) { - strcpy(p, str); - p[strlen(str) + 1] = '\0'; - dlg_listbox_add(ed->listbox, dlg, str); - dlg_editbox_set(ed->varbox, dlg, ""); - dlg_editbox_set(ed->valbox, dlg, ""); - } else { - dlg_error_msg(dlg, "Environment too big"); - } + conf_set_str_str(conf, CONF_environmt, key, val); + str = dupcat(key, "\t", val, NULL); + dlg_editbox_set(ed->varbox, dlg, ""); + dlg_editbox_set(ed->valbox, dlg, ""); + sfree(str); + sfree(key); + sfree(val); + dlg_refresh(ed->listbox, dlg); } else if (ctrl == ed->rembutton) { int i = dlg_listbox_index(ed->listbox, dlg); if (i < 0) { dlg_beep(dlg); } else { - char *p, *q, *str; - - dlg_listbox_del(ed->listbox, dlg, i); - p = cfg->environmt; - while (i > 0) { - if (!*p) - goto disaster; - while (*p) - p++; - p++; - i--; - } - q = p; - if (!*p) - goto disaster; - /* 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) - *q++ = *p++; - *q++ = *p++; + char *key, *val; + + key = conf_get_str_nthstrkey(conf, CONF_environmt, i); + if (key) { + /* Populate controls with the entry we're about to delete + * for ease of editing */ + val = conf_get_str_str(conf, CONF_environmt, key); + dlg_editbox_set(ed->varbox, dlg, key); + dlg_editbox_set(ed->valbox, dlg, val); + /* And delete it */ + conf_del_str_str(conf, CONF_environmt, key); } - *q = '\0'; - disaster:; } + dlg_refresh(ed->listbox, dlg); } } } @@ -979,18 +1070,25 @@ struct portfwd_data { static void portfwd_handler(union control *ctrl, void *dlg, void *data, int event) { - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; struct portfwd_data *pfd = (struct portfwd_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == pfd->listbox) { - char *p = cfg->portfwd; + char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); - while (*p) { + for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { + char *p; + if (!strcmp(val, "D")) + p = dupprintf("D%s\t", key+1); + else + p = dupprintf("%s\t%s", key, val); dlg_listbox_add(ctrl, dlg, p); - p += strlen(p) + 1; + sfree(p); } dlg_update_done(ctrl, dlg); } else if (ctrl == pfd->direction) { @@ -1005,137 +1103,110 @@ static void portfwd_handler(union control *ctrl, void *dlg, } } else if (event == EVENT_ACTION) { if (ctrl == pfd->addbutton) { - char str[sizeof(cfg->portfwd)]; - char *p; - int i, type; + char *family, *type, *src, *key, *val; int whichbutton; - i = 0; #ifndef NO_IPV6 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg); if (whichbutton == 1) - str[i++] = '4'; + family = "4"; else if (whichbutton == 2) - str[i++] = '6'; + family = "6"; + else + family = ""; #endif whichbutton = dlg_radiobutton_get(pfd->direction, dlg); if (whichbutton == 0) - type = 'L'; + type = "L"; else if (whichbutton == 1) - type = 'R'; + type = "R"; else - type = 'D'; - str[i++] = type; + type = "D"; - dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i); - if (!str[i]) { + src = dlg_editbox_get(pfd->sourcebox, dlg); + if (!*src) { dlg_error_msg(dlg, "You need to specify a source port number"); + sfree(src); return; } - p = str + strlen(str); - if (type != 'D') { - *p++ = '\t'; - dlg_editbox_get(pfd->destbox, dlg, p, - sizeof(str) - (p - str)); - if (!*p || !strchr(p, ':')) { + if (*type != 'D') { + val = dlg_editbox_get(pfd->destbox, dlg); + if (!*val || !strchr(val, ':')) { dlg_error_msg(dlg, "You need to specify a destination address\n" "in the form \"host.name:port\""); + sfree(src); + sfree(val); return; } - } else - *p = '\0'; - p = cfg->portfwd; - while (*p) { - if (strcmp(p,str) == 0) { - dlg_error_msg(dlg, "Specified forwarding already exists"); - break; - } - while (*p) - p++; - p++; - } - if (!*p) { - if ((p - cfg->portfwd) + strlen(str) + 2 <= - sizeof(cfg->portfwd)) { - strcpy(p, str); - p[strlen(str) + 1] = '\0'; - dlg_listbox_add(pfd->listbox, dlg, str); - dlg_editbox_set(pfd->sourcebox, dlg, ""); - dlg_editbox_set(pfd->destbox, dlg, ""); - } else { - dlg_error_msg(dlg, "Too many forwardings"); - } + } else { + type = "L"; + val = dupstr("D"); /* special case */ + } + + key = dupcat(family, type, src, NULL); + sfree(src); + + if (conf_get_str_str_opt(conf, CONF_portfwd, key)) { + dlg_error_msg(dlg, "Specified forwarding already exists"); + } else { + conf_set_str_str(conf, CONF_portfwd, key, val); } + + sfree(key); + sfree(val); + dlg_refresh(pfd->listbox, dlg); } else if (ctrl == pfd->rembutton) { int i = dlg_listbox_index(pfd->listbox, dlg); - if (i < 0) + if (i < 0) { dlg_beep(dlg); - else { - char *p, *q, *src, *dst; - char dir; - - dlg_listbox_del(pfd->listbox, dlg, i); - p = cfg->portfwd; - while (i > 0) { - if (!*p) - goto disaster2; - while (*p) - p++; - p++; - i--; - } - q = p; - if (!*p) - goto disaster2; - /* Populate the controls with the entry we're about to - * delete, for ease of editing. */ - { + } else { + char *key, *val, *p; + + key = conf_get_str_nthstrkey(conf, CONF_portfwd, i); + if (key) { static const char *const afs = "A46"; - char *afp = strchr(afs, *p); + static const char *const dirs = "LRD"; + char *afp; + int dir; +#ifndef NO_IPV6 + int idx; +#endif + + /* Populate controls with the entry we're about to delete + * for ease of editing */ + p = key; + + afp = strchr(afs, *p); #ifndef NO_IPV6 - int idx = afp ? afp-afs : 0; + 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; + + val = conf_get_str_str(conf, CONF_portfwd, key); + if (!strcmp(val, "D")) { + dir = 'D'; + val = ""; + } + 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) - *q++ = *p++; - *q++ = *p++; + + dlg_editbox_set(pfd->sourcebox, dlg, p); + dlg_editbox_set(pfd->destbox, dlg, val); + /* And delete it */ + conf_del_str_str(conf, CONF_portfwd, key); } - *q = '\0'; - disaster2:; } + dlg_refresh(pfd->listbox, dlg); } } } @@ -1154,8 +1225,10 @@ void setup_config_box(struct controlbox *b, int midsession, char *str; ssd = (struct sessionsaver_data *) - ctrl_alloc(b, sizeof(struct sessionsaver_data)); + ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data), + sessionsaver_data_free); memset(ssd, 0, sizeof(*ssd)); + ssd->savedsession = dupstr(""); ssd->midsession = midsession; /* @@ -1274,13 +1347,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 1, 100); s = ctrl_getset(b, "Session", "otheropts", NULL); - c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4, - HELPCTX(session_coe), - dlg_stdradiobutton_handler, - I(offsetof(Config, close_on_exit)), - "Always", I(FORCE_ON), - "Never", I(FORCE_OFF), - "Only on clean exit", I(AUTO), NULL); + ctrl_radiobuttons(s, "Close window on exit:", 'x', 4, + HELPCTX(session_coe), + conf_radiobutton_handler, + I(CONF_close_on_exit), + "Always", I(FORCE_ON), + "Never", I(FORCE_OFF), + "Only on clean exit", I(AUTO), NULL); /* * The Session/Logging panel. @@ -1305,7 +1378,7 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2, HELPCTX(logging_main), loggingbuttons_handler, - I(offsetof(Config, logtype)), + I(CONF_logtype), "None", 't', I(LGTYP_NONE), "Printable output", 'p', I(LGTYP_ASCII), "All session output", 'l', I(LGTYP_DEBUG), @@ -1316,19 +1389,19 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_filesel(s, "Log file name:", 'f', NULL, TRUE, "Select session log file name", HELPCTX(logging_filename), - dlg_stdfilesel_handler, I(offsetof(Config, logfilename))); + conf_filesel_handler, I(CONF_logfilename)); ctrl_text(s, "(Log file name can contain &Y, &M, &D for date," " &T for time, and &H for host name)", HELPCTX(logging_filename)); ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1, HELPCTX(logging_exists), - dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)), + conf_radiobutton_handler, I(CONF_logxfovr), "Always overwrite it", I(LGXF_OVR), "Always append to the end of it", I(LGXF_APN), "Ask the user every time", I(LGXF_ASK), NULL); ctrl_checkbox(s, "Flush log file frequently", 'u', HELPCTX(logging_flush), - dlg_stdcheckbox_handler, I(offsetof(Config,logflush))); + conf_checkbox_handler, I(CONF_logflush)); if ((midsession && protocol == PROT_SSH) || (!midsession && backend_from_proto(PROT_SSH))) { @@ -1336,10 +1409,10 @@ void setup_config_box(struct controlbox *b, int midsession, "Options specific to SSH packet logging"); ctrl_checkbox(s, "Omit known password fields", 'k', HELPCTX(logging_ssh_omit_password), - dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass))); + conf_checkbox_handler, I(CONF_logomitpass)); ctrl_checkbox(s, "Omit session data", 'd', HELPCTX(logging_ssh_omit_data), - dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata))); + conf_checkbox_handler, I(CONF_logomitdata)); } /* @@ -1350,37 +1423,36 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal", "general", "Set various terminal options"); ctrl_checkbox(s, "Auto wrap mode initially on", 'w', HELPCTX(terminal_autowrap), - dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode))); + conf_checkbox_handler, I(CONF_wrap_mode)); ctrl_checkbox(s, "DEC Origin Mode initially on", 'd', HELPCTX(terminal_decom), - dlg_stdcheckbox_handler, I(offsetof(Config,dec_om))); + conf_checkbox_handler, I(CONF_dec_om)); ctrl_checkbox(s, "Implicit CR in every LF", 'r', HELPCTX(terminal_lfhascr), - dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr))); + conf_checkbox_handler, I(CONF_lfhascr)); ctrl_checkbox(s, "Implicit LF in every CR", 'f', HELPCTX(terminal_crhaslf), - dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf))); + conf_checkbox_handler, I(CONF_crhaslf)); ctrl_checkbox(s, "Use background colour to erase screen", 'e', HELPCTX(terminal_bce), - dlg_stdcheckbox_handler, I(offsetof(Config,bce))); + conf_checkbox_handler, I(CONF_bce)); ctrl_checkbox(s, "Enable blinking text", 'n', HELPCTX(terminal_blink), - dlg_stdcheckbox_handler, I(offsetof(Config,blinktext))); + conf_checkbox_handler, I(CONF_blinktext)); ctrl_editbox(s, "Answerback to ^E:", 's', 100, HELPCTX(terminal_answerback), - dlg_stdeditbox_handler, I(offsetof(Config,answerback)), - I(sizeof(((Config *)0)->answerback))); + conf_editbox_handler, I(CONF_answerback), I(1)); s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options"); ctrl_radiobuttons(s, "Local echo:", 'l', 3, HELPCTX(terminal_localecho), - dlg_stdradiobutton_handler,I(offsetof(Config,localecho)), + conf_radiobutton_handler,I(CONF_localecho), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); ctrl_radiobuttons(s, "Local line editing:", 't', 3, HELPCTX(terminal_localedit), - dlg_stdradiobutton_handler,I(offsetof(Config,localedit)), + conf_radiobutton_handler,I(CONF_localedit), "Auto", I(AUTO), "Force on", I(FORCE_ON), "Force off", I(FORCE_OFF), NULL); @@ -1400,18 +1472,18 @@ void setup_config_box(struct controlbox *b, int midsession, "Change the sequences sent by:"); ctrl_radiobuttons(s, "The Backspace key", 'b', 2, HELPCTX(keyboard_backspace), - dlg_stdradiobutton_handler, - I(offsetof(Config, bksp_is_delete)), + conf_radiobutton_handler, + I(CONF_bksp_is_delete), "Control-H", I(0), "Control-? (127)", I(1), NULL); ctrl_radiobuttons(s, "The Home and End keys", 'e', 2, HELPCTX(keyboard_homeend), - dlg_stdradiobutton_handler, - I(offsetof(Config, rxvt_homeend)), + conf_radiobutton_handler, + I(CONF_rxvt_homeend), "Standard", I(0), "rxvt", I(1), NULL); ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3, HELPCTX(keyboard_funkeys), - dlg_stdradiobutton_handler, - I(offsetof(Config, funky_type)), + conf_radiobutton_handler, + I(CONF_funky_type), "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2), "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL); @@ -1419,8 +1491,8 @@ void setup_config_box(struct controlbox *b, int midsession, "Application keypad settings:"); ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3, HELPCTX(keyboard_appcursor), - dlg_stdradiobutton_handler, - I(offsetof(Config, app_cursor)), + conf_radiobutton_handler, + I(CONF_app_cursor), "Normal", I(0), "Application", I(1), NULL); ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3, HELPCTX(keyboard_appkeypad), @@ -1437,7 +1509,7 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1, HELPCTX(bell_style), - dlg_stdradiobutton_handler, I(offsetof(Config, beep)), + conf_radiobutton_handler, I(CONF_beep), "None (bell disabled)", I(BELL_DISABLED), "Make default system alert sound", I(BELL_DEFAULT), "Visual bell (flash window)", I(BELL_VISUAL), NULL); @@ -1446,19 +1518,19 @@ void setup_config_box(struct controlbox *b, int midsession, "Control the bell overload behaviour"); ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd', HELPCTX(bell_overload), - dlg_stdcheckbox_handler, I(offsetof(Config,bellovl))); + conf_checkbox_handler, I(CONF_bellovl)); ctrl_editbox(s, "Over-use means this many bells...", 'm', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1)); + conf_editbox_handler, I(CONF_bellovl_n), I(-1)); ctrl_editbox(s, "... in this many seconds", 't', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)), + conf_editbox_handler, I(CONF_bellovl_t), I(-TICKSPERSEC)); ctrl_text(s, "The bell is re-enabled after a few seconds of silence.", HELPCTX(bell_overload)); ctrl_editbox(s, "Seconds of silence required", 's', 20, HELPCTX(bell_overload), - dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)), + conf_editbox_handler, I(CONF_bellovl_s), I(-TICKSPERSEC)); /* @@ -1470,43 +1542,43 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Terminal/Features", "main", NULL); ctrl_checkbox(s, "Disable application cursor keys mode", 'u', HELPCTX(features_application), - dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c))); + conf_checkbox_handler, I(CONF_no_applic_c)); ctrl_checkbox(s, "Disable application keypad mode", 'k', HELPCTX(features_application), - dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k))); + conf_checkbox_handler, I(CONF_no_applic_k)); ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x', HELPCTX(features_mouse), - dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep))); + conf_checkbox_handler, I(CONF_no_mouse_rep)); ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's', HELPCTX(features_resize), - dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_resize))); + conf_checkbox_handler, + I(CONF_no_remote_resize)); ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w', HELPCTX(features_altscreen), - dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen))); + conf_checkbox_handler, I(CONF_no_alt_screen)); ctrl_checkbox(s, "Disable remote-controlled window title changing", 't', HELPCTX(features_retitle), - dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_wintitle))); + conf_checkbox_handler, + I(CONF_no_remote_wintitle)); ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3, HELPCTX(features_qtitle), - dlg_stdradiobutton_handler, - I(offsetof(Config,remote_qtitle_action)), + conf_radiobutton_handler, + I(CONF_remote_qtitle_action), "None", I(TITLE_NONE), "Empty string", I(TITLE_EMPTY), "Window title", I(TITLE_REAL), NULL); ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b', HELPCTX(features_dbackspace), - dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace))); + conf_checkbox_handler, I(CONF_no_dbackspace)); ctrl_checkbox(s, "Disable remote-controlled character set configuration", - 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler, - I(offsetof(Config,no_remote_charset))); + 'r', HELPCTX(features_charset), conf_checkbox_handler, + I(CONF_no_remote_charset)); ctrl_checkbox(s, "Disable Arabic text shaping", - 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler, - I(offsetof(Config, arabicshaping))); + 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler, + I(CONF_arabicshaping)); ctrl_checkbox(s, "Disable bidirectional text display", - 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler, - I(offsetof(Config, bidi))); + 'd', HELPCTX(features_bidi), conf_checkbox_handler, + I(CONF_bidi)); /* * The Window panel. @@ -1519,11 +1591,11 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 2, 50, 50); c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), - dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1)); + conf_editbox_handler, I(CONF_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)); + conf_editbox_handler, I(CONF_height),I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); @@ -1531,20 +1603,20 @@ void setup_config_box(struct controlbox *b, int midsession, "Control the scrollback in the window"); ctrl_editbox(s, "Lines of scrollback", 's', 50, HELPCTX(window_scrollback), - dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1)); + conf_editbox_handler, I(CONF_savelines), I(-1)); ctrl_checkbox(s, "Display scrollbar", 'd', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar))); + conf_checkbox_handler, I(CONF_scrollbar)); ctrl_checkbox(s, "Reset scrollback on keypress", 'k', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key))); + conf_checkbox_handler, I(CONF_scroll_on_key)); ctrl_checkbox(s, "Reset scrollback on display activity", 'p', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp))); + conf_checkbox_handler, I(CONF_scroll_on_disp)); ctrl_checkbox(s, "Push erased text into scrollback", 'e', HELPCTX(window_erased), - dlg_stdcheckbox_handler, - I(offsetof(Config,erase_to_scrollback))); + conf_checkbox_handler, + I(CONF_erase_to_scrollback)); /* * The Window/Appearance panel. @@ -1557,33 +1629,33 @@ void setup_config_box(struct controlbox *b, int midsession, "Adjust the use of the cursor"); ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3, HELPCTX(appearance_cursor), - dlg_stdradiobutton_handler, - I(offsetof(Config, cursor_type)), + conf_radiobutton_handler, + I(CONF_cursor_type), "Block", 'l', I(0), "Underline", 'u', I(1), "Vertical line", 'v', I(2), NULL); ctrl_checkbox(s, "Cursor blinks", 'b', HELPCTX(appearance_cursor), - dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur))); + conf_checkbox_handler, I(CONF_blink_cur)); s = ctrl_getset(b, "Window/Appearance", "font", "Font settings"); ctrl_fontsel(s, "Font used in the terminal window", 'n', HELPCTX(appearance_font), - dlg_stdfontsel_handler, I(offsetof(Config, font))); + conf_fontsel_handler, I(CONF_font)); s = ctrl_getset(b, "Window/Appearance", "mouse", "Adjust the use of the mouse pointer"); ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p', HELPCTX(appearance_hidemouse), - dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr))); + conf_checkbox_handler, I(CONF_hide_mouseptr)); s = ctrl_getset(b, "Window/Appearance", "border", "Adjust the window border"); ctrl_editbox(s, "Gap between text and window edge:", 'e', 20, HELPCTX(appearance_border), - dlg_stdeditbox_handler, - I(offsetof(Config,window_border)), I(-1)); + conf_editbox_handler, + I(CONF_window_border), I(-1)); /* * The Window/Behaviour panel. @@ -1596,17 +1668,16 @@ void setup_config_box(struct controlbox *b, int midsession, "Adjust the behaviour of the window title"); ctrl_editbox(s, "Window title:", 't', 100, HELPCTX(appearance_title), - dlg_stdeditbox_handler, I(offsetof(Config,wintitle)), - I(sizeof(((Config *)0)->wintitle))); + conf_editbox_handler, I(CONF_wintitle), I(1)); ctrl_checkbox(s, "Separate window and icon titles", 'i', HELPCTX(appearance_title), - dlg_stdcheckbox_handler, - I(CHECKBOX_INVERT | offsetof(Config,win_name_always))); + conf_checkbox_handler, + I(CHECKBOX_INVERT | CONF_win_name_always)); s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Warn before closing window", 'w', HELPCTX(behaviour_closewarn), - dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close))); + conf_checkbox_handler, I(CONF_warn_on_close)); /* * The Window/Translation panel. @@ -1623,21 +1694,21 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w', HELPCTX(translation_cjk_ambig_wide), - dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide))); + conf_checkbox_handler, I(CONF_cjk_ambig_wide)); str = dupprintf("Adjust how %s handles line drawing characters", appname); s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1, HELPCTX(translation_linedraw), - dlg_stdradiobutton_handler, - I(offsetof(Config, vtmode)), + conf_radiobutton_handler, + I(CONF_vtmode), "Use Unicode line drawing code points",'u',I(VT_UNICODE), "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN), NULL); ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d', HELPCTX(selection_linedraw), - dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp))); + conf_checkbox_handler, I(CONF_rawcnp)); /* * The Window/Selection panel. @@ -1648,13 +1719,13 @@ void setup_config_box(struct controlbox *b, int midsession, "Control use of mouse"); ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p', HELPCTX(selection_shiftdrag), - dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override))); + conf_checkbox_handler, I(CONF_mouse_override)); ctrl_radiobuttons(s, "Default selection mode (Alt+drag does the other one):", NO_SHORTCUT, 2, HELPCTX(selection_rect), - dlg_stdradiobutton_handler, - I(offsetof(Config, rect_select)), + conf_radiobutton_handler, + I(CONF_rect_select), "Normal", 'n', I(0), "Rectangular block", 'r', I(1), NULL); @@ -1692,13 +1763,17 @@ void setup_config_box(struct controlbox *b, int midsession, "General options for colour usage"); ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i', HELPCTX(colours_ansi), - dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour))); + conf_checkbox_handler, I(CONF_ansi_colour)); ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2', - HELPCTX(colours_xterm256), dlg_stdcheckbox_handler, - I(offsetof(Config,xterm_256_colour))); - ctrl_checkbox(s, "Bolded text is a different colour", 'b', - HELPCTX(colours_bold), - dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour))); + HELPCTX(colours_xterm256), conf_checkbox_handler, + I(CONF_xterm_256_colour)); + ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3, + HELPCTX(colours_bold), + conf_radiobutton_handler, I(CONF_bold_style), + "The font", I(1), + "The colour", I(2), + "Both", I(3), + NULL); str = dupprintf("Adjust the precise colours %s displays", appname); s = ctrl_getset(b, "Window/Colours", "adjust", str); @@ -1740,7 +1815,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Sending of null packets to keep session active"); ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20, HELPCTX(connection_keepalive), - dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)), + conf_editbox_handler, I(CONF_ping_interval), I(-1)); if (!midsession) { @@ -1748,19 +1823,19 @@ void setup_config_box(struct controlbox *b, int midsession, "Low-level TCP connection options"); ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n', HELPCTX(connection_nodelay), - dlg_stdcheckbox_handler, - I(offsetof(Config,tcp_nodelay))); + conf_checkbox_handler, + I(CONF_tcp_nodelay)); ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)", 'p', HELPCTX(connection_tcpkeepalive), - dlg_stdcheckbox_handler, - I(offsetof(Config,tcp_keepalives))); + conf_checkbox_handler, + I(CONF_tcp_keepalives)); #ifndef NO_IPV6 s = ctrl_getset(b, "Connection", "ipversion", "Internet protocol version"); ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(connection_ipversion), - dlg_stdradiobutton_handler, - I(offsetof(Config, addressfamily)), + conf_radiobutton_handler, + I(CONF_addressfamily), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), "IPv6", '6', I(ADDRTYPE_IPV6), @@ -1775,8 +1850,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Logical name of remote host"); ctrl_editbox(s, label, 'm', 100, HELPCTX(connection_loghost), - dlg_stdeditbox_handler, I(offsetof(Config,loghost)), - I(sizeof(((Config *)0)->loghost))); + conf_editbox_handler, I(CONF_loghost), I(1)); } } @@ -1791,8 +1865,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Login details"); ctrl_editbox(s, "Auto-login username", 'u', 50, HELPCTX(connection_username), - dlg_stdeditbox_handler, I(offsetof(Config,username)), - I(sizeof(((Config *)0)->username))); + conf_editbox_handler, I(CONF_username), I(1)); { /* We assume the local username is sufficiently stable * to include on the dialog box. */ @@ -1802,8 +1875,8 @@ void setup_config_box(struct controlbox *b, int midsession, sfree(user); ctrl_radiobuttons(s, "When username is not specified:", 'n', 4, HELPCTX(connection_username_from_env), - dlg_stdradiobutton_handler, - I(offsetof(Config, username_from_env)), + conf_radiobutton_handler, + I(CONF_username_from_env), "Prompt", I(FALSE), userlabel, I(TRUE), NULL); @@ -1814,12 +1887,10 @@ void setup_config_box(struct controlbox *b, int midsession, "Terminal details"); ctrl_editbox(s, "Terminal-type string", 't', 50, HELPCTX(connection_termtype), - dlg_stdeditbox_handler, I(offsetof(Config,termtype)), - I(sizeof(((Config *)0)->termtype))); + conf_editbox_handler, I(CONF_termtype), I(1)); ctrl_editbox(s, "Terminal speeds", 's', 50, HELPCTX(connection_termspeed), - dlg_stdeditbox_handler, I(offsetof(Config,termspeed)), - I(sizeof(((Config *)0)->termspeed))); + conf_editbox_handler, I(CONF_termspeed), I(1)); s = ctrl_getset(b, "Connection/Data", "env", "Environment variables"); @@ -1865,8 +1936,8 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); ctrl_radiobuttons(s, "Proxy type:", 't', 3, HELPCTX(proxy_type), - dlg_stdradiobutton_handler, - I(offsetof(Config, proxy_type)), + conf_radiobutton_handler, + I(CONF_proxy_type), "None", I(PROXY_NONE), "SOCKS 4", I(PROXY_SOCKS4), "SOCKS 5", I(PROXY_SOCKS5), @@ -1876,49 +1947,44 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_host)), - I(sizeof(((Config *)0)->proxy_host))); + conf_editbox_handler, + I(CONF_proxy_host), I(1)); c->generic.column = 0; c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(proxy_main), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_port)), + conf_editbox_handler, + I(CONF_proxy_port), I(-1)); c->generic.column = 1; ctrl_columns(s, 1, 100); ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100, HELPCTX(proxy_exclude), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_exclude_list)), - I(sizeof(((Config *)0)->proxy_exclude_list))); + conf_editbox_handler, + I(CONF_proxy_exclude_list), I(1)); ctrl_checkbox(s, "Consider proxying local host connections", 'x', HELPCTX(proxy_exclude), - dlg_stdcheckbox_handler, - I(offsetof(Config,even_proxy_localhost))); + conf_checkbox_handler, + I(CONF_even_proxy_localhost)); ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3, HELPCTX(proxy_dns), - dlg_stdradiobutton_handler, - I(offsetof(Config, proxy_dns)), + conf_radiobutton_handler, + I(CONF_proxy_dns), "No", I(FORCE_OFF), "Auto", I(AUTO), "Yes", I(FORCE_ON), NULL); ctrl_editbox(s, "Username", 'u', 60, HELPCTX(proxy_auth), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_username)), - I(sizeof(((Config *)0)->proxy_username))); + conf_editbox_handler, + I(CONF_proxy_username), I(1)); c = ctrl_editbox(s, "Password", 'w', 60, HELPCTX(proxy_auth), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_password)), - I(sizeof(((Config *)0)->proxy_password))); + conf_editbox_handler, + I(CONF_proxy_password), I(1)); c->editbox.password = 1; ctrl_editbox(s, "Telnet command", 'm', 100, HELPCTX(proxy_command), - dlg_stdeditbox_handler, - I(offsetof(Config,proxy_telnet_command)), - I(sizeof(((Config *)0)->proxy_telnet_command))); + conf_editbox_handler, + I(CONF_proxy_telnet_command), I(1)); } /* @@ -1939,24 +2005,24 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:", NO_SHORTCUT, 2, HELPCTX(telnet_oldenviron), - dlg_stdradiobutton_handler, - I(offsetof(Config, rfc_environ)), + conf_radiobutton_handler, + I(CONF_rfc_environ), "BSD (commonplace)", 'b', I(0), "RFC 1408 (unusual)", 'f', I(1), NULL); ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, HELPCTX(telnet_passive), - dlg_stdradiobutton_handler, - I(offsetof(Config, passive_telnet)), + conf_radiobutton_handler, + I(CONF_passive_telnet), "Passive", I(1), "Active", I(0), NULL); } ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', HELPCTX(telnet_specialkeys), - dlg_stdcheckbox_handler, - I(offsetof(Config,telnet_keyboard))); + conf_checkbox_handler, + I(CONF_telnet_keyboard)); ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M", 'm', HELPCTX(telnet_newline), - dlg_stdcheckbox_handler, - I(offsetof(Config,telnet_newline))); + conf_checkbox_handler, + I(CONF_telnet_newline)); } if (!midsession) { @@ -1971,8 +2037,7 @@ void setup_config_box(struct controlbox *b, int midsession, "Data to send to the server"); ctrl_editbox(s, "Local username:", 'l', 50, HELPCTX(rlogin_localuser), - dlg_stdeditbox_handler, I(offsetof(Config,localusername)), - I(sizeof(((Config *)0)->localusername))); + conf_editbox_handler, I(CONF_localusername), I(1)); } @@ -2002,14 +2067,13 @@ void setup_config_box(struct controlbox *b, int midsession, "Data to send to the server"); ctrl_editbox(s, "Remote command:", 'r', 100, HELPCTX(ssh_command), - dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)), - I(sizeof(((Config *)0)->remote_cmd))); + conf_editbox_handler, I(CONF_remote_cmd), I(1)); s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_checkbox(s, "Don't start a shell or command at all", 'n', HELPCTX(ssh_noshell), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_no_shell))); + conf_checkbox_handler, + I(CONF_ssh_no_shell)); } if (!midsession || protcfginfo != 1) { @@ -2017,8 +2081,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Enable compression", 'e', HELPCTX(ssh_compress), - dlg_stdcheckbox_handler, - I(offsetof(Config,compression))); + conf_checkbox_handler, + I(CONF_compression)); } if (!midsession) { @@ -2026,8 +2090,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4, HELPCTX(ssh_protocol), - dlg_stdradiobutton_handler, - I(offsetof(Config, sshprot)), + conf_radiobutton_handler, + I(CONF_sshprot), "1 only", 'l', I(0), "1", '1', I(1), "2", '2', I(2), @@ -2043,8 +2107,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i', HELPCTX(ssh_ciphers), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh2_des_cbc))); + conf_checkbox_handler, + I(CONF_ssh2_des_cbc)); } /* @@ -2068,13 +2132,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20, HELPCTX(ssh_kex_repeat), - dlg_stdeditbox_handler, - I(offsetof(Config,ssh_rekey_time)), + conf_editbox_handler, + I(CONF_ssh_rekey_time), I(-1)); ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20, HELPCTX(ssh_kex_repeat), - dlg_stdeditbox_handler, - I(offsetof(Config,ssh_rekey_data)), + conf_editbox_handler, + I(CONF_ssh_rekey_data), I(16)); ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)", HELPCTX(ssh_kex_repeat)); @@ -2091,41 +2155,41 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL); ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b', HELPCTX(ssh_auth_bypass), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_no_userauth))); + conf_checkbox_handler, + I(CONF_ssh_no_userauth)); ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)", 'd', HELPCTX(ssh_auth_banner), - dlg_stdcheckbox_handler, - I(offsetof(Config,ssh_show_banner))); + conf_checkbox_handler, + I(CONF_ssh_show_banner)); s = ctrl_getset(b, "Connection/SSH/Auth", "methods", "Authentication methods"); ctrl_checkbox(s, "Attempt authentication using Pageant", 'p', HELPCTX(ssh_auth_pageant), - dlg_stdcheckbox_handler, - I(offsetof(Config,tryagent))); + conf_checkbox_handler, + I(CONF_tryagent)); ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm', HELPCTX(ssh_auth_tis), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_tis_auth))); + conf_checkbox_handler, + I(CONF_try_tis_auth)); ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)", 'i', HELPCTX(ssh_auth_ki), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_ki_auth))); + conf_checkbox_handler, + I(CONF_try_ki_auth)); s = ctrl_getset(b, "Connection/SSH/Auth", "params", "Authentication parameters"); ctrl_checkbox(s, "Allow agent forwarding", 'f', HELPCTX(ssh_auth_agentfwd), - dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd))); + conf_checkbox_handler, I(CONF_agentfwd)); ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT, HELPCTX(ssh_auth_changeuser), - dlg_stdcheckbox_handler, - I(offsetof(Config,change_username))); + conf_checkbox_handler, + I(CONF_change_username)); ctrl_filesel(s, "Private key file for authentication:", 'k', FILTER_KEY_FILES, FALSE, "Select private key file", HELPCTX(ssh_auth_privkey), - dlg_stdfilesel_handler, I(offsetof(Config, keyfile))); + conf_filesel_handler, I(CONF_keyfile)); #ifndef NO_GSSAPI /* @@ -2138,13 +2202,13 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)", 't', HELPCTX(ssh_gssapi), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_gssapi_auth))); + conf_checkbox_handler, + I(CONF_try_gssapi_auth)); ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l', HELPCTX(ssh_gssapi_delegation), - dlg_stdcheckbox_handler, - I(offsetof(Config,gssapifwd))); + conf_checkbox_handler, + I(CONF_gssapifwd)); /* * GSSAPI library selection. @@ -2177,8 +2241,8 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_filesel(s, "User-supplied GSSAPI library path:", 's', FILTER_DYNLIB_FILES, FALSE, "Select library file", HELPCTX(ssh_gssapi_libraries), - dlg_stdfilesel_handler, - I(offsetof(Config, ssh_gss_custom))); + conf_filesel_handler, + I(CONF_ssh_gss_custom)); } #endif } @@ -2192,8 +2256,8 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL); ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p', HELPCTX(ssh_nopty), - dlg_stdcheckbox_handler, - I(offsetof(Config,nopty))); + conf_checkbox_handler, + I(CONF_nopty)); s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes", "Terminal modes"); @@ -2259,15 +2323,14 @@ void setup_config_box(struct controlbox *b, int midsession, s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); ctrl_checkbox(s, "Enable X11 forwarding", 'e', HELPCTX(ssh_tunnels_x11), - dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward))); + conf_checkbox_handler,I(CONF_x11_forward)); ctrl_editbox(s, "X display location", 'x', 50, HELPCTX(ssh_tunnels_x11), - dlg_stdeditbox_handler, I(offsetof(Config,x11_display)), - I(sizeof(((Config *)0)->x11_display))); + conf_editbox_handler, I(CONF_x11_display), I(1)); ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2, HELPCTX(ssh_tunnels_x11auth), - dlg_stdradiobutton_handler, - I(offsetof(Config, x11_auth)), + conf_radiobutton_handler, + I(CONF_x11_auth), "MIT-Magic-Cookie-1", I(X11_MIT), "XDM-Authorization-1", I(X11_XDM), NULL); } @@ -2282,12 +2345,12 @@ void setup_config_box(struct controlbox *b, int midsession, "Port forwarding"); ctrl_checkbox(s, "Local ports accept connections from other hosts",'t', HELPCTX(ssh_tunnels_portfwd_localhost), - dlg_stdcheckbox_handler, - I(offsetof(Config,lport_acceptall))); + conf_checkbox_handler, + I(CONF_lport_acceptall)); ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p', HELPCTX(ssh_tunnels_portfwd_localhost), - dlg_stdcheckbox_handler, - I(offsetof(Config,rport_acceptall))); + conf_checkbox_handler, + I(CONF_rport_acceptall)); ctrl_columns(s, 3, 55, 20, 25); c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd)); @@ -2355,34 +2418,37 @@ void setup_config_box(struct controlbox *b, int midsession, "Detection of known bugs in SSH servers"); ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20, HELPCTX(ssh_bugs_ignore1), - sshbug_handler, I(offsetof(Config,sshbug_ignore1))); + sshbug_handler, I(CONF_sshbug_ignore1)); ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20, HELPCTX(ssh_bugs_plainpw1), - sshbug_handler, I(offsetof(Config,sshbug_plainpw1))); + sshbug_handler, I(CONF_sshbug_plainpw1)); ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20, HELPCTX(ssh_bugs_rsa1), - sshbug_handler, I(offsetof(Config,sshbug_rsa1))); + sshbug_handler, I(CONF_sshbug_rsa1)); ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20, HELPCTX(ssh_bugs_ignore2), - sshbug_handler, I(offsetof(Config,sshbug_ignore2))); + sshbug_handler, I(CONF_sshbug_ignore2)); + ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j', + 20, HELPCTX(ssh_bugs_winadj), + sshbug_handler, I(CONF_sshbug_winadj)); ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20, HELPCTX(ssh_bugs_hmac2), - sshbug_handler, I(offsetof(Config,sshbug_hmac2))); + sshbug_handler, I(CONF_sshbug_hmac2)); ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20, HELPCTX(ssh_bugs_derivekey2), - sshbug_handler, I(offsetof(Config,sshbug_derivekey2))); + sshbug_handler, I(CONF_sshbug_derivekey2)); ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20, HELPCTX(ssh_bugs_rsapad2), - sshbug_handler, I(offsetof(Config,sshbug_rsapad2))); + sshbug_handler, I(CONF_sshbug_rsapad2)); ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20, HELPCTX(ssh_bugs_pksessid2), - sshbug_handler, I(offsetof(Config,sshbug_pksessid2))); + sshbug_handler, I(CONF_sshbug_pksessid2)); ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20, HELPCTX(ssh_bugs_rekey2), - sshbug_handler, I(offsetof(Config,sshbug_rekey2))); + sshbug_handler, I(CONF_sshbug_rekey2)); ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, HELPCTX(ssh_bugs_maxpkt2), - sshbug_handler, I(offsetof(Config,sshbug_maxpkt2))); + sshbug_handler, I(CONF_sshbug_maxpkt2)); } } } diff --git a/putty/CONFIGUR b/putty/CONFIGUR new file mode 100644 index 0000000..fb789fb --- /dev/null +++ b/putty/CONFIGUR @@ -0,0 +1,3 @@ +#!/bin/sh + +$(echo "$0" | sed '$s!configure$!unix/configure!') "$@" diff --git a/putty/CONTRIB/CYGTERMD/PTY.C b/putty/CONTRIB/CYGTERMD/PTY.C index dd4d1a4..19168de 100644 --- a/putty/CONTRIB/CYGTERMD/PTY.C +++ b/putty/CONTRIB/CYGTERMD/PTY.C @@ -122,9 +122,23 @@ int run_program_in_pty(const struct shell_data *shdata, close(fd); } #endif + /* + * Make the new pty our controlling terminal. On some OSes + * this is done with TIOCSCTTY; Cygwin doesn't have that, so + * instead it's done by simply opening the pty without + * O_NOCTTY. This code is primarily intended for Cygwin, but + * it's useful to have it work in other contexts for testing + * purposes, so I leave the TIOCSCTTY here anyway. + */ + if ((fd = open(ptyname, O_RDWR)) >= 0) { #ifdef TIOCSCTTY - ioctl(0, TIOCSCTTY, &i); + ioctl(fd, TIOCSCTTY, &i); #endif + close(fd); + } else { + perror("slave pty: open"); + exit(127); + } tcsetpgrp(0, getpgrp()); for (i = 0; i < shdata->nenvvars; i++) diff --git a/putty/CONTRIB/CYGTERMD/TELNET.C b/putty/CONTRIB/CYGTERMD/TELNET.C index 9602fd7..800c04b 100644 --- a/putty/CONTRIB/CYGTERMD/TELNET.C +++ b/putty/CONTRIB/CYGTERMD/TELNET.C @@ -424,7 +424,10 @@ void telnet_from_net(Telnet telnet, char *buf, int len) char cc = c; sel_write(telnet->pty, &cc, 1); - telnet->state = SEENCR; + if (c == CR) + telnet->state = SEENCR; + else + telnet->state = TOP_LEVEL; } break; case SEENIAC: diff --git a/putty/CONTRIB/LOGPARSE.PL b/putty/CONTRIB/LOGPARSE.PL new file mode 100644 index 0000000..bfddce0 --- /dev/null +++ b/putty/CONTRIB/LOGPARSE.PL @@ -0,0 +1,907 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use FileHandle; + +my $dumpchannels = 0; +my $dumpdata = 0; +while ($ARGV[0] =~ /^-/) { + my $opt = shift @ARGV; + if ($opt eq "--") { + last; # stop processing options + } elsif ($opt eq "-c") { + $dumpchannels = 1; + } elsif ($opt eq "-d") { + $dumpdata = 1; + } else { + die "unrecognised option '$opt'\n"; + } +} + +my @channels = (); # ultimate channel ids are indices in this array +my %chan_by_id = (); # indexed by 'c%d' or 's%d' for client and server ids +my %globalreq = (); # indexed by 'i' or 'o' + +my %packets = ( +#define SSH2_MSG_DISCONNECT 1 /* 0x1 */ + 'SSH2_MSG_DISCONNECT' => sub { + my ($direction, $seq, $data) = @_; + my ($reason, $description, $lang) = &parse("uss", $data); + printf "%s\n", &str($description); + }, +#define SSH2_MSG_IGNORE 2 /* 0x2 */ + 'SSH2_MSG_IGNORE' => sub { + my ($direction, $seq, $data) = @_; + my ($str) = &parse("s", $data); + printf "(%d bytes)\n", length $str; + }, +#define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */ + 'SSH2_MSG_UNIMPLEMENTED' => sub { + my ($direction, $seq, $data) = @_; + my ($rseq) = &parse("u", $data); + printf "i%d\n", $rseq; + }, +#define SSH2_MSG_DEBUG 4 /* 0x4 */ + 'SSH2_MSG_DEBUG' => sub { + my ($direction, $seq, $data) = @_; + my ($disp, $message, $lang) = &parse("bss", $data); + printf "%s\n", &str($message); + }, +#define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */ + 'SSH2_MSG_SERVICE_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($service) = &parse("s", $data); + printf "%s\n", &str($service); + }, +#define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */ + 'SSH2_MSG_SERVICE_ACCEPT' => sub { + my ($direction, $seq, $data) = @_; + my ($service) = &parse("s", $data); + printf "%s\n", &str($service); + }, +#define SSH2_MSG_KEXINIT 20 /* 0x14 */ + 'SSH2_MSG_KEXINIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_NEWKEYS 21 /* 0x15 */ + 'SSH2_MSG_NEWKEYS' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ + 'SSH2_MSG_KEXDH_INIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ + 'SSH2_MSG_KEXDH_REPLY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ + 'SSH2_MSG_KEX_DH_GEX_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ + 'SSH2_MSG_KEX_DH_GEX_GROUP' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ + 'SSH2_MSG_KEX_DH_GEX_INIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ + 'SSH2_MSG_KEX_DH_GEX_REPLY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ + 'SSH2_MSG_KEXRSA_PUBKEY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ + 'SSH2_MSG_KEXRSA_SECRET' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ + 'SSH2_MSG_KEXRSA_DONE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ + 'SSH2_MSG_USERAUTH_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($user, $service, $method) = &parse("sss", $data); + my $out = sprintf "%s %s %s", + &str($user), &str($service), &str($method); + if ($method eq "publickey") { + my ($real) = &parse("b", $data); + $out .= " real=$real"; + } elsif ($method eq "password") { + my ($change) = &parse("b", $data); + $out .= " change=$change"; + } + print "$out\n"; + }, +#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ + 'SSH2_MSG_USERAUTH_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($options) = &parse("s", $data); + printf "%s\n", &str($options); + }, +#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ + 'SSH2_MSG_USERAUTH_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */ + 'SSH2_MSG_USERAUTH_BANNER' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_PK_OK' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 /* 0x3c */ + 'SSH2_MSG_USERAUTH_INFO_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* 0x3d */ + 'SSH2_MSG_USERAUTH_INFO_RESPONSE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */ + 'SSH2_MSG_GLOBAL_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($type, $wantreply) = &parse("sb", $data); + printf "%s (%s)", $type, $wantreply eq "yes" ? "reply" : "noreply"; + my $request = [$seq, $type]; + push @{$globalreq{$direction}}, $request if $wantreply eq "yes"; + if ($type eq "tcpip-forward" or $type eq "cancel-tcpip-forward") { + my ($addr, $port) = &parse("su", $data); + printf " %s:%s", $addr, $port; + push @$request, $port; + } + print "\n"; + }, +#define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */ + 'SSH2_MSG_REQUEST_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$globalreq{$otherdir}}; + if (defined $request) { + printf "to %s", $request->[0]; + if ($request->[1] eq "tcpip-forward" and $request->[2] == 0) { + my ($port) = &parse("u", $data); + printf " port=%s", $port; + } + } else { + print "(spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */ + 'SSH2_MSG_REQUEST_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$globalreq{$otherdir}}; + if (defined $request) { + printf "to %s", $request->[0]; + } else { + print "(spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */ + 'SSH2_MSG_CHANNEL_OPEN' => sub { + my ($direction, $seq, $data) = @_; + my ($type, $sid, $winsize, $packet) = &parse("suuu", $data); + # CHANNEL_OPEN tells the other side the _sender's_ id for the + # channel, so this choice between "s" and "c" prefixes is + # opposite to every other message in the protocol, which all + # quote the _recipient's_ id of the channel. + $sid = ($direction eq "i" ? "s" : "c") . $sid; + my $chan = {'id'=>$sid, 'state'=>'halfopen', + 'i'=>{'win'=>0, 'seq'=>0}, + 'o'=>{'win'=>0, 'seq'=>0}}; + $chan->{$direction}{'win'} = $winsize; + push @channels, $chan; + my $index = $#channels; + $chan_by_id{$sid} = $index; + printf "ch%d (%s) %s (--%d)", $index, $chan->{'id'}, $type, + $chan->{$direction}{'win'}; + if ($type eq "x11") { + my ($addr, $port) = &parse("su", $data); + printf " from %s:%s", $addr, $port; + } elsif ($type eq "forwarded-tcpip") { + my ($saddr, $sport, $paddr, $pport) = &parse("susu", $data); + printf " to %s:%s from %s:%s", $saddr, $sport, $paddr, $pport; + } elsif ($type eq "direct-tcpip") { + my ($daddr, $dport, $saddr, $sport) = &parse("susu", $data); + printf " to %s:%s from %s:%s", $daddr, $dport, $saddr, $sport; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */ + 'SSH2_MSG_CHANNEL_OPEN_CONFIRMATION' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $sid, $winsize, $packet) = &parse("uuuu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + $sid = ($direction eq "i" ? "s" : "c") . $sid; + $chan_by_id{$sid} = $index; + my $chan = $channels[$index]; + $chan->{'id'} = ($direction eq "i" ? "$rid/$sid" : "$sid/$rid"); + $chan->{'state'} = 'open'; + $chan->{$direction}{'win'} = $winsize; + printf "ch%d (%s) (--%d)\n", $index, $chan->{'id'}, + $chan->{$direction}{'win'}; + }, +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */ + 'SSH2_MSG_CHANNEL_OPEN_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $reason, $desc, $lang) = &parse("uuss", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{'state'} = 'rejected'; + printf "ch%d (%s) %s\n", $index, $chan->{'id'}, &str($reason); + }, +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */ + 'SSH2_MSG_CHANNEL_WINDOW_ADJUST' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{$direction}{'win'} += $bytes; + printf "ch%d (%s) +%d (--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$direction}{'win'}; + }, +#define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */ + 'SSH2_MSG_CHANNEL_DATA' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{$direction}{'seq'} += $bytes; + printf "ch%d (%s), %s bytes (%d--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$direction}{'seq'}-$bytes, $chan->{$direction}{'seq'}; + my @realdata = splice @$data, 0, $bytes; + if ($dumpdata) { + my $filekey = $direction . "file"; + if (!defined $chan->{$filekey}) { + my $filename = sprintf "ch%d.%s", $index, $direction; + $chan->{$filekey} = FileHandle->new(">$filename"); + if (!defined $chan->{$filekey}) { + die "$filename: $!\n"; + } + } + die "channel data not present in $seq\n" if @realdata < $bytes; + my $rawdata = pack "C*", @realdata; + my $fh = $chan->{$filekey}; + print $fh $rawdata; + } + if (@realdata == $bytes and defined $chan->{$direction."data"}) { + my $rawdata = pack "C*", @realdata; + $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); + } + }, +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */ + 'SSH2_MSG_CHANNEL_EXTENDED_DATA' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $bytes) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + my $dir = $direction eq "i" ? 'sc' : 'cs'; + $chan->{$dir}{'seq'} += $bytes; + printf "ch%d (%s), %s bytes (%d--%d)\n", $index, $chan->{'id'}, $bytes, + $chan->{$dir}{$seq}-$bytes, $chan->{$dir}{$seq}; + printf "ch%d (%s), %s bytes\n", $index, $chan->{'id'}, $bytes; + my @realdata = splice @$data, 0, $bytes; + if ($dumpdata) { + # We treat EXTENDED_DATA as equivalent to DATA, for the + # moment. It's not clear what else would be a better thing + # to do with it, and this at least is the Right Answer if + # the data is going to a terminal and the aim is to debug + # the terminal emulator. + my $filekey = $direction . "file"; + if (!defined $chan->{$filekey}) { + my $filename = sprintf "ch%d.%s", $index, $direction; + $chan->{$filekey} = FileHandle->new; + if (!$chan->{$filekey}->open(">", $filename)) { + die "$filename: $!\n"; + } + } + die "channel data not present in $seq\n" if @realdata < $bytes; + my $rawdata = pack "C*", @realdata; + my $fh = $chan->{$filekey}; + print $fh $rawdata; + } + if (@realdata == $bytes and defined $chan->{$direction."data"}) { + my $rawdata = pack "C*", @realdata; + $chan->{$direction."data"}->($chan, $index, $direction, $rawdata); + } + }, +#define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */ + 'SSH2_MSG_CHANNEL_EOF' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)\n", $index, $chan->{'id'}; + }, +#define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */ + 'SSH2_MSG_CHANNEL_CLOSE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + $chan->{'state'} = ($chan->{'state'} eq "open" ? "halfclosed" : + $chan->{'state'} eq "halfclosed" ? "closed" : + "confused"); + if ($chan->{'state'} eq "closed") { + $chan->{'ifile'}->close if defined $chan->{'ifile'}; + $chan->{'ofile'}->close if defined $chan->{'ofile'}; + } + printf "ch%d (%s)\n", $index, $chan->{'id'}; + }, +#define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ + 'SSH2_MSG_CHANNEL_REQUEST' => sub { + my ($direction, $seq, $data) = @_; + my ($rid, $type, $wantreply) = &parse("usb", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s) %s (%s)", + $index, $chan->{'id'}, $type, $wantreply eq "yes" ? "reply" : "noreply"; + push @{$chan->{'requests_'.$direction}}, [$seq, $type] + if $wantreply eq "yes"; + if ($type eq "pty-req") { + my ($term, $w, $h, $pw, $ph, $modes) = &parse("suuuus", $data); + printf " %s %sx%s", &str($term), $w, $h; + } elsif ($type eq "x11-req") { + my ($single, $xprot, $xcookie, $xscreen) = &parse("bssu", $data); + print " one-off" if $single eq "yes"; + printf " %s :%s", $xprot, $xscreen; + } elsif ($type eq "exec") { + my ($command) = &parse("s", $data); + printf " %s", &str($command); + } elsif ($type eq "subsystem") { + my ($subsys) = &parse("s", $data); + printf " %s", &str($subsys); + if ($subsys eq "sftp") { + &sftp_setup($index); + } + } elsif ($type eq "window-change") { + my ($w, $h, $pw, $ph) = &parse("uuuu", $data); + printf " %sx%s", $w, $h; + } elsif ($type eq "xon-xoff") { + my ($can) = &parse("b", $data); + printf " %s", $can; + } elsif ($type eq "signal") { + my ($sig) = &parse("s", $data); + printf " %s", &str($sig); + } elsif ($type eq "exit-status") { + my ($status) = &parse("u", $data); + printf " %s", $status; + } elsif ($type eq "exit-signal") { + my ($sig, $core, $error, $lang) = &parse("sbss", $data); + printf " %s", &str($sig); + print " (core dumped)" if $core eq "yes"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ + 'SSH2_MSG_CHANNEL_SUCCESS' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)", $index, $chan->{'id'}; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$chan->{'requests_' . $otherdir}}; + if (defined $request) { + printf " to %s", $request->[0]; + } else { + print " (spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ + 'SSH2_MSG_CHANNEL_FAILURE' => sub { + my ($direction, $seq, $data) = @_; + my ($rid) = &parse("uu", $data); + $rid = ($direction eq "i" ? "c" : "s") . $rid; + my $index = $chan_by_id{$rid}; + my $chan = $channels[$index]; + printf "ch%d (%s)", $index, $chan->{'id'}; + my $otherdir = ($direction eq "i" ? "o" : "i"); + my $request = shift @{$chan->{'requests_' . $otherdir}}; + if (defined $request) { + printf " to %s", $request->[0]; + } else { + print " (spurious?)"; + } + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 + 'SSH2_MSG_USERAUTH_GSSAPI_RESPONSE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 + 'SSH2_MSG_USERAUTH_GSSAPI_TOKEN' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 + 'SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 + 'SSH2_MSG_USERAUTH_GSSAPI_ERROR' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 + 'SSH2_MSG_USERAUTH_GSSAPI_ERRTOK' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 + 'SSH2_MSG_USERAUTH_GSSAPI_MIC' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +); + +my %sftp_packets = ( +#define SSH_FXP_INIT 1 /* 0x1 */ + 0x1 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($ver) = &parse("u", $data); + printf "SSH_FXP_INIT %d\n", $ver; + }, +#define SSH_FXP_VERSION 2 /* 0x2 */ + 0x2 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($ver) = &parse("u", $data); + printf "SSH_FXP_VERSION %d\n", $ver; + }, +#define SSH_FXP_OPEN 3 /* 0x3 */ + 0x3 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path, $pflags) = &parse("usu", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPEN"); + printf " \"%s\" ", $path; + if ($pflags eq 0) { + print "0"; + } else { + my $sep = ""; + if ($pflags & 1) { $pflags ^= 1; print "${sep}READ"; $sep = "|"; } + if ($pflags & 2) { $pflags ^= 2; print "${sep}WRITE"; $sep = "|"; } + if ($pflags & 4) { $pflags ^= 4; print "${sep}APPEND"; $sep = "|"; } + if ($pflags & 8) { $pflags ^= 8; print "${sep}CREAT"; $sep = "|"; } + if ($pflags & 16) { $pflags ^= 16; print "${sep}TRUNC"; $sep = "|"; } + if ($pflags & 32) { $pflags ^= 32; print "${sep}EXCL"; $sep = "|"; } + if ($pflags) { print "${sep}${pflags}"; } + } + print "\n"; + }, +#define SSH_FXP_CLOSE 4 /* 0x4 */ + 0x4 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_CLOSE"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_READ 5 /* 0x5 */ + 0x5 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle, $offset, $len) = &parse("usUu", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READ"); + printf " \"%s\" %d %d", &stringescape($handle), $offset, $len; + print "\n"; + }, +#define SSH_FXP_WRITE 6 /* 0x6 */ + 0x6 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle, $offset, $wdata) = &parse("usUs", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_WRITE"); + printf " \"%s\" %d [%d bytes]", &stringescape($handle), $offset, length $wdata; + print "\n"; + }, +#define SSH_FXP_LSTAT 7 /* 0x7 */ + 0x7 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_LSTAT"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_FSTAT 8 /* 0x8 */ + 0x8 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSTAT"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_SETSTAT 9 /* 0x9 */ + 0x9 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_SETSTAT"); + my $attrs = &sftp_parse_attrs($data); + printf " \"%s\" %s", $path, $attrs; + print "\n"; + }, +#define SSH_FXP_FSETSTAT 10 /* 0xa */ + 0xa => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_FSETSTAT"); + my $attrs = &sftp_parse_attrs($data); + printf " \"%s\" %s", &stringescape($handle), $attrs; + print "\n"; + }, +#define SSH_FXP_OPENDIR 11 /* 0xb */ + 0xb => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_OPENDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_READDIR 12 /* 0xc */ + 0xc => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_READDIR"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_REMOVE 13 /* 0xd */ + 0xd => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REMOVE"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_MKDIR 14 /* 0xe */ + 0xe => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_MKDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_RMDIR 15 /* 0xf */ + 0xf => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RMDIR"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_REALPATH 16 /* 0x10 */ + 0x10 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_REALPATH"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_STAT 17 /* 0x11 */ + 0x11 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $path) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_STAT"); + printf " \"%s\"", $path; + print "\n"; + }, +#define SSH_FXP_RENAME 18 /* 0x12 */ + 0x12 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $srcpath, $dstpath) = &parse("uss", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_RENAME"); + printf " \"%s\" \"%s\"", $srcpath, $dstpath; + print "\n"; + }, +#define SSH_FXP_STATUS 101 /* 0x65 */ + 0x65 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $status) = &parse("uu", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_STATUS"); + print " "; + if ($status eq "0") { print "SSH_FX_OK"; } + elsif ($status eq "1") { print "SSH_FX_EOF"; } + elsif ($status eq "2") { print "SSH_FX_NO_SUCH_FILE"; } + elsif ($status eq "3") { print "SSH_FX_PERMISSION_DENIED"; } + elsif ($status eq "4") { print "SSH_FX_FAILURE"; } + elsif ($status eq "5") { print "SSH_FX_BAD_MESSAGE"; } + elsif ($status eq "6") { print "SSH_FX_NO_CONNECTION"; } + elsif ($status eq "7") { print "SSH_FX_CONNECTION_LOST"; } + elsif ($status eq "8") { print "SSH_FX_OP_UNSUPPORTED"; } + else { printf "[unknown status %d]", $status; } + print "\n"; + }, +#define SSH_FXP_HANDLE 102 /* 0x66 */ + 0x66 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $handle) = &parse("us", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_HANDLE"); + printf " \"%s\"", &stringescape($handle); + print "\n"; + }, +#define SSH_FXP_DATA 103 /* 0x67 */ + 0x67 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $retdata) = &parse("us", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_DATA"); + printf " [%d bytes]", length $retdata; + print "\n"; + }, +#define SSH_FXP_NAME 104 /* 0x68 */ + 0x68 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $count) = &parse("uu", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_NAME"); + for my $i (1..$count) { + my ($name, $longname) = &parse("ss", $data); + my $attrs = &sftp_parse_attrs($data); + print " [name=\"$name\", longname=\"$longname\", attrs=$attrs]"; + } + print "\n"; + }, +#define SSH_FXP_ATTRS 105 /* 0x69 */ + 0x69 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid) = &parse("u", $data); + &sftp_logreply($chan, $direction, $reqid, $id, "SSH_FXP_ATTRS"); + my $attrs = &sftp_parse_attrs($data); + printf " %s", $attrs; + print "\n"; + }, +#define SSH_FXP_EXTENDED 200 /* 0xc8 */ + 0xc8 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid, $type) = &parse("us", $data); + &sftp_logreq($chan, $direction, $reqid, $id, "SSH_FXP_EXTENDED"); + printf " \"%s\"", $type; + print "\n"; + }, +#define SSH_FXP_EXTENDED_REPLY 201 /* 0xc9 */ + 0xc9 => sub { + my ($chan, $index, $direction, $id, $data) = @_; + my ($reqid) = &parse("u", $data); + print "\n"; + &sftp_logreply($chan, $direction, $reqid,$id,"SSH_FXP_EXTENDED_REPLY"); + }, +); + +my ($direction, $seq, $ourseq, $type, $data, $recording); +my %ourseqs = ('i'=>0, 'o'=>0); + +$recording = 0; +while (<>) { + if ($recording) { + if (/^ [0-9a-fA-F]{8} ((?:[0-9a-fA-F]{2} )*[0-9a-fA-F]{2})/) { + push @$data, map { $_ eq "XX" ? -1 : hex $_ } split / /, $1; + } else { + $recording = 0; + my $fullseq = "$direction$ourseq"; + print "$fullseq: $type "; + if (defined $packets{$type}) { + $packets{$type}->($direction, $fullseq, $data); + } else { + printf "raw %s\n", join "", map { sprintf "%02x", $_ } @$data; + } + } + } + if (/^(Incoming|Outgoing) packet #0x([0-9a-fA-F]+), type \d+ \/ 0x[0-9a-fA-F]+ \((.*)\)/) { + $direction = ($1 eq "Incoming" ? 'i' : 'o'); + # $seq is the sequence number quoted in the log file. $ourseq + # is our own count of the sequence number, which differs in + # that it shouldn't wrap at 2^32, should anyone manage to run + # this script over such a huge log file. + $seq = hex $2; + $ourseq = $ourseqs{$direction}++; + $type = $3; + $data = []; + $recording = 1; + } +} + +if ($dumpchannels) { + my %stateorder = ('closed'=>0, 'rejected'=>1, + 'halfclosed'=>2, 'open'=>3, 'halfopen'=>4); + for my $index (0..$#channels) { + my $chan = $channels[$index]; + my $so = $stateorder{$chan->{'state'}}; + $so = 1000 unless defined $so; # any state I've missed above comes last + $chan->{'index'} = sprintf "ch%d", $index; + $chan->{'order'} = sprintf "%08d %08d", $so, $index; + } + my @sortedchannels = sort { $a->{'order'} cmp $b->{'order'} } @channels; + for my $chan (@sortedchannels) { + printf "%s (%s): %s\n", $chan->{'index'}, $chan->{'id'}, $chan->{'state'}; + } +} + +sub parseone { + my ($type, $data) = @_; + if ($type eq "u") { # uint32 + my @bytes = splice @$data, 0, 4; + return "" if @bytes < 4 or grep { $_<0 } @bytes; + return unpack "N", pack "C*", @bytes; + } elsif ($type eq "U") { # uint64 + my @bytes = splice @$data, 0, 8; + return "" if @bytes < 8 or grep { $_<0 } @bytes; + my @words = unpack "NN", pack "C*", @bytes; + return ($words[0] << 32) + $words[1]; + } elsif ($type eq "b") { # boolean + my $byte = shift @$data; + return "" if !defined $byte or $byte < 0; + return $byte ? "yes" : "no"; + } elsif ($type eq "B") { # byte + my $byte = shift @$data; + return "" if !defined $byte or $byte < 0; + return $byte; + } elsif ($type eq "s" or $type eq "m") { # string, mpint + my @bytes = splice @$data, 0, 4; + return "" if @bytes < 4 or grep { $_<0 } @bytes; + my $len = unpack "N", pack "C*", @bytes; + @bytes = splice @$data, 0, $len; + return "" if @bytes < $len or grep { $_<0 } @bytes; + if ($type eq "mpint") { + my $str = ""; + if ($bytes[0] >= 128) { + # Take two's complement. + @bytes = map { 0xFF ^ $_ } @bytes; + for my $i (reverse 0..$#bytes) { + if ($bytes[$i] < 0xFF) { + $bytes[$i]++; + last; + } else { + $bytes[$i] = 0; + } + } + $str = "-"; + } + $str .= "0x" . join "", map { sprintf "%02x", $_ } @bytes; + return $str; + } else { + return pack "C*", @bytes; + } + } +} + +sub parse { + my ($template, $data) = @_; + return map { &parseone($_, $data) } split //, $template; +} + +sub str { + # Quote as a string. If I get enthusiastic I might arrange for + # strange characters inside the string to be quoted. + my $str = shift @_; + return "'$str'"; +} + +sub sftp_setup { + my $index = shift @_; + my $chan = $channels[$index]; + $chan->{'obuf'} = $chan->{'ibuf'} = ''; + $chan->{'ocnt'} = $chan->{'icnt'} = 0; + $chan->{'odata'} = $chan->{'idata'} = \&sftp_data; + $chan->{'sftpreqs'} = {}; +} + +sub sftp_data { + my ($chan, $index, $direction, $data) = @_; + my $buf = \$chan->{$direction."buf"}; + my $cnt = \$chan->{$direction."cnt"}; + $$buf .= $data; + while (length $$buf >= 4) { + my $msglen = unpack "N", $$buf; + last if length $$buf < 4 + $msglen; + my $msg = substr $$buf, 4, $msglen; + $$buf = substr $$buf, 4 + $msglen; + $msg = [unpack "C*", $msg]; + my $type = shift @$msg; + my $id = sprintf "ch%d_sftp_%s%d", $index, $direction, ${$cnt}++; + print "$id: "; + if (defined $sftp_packets{$type}) { + $sftp_packets{$type}->($chan, $index, $direction, $id, $msg); + } else { + printf "unknown SFTP packet type %d\n", $type; + } + } +} + +sub sftp_logreq { + my ($chan, $direction, $reqid, $id, $name) = @_; + print "$name"; + if ($direction eq "o") { # requests coming _in_ are too weird to track + $chan->{'sftpreqs'}->{$reqid} = $id; + } +} + +sub sftp_logreply { + my ($chan, $direction, $reqid, $id, $name) = @_; + print "$name"; + if ($direction eq "i") { # replies going _out_ are too weird to track + if (defined $chan->{'sftpreqs'}->{$reqid}) { + print " to ", $chan->{'sftpreqs'}->{$reqid}; + $chan->{'sftpreqs'}->{$reqid} = undef; + } + } +} + +sub sftp_parse_attrs { + my ($data) = @_; + my ($flags) = &parse("u", $data); + return $flags if $flags eq ""; + my $out = "{"; + my $sep = ""; + if ($flags & 0x00000001) { # SSH_FILEXFER_ATTR_SIZE + $out .= $sep . sprintf "size=%d", &parse("U", $data); + $sep = ", "; + } + if ($flags & 0x00000002) { # SSH_FILEXFER_ATTR_UIDGID + $out .= $sep . sprintf "uid=%d", &parse("u", $data); + $out .= $sep . sprintf "gid=%d", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x00000004) { # SSH_FILEXFER_ATTR_PERMISSIONS + $out .= $sep . sprintf "perms=%#o", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x00000008) { # SSH_FILEXFER_ATTR_ACMODTIME + $out .= $sep . sprintf "atime=%d", &parse("u", $data); + $out .= $sep . sprintf "mtime=%d", &parse("u", $data); + $sep = ", "; + } + if ($flags & 0x80000000) { # SSH_FILEXFER_ATTR_EXTENDED + my $extcount = &parse("u", $data); + while ($extcount-- > 0) { + $out .= $sep . sprintf "\"%s\"=\"%s\"", &parse("ss", $data); + $sep = ", "; + } + } + $out .= "}"; + return $out; +} + +sub stringescape { + my ($str) = @_; + $str =~ s!\\!\\\\!g; + $str =~ s![^ -~]!sprintf "\\x%02X", ord $&!eg; + return $str; +} diff --git a/putty/CPROXY.C b/putty/CPROXY.C index 5537fca..d5049af 100644 --- a/putty/CPROXY.C +++ b/putty/CPROXY.C @@ -130,7 +130,8 @@ int proxy_socks5_handlechap (Proxy_Socket p) outbuf[2] = 0x04; /* Response */ outbuf[3] = 0x10; /* Length */ hmacmd5_chap(data, p->chap_current_datalen, - p->cfg.proxy_password, &outbuf[4]); + conf_get_str(p->conf, CONF_proxy_password), + &outbuf[4]); sk_write(p->sub_socket, (char *)outbuf, 20); break; case 0x11: @@ -159,7 +160,9 @@ int proxy_socks5_handlechap (Proxy_Socket p) int proxy_socks5_selectchap(Proxy_Socket p) { - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { + char *username = conf_get_str(p->conf, CONF_proxy_username); + char *password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { char chapbuf[514]; int ulen; chapbuf[0] = '\x01'; /* Version */ @@ -169,11 +172,11 @@ int proxy_socks5_selectchap(Proxy_Socket p) chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */ chapbuf[5] = '\x02'; /* Second attribute - username */ - ulen = strlen(p->cfg.proxy_username); + ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; chapbuf[6] = ulen; - memcpy(chapbuf+7, p->cfg.proxy_username, ulen); + memcpy(chapbuf+7, username, ulen); sk_write(p->sub_socket, chapbuf, ulen + 7); p->chap_num_attributes = 0; diff --git a/putty/DIALOG.C b/putty/DIALOG.C index 55bece8..63bcb1e 100644 --- a/putty/DIALOG.C +++ b/putty/DIALOG.C @@ -47,6 +47,7 @@ struct controlbox *ctrl_new_box(void) ret->ctrlsets = NULL; ret->nfrees = ret->freesize = 0; ret->frees = NULL; + ret->freefuncs = NULL; return ret; } @@ -59,9 +60,10 @@ void ctrl_free_box(struct controlbox *b) ctrl_free_set(b->ctrlsets[i]); } for (i = 0; i < b->nfrees; i++) - sfree(b->frees[i]); + b->freefuncs[i](b->frees[i]); sfree(b->ctrlsets); sfree(b->frees); + sfree(b->freefuncs); sfree(b); } @@ -181,7 +183,8 @@ struct controlset *ctrl_getset(struct controlbox *b, } /* Allocate some private data in a controlbox. */ -void *ctrl_alloc(struct controlbox *b, size_t size) +void *ctrl_alloc_with_free(struct controlbox *b, size_t size, + ctrl_freefn_t freefunc) { void *p; /* @@ -192,11 +195,24 @@ void *ctrl_alloc(struct controlbox *b, size_t size) if (b->nfrees >= b->freesize) { b->freesize = b->nfrees + 32; b->frees = sresize(b->frees, b->freesize, void *); + b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t); } - b->frees[b->nfrees++] = p; + b->frees[b->nfrees] = p; + b->freefuncs[b->nfrees] = freefunc; + b->nfrees++; return p; } +static void ctrl_default_free(void *p) +{ + sfree(p); +} + +void *ctrl_alloc(struct controlbox *b, size_t size) +{ + return ctrl_alloc_with_free(b, size, ctrl_default_free); +} + static union control *ctrl_new(struct controlset *s, int type, intorptr helpctx, handler_fn handler, intorptr context) @@ -456,140 +472,3 @@ void ctrl_free(union control *ctrl) } sfree(ctrl); } - -void dlg_stdradiobutton_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - int button; - /* - * For a standard radio button set, the context parameter gives - * offsetof(targetfield, Config), and the extra data per button - * gives the value the target field should take if that button - * is the one selected. - */ - if (event == EVENT_REFRESH) { - for (button = 0; button < ctrl->radio.nbuttons; button++) - if (*(int *)ATOFFSET(data, ctrl->radio.context.i) == - ctrl->radio.buttondata[button].i) - break; - /* We expected that `break' to happen, in all circumstances. */ - assert(button < ctrl->radio.nbuttons); - dlg_radiobutton_set(ctrl, dlg, button); - } else if (event == EVENT_VALCHANGE) { - button = dlg_radiobutton_get(ctrl, dlg); - assert(button >= 0 && button < ctrl->radio.nbuttons); - *(int *)ATOFFSET(data, ctrl->radio.context.i) = - ctrl->radio.buttondata[button].i; - } -} - -void dlg_stdcheckbox_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - int offset, invert; - - /* - * For a standard checkbox, the context parameter gives - * offsetof(targetfield, Config), optionally ORed with - * CHECKBOX_INVERT. - */ - offset = ctrl->checkbox.context.i; - if (offset & CHECKBOX_INVERT) { - offset &= ~CHECKBOX_INVERT; - invert = 1; - } else - invert = 0; - - /* - * C lacks a logical XOR, so the following code uses the idiom - * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1 - * iff exactly one of a and b is nonzero, otherwise 0.) - */ - - if (event == EVENT_REFRESH) { - dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert)); - } else if (event == EVENT_VALCHANGE) { - *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert; - } -} - -void dlg_stdeditbox_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard edit-box handler expects the main `context' - * field to contain the `offsetof' a field in the structure - * pointed to by `data'. The secondary `context2' field - * indicates the type of this field: - * - * - if context2 > 0, the field is a char array and context2 - * gives its size. - * - if context2 == -1, the field is an int and the edit box - * is numeric. - * - if context2 < -1, the field is an int and the edit box is - * _floating_, and (-context2) gives the scale. (E.g. if - * context2 == -1000, then typing 1.2 into the box will set - * the field to 1200.) - */ - int offset = ctrl->editbox.context.i; - int length = ctrl->editbox.context2.i; - - if (length > 0) { - char *field = (char *)ATOFFSET(data, offset); - if (event == EVENT_REFRESH) { - dlg_editbox_set(ctrl, dlg, field); - } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, field, length); - } - } else if (length < 0) { - int *field = (int *)ATOFFSET(data, offset); - char data[80]; - if (event == EVENT_REFRESH) { - if (length == -1) - sprintf(data, "%d", *field); - else - sprintf(data, "%g", (double)*field / (double)(-length)); - dlg_editbox_set(ctrl, dlg, data); - } else if (event == EVENT_VALCHANGE) { - dlg_editbox_get(ctrl, dlg, data, lenof(data)); - if (length == -1) - *field = atoi(data); - else - *field = (int)((-length) * atof(data)); - } - } -} - -void dlg_stdfilesel_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard file-selector handler expects the `context' - * field to contain the `offsetof' a Filename field in the - * structure pointed to by `data'. - */ - int offset = ctrl->fileselect.context.i; - - if (event == EVENT_REFRESH) { - dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset)); - } else if (event == EVENT_VALCHANGE) { - dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset)); - } -} - -void dlg_stdfontsel_handler(union control *ctrl, void *dlg, - void *data, int event) -{ - /* - * The standard file-selector handler expects the `context' - * field to contain the `offsetof' a FontSpec field in the - * structure pointed to by `data'. - */ - int offset = ctrl->fontselect.context.i; - - if (event == EVENT_REFRESH) { - dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset)); - } else if (event == EVENT_VALCHANGE) { - dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset)); - } -} diff --git a/putty/DIALOG.H b/putty/DIALOG.H index 67f79ed..db28da3 100644 --- a/putty/DIALOG.H +++ b/putty/DIALOG.H @@ -162,7 +162,7 @@ union control { * * The `data' parameter points to the writable data being * modified as a result of the configuration activity; for - * example, the PuTTY `Config' structure, although not + * example, the PuTTY `Conf' structure, although not * necessarily. * * The `dlg' parameter is passed back to the platform- @@ -427,6 +427,8 @@ struct controlset { union control **ctrls; /* actual array */ }; +typedef void (*ctrl_freefn_t)(void *); /* used by ctrl_alloc_with_free */ + /* * This is the container structure which holds a complete set of * controls. @@ -438,6 +440,7 @@ struct controlbox { int nfrees; int freesize; void **frees; /* array of aux data areas to free */ + ctrl_freefn_t *freefuncs; /* parallel array of free functions */ }; struct controlbox *ctrl_new_box(void); @@ -464,8 +467,14 @@ void ctrl_free(union control *); * and so data allocated through this function is better not used * to hold modifiable per-instance things. It's mostly here for * allocating structures to be passed as control handler params. + * + * ctrl_alloc_with_free also allows you to provide a function to free + * the structure, in case there are other dynamically allocated bits + * and pieces dangling off it. */ void *ctrl_alloc(struct controlbox *b, size_t size); +void *ctrl_alloc_with_free(struct controlbox *b, size_t size, + ctrl_freefn_t freefunc); /* * Individual routines to create `union control' structures in a controlset. @@ -522,60 +531,6 @@ union control *ctrl_checkbox(struct controlset *, char *label, char shortcut, union control *ctrl_tabdelay(struct controlset *, union control *); /* - * Standard handler routines to cover most of the common cases in - * the config box. - */ -/* - * The standard radio-button handler expects the main `context' - * field to contain the `offsetof' of an int field in the structure - * pointed to by `data', and expects each of the individual button - * data to give a value for that int field. - */ -void dlg_stdradiobutton_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard checkbox handler expects the main `context' field - * to contain the `offsetof' an int field in the structure pointed - * to by `data', optionally ORed with CHECKBOX_INVERT to indicate - * that the sense of the datum is opposite to the sense of the - * checkbox. - */ -#define CHECKBOX_INVERT (1<<30) -void dlg_stdcheckbox_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard edit-box handler expects the main `context' field - * to contain the `offsetof' a field in the structure pointed to by - * `data'. The secondary `context2' field indicates the type of - * this field: - * - * - if context2 > 0, the field is a char array and context2 gives - * its size. - * - if context2 == -1, the field is an int and the edit box is - * numeric. - * - if context2 < -1, the field is an int and the edit box is - * _floating_, and (-context2) gives the scale. (E.g. if - * context2 == -1000, then typing 1.2 into the box will set the - * field to 1200.) - */ -void dlg_stdeditbox_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard file-selector handler expects the main `context' - * field to contain the `offsetof' a Filename field in the - * structure pointed to by `data'. - */ -void dlg_stdfilesel_handler(union control *ctrl, void *dlg, - void *data, int event); -/* - * The standard font-selector handler expects the main `context' - * field to contain the `offsetof' a Font field in the structure - * pointed to by `data'. - */ -void dlg_stdfontsel_handler(union control *ctrl, void *dlg, - void *data, int event); - -/* * Routines the platform-independent dialog code can call to read * and write the values of controls. */ @@ -584,7 +539,7 @@ int dlg_radiobutton_get(union control *ctrl, void *dlg); void dlg_checkbox_set(union control *ctrl, void *dlg, int checked); int dlg_checkbox_get(union control *ctrl, void *dlg); void dlg_editbox_set(union control *ctrl, void *dlg, char const *text); -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length); +char *dlg_editbox_get(union control *ctrl, void *dlg); /* result must be freed by caller */ /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, void *dlg); void dlg_listbox_del(union control *ctrl, void *dlg, int index); @@ -604,10 +559,10 @@ int dlg_listbox_index(union control *ctrl, void *dlg); int dlg_listbox_issel(union control *ctrl, void *dlg, int index); void dlg_listbox_select(union control *ctrl, void *dlg, int index); void dlg_text_set(union control *ctrl, void *dlg, char const *text); -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn); -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn); -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fn); -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fn); +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn); +Filename *dlg_filesel_get(union control *ctrl, void *dlg); +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn); +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg); /* * Bracketing a large set of updates in these two functions will * cause the front end (if possible) to delay updating the screen diff --git a/putty/DOC/BLURB.BUT b/putty/DOC/BLURB.BUT index f03341d..4a99646 100644 --- a/putty/DOC/BLURB.BUT +++ b/putty/DOC/BLURB.BUT @@ -1,4 +1,6 @@ -\define{versionidblurb} \versionid $Id: blurb.but 9072 2011-01-05 12:01:00Z jacob $ +\define{versionidblurb} \versionid $Id: blurb.but 9993 2013-08-05 15:15:17Z jacob $ + +\define{dash} \u2013{-} \title PuTTY User Manual @@ -31,6 +33,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-2011 Simon Tatham. All +\copyright This manual is copyright 2001-2013 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. diff --git a/putty/DOC/CONFIG.BUT b/putty/DOC/CONFIG.BUT index 2a87f3a..9840aed 100644 --- a/putty/DOC/CONFIG.BUT +++ b/putty/DOC/CONFIG.BUT @@ -1,4 +1,4 @@ -\define{versionidconfig} \versionid $Id: config.but 9063 2010-12-29 14:11:25Z simon $ +\define{versionidconfig} \versionid $Id: config.but 9846 2013-05-28 23:46:44Z jacob $ \C{config} Configuring PuTTY @@ -1254,12 +1254,16 @@ mechanism for PuTTY and the server to communicate this information, so it must usually be manually configured. There are a lot of character sets to choose from. The \q{Remote -character set} option lets you select one. By default PuTTY will -attempt to choose a character set that is right for your \i{locale} as -reported by Windows; if it gets it wrong, you can select a different -one using this control. +character set} option lets you select one. -A few notable character sets are: +By default PuTTY will use the \i{UTF-8} encoding of \i{Unicode}, which +can represent pretty much any character; data coming from the server +is interpreted as UTF-8, and keystrokes are sent UTF-8 encoded. This +is what most modern distributions of Linux will expect by default. +However, if this is wrong for your server, you can select a different +character set using this control. + +A few other notable character sets are: \b The \i{ISO-8859} series are all standard character sets that include various accented characters appropriate for different sets of @@ -1273,12 +1277,6 @@ Euro symbol. \b If you want the old IBM PC character set with block graphics and line-drawing characters, you can select \q{\i{CP437}}. -\b PuTTY also supports \i{Unicode} mode, in which the data coming from -the server is interpreted as being in the \i{UTF-8} encoding of Unicode, -and keystrokes are sent UTF-8 encoded. If you select \q{UTF-8} as a -character set you can use this mode. Not all server-side applications -will support it. - If you need support for a numeric \i{code page} which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (\c{\i{CP866}} for example) in the list box. If the @@ -1541,20 +1539,22 @@ If you do not see \cq{colors#256} in the output, you may need to change your terminal setting. On modern Linux machines, you could try \cq{xterm-256color}. -\S{config-boldcolour} \q{Bolded text is a different colour} +\S{config-boldcolour} \q{Indicate bolded text by changing} \cfg{winhelp-topic}{colours.bold} When the server sends a \i{control sequence} indicating that some text -should be displayed in \i{bold}, PuTTY can handle this two ways. It can -either change the \i{font} for a bold version, or use the same font in a -brighter colour. This control lets you choose which. - -By default the box is checked, so non-bold text is displayed in -light grey and bold text is displayed in bright white (and similarly -in other colours). If you uncheck the box, bold and non-bold text -will be displayed in the same colour, and instead the font will -change to indicate the difference. +should be displayed in \i{bold}, PuTTY can handle this in several +ways. It can either change the \i{font} for a bold version, or use the +same font in a brighter colour, or it can do both (brighten the colour +\e{and} embolden the font). This control lets you choose which. + +By default bold is indicated by colour, so non-bold text is displayed +in light grey and bold text is displayed in bright white (and +similarly in other colours). If you change the setting to \q{The font} +box, bold and non-bold text will be displayed in the same colour, and +instead the font will change to indicate the difference. If you select +\q{Both}, the font and the colour will both change. \S{config-logpalette} \q{Attempt to use \i{logical palettes}} @@ -3206,6 +3206,29 @@ 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. +\S{config-ssh-bug-winadj} \q{Chokes on PuTTY's SSH-2 \cq{winadj} requests} + +\cfg{winhelp-topic}{ssh.bugs.winadj} + +PuTTY sometimes sends a special request to SSH servers in the middle +of channel data, with the name \cw{winadj@putty.projects.tartarus.org} +(see \k{sshnames-channel}). The purpose of this request is to measure +the round-trip time to the server, which PuTTY uses to tune its flow +control. The server does not actually have to \e{understand} the +message; it is expected to send back a \cw{SSH_MSG_CHANNEL_FAILURE} +message indicating that it didn't understand it. (All PuTTY needs for +its timing calculations is \e{some} kind of response.) + +It has been known for some SSH servers to get confused by this message +in one way or another \dash because it has a long name, or because +they can't cope with unrecognised request names even to the extent of +sending back the correct failure response, or because they handle it +sensibly but fill up the server's log file with pointless spam, or +whatever. PuTTY therefore supports this bug-compatibility flag: if it +believes the server has this bug, it will never send its +\cq{winadj@putty.projects.tartarus.org} request, and will make do +without its timing data. + \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply diff --git a/putty/DOC/ERRORS.BUT b/putty/DOC/ERRORS.BUT index 4de6713..ca1e59c 100644 --- a/putty/DOC/ERRORS.BUT +++ b/putty/DOC/ERRORS.BUT @@ -1,4 +1,4 @@ -\define{versioniderrors} \versionid $Id: errors.but 8897 2010-03-13 14:47:14Z jacob $ +\define{versioniderrors} \versionid $Id: errors.but 9627 2012-08-26 09:50:57Z jacob $ \C{errors} Common \i{error messages} @@ -58,20 +58,6 @@ in the same way as you would if it was new. See \k{gs-hostkey} for more information on host keys. -\H{errors-portfwd-space} \q{Out of space for port forwardings} - -PuTTY has a fixed-size buffer which it uses to store the details of -all \i{port forwardings} you have set up in an SSH session. If you -specify too many port forwardings on the PuTTY or Plink command line -and this buffer becomes full, you will see this error message. - -We need to fix this (fixed-size buffers are almost always a mistake) -but we haven't got round to it. If you actually have trouble with -this, let us know and we'll move it up our priority list. - -If you're running into this limit, you may want to consider using -dynamic port forwarding instead; see \k{using-port-forwarding}. - \H{errors-cipher-warning} \q{The first cipher supported by the server is ... below the configured warning threshold} diff --git a/putty/DOC/FAQ.BUT b/putty/DOC/FAQ.BUT index 913c254..d363280 100644 --- a/putty/DOC/FAQ.BUT +++ b/putty/DOC/FAQ.BUT @@ -1,4 +1,4 @@ -\define{versionidfaq} \versionid $Id: faq.but 8733 2009-11-01 22:06:05Z jacob $ +\define{versionidfaq} \versionid $Id: faq.but 9391 2012-01-30 00:29:32Z jacob $ \A{faq} PuTTY \i{FAQ} @@ -1043,6 +1043,23 @@ is triggered by PuTTY 0.58. This was fixed in 0.59. The \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}} entry in PuTTY's wishlist has more details. +\S{faq-system32}{Question} When I put PuTTY in +\cw{C:\\WINDOWS\\\i{SYSTEM32}} on my \i{64-bit Windows} system, +\i{\q{Duplicate Session}} doesn't work. + +The short answer is not to put the PuTTY executables in that location. + +On 64-bit systems, \cw{C:\\WINDOWS\\SYSTEM32} is intended to contain +only 64-bit binaries; Windows' 32-bit binaries live in +\cw{C:\\WINDOWS\\SYSWOW64}. When a 32-bit program such as PuTTY runs +on a 64-bit system, it cannot by default see the \q{real} +\cw{C:\\WINDOWS\\SYSTEM32} at all, because the +\W{http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx}{File +System Redirector} arranges that the running program sees the +appropriate kind of binaries in \cw{SYSTEM32}. Thus, operations in +the PuTTY suite that involve it accessing its own executables, such as +\i{\q{New Session}} and \q{Duplicate Session}, will not work. + \H{faq-secure} Security questions \S{faq-publicpc}{Question} Is it safe for me to download PuTTY and diff --git a/putty/DOC/INDEX.BUT b/putty/DOC/INDEX.BUT index aded2e9..a42f42e 100644 --- a/putty/DOC/INDEX.BUT +++ b/putty/DOC/INDEX.BUT @@ -1,4 +1,4 @@ -\define{versionidindex} \versionid $Id: index.but 9009 2010-09-25 16:18:02Z jacob $ +\define{versionidindex} \versionid $Id: index.but 9391 2012-01-30 00:29:32Z jacob $ \IM{Unix version} Unix version of PuTTY tools \IM{Unix version} Linux version of PuTTY tools @@ -849,3 +849,8 @@ saved sessions from \IM{GSSAPI credential delegation} GSSAPI credential delegation \IM{GSSAPI credential delegation} credential delegation, GSSAPI \IM{GSSAPI credential delegation} delegation, of GSSAPI credentials + +\IM{SYSTEM32} \cw{SYSTEM32} directory, on Windows + +\IM{64-bit Windows} 64-bit Windows +\IM{64-bit Windows} Windows, 64-bit diff --git a/putty/DOC/LICENCE.BUT b/putty/DOC/LICENCE.BUT index 62d8d74..986b38a 100644 --- a/putty/DOC/LICENCE.BUT +++ b/putty/DOC/LICENCE.BUT @@ -1,8 +1,8 @@ -\define{versionidlicence} \versionid $Id: licence.but 9072 2011-01-05 12:01:00Z jacob $ +\define{versionidlicence} \versionid $Id: licence.but 9993 2013-08-05 15:15:17Z jacob $ \A{licence} PuTTY \ii{Licence} -PuTTY is \i{copyright} 1997-2011 Simon Tatham. +PuTTY is \i{copyright} 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/putty/DOC/MAN-PTEL.BUT b/putty/DOC/MAN-PTEL.BUT index 1bdd334..f85eacb 100644 --- a/putty/DOC/MAN-PTEL.BUT +++ b/putty/DOC/MAN-PTEL.BUT @@ -37,7 +37,7 @@ Sorry.) \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, -so this option will be ignored. If \cw{BoldAsColour} is set to 0 +so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{puttytel} will overprint the normal font to make it look bolder. @@ -50,7 +50,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -74,12 +74,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if -the \cw{BoldAsColour} resource is set to 1 (the default). (This +the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) diff --git a/putty/DOC/MAN-PTER.BUT b/putty/DOC/MAN-PTER.BUT index 9e96f77..5ac3f7e 100644 --- a/putty/DOC/MAN-PTER.BUT +++ b/putty/DOC/MAN-PTER.BUT @@ -57,7 +57,7 @@ sets of defaults and choose between them. \dd Specify the font to use for bold text displayed in the terminal. If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, -so this option will be ignored. If \cw{BoldAsColour} is set to 0 +so this option will be ignored. If \cw{BoldAsColour} is set to 0 or 2 and you do not specify a bold font, \cw{pterm} will overprint the normal font to make it look bolder. @@ -70,7 +70,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -94,12 +94,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video text, if -the \cw{BoldAsColour} resource is set to 1 (the default). (This +the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) @@ -498,7 +498,7 @@ controls the font used to display normal text. The default is \dd This resource is the same as the \cw{\-fb} command-line option: it controls the font used to display bold text when \cw{BoldAsColour} -is turned off. The default is unset (the font will be bolded by +is set to 0 or 2. The default is unset (the font will be bolded by printing it twice at a one-pixel offset). \dt \cw{pterm.WideFont} @@ -511,7 +511,7 @@ default is unset (double-width characters cannot be displayed). \dd This resource is the same as the \cw{\-fwb} command-line option: it controls the font used to display double-width characters in bold, -when \cw{BoldAsColour} is turned off. The default is unset +when \cw{BoldAsColour} is set to 0 or 2. The default is unset (double-width characters are displayed in bold by printing them twice at a one-pixel offset). @@ -529,10 +529,11 @@ than 1 (in one direction or the other). \dt \cw{pterm.BoldAsColour} -\dd This option should be set to either 0 or 1; the default is 1. It -specifies the default state of auto wrap mode. When set to 1, bold +\dd This option should be set to either 0, 1, or 2; the default is 1. +It specifies how bold text should be displayed. When set to 1, bold text is shown by displaying it in a brighter colour; when set to 0, -bold text is shown by displaying it in a heavier font. +bold text is shown by displaying it in a heavier font; when set to 2, +both effects happen at once (a heavy font \e{and} a brighter colour). \dt \cw{pterm.Colour0}, \cw{pterm.Colour1}, ..., \cw{pterm.Colour21} diff --git a/putty/DOC/MAN-PUTT.BUT b/putty/DOC/MAN-PUTT.BUT index 004c88e..27b831a 100644 --- a/putty/DOC/MAN-PUTT.BUT +++ b/putty/DOC/MAN-PUTT.BUT @@ -37,7 +37,7 @@ Sorry.) If the \cw{BoldAsColour} resource is set to 1 (the default), bold text will be displayed in different colours instead of a different font, so this option will be ignored. If \cw{BoldAsColour} is set to -0 and you do not specify a bold font, \cw{putty} will overprint the +0 or 2 and you do not specify a bold font, \cw{putty} will overprint the normal font to make it look bolder. \dt \cw{\-fw} \e{font-name} @@ -49,7 +49,7 @@ Chinese, Japanese and Korean text) displayed in the terminal. \dd Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \cw{-fb}, this -will be ignored unless the \cw{BoldAsColour} resource is set to 0. +will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2. \dt \cw{\-geometry} \e{geometry} @@ -73,12 +73,12 @@ terminal. \dt \cw{\-bfg} \e{colour} \dd Specify the foreground colour to use for bold text, if the -\cw{BoldAsColour} resource is set to 1 (the default). +\cw{BoldAsColour} resource is set to 1 (the default) or 2. \dt \cw{\-bbg} \e{colour} \dd Specify the foreground colour to use for bold reverse-video -text, if the \cw{BoldAsColour} resource is set to 1 (the default). +text, if the \cw{BoldAsColour} resource is set to 1 (the default) or 2. (This colour is best thought of as the bold version of the background colour; so it only appears when text is displayed \e{in} the background colour.) diff --git a/putty/DOC/PLINK.BUT b/putty/DOC/PLINK.BUT index bd0104b..82fad08 100644 --- a/putty/DOC/PLINK.BUT +++ b/putty/DOC/PLINK.BUT @@ -1,4 +1,4 @@ -\define{versionidplink} \versionid $Id: plink.but 9366 2011-12-10 12:08:09Z simon $ +\define{versionidplink} \versionid $Id: plink.but 9998 2013-08-06 17:09:07Z simon $ \C{plink} Using the command-line connection tool \i{Plink} @@ -43,7 +43,7 @@ use Plink: \c Z:\sysosd>plink \c PuTTY Link: command-line connection utility -\c Release 0.62 +\c Release 0.63 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: diff --git a/putty/DOC/PSCP.BUT b/putty/DOC/PSCP.BUT index 9bf0089..d44c552 100644 --- a/putty/DOC/PSCP.BUT +++ b/putty/DOC/PSCP.BUT @@ -1,4 +1,4 @@ -\define{versionidpscp} \versionid $Id: pscp.but 9366 2011-12-10 12:08:09Z simon $ +\define{versionidpscp} \versionid $Id: pscp.but 9998 2013-08-06 17:09:07Z simon $ \#FIXME: Need examples @@ -41,7 +41,7 @@ use PSCP: \c Z:\owendadmin>pscp \c PuTTY Secure Copy client -\c Release 0.62 +\c Release 0.63 \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/putty/DOC/PUBKEY.BUT b/putty/DOC/PUBKEY.BUT index 580c6ee..4b55564 100644 --- a/putty/DOC/PUBKEY.BUT +++ b/putty/DOC/PUBKEY.BUT @@ -1,4 +1,4 @@ -\define{versionidpubkey} \versionid $Id: pubkey.but 8607 2009-07-12 12:02:58Z simon $ +\define{versionidpubkey} \versionid $Id: pubkey.but 9422 2012-03-04 01:01:11Z jacob $ \C{pubkey} Using public keys for SSH authentication @@ -151,18 +151,6 @@ of the key PuTTYgen will generate. Currently 1024 bits should be sufficient for most purposes. -Note that an RSA key is generated by finding two primes of half the -length requested, and then multiplying them together. For example, -if you ask PuTTYgen for a 1024-bit RSA key, it will create two -512-bit primes and multiply them. The result of this multiplication -might be 1024 bits long, or it might be only 1023; so you may not -get the exact length of key you asked for. This is perfectly normal, -and you do not need to worry. The lengths should only ever differ by -one, and there is no perceptible drop in security as a result. - -DSA keys are not created by multiplying primes together, so they -should always be exactly the length you asked for. - \S{puttygen-generate} The \q{Generate} button \cfg{winhelp-topic}{puttygen.generate} diff --git a/putty/EMPTY.H b/putty/EMPTY.H new file mode 100644 index 0000000..e607ce8 --- /dev/null +++ b/putty/EMPTY.H @@ -0,0 +1 @@ +/* Empty file touched by automake makefile to force rebuild of version.o */ diff --git a/putty/IMPORT.C b/putty/IMPORT.C index 1490c64..36aa765 100644 --- a/putty/IMPORT.C +++ b/putty/IMPORT.C @@ -289,8 +289,8 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) if (len < 4) goto error; - bytes = GET_32BIT(d); - if (len < 4+bytes) + bytes = toint(GET_32BIT(d)); + if (bytes < 0 || len-4 < bytes) goto error; ret->start = d + 4; @@ -321,7 +321,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, const char **errmsg_p) { struct openssh_key *ret; - FILE *fp; + FILE *fp = NULL; char *line = NULL; char *errmsg, *p; int headers_done; @@ -334,7 +334,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, ret->encrypted = 0; memset(ret->iv, 0, sizeof(ret->iv)); - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; @@ -358,7 +358,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, errmsg = "unrecognised key type"; goto error; } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; @@ -370,8 +370,11 @@ static struct openssh_key *load_openssh_key(const Filename *filename, } strip_crlf(line); if (0 == strncmp(line, "-----END ", 9) && - 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) + 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { + sfree(line); + line = NULL; break; /* done */ + } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; @@ -442,17 +445,20 @@ static struct openssh_key *load_openssh_key(const Filename *filename, memcpy(ret->keyblob + ret->keyblob_len, out, len); ret->keyblob_len += len; - memset(out, 0, sizeof(out)); + smemclr(out, sizeof(out)); } p++; } } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } + fclose(fp); + fp = NULL; + if (ret->keyblob_len == 0 || !ret->keyblob) { errmsg = "key body not present"; goto error; @@ -463,26 +469,27 @@ static struct openssh_key *load_openssh_key(const Filename *filename, goto error; } - memset(base64_bit, 0, sizeof(base64_bit)); + smemclr(base64_bit, sizeof(base64_bit)); if (errmsg_p) *errmsg_p = NULL; return ret; error: if (line) { - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } - memset(base64_bit, 0, sizeof(base64_bit)); + smemclr(base64_bit, sizeof(base64_bit)); if (ret) { if (ret->keyblob) { - memset(ret->keyblob, 0, ret->keyblob_size); + smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } - memset(ret, 0, sizeof(*ret)); + smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; + if (fp) fclose(fp); return NULL; } @@ -494,9 +501,9 @@ int openssh_encrypted(const Filename *filename) if (!key) return 0; ret = key->encrypted; - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); return ret; } @@ -564,8 +571,8 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, aes_free_context(ctx); } - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* @@ -588,12 +595,13 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, p = key->keyblob; - /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ + /* Expect the SEQUENCE header. Take its absence as a failure to + * decrypt, if the key was encrypted. */ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); p += ret; if (ret < 0 || id != 16) { errmsg = "ASN.1 decoding failure"; - retval = SSH2_WRONG_PASSPHRASE; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } @@ -625,7 +633,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; - retval = SSH2_WRONG_PASSPHRASE; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } @@ -698,12 +706,12 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, error: if (blob) { - memset(blob, 0, blobsize); + smemclr(blob, blobsize); sfree(blob); } - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return retval; @@ -740,6 +748,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; Bignum bd, bp, bq, bdmp1, bdmq1; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); @@ -793,6 +805,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos p, q, g, y, x; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); @@ -911,15 +927,15 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, */ des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb", TRUE); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs(header, fp); @@ -936,19 +952,19 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, error: if (outblob) { - memset(outblob, 0, outlen); + smemclr(outblob, outlen); sfree(outblob); } if (spareblob) { - memset(spareblob, 0, sparelen); + smemclr(spareblob, sparelen); sfree(spareblob); } if (privblob) { - memset(privblob, 0, privlen); + smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { - memset(pubblob, 0, publen); + smemclr(pubblob, publen); sfree(pubblob); } return ret; @@ -1053,7 +1069,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; @@ -1067,7 +1083,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, errmsg = "file does not begin with ssh.com key header"; goto error; } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; @@ -1078,8 +1094,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, goto error; } strip_crlf(line); - if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) + if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) { + sfree(line); + line = NULL; break; /* done */ + } if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; @@ -1112,7 +1131,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, len += line2len - 1; assert(!line[len]); - memset(line2, 0, strlen(line2)); + smemclr(line2, strlen(line2)); sfree(line2); line2 = NULL; } @@ -1158,7 +1177,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, p++; } } - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } @@ -1168,21 +1187,25 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, goto error; } + fclose(fp); if (errmsg_p) *errmsg_p = NULL; return ret; error: + if (fp) + fclose(fp); + if (line) { - memset(line, 0, strlen(line)); + smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret) { if (ret->keyblob) { - memset(ret->keyblob, 0, ret->keyblob_size); + smemclr(ret->keyblob, ret->keyblob_size); sfree(ret->keyblob); } - memset(ret, 0, sizeof(*ret)); + smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; @@ -1194,45 +1217,51 @@ int sshcom_encrypted(const Filename *filename, char **comment) struct sshcom_key *key = load_sshcom_key(filename, NULL); int pos, len, answer; + answer = 0; + *comment = NULL; if (!key) - return 0; + goto done; /* * Check magic number. */ - if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) - return 0; /* key is invalid */ + if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) { + goto done; /* key is invalid */ + } /* * Find the cipher-type string. */ - answer = 0; pos = 8; if (key->keyblob_len < pos+4) goto done; /* key is far too short */ - pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */ - if (key->keyblob_len < pos+4) + len = toint(GET_32BIT(key->keyblob + pos)); + if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* key is far too short */ - len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */ - if (key->keyblob_len < pos+4+len) + pos += 4 + len; /* skip key type */ + len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */ + if (len < 0 || len > key->keyblob_len - pos - 4) goto done; /* cipher type string is incomplete */ if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) answer = 1; done: - *comment = dupstr(key->comment); - memset(key->keyblob, 0, key->keyblob_size); - sfree(key->keyblob); - memset(key, 0, sizeof(*key)); - sfree(key); + if (key) { + *comment = dupstr(key->comment); + smemclr(key->keyblob, key->keyblob_size); + sfree(key->keyblob); + smemclr(key, sizeof(*key)); + sfree(key); + } else { + *comment = dupstr(""); + } return answer; } static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) { - int bits; - int bytes; + unsigned bits, bytes; unsigned char *d = (unsigned char *) data; if (len < 4) @@ -1304,7 +1333,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, */ pos = 8; if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a key type string"; goto error; } @@ -1324,7 +1354,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, * Determine the cipher type. */ if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain a cipher type string"; goto error; } @@ -1342,7 +1373,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, * Get hold of the encrypted part of the key. */ if (key->keyblob_len < pos+4 || - (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + (len = toint(GET_32BIT(key->keyblob + pos))) < 0 || + len > key->keyblob_len - pos - 4) { errmsg = "key blob does not contain actual key data"; goto error; } @@ -1390,8 +1422,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); /* * Hereafter we return WRONG_PASSPHRASE for any parsing @@ -1406,7 +1438,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, /* * Strip away the containing string to get to the real meat. */ - len = GET_32BIT(ciphertext); + len = toint(GET_32BIT(ciphertext)); if (len < 0 || len > cipherlen-4) { errmsg = "containing string was ill-formed"; goto error; @@ -1447,9 +1479,12 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, pos += put_mp(blob+pos, p.start, p.bytes); pos += put_mp(blob+pos, u.start, u.bytes); privlen = pos - publen; - } else if (type == DSA) { + } else { struct mpint_pos p, q, g, x, y; int pos = 4; + + assert(type == DSA); /* the only other option from the if above */ + if (GET_32BIT(ciphertext) != 0) { errmsg = "predefined DSA parameters not supported"; goto error; @@ -1474,8 +1509,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, publen = pos; pos += put_mp(blob+pos, x.start, x.bytes); privlen = pos - publen; - } else - return NULL; + } assert(privlen > 0); /* should have bombed by now if not */ @@ -1494,12 +1528,12 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, error: if (blob) { - memset(blob, 0, blobsize); + smemclr(blob, blobsize); sfree(blob); } - memset(key->keyblob, 0, key->keyblob_size); + smemclr(key->keyblob, key->keyblob_size); sfree(key->keyblob); - memset(key, 0, sizeof(*key)); + smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return ret; @@ -1535,6 +1569,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos n, e, d, p, q, iqmp; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); @@ -1560,6 +1598,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, int pos; struct mpint_pos p, q, g, y, x; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ pos = 4 + GET_32BIT(pubblob); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); @@ -1664,15 +1706,15 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, cipherlen); - memset(&md5c, 0, sizeof(md5c)); - memset(keybuf, 0, sizeof(keybuf)); + smemclr(&md5c, sizeof(md5c)); + smemclr(keybuf, sizeof(keybuf)); } /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb", TRUE); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); @@ -1700,15 +1742,15 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, error: if (outblob) { - memset(outblob, 0, outlen); + smemclr(outblob, outlen); sfree(outblob); } if (privblob) { - memset(privblob, 0, privlen); + smemclr(privblob, privlen); sfree(privblob); } if (pubblob) { - memset(pubblob, 0, publen); + smemclr(pubblob, publen); sfree(pubblob); } return ret; diff --git a/putty/LDISC.C b/putty/LDISC.C index 119a02a..7ed42a3 100644 --- a/putty/LDISC.C +++ b/putty/LDISC.C @@ -12,12 +12,12 @@ #include "terminal.h" #include "ldisc.h" -#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \ - (ldisc->cfg->localecho == AUTO && \ +#define ECHOING (ldisc->localecho == FORCE_ON || \ + (ldisc->localecho == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \ term_ldisc(ldisc->term, LD_ECHO)))) -#define EDITING (ldisc->cfg->localedit == FORCE_ON || \ - (ldisc->cfg->localedit == AUTO && \ +#define EDITING (ldisc->localedit == FORCE_ON || \ + (ldisc->localedit == AUTO && \ (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \ term_ldisc(ldisc->term, LD_EDIT)))) @@ -76,7 +76,7 @@ static void bsb(Ldisc ldisc, int n) #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) -void *ldisc_create(Config *mycfg, Terminal *term, +void *ldisc_create(Conf *conf, Terminal *term, Backend *back, void *backhandle, void *frontend) { @@ -87,12 +87,13 @@ void *ldisc_create(Config *mycfg, Terminal *term, ldisc->bufsiz = 0; ldisc->quotenext = 0; - ldisc->cfg = mycfg; ldisc->back = back; ldisc->backhandle = backhandle; ldisc->term = term; ldisc->frontend = frontend; + ldisc_configure(ldisc, conf); + /* Link ourselves into the backend and the terminal */ if (term) term->ldisc = ldisc; @@ -102,6 +103,17 @@ void *ldisc_create(Config *mycfg, Terminal *term, return ldisc; } +void ldisc_configure(void *handle, Conf *conf) +{ + Ldisc ldisc = (Ldisc) handle; + + ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard); + ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline); + ldisc->protocol = conf_get_int(conf, CONF_protocol); + ldisc->localecho = conf_get_int(conf, CONF_localecho); + ldisc->localedit = conf_get_int(conf, CONF_localedit); +} + void ldisc_free(void *handle) { Ldisc ldisc = (Ldisc) handle; @@ -203,7 +215,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) * configured telnet specials off! This breaks * talkers otherwise. */ - if (!ldisc->cfg->telnet_keyboard) + if (!ldisc->telnet_keyboard) goto default_case; if (c == CTRL('C')) ldisc->back->special(ldisc->backhandle, TS_IP); @@ -255,7 +267,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) * default clause because of the break. */ case CTRL('J'): - if (ldisc->cfg->protocol == PROT_RAW && + if (ldisc->protocol == PROT_RAW && ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); @@ -264,9 +276,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) case KCTRL('M'): /* send with newline */ if (ldisc->buflen > 0) ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen); - if (ldisc->cfg->protocol == PROT_RAW) + if (ldisc->protocol == PROT_RAW) ldisc->back->send(ldisc->backhandle, "\r\n", 2); - else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline) + else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); @@ -300,27 +312,27 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) if (len > 0) { if (ECHOING) c_write(ldisc, buf, len); - if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) { + if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) { switch (buf[0]) { case CTRL('M'): - if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline) + if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) ldisc->back->special(ldisc->backhandle, TS_EOL); else ldisc->back->send(ldisc->backhandle, "\r", 1); break; case CTRL('?'): case CTRL('H'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_EC); break; } case CTRL('C'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_IP); break; } case CTRL('Z'): - if (ldisc->cfg->telnet_keyboard) { + if (ldisc->telnet_keyboard) { ldisc->back->special(ldisc->backhandle, TS_SUSP); break; } diff --git a/putty/LDISC.H b/putty/LDISC.H index ef84f6d..030c0ce 100644 --- a/putty/LDISC.H +++ b/putty/LDISC.H @@ -11,10 +11,14 @@ typedef struct ldisc_tag { Terminal *term; Backend *back; - Config *cfg; void *backhandle; void *frontend; + /* + * Values cached out of conf. + */ + int telnet_keyboard, telnet_newline, protocol, localecho, localedit; + char *buf; int buflen, bufsiz, quotenext; } *Ldisc; diff --git a/putty/LDISCUCS.C b/putty/LDISCUCS.C index eda383b..5cece3e 100644 --- a/putty/LDISCUCS.C +++ b/putty/LDISCUCS.C @@ -51,13 +51,12 @@ void luni_send(void *handle, wchar_t * widebuf, int len, int interactive) for (p = linebuffer, i = 0; i < len; i++) { unsigned long ch = widebuf[i]; - if ((ch & 0xF800) == 0xD800) { + if (IS_SURROGATE(ch)) { #ifdef PLATFORM_IS_UTF16 if (i+1 < len) { unsigned long ch2 = widebuf[i+1]; - if ((ch & 0xFC00) == 0xD800 && - (ch2 & 0xFC00) == 0xDC00) { - ch = 0x10000 + ((ch & 0x3FF) << 10) + (ch2 & 0x3FF); + if (IS_SURROGATE_PAIR(ch, ch2)) { + ch = FROM_SURROGATES(ch, ch2); i++; } } else diff --git a/putty/LICENCE b/putty/LICENCE index d404fee..dece9d2 100644 --- a/putty/LICENCE +++ b/putty/LICENCE @@ -1,4 +1,4 @@ -PuTTY is copyright 1997-2011 Simon Tatham. +PuTTY is copyright 1997-2013 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, diff --git a/putty/LOGGING.C b/putty/LOGGING.C index 8fc5819..153ba67 100644 --- a/putty/LOGGING.C +++ b/putty/LOGGING.C @@ -16,12 +16,13 @@ struct LogContext { FILE *lgfp; enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state; bufchain queue; - Filename currlogfilename; + Filename *currlogfilename; void *frontend; - Config cfg; + Conf *conf; + int logtype; /* cached out of conf */ }; -static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm); +static Filename *xlatlognam(Filename *s, char *hostname, struct tm *tm); /* * Internal wrapper function which must be called for _all_ output @@ -75,7 +76,7 @@ static void logprintf(struct LogContext *ctx, const char *fmt, ...) */ void logflush(void *handle) { struct LogContext *ctx = (struct LogContext *)handle; - if (ctx->cfg.logtype > 0) + if (ctx->logtype > 0) if (ctx->state == L_OPEN) fflush(ctx->lgfp); } @@ -110,12 +111,12 @@ static void logfopen_callback(void *handle, int mode) ctx->state == L_ERROR ? (mode == 0 ? "Disabled writing" : "Error writing") : (mode == 1 ? "Appending" : "Writing new"), - (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" : - ctx->cfg.logtype == LGTYP_DEBUG ? "raw" : - ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" : - ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" : + (ctx->logtype == LGTYP_ASCII ? "ASCII" : + ctx->logtype == LGTYP_DEBUG ? "raw" : + ctx->logtype == LGTYP_PACKETS ? "SSH packets" : + ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" : "unknown"), - filename_to_str(&ctx->currlogfilename)); + filename_to_str(ctx->currlogfilename)); logevent(ctx->frontend, event); sfree(event); @@ -148,19 +149,24 @@ void logfopen(void *handle) if (ctx->state != L_CLOSED) return; - if (!ctx->cfg.logtype) + if (!ctx->logtype) return; tm = ltime(); /* substitute special codes in file name */ - xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm); + if (ctx->currlogfilename) + filename_free(ctx->currlogfilename); + ctx->currlogfilename = + xlatlognam(conf_get_filename(ctx->conf, CONF_logfilename), + conf_get_str(ctx->conf, CONF_host), &tm); ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE); /* file already present? */ if (ctx->lgfp) { + int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr); fclose(ctx->lgfp); - if (ctx->cfg.logxfovr != LGXF_ASK) { - mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1); + if (logxfovr != LGXF_ASK) { + mode = ((logxfovr == LGXF_OVR) ? 2 : 1); } else mode = askappend(ctx->frontend, ctx->currlogfilename, logfopen_callback, ctx); @@ -189,8 +195,8 @@ void logfclose(void *handle) void logtraffic(void *handle, unsigned char c, int logmode) { struct LogContext *ctx = (struct LogContext *)handle; - if (ctx->cfg.logtype > 0) { - if (ctx->cfg.logtype == logmode) + if (ctx->logtype > 0) { + if (ctx->logtype == logmode) logwrite(ctx, &c, 1); } } @@ -214,8 +220,8 @@ void log_eventlog(void *handle, const char *event) /* If we don't have a context yet (eg winnet.c init) then skip entirely */ if (!ctx) return; - if (ctx->cfg.logtype != LGTYP_PACKETS && - ctx->cfg.logtype != LGTYP_SSHRAW) + if (ctx->logtype != LGTYP_PACKETS && + ctx->logtype != LGTYP_SSHRAW) return; logprintf(ctx, "Event Log: %s\r\n", event); logflush(ctx); @@ -236,8 +242,8 @@ void log_packet(void *handle, int direction, int type, int p = 0, b = 0, omitted = 0; int output_pos = 0; /* NZ if pending output in dumpdata */ - if (!(ctx->cfg.logtype == LGTYP_SSHRAW || - (ctx->cfg.logtype == LGTYP_PACKETS && texttype))) + if (!(ctx->logtype == LGTYP_SSHRAW || + (ctx->logtype == LGTYP_PACKETS && texttype))) return; /* Packet header. */ @@ -252,8 +258,21 @@ void log_packet(void *handle, int direction, int type, type, type, texttype); } } else { - logprintf(ctx, "%s raw data\r\n", - direction == PKT_INCOMING ? "Incoming" : "Outgoing"); + /* + * Raw data is logged with a timestamp, so that it's possible + * to determine whether a mysterious delay occurred at the + * client or server end. (Timestamping the raw data avoids + * cluttering the normal case of only logging decrypted SSH + * messages, and also adds conceptual rigour in the case where + * an SSH message arrives in several pieces.) + */ + char buf[256]; + struct tm tm; + tm = ltime(); + strftime(buf, 24, "%Y-%m-%d %H:%M:%S", &tm); + logprintf(ctx, "%s raw data at %s\r\n", + direction == PKT_INCOMING ? "Incoming" : "Outgoing", + buf); } /* @@ -326,13 +345,15 @@ void log_packet(void *handle, int direction, int type, logflush(ctx); } -void *log_init(void *frontend, Config *cfg) +void *log_init(void *frontend, Conf *conf) { struct LogContext *ctx = snew(struct LogContext); ctx->lgfp = NULL; ctx->state = L_CLOSED; ctx->frontend = frontend; - ctx->cfg = *cfg; /* STRUCTURE COPY */ + ctx->conf = conf_copy(conf); + ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); + ctx->currlogfilename = NULL; bufchain_init(&ctx->queue); return ctx; } @@ -343,16 +364,20 @@ void log_free(void *handle) logfclose(ctx); bufchain_clear(&ctx->queue); + if (ctx->currlogfilename) + filename_free(ctx->currlogfilename); sfree(ctx); } -void log_reconfig(void *handle, Config *cfg) +void log_reconfig(void *handle, Conf *conf) { struct LogContext *ctx = (struct LogContext *)handle; int reset_logging; - if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) || - ctx->cfg.logtype != cfg->logtype) + if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename), + conf_get_filename(conf, CONF_logfilename)) || + conf_get_int(ctx->conf, CONF_logtype) != + conf_get_int(conf, CONF_logtype)) reset_logging = TRUE; else reset_logging = FALSE; @@ -360,7 +385,10 @@ void log_reconfig(void *handle, Config *cfg) if (reset_logging) logfclose(ctx); - ctx->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(ctx->conf); + ctx->conf = conf_copy(conf); + + ctx->logtype = conf_get_int(ctx->conf, CONF_logtype); if (reset_logging) logfopen(ctx); @@ -372,17 +400,19 @@ void log_reconfig(void *handle, Config *cfg) * * "&Y":YYYY "&m":MM "&d":DD "&T":hhmmss "&h": "&&":& */ -static void xlatlognam(Filename *dest, Filename src, - char *hostname, struct tm *tm) { +static Filename *xlatlognam(Filename *src, char *hostname, struct tm *tm) +{ char buf[10], *bufp; int size; - char buffer[FILENAME_MAX]; - int len = sizeof(buffer)-1; - char *d; + char *buffer; + int buflen, bufsize; const char *s; + Filename *ret; - d = buffer; - s = filename_to_str(&src); + bufsize = FILENAME_MAX; + buffer = snewn(bufsize, char); + buflen = 0; + s = filename_to_str(src); while (*s) { /* Let (bufp, len) be the string to append. */ @@ -418,13 +448,16 @@ static void xlatlognam(Filename *dest, Filename src, buf[0] = *s++; size = 1; } - if (size > len) - size = len; - memcpy(d, bufp, size); - d += size; - len -= size; + if (bufsize <= buflen + size) { + bufsize = (buflen + size) * 5 / 4 + 512; + buffer = sresize(buffer, bufsize, char); + } + memcpy(buffer + buflen, bufp, size); + buflen += size; } - *d = '\0'; + buffer[buflen] = '\0'; - *dest = filename_from_str(buffer); + ret = filename_from_str(buffer); + sfree(buffer); + return ret; } diff --git a/putty/MACOSX/MAKEFILE b/putty/MACOSX/MAKEFILE index 1771e0b..d654584 100644 --- a/putty/MACOSX/MAKEFILE +++ b/putty/MACOSX/MAKEFILE @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -128,20 +133,21 @@ PuTTY: PuTTY.app/Contents/MacOS/PuTTY \ PuTTY.app/Contents/Resources/PuTTY.icns \ PuTTY.app/Contents/Info.plist $(PuTTY_extra) -PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o config.o \ - cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o osxctrls.o \ - osxdlg.o osxmain.o osxsel.o osxwin.o pgssapi.o pinger.o \ - portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \ - settings.o slookup.o ssh.o sshaes.o ssharcf.o sshblowf.o \ - sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ - sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ - sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o testback.o \ - time.o timing.o toucs.o tree234.o utf8.o ux_x11.o uxagentc.o \ - uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxprint.o \ - uxproxy.o uxpty.o uxsel.o uxser.o uxsignal.o uxstore.o \ - uxucs.o version.o wcwidth.o wildcard.o x11fwd.o xenc.o - $(CC) $(MLDFLAGS) -o $@ be_all_s.o config.o cproxy.o dialog.o \ +PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o conf.o \ + config.o cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o \ + localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ + osxctrls.o osxdlg.o osxmain.o osxsel.o osxwin.o pgssapi.o \ + pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o \ + sercfg.o settings.o slookup.o ssh.o sshaes.o ssharcf.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o \ + sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o \ + testback.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ + uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxprint.o uxproxy.o uxpty.o uxsel.o uxser.o uxsignal.o \ + uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ + xenc.o + $(CC) $(MLDFLAGS) -o $@ be_all_s.o conf.o config.o cproxy.o dialog.o \ fromucs.o ldisc.o ldiscucs.o localenc.o logging.o macenc.o \ mimeenc.o minibidi.o misc.o osxctrls.o osxdlg.o osxmain.o \ osxsel.o osxwin.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ @@ -155,16 +161,16 @@ PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o config.o \ uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ wcwidth.o wildcard.o x11fwd.o xenc.o -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) $(ULDFLAGS) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o settings.o ssh.o sshaes.o ssharcf.o sshblowf.o \ sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ @@ -174,7 +180,7 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ uxnet.o uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o \ uxsignal.o uxstore.o version.o wildcard.o x11fwd.o -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -182,7 +188,7 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o cproxy.o int64.o \ + $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ @@ -192,7 +198,7 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ x11fwd.o -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -200,7 +206,7 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o cproxy.o int64.o \ + $(CC) $(ULDFLAGS) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o \ logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o \ psftp.o settings.o sftp.o ssh.o sshaes.o ssharcf.o \ sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ @@ -210,16 +216,16 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ uxnoise.o uxproxy.o uxsel.o uxsftp.o uxstore.o version.o \ wildcard.o x11fwd.o -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) $(ULDFLAGS) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o \ - sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ - sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ - sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ - uxnoise.o uxstore.o version.o +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ + sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ + sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ + uxstore.o version.o + $(CC) $(ULDFLAGS) -o $@ cmdgen.o conf.o import.o misc.o notiming.o \ + sshaes.o sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o \ + sshprime.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o \ + sshsh512.o sshsha.o time.o tree234.o uxcons.o uxgen.o \ + uxmisc.o uxnoise.o uxstore.o version.o be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -251,6 +257,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -529,7 +540,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< diff --git a/putty/MACOSX/OSXMAIN.M b/putty/MACOSX/OSXMAIN.M index 1c7599c..6a9521a 100644 --- a/putty/MACOSX/OSXMAIN.M +++ b/putty/MACOSX/OSXMAIN.M @@ -85,6 +85,24 @@ static void commonfatalbox(char *p, va_list ap) exit(1); } +void nonfatal(void *frontend, char *p, ...) +{ + char *errorbuf; + NSAlert *alert; + va_list ap; + + va_start(ap, p); + errorbuf = dupvprintf(p, ap); + va_end(ap); + + alert = [[[NSAlert alloc] init] autorelease]; + [alert addButtonWithTitle:@"Error"]; + [alert setInformativeText:[NSString stringWithCString:errorbuf]]; + [alert runModal]; + + sfree(errorbuf); +} + void fatalbox(char *p, ...) { va_list ap; diff --git a/putty/MACOSX/OSXWIN.M b/putty/MACOSX/OSXWIN.M index 9de1e08..ff07080 100644 --- a/putty/MACOSX/OSXWIN.M +++ b/putty/MACOSX/OSXWIN.M @@ -109,11 +109,11 @@ nfg = nbg; nbg = t; } - if (cfg.bold_colour && (attr & ATTR_BOLD)) { + if ((cfg.bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (cfg.bold_colour && (attr & ATTR_BLINK)) { + if ((cfg.bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -129,7 +129,7 @@ widefactor = 1; } - /* FIXME: ATTR_BOLD without cfg.bold_colour */ + /* FIXME: ATTR_BOLD if cfg.bold_style & 1 */ if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; @@ -957,7 +957,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0]; diff --git a/putty/MACOSX/README.OSX b/putty/MACOSX/README.OSX index e9243a0..c059846 100644 --- a/putty/MACOSX/README.OSX +++ b/putty/MACOSX/README.OSX @@ -20,6 +20,15 @@ say you weren't warned! Other ways in which the port is currently unfinished include: +Bit rot +------- + + - the conversion of the old fixed-size 'Config' structure to the + new dynamic 'Conf' was never applied to this directory + + - probably other things are out of date too; it would need some + work to make it compile again + Missing terminal window features -------------------------------- diff --git a/putty/MINIBIDI.C b/putty/MINIBIDI.C index 4c2eea5..1af6c05 100644 --- a/putty/MINIBIDI.C +++ b/putty/MINIBIDI.C @@ -1,5 +1,5 @@ /************************************************************************ - * $Id: minibidi.c 9169 2011-05-07 10:57:19Z simon $ + * $Id: minibidi.c 9426 2012-03-05 18:34:40Z simon $ * * ------------ * Description: @@ -14,9 +14,9 @@ * ----------------- * Revision Details: (Updated by Revision Control System) * ----------------- - * $Date: 2011-05-07 11:57:19 +0100 (Sat, 07 May 2011) $ + * $Date: 2012-03-05 18:34:40 +0000 (Mon, 05 Mar 2012) $ * $Author: simon $ - * $Revision: 9169 $ + * $Revision: 9426 $ * * (www.arabeyes.org - under MIT license) * @@ -58,7 +58,7 @@ shapetypes[(xh)-SHAPE_FIRST].type : SU) /*))*/ #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) typedef struct bidi_char { - wchar_t origwc, wc; + unsigned int origwc, wc; unsigned short index; } bidi_char; @@ -70,7 +70,7 @@ unsigned char setOverrideBits(unsigned char level, unsigned char override); int getPreviousLevel(unsigned char* level, int from); int do_shape(bidi_char *line, bidi_char *to, int count); int do_bidi(bidi_char *line, int count); -void doMirror(wchar_t* ch); +void doMirror(unsigned int *ch); /* character types */ enum { @@ -1636,7 +1636,7 @@ int do_bidi(bidi_char *line, int count) * takes a pointer to a character that is checked for * having a mirror glyph. */ -void doMirror(wchar_t* ch) +void doMirror(unsigned int *ch) { if ((*ch & 0xFF00) == 0) { switch (*ch) { diff --git a/putty/MISC.C b/putty/MISC.C index 4aeab50..d652b39 100644 --- a/putty/MISC.C +++ b/putty/MISC.C @@ -99,24 +99,48 @@ prompts_t *new_prompts(void *frontend) p->name_reqd = p->instr_reqd = FALSE; return p; } -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len) +void add_prompt(prompts_t *p, char *promptstr, int echo) { prompt_t *pr = snew(prompt_t); - char *result = snewn(len, char); pr->prompt = promptstr; pr->echo = echo; - pr->result = result; - pr->result_len = len; + pr->result = NULL; + pr->resultsize = 0; p->n_prompts++; p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); p->prompts[p->n_prompts-1] = pr; } +void prompt_ensure_result_size(prompt_t *pr, int newlen) +{ + if ((int)pr->resultsize < newlen) { + char *newbuf; + newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ + + /* + * We don't use sresize / realloc here, because we will be + * storing sensitive stuff like passwords in here, and we want + * to make sure that the data doesn't get copied around in + * memory without the old copy being destroyed. + */ + newbuf = snewn(newlen, char); + memcpy(newbuf, pr->result, pr->resultsize); + smemclr(pr->result, pr->resultsize); + sfree(pr->result); + pr->result = newbuf; + pr->resultsize = newlen; + } +} +void prompt_set_result(prompt_t *pr, const char *newstr) +{ + prompt_ensure_result_size(pr, strlen(newstr) + 1); + strcpy(pr->result, newstr); +} void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; - memset(pr->result, 0, pr->result_len); /* burn the evidence */ + smemclr(pr->result, pr->resultsize); /* burn the evidence */ sfree(pr->result); sfree(pr->prompt); sfree(pr); @@ -176,6 +200,37 @@ char *dupcat(const char *s1, ...) return p; } +void burnstr(char *string) /* sfree(str), only clear it first */ +{ + if (string) { + smemclr(string, strlen(string)); + sfree(string); + } +} + +int toint(unsigned u) +{ + /* + * Convert an unsigned to an int, without running into the + * undefined behaviour which happens by the strict C standard if + * the value overflows. You'd hope that sensible compilers would + * do the sensible thing in response to a cast, but actually I + * don't trust modern compilers not to do silly things like + * assuming that _obviously_ you wouldn't have caused an overflow + * and so they can elide an 'if (i < 0)' test immediately after + * the cast. + * + * Sensible compilers ought of course to optimise this entire + * function into 'just return the input value'! + */ + if (u <= (unsigned)INT_MAX) + return (int)u; + else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ + return INT_MIN + (int)(u - (unsigned)INT_MIN); + else + return INT_MIN; /* fallback; should never occur on binary machines */ +} + /* * Do an sprintf(), but into a custom-allocated buffer. * @@ -332,12 +387,11 @@ void base64_encode_atom(unsigned char *data, int n, char *out) * - return the current size of the buffer chain in bytes */ -#define BUFFER_GRANULE 512 +#define BUFFER_MIN_GRANULE 512 struct bufchain_granule { struct bufchain_granule *next; - int buflen, bufpos; - char buf[BUFFER_GRANULE]; + char *bufpos, *bufend, *bufmax; }; void bufchain_init(bufchain *ch) @@ -371,28 +425,29 @@ void bufchain_add(bufchain *ch, const void *data, int len) ch->buffersize += len; - if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) { - int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen); - memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen); - buf += copylen; - len -= copylen; - ch->tail->buflen += copylen; - } while (len > 0) { - int grainlen = min(len, BUFFER_GRANULE); - struct bufchain_granule *newbuf; - newbuf = snew(struct bufchain_granule); - newbuf->bufpos = 0; - newbuf->buflen = grainlen; - memcpy(newbuf->buf, buf, grainlen); - buf += grainlen; - len -= grainlen; - if (ch->tail) - ch->tail->next = newbuf; - else - ch->head = ch->tail = newbuf; - newbuf->next = NULL; - ch->tail = newbuf; + if (ch->tail && ch->tail->bufend < ch->tail->bufmax) { + int copylen = min(len, ch->tail->bufmax - ch->tail->bufend); + memcpy(ch->tail->bufend, buf, copylen); + buf += copylen; + len -= copylen; + ch->tail->bufend += copylen; + } + if (len > 0) { + int grainlen = + max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE); + struct bufchain_granule *newbuf; + newbuf = smalloc(grainlen); + newbuf->bufpos = newbuf->bufend = + (char *)newbuf + sizeof(struct bufchain_granule); + newbuf->bufmax = (char *)newbuf + grainlen; + newbuf->next = NULL; + if (ch->tail) + ch->tail->next = newbuf; + else + ch->head = newbuf; + ch->tail = newbuf; + } } } @@ -404,13 +459,13 @@ void bufchain_consume(bufchain *ch, int len) while (len > 0) { int remlen = len; assert(ch->head != NULL); - if (remlen >= ch->head->buflen - ch->head->bufpos) { - remlen = ch->head->buflen - ch->head->bufpos; + if (remlen >= ch->head->bufend - ch->head->bufpos) { + remlen = ch->head->bufend - ch->head->bufpos; tmp = ch->head; ch->head = tmp->next; - sfree(tmp); if (!ch->head) ch->tail = NULL; + sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; @@ -420,8 +475,8 @@ void bufchain_consume(bufchain *ch, int len) void bufchain_prefix(bufchain *ch, void **data, int *len) { - *len = ch->head->buflen - ch->head->bufpos; - *data = ch->head->buf + ch->head->bufpos; + *len = ch->head->bufend - ch->head->bufpos; + *data = ch->head->bufpos; } void bufchain_fetch(bufchain *ch, void *data, int len) @@ -436,9 +491,9 @@ void bufchain_fetch(bufchain *ch, void *data, int len) int remlen = len; assert(tmp != NULL); - if (remlen >= tmp->buflen - tmp->bufpos) - remlen = tmp->buflen - tmp->bufpos; - memcpy(data_c, tmp->buf + tmp->bufpos, remlen); + if (remlen >= tmp->bufend - tmp->bufpos) + remlen = tmp->bufend - tmp->bufpos; + memcpy(data_c, tmp->bufpos, remlen); tmp = tmp->next; len -= remlen; @@ -635,21 +690,61 @@ void debug_memdump(void *buf, int len, int L) #endif /* def DEBUG */ /* - * Determine whether or not a Config structure represents a session - * which can sensibly be launched right now. + * Determine whether or not a Conf represents a session which can + * sensibly be launched right now. */ -int cfg_launchable(const Config *cfg) +int conf_launchable(Conf *conf) { - if (cfg->protocol == PROT_SERIAL) - return cfg->serline[0] != 0; + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + return conf_get_str(conf, CONF_serline)[0] != 0; else - return cfg->host[0] != 0; + return conf_get_str(conf, CONF_host)[0] != 0; } -char const *cfg_dest(const Config *cfg) +char const *conf_dest(Conf *conf) { - if (cfg->protocol == PROT_SERIAL) - return cfg->serline; + if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) + return conf_get_str(conf, CONF_serline); else - return cfg->host; + return conf_get_str(conf, CONF_host); } + +#ifndef PLATFORM_HAS_SMEMCLR +/* + * Securely wipe memory. + * + * The actual wiping is no different from what memset would do: the + * point of 'securely' is to try to be sure over-clever compilers + * won't optimise away memsets on variables that are about to be freed + * or go out of scope. See + * https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html + * + * Some platforms (e.g. Windows) may provide their own version of this + * function. + */ +void smemclr(void *b, size_t n) { + volatile char *vp; + + if (b && n > 0) { + /* + * Zero out the memory. + */ + memset(b, 0, n); + + /* + * Perform a volatile access to the object, forcing the + * compiler to admit that the previous memset was important. + * + * This while loop should in practice run for zero iterations + * (since we know we just zeroed the object out), but in + * theory (as far as the compiler knows) it might range over + * the whole object. (If we had just written, say, '*vp = + * *vp;', a compiler could in principle have 'helpfully' + * optimised the memset into only zeroing out the first byte. + * This should be robust.) + */ + vp = b; + while (*vp) vp++; + } +} +#endif diff --git a/putty/MISC.H b/putty/MISC.H index 1123314..b44e233 100644 --- a/putty/MISC.H +++ b/putty/MISC.H @@ -28,6 +28,9 @@ char *dupstr(const char *s); char *dupcat(const char *s1, ...); char *dupprintf(const char *fmt, ...); char *dupvprintf(const char *fmt, va_list ap); +void burnstr(char *string); + +int toint(unsigned); char *fgetline(FILE *fp); @@ -49,6 +52,8 @@ void bufchain_fetch(bufchain *ch, void *data, int len); struct tm ltime(void); +void smemclr(void *b, size_t len); + /* * Debugging functions. * diff --git a/putty/MKAUTO.SH b/putty/MKAUTO.SH index 6548951..17e98bb 100644 --- a/putty/MKAUTO.SH +++ b/putty/MKAUTO.SH @@ -7,41 +7,5 @@ # well as from outside. test -f unix.h && cd .. -# Persuade automake to give us a copy of its install-sh. This is a -# pain because I don't actually want to have to _use_ automake. -# Instead, I construct a trivial unrelated automake project in a -# temporary subdirectory, run automake so that it'll copy -# install-sh into that directory, then copy it back out again. -# Hideous, but it should work. - -mkdir automake-grievous-hack -cat > automake-grievous-hack/hello.c << EOF -#include -int main(int argc, char **argv) -{ - printf("hello, world\n"); - return 0; -} -EOF -cat > automake-grievous-hack/Makefile.am << EOF -bin_PROGRAMS = hello -hello_SOURCES = hello.c -EOF -cat > automake-grievous-hack/configure.ac << EOF -AC_INIT -AM_INIT_AUTOMAKE(hello, 1.0) -AC_CONFIG_FILES([Makefile]) -AC_PROG_CC -AC_OUTPUT -EOF -echo Some news > automake-grievous-hack/NEWS -echo Some text > automake-grievous-hack/README -echo Some people > automake-grievous-hack/AUTHORS -echo Some changes > automake-grievous-hack/ChangeLog -rm -f install-sh # this won't work if we accidentally have one _here_ -(cd automake-grievous-hack && autoreconf -i && \ - cp install-sh ../unix/install-sh) -rm -rf automake-grievous-hack - -# That was the hard bit. Now run autoconf on our real configure.in. -(cd unix && autoreconf && rm -rf aclocal.m4 autom4te.cache) +# Run autoconf on our real configure.in. +(cd unix && autoreconf -i && rm -rf autom4te.cache) diff --git a/putty/MKFILES.PL b/putty/MKFILES.PL index 8075ddb..24379fc 100644 --- a/putty/MKFILES.PL +++ b/putty/MKFILES.PL @@ -17,8 +17,24 @@ use warnings; use FileHandle; +use File::Basename; use Cwd; +if ($#ARGV >= 0 and ($ARGV[0] eq "-u" or $ARGV[0] eq "-U")) { + # Convenience for Unix users: -u means that after we finish what + # we're doing here, we also run mkauto.sh and then 'configure' in + # the Unix subdirectory. So it's a one-stop shop for regenerating + # the actual end-product Unix makefile. + # + # Arguments supplied after -u go to configure. + # + # -U is identical, but runs 'configure' at the _top_ level, for + # people who habitually do that. + $do_unix = ($ARGV[0] eq "-U" ? 2 : 1); + shift @ARGV; + @confargs = @ARGV; +} + open IN, "Recipe" or do { # We want to deal correctly with being run from one of the # subdirs in the source tree. So if we can't find Recipe here, @@ -64,9 +80,17 @@ while () { if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;} + if ($_[0] eq "!cflags" and &mfval($_[1])) { + ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line + $rest = 1 if $rest eq ""; + $cflags{$_[1]}->{$_[2]} = $rest; + next; + } if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; } if ($_[0] eq "!begin") { - if (&mfval($_[1])) { + if ($_[1] =~ /^>(.*)/) { + $divert = \$auxfiles{$1}; + } elsif (&mfval($_[1])) { $sect = $_[2] ? $_[2] : "end"; $divert = \($makefile_extra{$_[1]}->{$sect}); } else { @@ -123,6 +147,12 @@ while () { close IN; +foreach $aux (sort keys %auxfiles) { + open AUX, ">$aux"; + print AUX $auxfiles{$aux}; + close AUX; +} + # Now retrieve the complete list of objects and resource files, and # construct dependency data for them. While we're here, expand the # object list for each program, and complain if its type isn't set. @@ -177,11 +207,13 @@ foreach $i (@prognames) { # file name into a listref containing further source file names. %further = (); +%allsourcefiles = (); # this is wanted by some makefiles while (scalar @scanlist > 0) { $file = shift @scanlist; next if defined $further{$file}; # skip if we've already done it $further{$file} = []; $dirfile = &findfile($file); + $allsourcefiles{$dirfile} = 1; open IN, "$dirfile" or die "unable to open source file $file\n"; while () { chomp; @@ -226,7 +258,7 @@ sub mfval($) { # prints a warning and returns false; if (grep { $type eq $_ } ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix", - "ac","osx",)) { + "am","osx",)) { return 1; } warn "$.:unknown makefile type '$type'\n"; @@ -391,6 +423,8 @@ sub manpages { return (); } +$orig_dir = cwd; + # Now we're ready to output the actual Makefiles. if (defined $makefiles{'cygwin'}) { @@ -399,7 +433,7 @@ if (defined $makefiles{'cygwin'}) { ##-- CygWin makefile open OUT, ">$makefiles{'cygwin'}"; select OUT; print - "# Makefile for $project_name under cygwin.\n". + "# Makefile for $project_name under Cygwin, MinGW, or Winelib.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; # gcc command line option is -D not /D @@ -419,7 +453,7 @@ if (defined $makefiles{'cygwin'}) { "# RCINC = --include-dir c:\\cygwin\\include\\\n". "\n". &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT". - " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " . + " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -DNO_SECUREZEROMEMORY " . (join " ", map {"-I$dirpfx$_"} @srcdirs)) . "\n". "LDFLAGS = -mno-cygwin -s\n". @@ -450,7 +484,7 @@ if (defined $makefiles{'cygwin'}) { join " ", @{$d->{deps}})), "\n"; } if ($d->{obj} =~ /\.res\.o$/) { - print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." ".$d->{obj}."\n\n"; + print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n"; } else { print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n"; } @@ -458,7 +492,7 @@ if (defined $makefiles{'cygwin'}) { print "\n"; print $makefile_extra{'cygwin'}->{'end'}; print "\nclean:\n". - "\trm -f *.o *.exe *.res.o *.map\n". + "\trm -f *.o *.exe *.res.o *.so *.map\n". "\n". "FORCE:\n"; select STDOUT; close OUT; @@ -669,8 +703,6 @@ if (defined $makefiles{'vc'}) { if (defined $makefiles{'vcproj'}) { $dirpfx = &dirpfx($makefiles{'vcproj'}, "\\"); - $orig_dir = cwd; - ##-- MSVC 6 Workspace and projects # # Note: All files created in this section are written in binary @@ -1084,71 +1116,92 @@ if (defined $makefiles{'unix'}) { select STDOUT; close OUT; } -if (defined $makefiles{'ac'}) { - $dirpfx = &dirpfx($makefiles{'ac'}, "/"); +if (defined $makefiles{'am'}) { + $dirpfx = "\$(srcdir)/" . &dirpfx($makefiles{'am'}, "/"); - ##-- Unix/autoconf makefile - open OUT, ">$makefiles{'ac'}"; select OUT; + ##-- Unix/autoconf Makefile.am + open OUT, ">$makefiles{'am'}"; select OUT; print - "# Makefile.in for $project_name under Unix with Autoconf.\n". + "# Makefile.am for $project_name under Unix with Autoconf/Automake.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/([=" ])\/D/$1-D/gs; - print $_; - print - "\n". - "CC = \@CC\@\n". - "\n". - &splitline("CFLAGS = \@CFLAGS\@ \@PUTTYCFLAGS\@ \@CPPFLAGS\@ " . - "\@DEFS\@ \@GTK_CFLAGS\@ " . - (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". - "XLDFLAGS = \@LDFLAGS\@ \@LIBS\@ \@GTK_LIBS\@\n". - "ULDFLAGS = \@LDFLAGS\@ \@LIBS\@\n". - "INSTALL=\@INSTALL\@\n". - "INSTALL_PROGRAM=\$(INSTALL)\n". - "INSTALL_DATA=\$(INSTALL)\n". - "prefix=\@prefix\@\n". - "exec_prefix=\@exec_prefix\@\n". - "bindir=\@bindir\@\n". - "datarootdir=\@datarootdir\@\n". - "mandir=\@mandir\@\n". - "man1dir=\$(mandir)/man1\n". - "\n". - &def($makefile_extra{'gtk'}->{'vars'}) . - "\n". - ".SUFFIXES:\n". - "\n". - "\n". - "all: \@all_targets\@\n". - &splitline("all-cli:" . join "", map { " $_" } &progrealnames("U"))."\n". - &splitline("all-gtk:" . join "", map { " $_" } &progrealnames("X"))."\n"; - print "\n"; - foreach $p (&prognames("X:U")) { + "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n\n"; + + # Complete list of source and header files. Not used by the + # auto-generated parts of this makefile, but Recipe might like to + # have it available as a variable so that mandatory-rebuild things + # (version.o) can conveniently be made to depend on it. + @sources = ("allsources", "=", + map {"${dirpfx}$_"} sort keys %allsourcefiles); + print &splitline(join " ", @sources), "\n\n"; + + @cliprogs = ("bin_PROGRAMS", "="); + foreach $p (&prognames("U")) { ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", undef, undef); - print &splitline($prog . ": " . $objstr), "\n"; - $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC) -o \$@ " . - $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n"; + push @cliprogs, $prog; } - foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) { - if ($forceobj{$d->{obj_orig}}) { - printf("%s: FORCE\n", $d->{obj}); - } else { - print &splitline(sprintf("%s: %s", $d->{obj}, - join " ", @{$d->{deps}})), "\n"; + @allprogs = @cliprogs; + foreach $p (&prognames("X")) { + ($prog, $type) = split ",", $p; + push @allprogs, $prog; + } + print "if HAVE_GTK\n"; + print &splitline(join " ", @allprogs), "\n"; + print "else\n"; + print &splitline(join " ", @cliprogs), "\n"; + print "endif\n\n"; + + %objtosrc = (); + foreach $d (&deps("X", undef, $dirpfx, "/", "am")) { + $objtosrc{$d->{obj}} = $d->{deps}->[0]; + } + + print &splitline(join " ", "AM_CPPFLAGS", "=", + map {"-I$dirpfx$_"} @srcdirs), "\n"; + + @amcflags = ("\$(COMPAT)", "\$(XFLAGS)", "\$(WARNINGOPTS)"); + print "if HAVE_GTK\n"; + print &splitline(join " ", "AM_CFLAGS", "=", + "\$(GTK_CFLAGS)", @amcflags), "\n"; + print "else\n"; + print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n"; + print "endif\n\n"; + + %amspeciallibs = (); + foreach $obj (sort { $a cmp $b } keys %{$cflags{'am'}}) { + print "lib${obj}_a_SOURCES = ", $objtosrc{$obj}, "\n"; + print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", @amcflags, + $cflags{'am'}->{$obj}), "\n"; + $amspeciallibs{$obj} = "lib${obj}.a"; + } + print &splitline(join " ", "noinst_LIBRARIES", "=", + sort { $a cmp $b } values %amspeciallibs), "\n\n"; + + foreach $p (&prognames("X:U")) { + ($prog, $type) = split ",", $p; + print "if HAVE_GTK\n" if $type eq "X"; + @progsources = ("${prog}_SOURCES", "="); + %sourcefiles = (); + @ldadd = (); + $objstr = &objects($p, "X", undef, undef); + foreach $obj (split / /,$objstr) { + if ($amspeciallibs{$obj}) { + push @ldadd, $amspeciallibs{$obj}; + } else { + $sourcefiles{$objtosrc{$obj}} = 1; + } } - print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); + push @progsources, sort { $a cmp $b } keys %sourcefiles; + print &splitline(join " ", @progsources), "\n"; + if ($type eq "X") { + push @ldadd, "\$(GTK_LIBS)"; + } + if (@ldadd) { + print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n"; + } + print "endif\n" if $type eq "X"; + print "\n"; } - print "\n"; - print $makefile_extra{'gtk'}->{'end'}; - print "\nclean:\n". - "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n"; - print "\ndistclean: clean\n". - "\t". &splitline("rm -f config.status config.cache config.log ". - "configure.lineno config.status.lineno Makefile") . "\n"; - print "\nFORCE:\n"; + print $makefile_extra{'am'}->{'end'}; select STDOUT; close OUT; } @@ -1336,6 +1389,8 @@ if (defined $makefiles{'devcppproj'}) { create_devcpp_project(\%all_object_deps, $progname); } + chdir $orig_dir; + sub create_devcpp_project { my ($all_object_deps, $progname) = @_; # Construct program's dependency info (Taken from 'vcproj', seems to work right here, too.) @@ -1491,3 +1546,17 @@ if (defined $makefiles{'devcppproj'}) { chdir ".."; } } + +# All done, so do the Unix postprocessing if asked to. + +if ($do_unix) { + chdir $orig_dir; + system "./mkauto.sh"; + die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0; + if ($do_unix == 1) { + chdir ($targetdir = dirname($makefiles{"am"})) + or die "$targetdir: chdir: $!\n"; + } + system "./configure", @confargs; + die "mkfiles.pl: configure returned $?\n" if $? > 0; +} diff --git a/putty/MKUNXARC.SH b/putty/MKUNXARC.SH index aed6728..5631832 100644 --- a/putty/MKUNXARC.SH +++ b/putty/MKUNXARC.SH @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Build a Unix source distribution from the PuTTY CVS area. # @@ -10,16 +10,19 @@ case "$1" in ????-??-??) case "$1" in *[!-0-9]*) echo "Malformed snapshot ID '$1'" >&2;exit 1;;esac - arcsuffix="-`cat LATEST.VER`-$1" + autoconfver="`cat LATEST.VER`-$1" + arcsuffix="-$autoconfver" ver="-DSNAPSHOT=$1" docver= ;; r*) - arcsuffix="-$1" - ver="-DSVN_REV=$1" + autoconfver="$1" + arcsuffix="-$autoconfver" + ver="-DSVN_REV=${1#r}" docver= ;; '') + autoconfver="X.XX" # got to put something in here! arcsuffix= ver= docver= @@ -35,7 +38,8 @@ case "$1" in ;; *) case "$1" in *[!.0-9a-z~]*) echo "Malformed release ID '$1'">&2;exit 1;;esac - arcsuffix="-$1" + autoconfver="$1" + arcsuffix="-$autoconfver" ver="-DRELEASE=$1" docver="VERSION=\"PuTTY release $1\"" ;; @@ -43,7 +47,6 @@ esac perl mkfiles.pl (cd doc && make -s ${docver:+"$docver"}) -sh mkauto.sh 2>/dev/null relver=`cat LATEST.VER` arcname="putty$arcsuffix" @@ -58,6 +61,7 @@ find . -name uxarc -prune -o \ -name CVS -prune -o \ -name .cvsignore -prune -o \ -name .svn -prune -o \ + -name configure.ac -prune -o \ -name '*.zip' -prune -o \ -name '*.tar.gz' -prune -o \ -type f -exec ln -s $PWD/{} uxarc/$arcname/{} \; @@ -66,5 +70,8 @@ if test "x$ver" != "x"; then md5sum `find . -name '*.[ch]' -print` > manifest; echo "$ver" > version.def) fi +sed "s/^AC_INIT(putty,.*/AC_INIT(putty, $autoconfver)/" unix/configure.ac > uxarc/$arcname/unix/configure.ac +(cd uxarc/$arcname && sh mkauto.sh) 2>errors || { cat errors >&2; exit 1; } + tar -C uxarc -chzof $arcname.tar.gz $arcname rm -rf uxarc diff --git a/putty/NETWORK.H b/putty/NETWORK.H index b1b5590..4c9fef0 100644 --- a/putty/NETWORK.H +++ b/putty/NETWORK.H @@ -15,7 +15,7 @@ #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -37,6 +37,7 @@ struct socket_function_table { void (*close) (Socket s); int (*write) (Socket s, const char *data, int len); int (*write_oob) (Socket s, const char *data, int len); + void (*write_eof) (Socket s); void (*flush) (Socket s); void (*set_private_ptr) (Socket s, void *ptr); void *(*get_private_ptr) (Socket s); @@ -94,18 +95,18 @@ struct plug_function_table { Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg); + Plug plug, Conf *conf); Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, - const Config *cfg, int addressfamily); + Conf *conf, int addressfamily); SockAddr name_lookup(char *host, int port, char **canonicalname, - const Config *cfg, int addressfamily); + Conf *conf, int addressfamily); /* platform-dependent callback from new_connection() */ /* (same caveat about addr as new_connection()) */ Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg); + Plug plug, Conf *conf); /* socket functions */ @@ -115,8 +116,9 @@ void sk_cleanup(void); /* called just before program exit */ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family); SockAddr sk_nonamelookup(const char *host); void sk_getaddr(SockAddr addr, char *buf, int buflen); -int sk_hostname_is_local(char *name); +int sk_hostname_is_local(const char *name); int sk_address_is_local(SockAddr addr); +int sk_address_is_special_local(SockAddr addr); int sk_addrtype(SockAddr addr); void sk_addrcopy(SockAddr addr, char *buf); void sk_addr_free(SockAddr addr); @@ -140,6 +142,7 @@ Socket sk_register(OSSocket sock, Plug plug); #define sk_close(s) (((*s)->close) (s)) #define sk_write(s,buf,len) (((*s)->write) (s, buf, len)) #define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len)) +#define sk_write_eof(s) (((*s)->write_eof) (s)) #define sk_flush(s) (((*s)->flush) (s)) #ifdef DEFINE_PLUG_METHOD_MACROS diff --git a/putty/NOTIMING.C b/putty/NOTIMING.C index aed3e45..75cc214 100644 --- a/putty/NOTIMING.C +++ b/putty/NOTIMING.C @@ -11,7 +11,7 @@ #include "putty.h" -long schedule_timer(int ticks, timer_fn_t fn, void *ctx) +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { return 0; } diff --git a/putty/PINGER.C b/putty/PINGER.C index b6fde24..071d02c 100644 --- a/putty/PINGER.C +++ b/putty/PINGER.C @@ -8,18 +8,18 @@ struct pinger_tag { int interval; int pending; - long next; + unsigned long next; Backend *back; void *backhandle; }; static void pinger_schedule(Pinger pinger); -static void pinger_timer(void *ctx, long now) +static void pinger_timer(void *ctx, unsigned long now) { Pinger pinger = (Pinger)ctx; - if (pinger->pending && now - pinger->next >= 0) { + if (pinger->pending && now == pinger->next) { pinger->back->special(pinger->backhandle, TS_PING); pinger->pending = FALSE; pinger_schedule(pinger); @@ -43,11 +43,11 @@ static void pinger_schedule(Pinger pinger) } } -Pinger pinger_new(Config *cfg, Backend *back, void *backhandle) +Pinger pinger_new(Conf *conf, Backend *back, void *backhandle) { Pinger pinger = snew(struct pinger_tag); - pinger->interval = cfg->ping_interval; + pinger->interval = conf_get_int(conf, CONF_ping_interval); pinger->pending = FALSE; pinger->back = back; pinger->backhandle = backhandle; @@ -56,10 +56,11 @@ Pinger pinger_new(Config *cfg, Backend *back, void *backhandle) return pinger; } -void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg) +void pinger_reconfig(Pinger pinger, Conf *oldconf, Conf *newconf) { - if (oldcfg->ping_interval != newcfg->ping_interval) { - pinger->interval = newcfg->ping_interval; + int newinterval = conf_get_int(newconf, CONF_ping_interval); + if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) { + pinger->interval = newinterval; pinger_schedule(pinger); } } diff --git a/putty/PORTFWD.C b/putty/PORTFWD.C index e5874a6..bb9abe6 100644 --- a/putty/PORTFWD.C +++ b/putty/PORTFWD.C @@ -33,14 +33,16 @@ struct PFwdPrivate { int dynamic; /* * `hostname' and `port' are the real hostname and port, once - * we know what we're connecting to; they're unused for this - * purpose while conducting a local SOCKS exchange, which means - * we can also use them as a buffer and pointer for reading - * data from the SOCKS client. + * we know what we're connecting to. */ - char hostname[256+8]; + char *hostname; int port; /* + * `socksbuf' is the buffer we use to accumulate a SOCKS request. + */ + char *socksbuf; + int sockslen, sockssize; + /* * When doing dynamic port forwarding, we can receive * connection data before we are actually able to send it; so * we may have to temporarily hold some in a dynamically @@ -50,6 +52,26 @@ struct PFwdPrivate { int buflen; }; +static struct PFwdPrivate *new_portfwd_private(void) +{ + struct PFwdPrivate *pr = snew(struct PFwdPrivate); + pr->hostname = NULL; + pr->socksbuf = NULL; + pr->sockslen = pr->sockssize = 0; + pr->buffer = NULL; + return pr; +} + +static void free_portfwd_private(struct PFwdPrivate *pr) +{ + if (!pr) + return; + sfree(pr->hostname); + sfree(pr->socksbuf); + sfree(pr->buffer); + sfree(pr); +} + static void pfd_log(Plug plug, int type, SockAddr addr, int port, const char *error_msg, int error_code) { @@ -61,14 +83,20 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code, { struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; - /* - * We have no way to communicate down the forwarded connection, - * so if an error occurred on the socket, we just ignore it - * and treat it like a proper close. - */ - if (pr->c) - sshfwd_close(pr->c); - pfd_close(pr->s); + if (error_msg) { + /* + * Socket error. Slam the connection instantly shut. + */ + sshfwd_unclean_close(pr->c); + } else { + /* + * Ordinary EOF received on socket. Send an EOF on the SSH + * channel. + */ + if (pr->c) + sshfwd_write_eof(pr->c); + } + return 1; } @@ -77,37 +105,26 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; if (pr->dynamic) { while (len--) { - /* - * Throughout SOCKS negotiation, "hostname" is re-used as a - * random protocol buffer with "port" storing the length. - */ - if (pr->port >= lenof(pr->hostname)) { - /* Request too long. */ - if ((pr->dynamic >> 12) == 4) { - /* Send back a SOCKS 4 error before closing. */ - char data[8]; - memset(data, 0, sizeof(data)); - data[1] = 91; /* generic `request rejected' */ - sk_write(pr->s, data, 8); - } - pfd_close(pr->s); - return 1; + if (pr->sockslen >= pr->sockssize) { + pr->sockssize = pr->sockslen * 5 / 4 + 256; + pr->socksbuf = sresize(pr->socksbuf, pr->sockssize, char); } - pr->hostname[pr->port++] = *data++; + pr->socksbuf[pr->sockslen++] = *data++; /* * Now check what's in the buffer to see if it's a * valid and complete message in the SOCKS exchange. */ if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) && - pr->hostname[0] == 4) { + pr->socksbuf[0] == 4) { /* * SOCKS 4. */ if (pr->dynamic == 1) pr->dynamic = 0x4000; - if (pr->port < 2) continue;/* don't have command code yet */ - if (pr->hostname[1] != 1) { + if (pr->sockslen < 2) + continue; /* don't have command code yet */ + if (pr->socksbuf[1] != 1) { /* Not CONNECT. */ /* Send back a SOCKS 4 error before closing. */ char data[8]; @@ -117,15 +134,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) pfd_close(pr->s); return 1; } - if (pr->port <= 8) continue; /* haven't started user/hostname */ - if (pr->hostname[pr->port-1] != 0) + if (pr->sockslen <= 8) + continue; /* haven't started user/hostname */ + if (pr->socksbuf[pr->sockslen-1] != 0) continue; /* haven't _finished_ user/hostname */ /* * Now we have a full SOCKS 4 request. Check it to * see if it's a SOCKS 4A request. */ - if (pr->hostname[4] == 0 && pr->hostname[5] == 0 && - pr->hostname[6] == 0 && pr->hostname[7] != 0) { + if (pr->socksbuf[4] == 0 && pr->socksbuf[5] == 0 && + pr->socksbuf[6] == 0 && pr->socksbuf[7] != 0) { /* * It's SOCKS 4A. So if we haven't yet * collected the host name, we should continue @@ -135,15 +153,17 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) int len; if (pr->dynamic == 0x4000) { pr->dynamic = 0x4001; - pr->port = 8; /* reset buffer to overwrite name */ + pr->sockslen = 8; /* reset buffer to overwrite name */ continue; } - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - len= pr->port - 8; - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - memmove(pr->hostname, pr->hostname + 8, len); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + len = pr->sockslen - 8; + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = snewn(len+1, char); + pr->hostname[len] = '\0'; + memcpy(pr->hostname, pr->socksbuf + 8, len); goto connect; } else { /* @@ -151,21 +171,21 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * the IP address into the hostname string and * then just go. */ - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } } if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) && - pr->hostname[0] == 5) { + pr->socksbuf[0] == 5) { /* * SOCKS 5. */ @@ -178,12 +198,13 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) /* * We're receiving a set of method identifiers. */ - if (pr->port < 2) continue;/* no method count yet */ - if (pr->port < 2 + (unsigned char)pr->hostname[1]) + if (pr->sockslen < 2) + continue; /* no method count yet */ + if (pr->sockslen < 2 + (unsigned char)pr->socksbuf[1]) continue; /* no methods yet */ method = 0xFF; /* invalid */ - for (i = 0; i < (unsigned char)pr->hostname[1]; i++) - if (pr->hostname[2+i] == 0) { + for (i = 0; i < (unsigned char)pr->socksbuf[1]; i++) + if (pr->socksbuf[2+i] == 0) { method = 0;/* no auth */ break; } @@ -191,7 +212,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) data[1] = method; sk_write(pr->s, data, 2); pr->dynamic = 0x5001; - pr->port = 0; /* re-empty the buffer */ + pr->sockslen = 0; /* re-empty the buffer */ continue; } @@ -213,16 +234,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) reply[0] = 5; /* VER */ reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */ - if (pr->port < 6) continue; - atype = (unsigned char)pr->hostname[3]; + if (pr->sockslen < 6) continue; + atype = (unsigned char)pr->socksbuf[3]; if (atype == 1) /* IPv4 address */ alen = 4; if (atype == 4) /* IPv6 address */ alen = 16; if (atype == 3) /* domain name has leading length */ - alen = 1 + (unsigned char)pr->hostname[4]; - if (pr->port < 6 + alen) continue; - if (pr->hostname[1] != 1 || pr->hostname[2] != 0) { + alen = 1 + (unsigned char)pr->socksbuf[4]; + if (pr->sockslen < 6 + alen) continue; + if (pr->socksbuf[1] != 1 || pr->socksbuf[2] != 0) { /* Not CONNECT or reserved field nonzero - error */ reply[1] = 1; /* generic failure */ sk_write(pr->s, (char *) reply, lenof(reply)); @@ -233,21 +254,22 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * Now we have a viable connect request. Switch * on atype. */ - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen); + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+4+alen); if (atype == 1) { /* REP=0 (success) already */ sk_write(pr->s, (char *) reply, lenof(reply)); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } else if (atype == 3) { /* REP=0 (success) already */ sk_write(pr->s, (char *) reply, lenof(reply)); - memmove(pr->hostname, pr->hostname + 5, alen-1); + pr->hostname = snewn(alen, char); pr->hostname[alen-1] = '\0'; + memcpy(pr->hostname, pr->socksbuf + 5, alen-1); goto connect; } else { /* @@ -277,6 +299,8 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * connection. */ connect: + sfree(pr->socksbuf); + pr->socksbuf = NULL; /* * Freeze the socket until the SSH server confirms the @@ -325,7 +349,7 @@ static void pfd_sent(Plug plug, int bufsize) * Called when receiving a PORT OPEN from the server */ const char *pfd_newconnect(Socket *s, char *hostname, int port, - void *c, const Config *cfg, int addressfamily) + void *c, Conf *conf, int addressfamily) { static const struct plug_function_table fn_table = { pfd_log, @@ -343,17 +367,17 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port, /* * Try to find host. */ - addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily); + addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); + sfree(dummy_realhost); return err; } /* * Open socket. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->throttled = pr->throttle_override = 0; pr->ready = 1; @@ -362,9 +386,10 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port, pr->dynamic = 0; pr->s = *s = new_connection(addr, dummy_realhost, port, - 0, 1, 0, 0, (Plug) pr, cfg); + 0, 1, 0, 0, (Plug) pr, conf); + sfree(dummy_realhost); if ((err = sk_socket_error(*s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -390,8 +415,7 @@ static int pfd_accepting(Plug p, OSSocket sock) const char *err; org = (struct PFwdPrivate *)p; - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; @@ -399,7 +423,7 @@ static int pfd_accepting(Plug p, OSSocket sock) pr->s = s = sk_register(sock, (Plug) pr); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err != NULL; } @@ -414,12 +438,12 @@ static int pfd_accepting(Plug p, OSSocket sock) sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ } else { pr->dynamic = 0; - strcpy(pr->hostname, org->hostname); + pr->hostname = dupstr(org->hostname); pr->port = org->port; pr->c = new_sock_channel(org->backhandle, s); if (pr->c == NULL) { - sfree(pr); + free_portfwd_private(pr); return 1; } else { /* asks to forward to the specified host/port for this */ @@ -435,7 +459,7 @@ static int pfd_accepting(Plug p, OSSocket sock) sets up a listener on the local machine on (srcaddr:)port */ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, - int port, void *backhandle, const Config *cfg, + int port, void *backhandle, Conf *conf, void **sockdata, int address_family) { static const struct plug_function_table fn_table = { @@ -453,12 +477,11 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, /* * Open socket. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; if (desthost) { - strcpy(pr->hostname, desthost); + pr->hostname = dupstr(desthost); pr->port = destport; pr->dynamic = 0; } else @@ -468,9 +491,10 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, pr->backhandle = backhandle; pr->s = s = new_listener(srcaddr, port, (Plug) pr, - !cfg->lport_acceptall, cfg, address_family); + !conf_get_int(conf, CONF_lport_acceptall), + conf, address_family); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -490,8 +514,7 @@ void pfd_close(Socket s) pr = (struct PFwdPrivate *) sk_get_private_ptr(s); - sfree(pr->buffer); - sfree(pr); + free_portfwd_private(pr); sk_close(s); } @@ -536,6 +559,10 @@ int pfd_send(Socket s, char *data, int len) return sk_write(s, data, len); } +void pfd_send_eof(Socket s) +{ + sk_write_eof(s); +} void pfd_confirm(Socket s) { diff --git a/putty/PPROXY.C b/putty/PPROXY.C index 5ab31b2..e2954a2 100644 --- a/putty/PPROXY.C +++ b/putty/PPROXY.C @@ -11,7 +11,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { return NULL; } diff --git a/putty/PROXY.C b/putty/PROXY.C index 1f42999..a48a73b 100644 --- a/putty/PROXY.C +++ b/putty/PROXY.C @@ -14,9 +14,10 @@ #include "network.h" #include "proxy.h" -#define do_proxy_dns(cfg) \ - (cfg->proxy_dns == FORCE_ON || \ - (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4)) +#define do_proxy_dns(conf) \ + (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \ + (conf_get_int(conf, CONF_proxy_dns) == AUTO && \ + conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4)) /* * Call this when proxy negotiation is complete, so that this @@ -64,6 +65,9 @@ void proxy_activate (Proxy_Socket p) */ if (p->pending_flush) sk_flush(p->sub_socket); + /* if we have a pending EOF to send, send it */ + if (p->pending_eof) sk_write_eof(p->sub_socket); + /* if the backend wanted the socket unfrozen, try to unfreeze. * our set_frozen handler will flush buffered receive data before * unfreezing the actual underlying socket. @@ -116,6 +120,17 @@ static int sk_proxy_write_oob (Socket s, const char *data, int len) return sk_write_oob(ps->sub_socket, data, len); } +static void sk_proxy_write_eof (Socket s) +{ + Proxy_Socket ps = (Proxy_Socket) s; + + if (ps->state != PROXY_STATE_ACTIVE) { + ps->pending_eof = 1; + return; + } + sk_write_eof(ps->sub_socket); +} + static void sk_proxy_flush (Socket s) { Proxy_Socket ps = (Proxy_Socket) s; @@ -262,8 +277,8 @@ static int plug_proxy_accepting (Plug p, OSSocket sock) * This function can accept a NULL pointer as `addr', in which case * it will only check the host name. */ -static int proxy_for_destination (SockAddr addr, char *hostname, int port, - const Config *cfg) +static int proxy_for_destination (SockAddr addr, const char *hostname, + int port, Conf *conf) { int s = 0, e = 0; char hostip[64]; @@ -271,10 +286,19 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, const char *exclude_list; /* + * Special local connections such as Unix-domain sockets + * unconditionally cannot be proxied, even in proxy-localhost + * mode. There just isn't any way to ask any known proxy type for + * them. + */ + if (addr && sk_address_is_special_local(addr)) + return 0; /* do not proxy */ + + /* * Check the host name and IP against the hard-coded * representations of `localhost'. */ - if (!cfg->even_proxy_localhost && + if (!conf_get_int(conf, CONF_even_proxy_localhost) && (sk_hostname_is_local(hostname) || (addr && sk_address_is_local(addr)))) return 0; /* do not proxy */ @@ -288,7 +312,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, hostname_len = strlen(hostname); - exclude_list = cfg->proxy_exclude_list; + exclude_list = conf_get_str(conf, CONF_proxy_exclude_list); /* now parse the exclude list, and see if either our IP * or hostname matches anything in it. @@ -349,11 +373,11 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port, } SockAddr name_lookup(char *host, int port, char **canonicalname, - const Config *cfg, int addressfamily) + Conf *conf, int addressfamily) { - if (cfg->proxy_type != PROXY_NONE && - do_proxy_dns(cfg) && - proxy_for_destination(NULL, host, port, cfg)) { + if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && + do_proxy_dns(conf) && + proxy_for_destination(NULL, host, port, conf)) { *canonicalname = dupstr(host); return sk_nonamelookup(host); } @@ -364,13 +388,14 @@ SockAddr name_lookup(char *host, int port, char **canonicalname, Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { static const struct socket_function_table socket_fn_table = { sk_proxy_plug, sk_proxy_close, sk_proxy_write, sk_proxy_write_oob, + sk_proxy_write_eof, sk_proxy_flush, sk_proxy_set_private_ptr, sk_proxy_get_private_ptr, @@ -386,30 +411,32 @@ Socket new_connection(SockAddr addr, char *hostname, plug_proxy_accepting }; - if (cfg->proxy_type != PROXY_NONE && - proxy_for_destination(addr, hostname, port, cfg)) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && + proxy_for_destination(addr, hostname, port, conf)) { Proxy_Socket ret; Proxy_Plug pplug; SockAddr proxy_addr; char *proxy_canonical_name; Socket sret; + int type; if ((sret = platform_new_connection(addr, hostname, port, privport, oobinline, nodelay, keepalive, - plug, cfg)) != + plug, conf)) != NULL) return sret; ret = snew(struct Socket_proxy_tag); ret->fn = &socket_fn_table; - ret->cfg = *cfg; /* STRUCTURE COPY */ + ret->conf = conf_copy(conf); ret->plug = plug; ret->remote_addr = addr; /* will need to be freed on close */ ret->remote_port = port; ret->error = NULL; ret->pending_flush = 0; + ret->pending_eof = 0; ret->freeze = 0; bufchain_init(&ret->pending_input_data); @@ -419,14 +446,15 @@ Socket new_connection(SockAddr addr, char *hostname, ret->sub_socket = NULL; ret->state = PROXY_STATE_NEW; ret->negotiate = NULL; - - if (cfg->proxy_type == PROXY_HTTP) { + + type = conf_get_int(conf, CONF_proxy_type); + if (type == PROXY_HTTP) { ret->negotiate = proxy_http_negotiate; - } else if (cfg->proxy_type == PROXY_SOCKS4) { + } else if (type == PROXY_SOCKS4) { ret->negotiate = proxy_socks4_negotiate; - } else if (cfg->proxy_type == PROXY_SOCKS5) { + } else if (type == PROXY_SOCKS5) { ret->negotiate = proxy_socks5_negotiate; - } else if (cfg->proxy_type == PROXY_TELNET) { + } else if (type == PROXY_TELNET) { ret->negotiate = proxy_telnet_negotiate; } else { ret->error = "Proxy error: Unknown proxy method"; @@ -440,10 +468,13 @@ Socket new_connection(SockAddr addr, char *hostname, pplug->proxy_socket = ret; /* look-up proxy */ - proxy_addr = sk_namelookup(cfg->proxy_host, - &proxy_canonical_name, cfg->addressfamily); + proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host), + &proxy_canonical_name, + conf_get_int(conf, CONF_addressfamily)); if (sk_addr_error(proxy_addr) != NULL) { ret->error = "Proxy error: Unable to resolve proxy host name"; + sfree(pplug); + sk_addr_free(proxy_addr); return (Socket)ret; } sfree(proxy_canonical_name); @@ -451,7 +482,8 @@ Socket new_connection(SockAddr addr, char *hostname, /* create the actual socket we will be using, * connected to our proxy server and port. */ - ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port, + ret->sub_socket = sk_new(proxy_addr, + conf_get_int(conf, CONF_proxy_port), privport, oobinline, nodelay, keepalive, (Plug) pplug); if (sk_socket_error(ret->sub_socket) != NULL) @@ -469,7 +501,7 @@ Socket new_connection(SockAddr addr, char *hostname, } Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only, - const Config *cfg, int addressfamily) + Conf *conf, int addressfamily) { /* TODO: SOCKS (and potentially others) support inbound * TODO: connections via the proxy. support them. @@ -525,6 +557,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change) * request */ char *buf, dest[512]; + char *username, *password; sk_getaddr(p->remote_addr, dest, lenof(dest)); @@ -533,18 +566,22 @@ int proxy_http_negotiate (Proxy_Socket p, int change) sk_write(p->sub_socket, buf, strlen(buf)); sfree(buf); - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { - char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)]; - char buf2[sizeof(buf)*4/3 + 100]; + username = conf_get_str(p->conf, CONF_proxy_username); + password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { + char *buf, *buf2; int i, j, len; - sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password); + buf = dupprintf("%s:%s", username, password); len = strlen(buf); + buf2 = snewn(len * 4 / 3 + 100, char); sprintf(buf2, "Proxy-Authorization: Basic "); for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4) base64_encode_atom((unsigned char *)(buf+i), (len-i > 3 ? 3 : len-i), buf2+j); strcpy(buf2+j, "\r\n"); sk_write(p->sub_socket, buf2, strlen(buf2)); + sfree(buf); + sfree(buf2); } sk_write(p->sub_socket, "\r\n", 2); @@ -711,11 +748,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) int length, type, namelen; char *command, addr[4], hostname[512]; + char *username; type = sk_addrtype(p->remote_addr); if (type == ADDRTYPE_IPV6) { - plug_closing(p->plug, "Proxy error: SOCKS version 4 does" - " not support IPv6", PROXY_ERROR_GENERAL, 0); + p->error = "Proxy error: SOCKS version 4 does not support IPv6"; return 1; } else if (type == ADDRTYPE_IPV4) { namelen = 0; @@ -728,9 +765,10 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) addr[3] = 1; } - length = strlen(p->cfg.proxy_username) + namelen + 9; + username = conf_get_str(p->conf, CONF_proxy_username); + length = strlen(username) + namelen + 9; command = snewn(length, char); - strcpy(command + 8, p->cfg.proxy_username); + strcpy(command + 8, username); command[0] = 4; /* version 4 */ command[1] = 1; /* CONNECT command */ @@ -743,10 +781,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change) memcpy(command + 4, addr, 4); /* hostname */ - memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1, + memcpy(command + 8 + strlen(username) + 1, hostname, namelen); sk_write(p->sub_socket, command, length); + sfree(username); sfree(command); p->state = 1; @@ -868,10 +907,13 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) */ char command[5]; + char *username, *password; int len; command[0] = 5; /* version 5 */ - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { + username = conf_get_str(p->conf, CONF_proxy_username); + password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { command[2] = 0x00; /* no authentication */ len = 3; proxy_socks5_offerencryptedauth (command, &len); @@ -1148,18 +1190,20 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) } if (p->state == 5) { - if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) { - char userpwbuf[514]; + char *username = conf_get_str(p->conf, CONF_proxy_username); + char *password = conf_get_str(p->conf, CONF_proxy_password); + if (username[0] || password[0]) { + char userpwbuf[255 + 255 + 3]; int ulen, plen; - ulen = strlen(p->cfg.proxy_username); + ulen = strlen(username); if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; - plen = strlen(p->cfg.proxy_password); + plen = strlen(password); if (plen > 255) plen = 255; if (plen < 1) plen = 1; userpwbuf[0] = 1; /* version number of subnegotiation */ userpwbuf[1] = ulen; - memcpy(userpwbuf+2, p->cfg.proxy_username, ulen); + memcpy(userpwbuf+2, username, ulen); userpwbuf[ulen+2] = plen; - memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen); + memcpy(userpwbuf+ulen+3, password, plen); sk_write(p->sub_socket, userpwbuf, ulen + plen + 3); p->state = 7; } else @@ -1192,8 +1236,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) * standardised or at all well-defined.) */ -char *format_telnet_command(SockAddr addr, int port, const Config *cfg) +char *format_telnet_command(SockAddr addr, int port, Conf *conf) { + char *fmt = conf_get_str(conf, CONF_proxy_telnet_command); char *ret = NULL; int retlen = 0, retsize = 0; int so = 0, eo = 0; @@ -1208,22 +1253,21 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) * %%, %host, %port, %user, and %pass */ - while (cfg->proxy_telnet_command[eo] != 0) { + while (fmt[eo] != 0) { /* scan forward until we hit end-of-line, * or an escape character (\ or %) */ - while (cfg->proxy_telnet_command[eo] != 0 && - cfg->proxy_telnet_command[eo] != '%' && - cfg->proxy_telnet_command[eo] != '\\') eo++; + while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\') + eo++; /* if we hit eol, break out of our escaping loop */ - if (cfg->proxy_telnet_command[eo] == 0) break; + if (fmt[eo] == 0) break; /* if there was any unescaped text before the escape * character, send that now */ if (eo != so) { ENSURE(eo - so); - memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } @@ -1231,15 +1275,15 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) /* if the escape character was the last character of * the line, we'll just stop and send it. */ - if (cfg->proxy_telnet_command[eo] == 0) break; + if (fmt[eo] == 0) break; - if (cfg->proxy_telnet_command[so] == '\\') { + if (fmt[so] == '\\') { /* we recognize \\, \%, \r, \n, \t, \x??. * anything else, we just send unescaped (including the \). */ - switch (cfg->proxy_telnet_command[eo]) { + switch (fmt[eo]) { case '\\': ENSURE(1); @@ -1280,15 +1324,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) for (;;) { eo++; - if (cfg->proxy_telnet_command[eo] >= '0' && - cfg->proxy_telnet_command[eo] <= '9') - v += cfg->proxy_telnet_command[eo] - '0'; - else if (cfg->proxy_telnet_command[eo] >= 'a' && - cfg->proxy_telnet_command[eo] <= 'f') - v += cfg->proxy_telnet_command[eo] - 'a' + 10; - else if (cfg->proxy_telnet_command[eo] >= 'A' && - cfg->proxy_telnet_command[eo] <= 'F') - v += cfg->proxy_telnet_command[eo] - 'A' + 10; + if (fmt[eo] >= '0' && fmt[eo] <= '9') + v += fmt[eo] - '0'; + else if (fmt[eo] >= 'a' && fmt[eo] <= 'f') + v += fmt[eo] - 'a' + 10; + else if (fmt[eo] >= 'A' && fmt[eo] <= 'F') + v += fmt[eo] - 'A' + 10; else { /* non hex character, so we abort and just * send the whole thing unescaped (including \x) @@ -1315,7 +1356,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) default: ENSURE(2); - memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2); + memcpy(ret+retlen, fmt + so, 2); retlen += 2; eo++; break; @@ -1327,13 +1368,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) * unescaped (including the %). */ - if (cfg->proxy_telnet_command[eo] == '%') { + if (fmt[eo] == '%') { ENSURE(1); ret[retlen++] = '%'; eo++; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "host", 4) == 0) { + else if (strnicmp(fmt + eo, "host", 4) == 0) { char dest[512]; int destlen; sk_getaddr(addr, dest, lenof(dest)); @@ -1343,8 +1383,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) retlen += destlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "port", 4) == 0) { + else if (strnicmp(fmt + eo, "port", 4) == 0) { char portstr[8], portlen; portlen = sprintf(portstr, "%i", port); ENSURE(portlen); @@ -1352,35 +1391,35 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) retlen += portlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "user", 4) == 0) { - int userlen = strlen(cfg->proxy_username); + else if (strnicmp(fmt + eo, "user", 4) == 0) { + char *username = conf_get_str(conf, CONF_proxy_username); + int userlen = strlen(username); ENSURE(userlen); - memcpy(ret+retlen, cfg->proxy_username, userlen); + memcpy(ret+retlen, username, userlen); retlen += userlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "pass", 4) == 0) { - int passlen = strlen(cfg->proxy_password); + else if (strnicmp(fmt + eo, "pass", 4) == 0) { + char *password = conf_get_str(conf, CONF_proxy_password); + int passlen = strlen(password); ENSURE(passlen); - memcpy(ret+retlen, cfg->proxy_password, passlen); + memcpy(ret+retlen, password, passlen); retlen += passlen; eo += 4; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "proxyhost", 9) == 0) { - int phlen = strlen(cfg->proxy_host); + else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) { + char *host = conf_get_str(conf, CONF_proxy_host); + int phlen = strlen(host); ENSURE(phlen); - memcpy(ret+retlen, cfg->proxy_host, phlen); + memcpy(ret+retlen, host, phlen); retlen += phlen; eo += 9; } - else if (strnicmp(cfg->proxy_telnet_command + eo, - "proxyport", 9) == 0) { + else if (strnicmp(fmt + eo, "proxyport", 9) == 0) { + int port = conf_get_int(conf, CONF_proxy_port); char pport[50]; int pplen; - sprintf(pport, "%d", cfg->proxy_port); + sprintf(pport, "%d", port); pplen = strlen(pport); ENSURE(pplen); memcpy(ret+retlen, pport, pplen); @@ -1404,7 +1443,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg) /* if there is any unescaped text at the end of the line, send it */ if (eo != so) { ENSURE(eo - so); - memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + memcpy(ret + retlen, fmt + so, eo - so); retlen += eo - so; } @@ -1421,7 +1460,7 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change) char *formatted_cmd; formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port, - &p->cfg); + p->conf); sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd)); sfree(formatted_cmd); diff --git a/putty/PROXY.H b/putty/PROXY.H index 683b260..9e64aad 100644 --- a/putty/PROXY.H +++ b/putty/PROXY.H @@ -30,6 +30,7 @@ struct Socket_proxy_tag { bufchain pending_oob_output_data; int pending_flush; bufchain pending_input_data; + int pending_eof; #define PROXY_STATE_NEW -1 #define PROXY_STATE_ACTIVE 0 @@ -80,7 +81,7 @@ struct Socket_proxy_tag { OSSocket accepting_sock; /* configuration, used to look up proxy settings */ - Config cfg; + Conf *conf; /* CHAP transient data */ int chap_num_attributes; @@ -110,7 +111,7 @@ extern int proxy_socks5_negotiate (Proxy_Socket, int); * This may be reused by local-command proxies on individual * platforms. */ -char *format_telnet_command(SockAddr addr, int port, const Config *cfg); +char *format_telnet_command(SockAddr addr, int port, Conf *conf); /* * These are implemented in cproxy.c or nocproxy.c, depending on diff --git a/putty/PSCP.C b/putty/PSCP.C index 14fff5c..66275c7 100644 --- a/putty/PSCP.C +++ b/putty/PSCP.C @@ -44,7 +44,8 @@ static int using_sftp = 0; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; +int sent_eof = FALSE; static void source(char *src); static void rsource(char *src); @@ -128,6 +129,19 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +void nonfatal(char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + tell_str(stderr, str2); + sfree(str2); + errs++; +} void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; @@ -214,6 +228,19 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. + */ + if (!sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from server"); + } + return FALSE; +} static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; @@ -298,6 +325,7 @@ static void bump(char *fmt, ...) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } @@ -305,6 +333,29 @@ static void bump(char *fmt, ...) } /* + * Wait for the reply to a single SFTP request. Parallels the same + * function in psftp.c (but isn't centralised into sftp.c because the + * latter module handles SFTP only and shouldn't assume that SFTP is + * the only thing going on by calling connection_fatal). + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} + +/* * Open an SSH connection to user@host and execute cmd. */ static void do_cmd(char *host, char *user, char *cmd) @@ -333,88 +384,90 @@ static void do_cmd(char *host, char *user, char *cmd) */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ - Config cfg2; - cfg2.host[0] = '\0'; - do_defaults(host, &cfg2); - if (cfg2.host[0] != '\0') { + Conf *conf2 = conf_new(); + conf_set_str(conf2, CONF_host, ""); + do_defaults(host, conf2); + if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ - do_defaults(host, &cfg); + do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } } else { /* Patch in hostname `host' to session details. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ - if (cfg.protocol != PROT_SSH) { - cfg.protocol = PROT_SSH; - cfg.port = 22; + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); } /* * Enact command-line overrides. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; + + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } - } - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; } - cfg.host[p1] = '\0'; + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; - } else if (cfg.username[0] == '\0') { + conf_set_str(conf, CONF_username, user); + } else if (conf_get_str(conf, CONF_username)[0] == '\0') { user = get_username(); if (!user) bump("Empty user name"); else { if (verbose) tell_user(stderr, "Guessing user name: %s", user); - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); sfree(user); } } @@ -424,10 +477,14 @@ static void do_cmd(char *host, char *user, char *cmd) * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ - cfg.x11_forward = 0; - cfg.agentfwd = 0; - cfg.portfwd[0] = cfg.portfwd[1] = '\0'; - cfg.ssh_simple = TRUE; + conf_set_int(conf, CONF_x11_forward, 0); + conf_set_int(conf, CONF_agentfwd, 0); + conf_set_int(conf, CONF_ssh_simple, TRUE); + { + char *key; + while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) + conf_del_str_str(conf, CONF_portfwd, key); + } /* * Set up main and possibly fallback command depending on @@ -435,49 +492,54 @@ static void do_cmd(char *host, char *user, char *cmd) * Attempt to start the SFTP subsystem as a first choice, * falling back to the provided scp command if that fails. */ - cfg.remote_cmd_ptr2 = NULL; + conf_set_str(conf, CONF_remote_cmd2, ""); if (try_sftp) { /* First choice is SFTP subsystem. */ main_cmd_is_sftp = 1; - strcpy(cfg.remote_cmd, "sftp"); - cfg.ssh_subsys = TRUE; + conf_set_str(conf, CONF_remote_cmd, "sftp"); + conf_set_int(conf, CONF_ssh_subsys, TRUE); if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; - cfg.remote_cmd_ptr2 = cmd; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, cmd); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); } else { /* Since we're not going to try SCP, we may as well try * harder to find an SFTP server, since in the current * implementation we have a spare slot. */ fallback_cmd_is_sftp = 1; /* see psftp.c for full explanation of this kludge */ - cfg.remote_cmd_ptr2 = - "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" - "exec sftp-server"; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, + "test -x /usr/lib/sftp-server &&" + " exec /usr/lib/sftp-server\n" + "test -x /usr/local/lib/sftp-server &&" + " exec /usr/local/lib/sftp-server\n" + "exec sftp-server"); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); } } else { /* Don't try SFTP at all; just try the scp command. */ main_cmd_is_sftp = 0; - cfg.remote_cmd_ptr = cmd; - cfg.ssh_subsys = FALSE; + conf_set_str(conf, CONF_remote_cmd, cmd); + conf_set_int(conf, CONF_ssh_subsys, FALSE); } - cfg.nopty = TRUE; + conf_set_int(conf, CONF_nopty, TRUE); back = &ssh_backend; - err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, - 0, cfg.tcp_keepalives); + err = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, 0, + conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); ssh_scp_init(); if (verbose && realhost != NULL && errs == 0) - tell_user(stderr, "Connected to %s\n", realhost); + tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } @@ -626,7 +688,7 @@ static int response(void) } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) - tell_user(stderr, "%s\n", rbuf); + tell_user(stderr, "%s", rbuf); else bump("%s", rbuf); errs++; @@ -659,7 +721,7 @@ void scp_sftp_listdir(char *dirname) struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int nnames, namesize; int i; @@ -671,10 +733,9 @@ void scp_sftp_listdir(char *dirname) printf("Listing directory %s\n", dirname); - sftp_register(req = fxp_opendir_send(dirname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dirname); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dirname, fxp_error()); @@ -684,10 +745,9 @@ void scp_sftp_listdir(char *dirname) while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -710,22 +770,24 @@ void scp_sftp_listdir(char *dirname) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); + + sfree(ournames); } } @@ -760,7 +822,7 @@ int scp_source_setup(char *target, int shouldbedir) * directory. */ struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int ret; @@ -770,10 +832,9 @@ int scp_source_setup(char *target, int shouldbedir) return 1; } - sftp_register(req = fxp_stat_send(target)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(target); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; @@ -819,12 +880,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime) } } -int scp_send_filename(char *name, uint64 size, int modes) +int scp_send_filename(char *name, uint64 size, int permissions) { if (using_sftp) { char *fullname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; + struct fxp_attrs attrs; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); @@ -832,15 +894,19 @@ int scp_send_filename(char *name, uint64 size, int modes) fullname = dupstr(scp_sftp_remotepath); } - sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); + + req = fxp_open_send(fullname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); + sfree(fullname); errs++; return 1; } @@ -853,7 +919,9 @@ int scp_send_filename(char *name, uint64 size, int modes) char buf[40]; char sizestr[40]; uint64_decimal(size, sizestr); - sprintf(buf, "C%04o %s ", modes, sizestr); + if (permissions < 0) + permissions = 0644; + sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); @@ -874,8 +942,10 @@ int scp_send_filedata(char *data, int len) while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); - if (!ret) { - tell_user(stderr, "error while writing: %s\n", fxp_error()); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return 1; } @@ -909,12 +979,19 @@ int scp_send_finish(void) if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); - xfer_upload_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return 1; + } } xfer_cleanup(scp_sftp_xfer); @@ -925,19 +1002,17 @@ int scp_send_finish(void) attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; - sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fsetstat_recv(pktin, rreq); + req = fxp_fsetstat_send(scp_sftp_filehandle, attrs); + pktin = sftp_wait_for_reply(req); + ret = fxp_fsetstat_recv(pktin, req); if (!ret) { - tell_user(stderr, "unable to set file times: %s\n", fxp_error()); + tell_user(stderr, "unable to set file times: %s", fxp_error()); errs++; } } - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); scp_has_times = 0; return 0; } else { @@ -967,7 +1042,7 @@ int scp_send_dirname(char *name, int modes) char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (scp_sftp_targetisdir) { @@ -983,25 +1058,24 @@ int scp_send_dirname(char *name, int modes) * exists and is a directory we will assume we were either * successful or it didn't matter. */ - sftp_register(req = fxp_mkdir_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_mkdir_recv(pktin, req); if (!ret) err = fxp_error(); else err = "server reported no error"; - sftp_register(req = fxp_stat_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); + sfree(fullname); errs++; return 1; } @@ -1057,6 +1131,9 @@ int scp_sink_setup(char *source, int preserve, int recursive) if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; + + sfree(newsource); + dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); @@ -1133,7 +1210,7 @@ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ - int mode; /* access mode (not ENDDIR) */ + long permissions; /* access permissions (not ENDDIR) */ uint64 size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */ @@ -1146,7 +1223,7 @@ int scp_get_sink_action(struct scp_sink_action *act) int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (!scp_sftp_dirstack_head) { @@ -1215,14 +1292,14 @@ int scp_get_sink_action(struct scp_sink_action *act) * Now we have a filename. Stat it, and see if it's a file * or a directory. */ - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { tell_user(stderr, "unable to identify %s: %s", fname, ret ? "file type not supplied" : fxp_error()); + if (must_free_fname) sfree(fname); errs++; return 1; } @@ -1271,13 +1348,12 @@ int scp_get_sink_action(struct scp_sink_action *act) * list), we must push the other (target,namelist) pair * on a stack. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { - tell_user(stderr, "scp: unable to open directory %s: %s", + tell_user(stderr, "pscp: unable to open directory %s: %s", fname, fxp_error()); if (must_free_fname) sfree(fname); errs++; @@ -1288,16 +1364,20 @@ int scp_get_sink_action(struct scp_sink_action *act) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; - tell_user(stderr, "scp: reading directory %s: %s\n", + tell_user(stderr, "pscp: reading directory %s: %s", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + if (must_free_fname) sfree(fname); sfree(ournames); errs++; @@ -1321,7 +1401,7 @@ int scp_get_sink_action(struct scp_sink_action *act) */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" - "supplied filename '%s'\n", + "supplied filename '%s'", names->names[i].filename); } else ournames[nnames++] = names->names[i]; @@ -1329,10 +1409,9 @@ int scp_get_sink_action(struct scp_sink_action *act) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); newitem = snew(struct scp_sftp_dirstack); newitem->next = scp_sftp_dirstack_head; @@ -1359,7 +1438,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1381,7 +1460,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1425,7 +1504,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ - tell_user(stderr, "%s\n", act->buf); + tell_user(stderr, "%s", act->buf); errs++; continue; /* go round again */ case '\02': /* fatal error */ @@ -1463,7 +1542,8 @@ int scp_get_sink_action(struct scp_sink_action *act) { char sizestr[40]; - if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2) + if (sscanf(act->buf, "%lo %s %n", &act->permissions, + sizestr, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->size = uint64_from_decimal(sizestr); act->name = act->buf + i; @@ -1476,12 +1556,11 @@ int scp_accept_filexfer(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", @@ -1510,9 +1589,10 @@ int scp_recv_filedata(char *data, int len) xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return -1; } @@ -1542,7 +1622,7 @@ int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Ensure that xfer_done() will work correctly, so we can @@ -1552,19 +1632,25 @@ int scp_finish_filerecv(void) xfer_set_error(scp_sftp_xfer); while (!xfer_done(scp_sftp_xfer)) { void *vbuf; - int len; + int ret, len; pktin = sftp_recv(); - xfer_download_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return -1; + } if (xfer_download_data(scp_sftp_xfer, &vbuf, &len)) sfree(vbuf); } xfer_cleanup(scp_sftp_xfer); - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } else { back->send(backhandle, "", 1); @@ -1583,7 +1669,7 @@ static void run_err(const char *fmt, ...) va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); - str2 = dupcat("scp: ", str, "\n", NULL); + str2 = dupcat("pscp: ", str, "\n", NULL); sfree(str); scp_send_errmsg(str2); tell_user(stderr, "%s", str2); @@ -1598,6 +1684,7 @@ static void source(char *src) { uint64 size; unsigned long mtime, atime; + long permissions; char *last; RFile *f; int attr; @@ -1645,14 +1732,16 @@ static void source(char *src) if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; - f = open_existing_file(src, &size, &mtime, &atime); + f = open_existing_file(src, &size, &mtime, &atime, &permissions); if (f == NULL) { run_err("%s: Cannot open file", src); return; } if (preserve) { - if (scp_send_filetimes(mtime, atime)) + if (scp_send_filetimes(mtime, atime)) { + close_rfile(f); return; + } } if (verbose) { @@ -1660,8 +1749,10 @@ static void source(char *src) uint64_decimal(size, sizestr); tell_user(stderr, "Sending file %s, size=%s", last, sizestr); } - if (scp_send_filename(last, size, 0644)) + if (scp_send_filename(last, size, permissions)) { + close_rfile(f); return; + } stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); @@ -1864,27 +1955,34 @@ static void sink(char *targ, char *src) if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); + sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); + sfree(destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ + sfree(destfname); continue; } - f = open_new_file(destfname); + f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); + sfree(destfname); continue; } - if (scp_accept_filexfer()) + if (scp_accept_filexfer()) { + sfree(destfname); + close_wfile(f); return; + } stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); @@ -1931,6 +2029,7 @@ static void sink(char *targ, char *src) close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); + sfree(destfname); continue; } (void) scp_finish_filerecv(); @@ -2221,14 +2320,15 @@ int psftp_main(int argc, char *argv[]) sk_init(); /* Load Default Settings before doing anything else. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; - ret = cmdline_process_param(argv[i], i+1connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed(); diff --git a/putty/PSFTP.C b/putty/PSFTP.C index 3583fd7..d8b87af 100644 --- a/putty/PSFTP.C +++ b/putty/PSFTP.C @@ -36,7 +36,28 @@ void do_sftp_cleanup(); char *pwd, *homedir; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; +int sent_eof = FALSE; + +/* ---------------------------------------------------------------------- + * Manage sending requests and waiting for replies. + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} /* ---------------------------------------------------------------------- * Higher-level helper functions used in commands. @@ -51,7 +72,7 @@ char *canonify(char *name) { char *fullname, *canonname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; if (name[0] == '/') { fullname = dupstr(name); @@ -64,10 +85,9 @@ char *canonify(char *name) fullname = dupcat(pwd, slash, name, NULL); } - sftp_register(req = fxp_realpath_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - canonname = fxp_realpath_recv(pktin, rreq); + req = fxp_realpath_send(fullname); + pktin = sftp_wait_for_reply(req); + canonname = fxp_realpath_recv(pktin, req); if (canonname) { sfree(fullname); @@ -122,13 +142,12 @@ char *canonify(char *name) */ fullname[i] = '\0'; /* separate the string */ if (i == 0) { - sftp_register(req = fxp_realpath_send("/")); + req = fxp_realpath_send("/"); } else { - sftp_register(req = fxp_realpath_send(fullname)); + req = fxp_realpath_send(fullname); } - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - canonname = fxp_realpath_recv(pktin, rreq); + pktin = sftp_wait_for_reply(req); + canonname = fxp_realpath_recv(pktin, req); if (!canonname) { /* Even that failed. Restore our best guess at the @@ -207,11 +226,12 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) { struct fxp_handle *fh; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_xfer *xfer; uint64 offset; WFile *file; int ret, shown_err = FALSE; + struct fxp_attrs attrs; /* * In recursive mode, see if we're dealing with a directory. @@ -219,13 +239,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * subsequent FXP_OPEN will return a usable error message.) */ if (recurse) { - struct fxp_attrs attrs; int result; - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && @@ -251,10 +269,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * Now get the list of filenames in the remote * directory. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { printf("%s: unable to open directory: %s\n", @@ -266,15 +283,19 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("%s: reading directory: %s\n", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + sfree(ournames); return 0; } @@ -300,10 +321,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) } fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Sort the names into a clear order. This ought to @@ -312,7 +332,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) * readdirs on the same remote directory return a * different order. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * If we're in restart mode, find the last filename on @@ -327,11 +348,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) while (i < nnames) { char *nextoutfname; int ret; - if (outfname) - nextoutfname = dir_file_cat(outfname, - ournames[i]->filename); - else - nextoutfname = dupstr(ournames[i]->filename); + nextoutfname = dir_file_cat(outfname, + ournames[i]->filename); ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); sfree(nextoutfname); if (ret) @@ -353,11 +371,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) int ret; nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); - if (outfname) - nextoutfname = dir_file_cat(outfname, - ournames[i]->filename); - else - nextoutfname = dupstr(ournames[i]->filename); + nextoutfname = dir_file_cat(outfname, ournames[i]->filename); ret = sftp_get_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); @@ -383,10 +397,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) } } - sftp_register(req = fxp_open_send(fname, SSH_FXF_READ)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fh = fxp_open_recv(pktin, rreq); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + if (!fxp_stat_recv(pktin, req, &attrs)) + attrs.flags = 0; + + req = fxp_open_send(fname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + fh = fxp_open_recv(pktin, req); if (!fh) { printf("%s: open for read: %s\n", fname, fxp_error()); @@ -396,16 +414,15 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) if (restart) { file = open_existing_wfile(outfname, NULL); } else { - file = open_new_file(outfname); + file = open_new_file(outfname, GET_PERMISSIONS(attrs)); } if (!file) { printf("local: unable to open %s\n", outfname); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } @@ -416,10 +433,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) close_wfile(file); printf("reget: cannot restart %s - file too large\n", outfname); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } @@ -447,12 +463,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) xfer_download_queue(xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { if (!shown_err) { printf("error while reading: %s\n", fxp_error()); shown_err = TRUE; } + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); ret = 0; } @@ -483,10 +500,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) close_wfile(file); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return ret; } @@ -496,10 +512,12 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) struct fxp_handle *fh; struct fxp_xfer *xfer; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; uint64 offset; RFile *file; int ret, err, eof; + struct fxp_attrs attrs; + long permissions; /* * In recursive mode, see if we're dealing with a directory. @@ -507,7 +525,6 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * subsequent fopen will return an error message.) */ if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) { - struct fxp_attrs attrs; int result; int nnames, namesize; char *name, **ournames; @@ -518,17 +535,15 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * First, attempt to create the destination directory, * unless it already exists. */ - sftp_register(req = fxp_stat_send(outfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(outfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { - sftp_register(req = fxp_mkdir_send(outfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(outfname); + pktin = sftp_wait_for_reply(req); + result = fxp_mkdir_recv(pktin, req); if (!result) { printf("%s: create directory: %s\n", @@ -563,7 +578,8 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * same directory, just in case two readdirs on the same * local directory return a different order. */ - qsort(ournames, nnames, sizeof(*ournames), bare_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), bare_name_compare); /* * If we're in restart mode, find the last filename on this @@ -578,10 +594,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int 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); + req = fxp_stat_send(nextoutfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); sfree(nextoutfname); if (!result) break; @@ -601,10 +616,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) char *nextfname, *nextoutfname; int ret; - if (fname) - nextfname = dir_file_cat(fname, ournames[i]); - else - nextfname = dupstr(ournames[i]); + nextfname = dir_file_cat(fname, ournames[i]); nextoutfname = dupcat(outfname, "/", ournames[i], NULL); ret = sftp_put_file(nextfname, nextoutfname, recurse, restart); restart = FALSE; /* after first partial file, do full */ @@ -630,20 +642,22 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) return 1; } - file = open_existing_file(fname, NULL, NULL, NULL); + file = open_existing_file(fname, NULL, NULL, NULL, &permissions); if (!file) { printf("local: unable to open %s\n", fname); return 0; } + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); if (restart) { - sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE)); + req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs); } else { - sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); + req = fxp_open_send(outfname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); } - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fh = fxp_open_recv(pktin, rreq); + pktin = sftp_wait_for_reply(req); + fh = fxp_open_recv(pktin, req); if (!fh) { close_rfile(file); @@ -656,10 +670,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) struct fxp_attrs attrs; int ret; - sftp_register(req = fxp_fstat_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fstat_recv(pktin, rreq, &attrs); + req = fxp_fstat_send(fh); + pktin = sftp_wait_for_reply(req); + ret = fxp_fstat_recv(pktin, req, &attrs); if (!ret) { close_rfile(file); @@ -709,19 +722,22 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) if (!xfer_done(xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(xfer, pktin); - if (ret <= 0 && !err) { - printf("error while writing: %s\n", fxp_error()); - err = 1; + if (ret <= 0) { + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + if (!err) { + printf("error while writing: %s\n", fxp_error()); + err = 1; + } } } } xfer_cleanup(xfer); - sftp_register(req = fxp_close_send(fh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(fh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); close_rfile(file); @@ -743,7 +759,7 @@ typedef struct SftpWildcardMatcher { SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; char *wildcard; char *unwcdir, *tmpdir, *cdir; int len, check; @@ -774,10 +790,9 @@ SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name) cdir = canonify(unwcdir); - sftp_register(req = fxp_opendir_send(cdir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(cdir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh) { swcm = snew(SftpWildcardMatcher); @@ -800,7 +815,7 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) { struct fxp_name *name; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; while (1) { if (swcm->names && swcm->namepos >= swcm->names->nnames) { @@ -809,17 +824,26 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) } if (!swcm->names) { - sftp_register(req = fxp_readdir_send(swcm->dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - swcm->names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(swcm->dirh); + pktin = sftp_wait_for_reply(req); + swcm->names = fxp_readdir_recv(pktin, req); if (!swcm->names) { if (fxp_error_type() != SSH_FX_EOF) printf("%s: reading directory: %s\n", swcm->prefix, fxp_error()); return NULL; - } + } else if (swcm->names->nnames == 0) { + /* + * Another failure mode which we treat as EOF is if + * the server reports success from FXP_READDIR but + * returns no actual names. This is unusual, since + * from most servers you'd expect at least "." and + * "..", but there's nothing forbidding a server from + * omitting those if it wants to. + */ + return NULL; + } swcm->namepos = 0; } @@ -854,12 +878,11 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_close_send(swcm->dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(swcm->dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); if (swcm->names) fxp_free_names(swcm->names); @@ -899,6 +922,7 @@ int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx) printf("%s: canonify: %s\n", newname, fxp_error()); ret = 0; } + sfree(newname); matched = TRUE; ret &= func(ctx, cname); sfree(cname); @@ -970,6 +994,7 @@ int sftp_cmd_close(struct sftp_command *cmd) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); @@ -989,7 +1014,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) int nnames, namesize; char *dir, *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int i; if (back == NULL) { @@ -1010,6 +1035,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) char *tmpdir; int len, check; + sfree(unwcdir); wildcard = stripslashes(dir, 0); unwcdir = dupstr(dir); len = wildcard - dir; @@ -1036,10 +1062,9 @@ int sftp_cmd_ls(struct sftp_command *cmd) printf("Listing directory %s\n", cdir); - sftp_register(req = fxp_opendir_send(cdir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(cdir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dir, fxp_error()); @@ -1049,10 +1074,9 @@ int sftp_cmd_ls(struct sftp_command *cmd) while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -1076,16 +1100,16 @@ int sftp_cmd_ls(struct sftp_command *cmd) fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare); /* * And print them. @@ -1111,7 +1135,7 @@ int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; char *dir; if (back == NULL) { @@ -1129,10 +1153,9 @@ int sftp_cmd_cd(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_opendir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dir); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (!dirh) { printf("Directory %s: %s\n", dir, fxp_error()); @@ -1140,10 +1163,9 @@ int sftp_cmd_cd(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); sfree(pwd); pwd = dir; @@ -1236,7 +1258,9 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple) fname = canonify(origwfname); if (!fname) { + sftp_finish_wildcard_matching(swcm); printf("%s: canonify: %s\n", origwfname, fxp_error()); + sfree(origwfname); sfree(unwcfname); return 0; } @@ -1392,7 +1416,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; int i, ret; @@ -1414,10 +1438,9 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) return 0; } - sftp_register(req = fxp_mkdir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(dir); + pktin = sftp_wait_for_reply(req); + result = fxp_mkdir_recv(pktin, req); if (!result) { printf("mkdir %s: %s\n", dir, fxp_error()); @@ -1434,13 +1457,12 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) static int sftp_action_rmdir(void *vctx, char *dir) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; - sftp_register(req = fxp_rmdir_send(dir)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_rmdir_recv(pktin, rreq); + req = fxp_rmdir_send(dir); + pktin = sftp_wait_for_reply(req); + result = fxp_rmdir_recv(pktin, req); if (!result) { printf("rmdir %s: %s\n", dir, fxp_error()); @@ -1476,13 +1498,12 @@ int sftp_cmd_rmdir(struct sftp_command *cmd) static int sftp_action_rm(void *vctx, char *fname) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; - sftp_register(req = fxp_remove_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_remove_recv(pktin, rreq); + req = fxp_remove_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_remove_recv(pktin, req); if (!result) { printf("rm %s: %s\n", fname, fxp_error()); @@ -1518,14 +1539,13 @@ int sftp_cmd_rm(struct sftp_command *cmd) static int check_is_dir(char *dstfname) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int result; - sftp_register(req = fxp_stat_send(dstfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(dstfname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && @@ -1544,7 +1564,7 @@ static int sftp_action_mv(void *vctx, char *srcfname) { struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; const char *error; char *finalfname, *newcanon = NULL; int ret, result; @@ -1569,10 +1589,9 @@ static int sftp_action_mv(void *vctx, char *srcfname) finalfname = ctx->dstfname; } - sftp_register(req = fxp_rename_send(srcfname, finalfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_rename_recv(pktin, rreq); + req = fxp_rename_send(srcfname, finalfname); + pktin = sftp_wait_for_reply(req); + result = fxp_rename_recv(pktin, req); error = result ? NULL : fxp_error(); @@ -1641,15 +1660,14 @@ static int sftp_action_chmod(void *vctx, char *fname) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int result; unsigned oldperms, newperms; struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx; - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + result = fxp_stat_recv(pktin, req, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { printf("get attrs for %s: %s\n", fname, @@ -1666,10 +1684,9 @@ static int sftp_action_chmod(void *vctx, char *fname) if (oldperms == newperms) return 1; /* no need to do anything! */ - sftp_register(req = fxp_setstat_send(fname, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_setstat_recv(pktin, rreq); + req = fxp_setstat_send(fname, attrs); + pktin = sftp_wait_for_reply(req); + result = fxp_setstat_recv(pktin, req); if (!result) { printf("set attrs for %s: %s\n", fname, fxp_error()); @@ -2219,6 +2236,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) cmd->obey = sftp_cmd_quit; if ((mode == 0) || (modeflags & 1)) printf("quit\n"); + sfree(line); return cmd; /* eof */ } @@ -2265,10 +2283,13 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) * >this has "quotes" in< * >and"this"< */ - while (*p) { + while (1) { /* skip whitespace */ while (*p && (*p == ' ' || *p == '\t')) p++; + /* terminate loop */ + if (!*p) + break; /* mark start of word */ q = r = p; /* q sits at start, r writes word */ quoting = 0; @@ -2316,7 +2337,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) static int do_sftp_init(void) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Do protocol initialisation. @@ -2330,10 +2351,9 @@ static int do_sftp_init(void) /* * Find out where our home directory is. */ - sftp_register(req = fxp_realpath_send(".")); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - homedir = fxp_realpath_recv(pktin, rreq); + req = fxp_realpath_send("."); + pktin = sftp_wait_for_reply(req); + homedir = fxp_realpath_recv(pktin, req); if (!homedir) { fprintf(stderr, @@ -2352,6 +2372,7 @@ void do_sftp_cleanup() char ch; if (back) { back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); back->free(backhandle); sftp_cleanup_request(); @@ -2458,6 +2479,18 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +void nonfatal(char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + fputs(str2, stderr); + sfree(str2); +} void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; @@ -2560,6 +2593,19 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. + */ + if (!sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from SFTP server"); + } + return FALSE; +} int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; @@ -2664,32 +2710,31 @@ static int psftp_connect(char *userhost, char *user, int portnumber) */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ - Config cfg2; - cfg2.host[0] = '\0'; - do_defaults(host, &cfg2); - if (cfg2.host[0] != '\0') { + Conf *conf2 = conf_new(); + conf_set_str(conf2, CONF_host, ""); + do_defaults(host, conf2); + if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ - do_defaults(host, &cfg); + do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } + conf_free(conf2); } else { /* Patch in hostname `host' to session details. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ - if (cfg.protocol != PROT_SSH) { - cfg.protocol = PROT_SSH; - cfg.port = 22; + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); } /* @@ -2698,78 +2743,82 @@ static int psftp_connect(char *userhost, char *user, int portnumber) * work for SFTP. (Can be overridden with `-1' option.) * But if it says `2 only' or `2', respect which. */ - if (cfg.sshprot != 2 && cfg.sshprot != 3) - cfg.sshprot = 2; + if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2) /* is it 2 or 3? */ + conf_set_int(conf, CONF_sshprot, 2); /* * Enact command-line overrides. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } - - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; - } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); - } - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - p2++; } - cfg.host[p1] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } if (portnumber) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ - cfg.x11_forward = 0; - cfg.agentfwd = 0; - cfg.portfwd[0] = cfg.portfwd[1] = '\0'; - cfg.ssh_simple = TRUE; + conf_set_int(conf, CONF_x11_forward, 0); + conf_set_int(conf, CONF_agentfwd, 0); + conf_set_int(conf, CONF_ssh_simple, TRUE); + { + char *key; + while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) + conf_del_str_str(conf, CONF_portfwd, key); + } /* Set up subsystem name. */ - strcpy(cfg.remote_cmd, "sftp"); - cfg.ssh_subsys = TRUE; - cfg.nopty = TRUE; + conf_set_str(conf, CONF_remote_cmd, "sftp"); + conf_set_int(conf, CONF_ssh_subsys, TRUE); + conf_set_int(conf, CONF_nopty, TRUE); /* * Set up fallback option, for SSH-1 servers or servers with the @@ -2788,21 +2837,26 @@ static int psftp_connect(char *userhost, char *user, int portnumber) * obvious pathnames and then give up, and when it does give up * it will print the preferred pathname in the error messages. */ - cfg.remote_cmd_ptr2 = - "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" - "exec sftp-server"; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, + "test -x /usr/lib/sftp-server &&" + " exec /usr/lib/sftp-server\n" + "test -x /usr/local/lib/sftp-server &&" + " exec /usr/local/lib/sftp-server\n" + "exec sftp-server"); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); back = &ssh_backend; - err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, - 0, cfg.tcp_keepalives); + err = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, 0, + conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) { fprintf(stderr, "ssh_init: %s\n", err); return 1; } - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); while (!back->sendok(backhandle)) { @@ -2854,7 +2908,8 @@ int psftp_main(int argc, char *argv[]) userhost = user = NULL; /* Load Default Settings before doing anything else. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { @@ -2866,7 +2921,7 @@ int psftp_main(int argc, char *argv[]) userhost = dupstr(argv[i]); continue; } - ret = cmdline_process_param(argv[i], i+1connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); diff --git a/putty/PSFTP.H b/putty/PSFTP.H index 3e13887..e7e8ecc 100644 --- a/putty/PSFTP.H +++ b/putty/PSFTP.H @@ -85,15 +85,17 @@ void gui_enable(char *arg); */ typedef struct RFile RFile; typedef struct WFile WFile; -/* Output params size, mtime and atime can all be NULL if desired */ +/* Output params size, perms, mtime and atime can all be NULL if + * desired. perms will be -1 if the OS does not support POSIX permissions. */ RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime); + unsigned long *mtime, unsigned long *atime, + long *perms); WFile *open_existing_wfile(char *name, uint64 *size); /* Returns <0 on error, 0 on eof, or number of bytes read, as usual */ int read_from_file(RFile *f, void *buffer, int length); /* Closes and frees the RFile */ void close_rfile(RFile *f); -WFile *open_new_file(char *name); +WFile *open_new_file(char *name, long perms); /* Returns <0 on error, 0 on eof, or number of bytes written, as usual */ int write_to_file(WFile *f, void *buffer, int length); void set_file_times(WFile *f, unsigned long mtime, unsigned long atime); diff --git a/putty/PUTTY.H b/putty/PUTTY.H index 6363105..71f6ef5 100644 --- a/putty/PUTTY.H +++ b/putty/PUTTY.H @@ -18,7 +18,7 @@ #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -304,7 +304,7 @@ enum { }; enum { - /* Protocol back ends. (cfg.protocol) */ + /* Protocol back ends. (CONF_protocol) */ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ @@ -312,22 +312,22 @@ enum { }; enum { - /* Bell settings (cfg.beep) */ + /* Bell settings (CONF_beep) */ BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER }; enum { - /* Taskbar flashing indication on bell (cfg.beep_ind) */ + /* Taskbar flashing indication on bell (CONF_beep_ind) */ B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY }; enum { - /* Resize actions (cfg.resize_action) */ + /* Resize actions (CONF_resize_action) */ RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER }; enum { - /* Function key types (cfg.funky_type) */ + /* Function key types (CONF_funky_type) */ FUNKY_TILDE, FUNKY_LINUX, FUNKY_XTERM, @@ -415,12 +415,11 @@ enum { struct backend_tag { const char *(*init) (void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive); + Conf *conf, char *host, int port, char **realhost, + int nodelay, int keepalive); void (*free) (void *handle); /* back->reconfig() passes in a replacement configuration. */ - void (*reconfig) (void *handle, Config *cfg); + void (*reconfig) (void *handle, Conf *conf); /* back->send() returns the current amount of buffered data. */ int (*send) (void *handle, char *buf, int len); /* back->sendbuffer() does the same thing but without attempting a send */ @@ -462,214 +461,6 @@ extern const int be_default_protocol; extern const char *const appname; /* - * IMPORTANT POLICY POINT: everything in this structure which wants - * to be treated like an integer must be an actual, honest-to- - * goodness `int'. No enum-typed variables. This is because parts - * of the code will want to pass around `int *' pointers to them - * and we can't run the risk of porting to some system on which the - * enum comes out as a different size from int. - */ -struct config_tag { - /* Basic options */ - char host[512]; - int port; - int protocol; - int addressfamily; - int close_on_exit; - int warn_on_close; - int ping_interval; /* in seconds */ - int tcp_nodelay; - int tcp_keepalives; - char loghost[512]; /* logical host being contacted, for host key check */ - /* Proxy options */ - char proxy_exclude_list[512]; - int proxy_dns; - int even_proxy_localhost; - int proxy_type; - char proxy_host[512]; - int proxy_port; - char proxy_username[128]; - char proxy_password[128]; - char proxy_telnet_command[512]; - /* SSH options */ - char remote_cmd[512]; - char *remote_cmd_ptr; /* might point to a larger command - * but never for loading/saving */ - char *remote_cmd_ptr2; /* might point to a larger command - * but never for loading/saving */ - int nopty; - int compression; - int ssh_kexlist[KEX_MAX]; - int ssh_rekey_time; /* in minutes */ - char ssh_rekey_data[16]; - int tryagent; - int agentfwd; - int change_username; /* allow username switching in SSH-2 */ - int ssh_cipherlist[CIPHER_MAX]; - Filename keyfile; - int sshprot; /* use v1 or v2 when both available */ - int ssh2_des_cbc; /* "des-cbc" unrecommended SSH-2 cipher */ - int ssh_no_userauth; /* bypass "ssh-userauth" (SSH-2 only) */ - int ssh_show_banner; /* show USERAUTH_BANNERs (SSH-2 only) */ - int try_tis_auth; - int try_ki_auth; - int try_gssapi_auth; /* attempt gssapi auth */ - int gssapifwd; /* forward tgt via gss */ - int ssh_gsslist[4]; /* preference order for local GSS libs */ - Filename ssh_gss_custom; - int ssh_subsys; /* run a subsystem rather than a command */ - int ssh_subsys2; /* fallback to go with remote_cmd_ptr2 */ - int ssh_no_shell; /* avoid running a shell */ - char ssh_nc_host[512]; /* host to connect to in `nc' mode */ - int ssh_nc_port; /* port to connect to in `nc' mode */ - /* Telnet options */ - char termtype[32]; - char termspeed[32]; - char ttymodes[768]; /* MODE\tVvalue\0MODE\tA\0\0 */ - char environmt[1024]; /* VAR\tvalue\0VAR\tvalue\0\0 */ - char username[100]; - int username_from_env; - char localusername[100]; - int rfc_environ; - int passive_telnet; - /* Serial port options */ - char serline[256]; - int serspeed; - int serdatabits, serstopbits; - int serparity; - int serflow; - /* Keyboard options */ - int bksp_is_delete; - int rxvt_homeend; - int funky_type; - int no_applic_c; /* totally disable app cursor keys */ - int no_applic_k; /* totally disable app keypad */ - int no_mouse_rep; /* totally disable mouse reporting */ - int no_remote_resize; /* disable remote resizing */ - int no_alt_screen; /* disable alternate screen */ - int no_remote_wintitle; /* disable remote retitling */ - int no_dbackspace; /* disable destructive backspace */ - int no_remote_charset; /* disable remote charset config */ - int remote_qtitle_action; /* remote win title query action */ - int app_cursor; - int app_keypad; - int nethack_keypad; - int telnet_keyboard; - int telnet_newline; - int alt_f4; /* is it special? */ - int alt_space; /* is it special? */ - int alt_only; /* is it special? */ - int localecho; - int localedit; - int alwaysontop; - int fullscreenonaltenter; - int scroll_on_key; - int scroll_on_disp; - int erase_to_scrollback; - int compose_key; - int ctrlaltkeys; - char wintitle[256]; /* initial window title */ - /* Terminal options */ - int savelines; - int dec_om; - int wrap_mode; - int lfhascr; - int cursor_type; /* 0=block 1=underline 2=vertical */ - int blink_cur; - int beep; - int beep_ind; - int bellovl; /* bell overload protection active? */ - int bellovl_n; /* number of bells to cause overload */ - int bellovl_t; /* time interval for overload (seconds) */ - int bellovl_s; /* period of silence to re-enable bell (s) */ - Filename bell_wavefile; - int scrollbar; - int scrollbar_in_fullscreen; - int resize_action; - int bce; - int blinktext; - int win_name_always; - int width, height; - FontSpec font; - int font_quality; - Filename logfilename; - int logtype; - int logxfovr; - int logflush; - int logomitpass; - int logomitdata; - int hide_mouseptr; - int sunken_edge; - int window_border; - char answerback[256]; - char printer[128]; - int arabicshaping; - int bidi; - /* Colour options */ - int ansi_colour; - int xterm_256_colour; - int system_colour; - int try_palette; - int bold_colour; - unsigned char colours[22][3]; - /* Selection options */ - int mouse_is_xterm; - int rect_select; - int rawcnp; - int rtf_paste; - int mouse_override; - short wordness[256]; - /* translations */ - int vtmode; - char line_codepage[128]; - int cjk_ambig_wide; - int utf8_override; - int xlat_capslockcyr; - /* X11 forwarding */ - int x11_forward; - char x11_display[128]; - int x11_auth; - Filename xauthfile; - /* port forwarding */ - int lport_acceptall; /* accept conns from hosts other than localhost */ - int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */ - /* - * The port forwarding string contains a number of - * NUL-terminated substrings, terminated in turn by an empty - * string (i.e. a second NUL immediately after the previous - * one). Each string can be of one of the following forms: - * - * [LR]localport\thost:port - * [LR]localaddr:localport\thost:port - * Dlocalport - * Dlocaladdr:localport - */ - char portfwd[1024]; - /* SSH bug compatibility modes */ - int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1, - sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2, - sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2, - sshbug_ignore2; - /* - * 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; - int scrollbar_on_left; - int shadowbold; - FontSpec boldfont; - FontSpec widefont; - FontSpec wideboldfont; - int shadowboldoffset; - int crhaslf; - char winclass[256]; -}; - -/* * Some global flags denoting the type of application. * * FLAG_VERBOSE is set when the user requests verbose details. @@ -734,8 +525,19 @@ struct RSAKey; /* be a little careful of scope */ typedef struct { char *prompt; int echo; - char *result; /* allocated/freed by caller */ - size_t result_len; + /* + * 'result' must be a dynamically allocated array of exactly + * 'resultsize' chars. The code for actually reading input may + * realloc it bigger (and adjust resultsize accordingly) if it has + * to. The caller should free it again when finished with it. + * + * If resultsize==0, then result may be NULL. When setting up a + * prompt_t, it's therefore easiest to initialise them this way, + * which means all actual allocation is done by the callee. This + * is what add_prompt does. + */ + char *result; + size_t resultsize; } prompt_t; typedef struct { /* @@ -758,7 +560,9 @@ typedef struct { * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len); +void add_prompt(prompts_t *p, char *promptstr, int echo); +void prompt_set_result(prompt_t *pr, const char *newstr); +void prompt_ensure_result_size(prompt_t *pr, int len); /* Burn the evidence. (Assumes _all_ strings want free()ing.) */ void free_prompts(prompts_t *p); @@ -785,6 +589,7 @@ void get_clip(void *frontend, wchar_t **, int *); void optimised_move(void *frontend, int, int, int); void set_raw_mouse_mode(void *frontend, int); void connection_fatal(void *frontend, char *, ...); +void nonfatal(char *, ...); void fatalbox(char *, ...); void modalfatalbox(char *, ...); #ifdef macintosh @@ -804,6 +609,11 @@ void ldisc_update(void *frontend, int echo, int edit); void update_specials_menu(void *frontend); int from_backend(void *frontend, int is_stderr, const char *data, int len); int from_backend_untrusted(void *frontend, const char *data, int len); +/* Called when the back end wants to indicate that EOF has arrived on + * the server-to-client stream. Returns FALSE to indicate that we + * intend to keep the session open in the other direction, or TRUE to + * indicate that if they're closing so are we. */ +int from_backend_eof(void *frontend); void notify_remote_exit(void *frontend); /* Get a sensible value for a tty mode. NULL return = don't set. * Otherwise, returned value should be freed by caller. */ @@ -839,6 +649,270 @@ void set_busy_status(void *frontend, int status); void cleanup_exit(int); /* + * Exports from conf.c, and a big enum (via parametric macro) of + * configuration option keys. + */ +#define CONFIG_OPTIONS(X) \ + /* X(value-type, subkey-type, keyword) */ \ + X(STR, NONE, host) \ + X(INT, NONE, port) \ + X(INT, NONE, protocol) \ + X(INT, NONE, addressfamily) \ + X(INT, NONE, close_on_exit) \ + X(INT, NONE, warn_on_close) \ + X(INT, NONE, ping_interval) /* in seconds */ \ + X(INT, NONE, tcp_nodelay) \ + X(INT, NONE, tcp_keepalives) \ + X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \ + /* Proxy options */ \ + X(STR, NONE, proxy_exclude_list) \ + X(INT, NONE, proxy_dns) \ + X(INT, NONE, even_proxy_localhost) \ + X(INT, NONE, proxy_type) \ + X(STR, NONE, proxy_host) \ + X(INT, NONE, proxy_port) \ + X(STR, NONE, proxy_username) \ + X(STR, NONE, proxy_password) \ + X(STR, NONE, proxy_telnet_command) \ + /* SSH options */ \ + X(STR, NONE, remote_cmd) \ + X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \ + X(INT, NONE, nopty) \ + X(INT, NONE, compression) \ + X(INT, INT, ssh_kexlist) \ + X(INT, NONE, ssh_rekey_time) /* in minutes */ \ + X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ + X(INT, NONE, tryagent) \ + X(INT, NONE, agentfwd) \ + X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \ + X(INT, INT, ssh_cipherlist) \ + X(FILENAME, NONE, keyfile) \ + X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \ + X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ + X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ + X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ + X(INT, NONE, try_tis_auth) \ + X(INT, NONE, try_ki_auth) \ + X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \ + X(INT, NONE, gssapifwd) /* forward tgt via gss */ \ + X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \ + X(FILENAME, NONE, ssh_gss_custom) \ + X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \ + X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \ + X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \ + X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \ + X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \ + /* Telnet options */ \ + X(STR, NONE, termtype) \ + X(STR, NONE, termspeed) \ + X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \ + X(STR, STR, environmt) \ + X(STR, NONE, username) \ + X(INT, NONE, username_from_env) \ + X(STR, NONE, localusername) \ + X(INT, NONE, rfc_environ) \ + X(INT, NONE, passive_telnet) \ + /* Serial port options */ \ + X(STR, NONE, serline) \ + X(INT, NONE, serspeed) \ + X(INT, NONE, serdatabits) \ + X(INT, NONE, serstopbits) \ + X(INT, NONE, serparity) \ + X(INT, NONE, serflow) \ + /* Keyboard options */ \ + X(INT, NONE, bksp_is_delete) \ + X(INT, NONE, rxvt_homeend) \ + X(INT, NONE, funky_type) \ + X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \ + X(INT, NONE, no_applic_k) /* totally disable app keypad */ \ + X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \ + X(INT, NONE, no_remote_resize) /* disable remote resizing */ \ + X(INT, NONE, no_alt_screen) /* disable alternate screen */ \ + X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \ + X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \ + X(INT, NONE, no_remote_charset) /* disable remote charset config */ \ + X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \ + X(INT, NONE, app_cursor) \ + X(INT, NONE, app_keypad) \ + X(INT, NONE, nethack_keypad) \ + X(INT, NONE, telnet_keyboard) \ + X(INT, NONE, telnet_newline) \ + X(INT, NONE, alt_f4) /* is it special? */ \ + X(INT, NONE, alt_space) /* is it special? */ \ + X(INT, NONE, alt_only) /* is it special? */ \ + X(INT, NONE, localecho) \ + X(INT, NONE, localedit) \ + X(INT, NONE, alwaysontop) \ + X(INT, NONE, fullscreenonaltenter) \ + X(INT, NONE, scroll_on_key) \ + X(INT, NONE, scroll_on_disp) \ + X(INT, NONE, erase_to_scrollback) \ + X(INT, NONE, compose_key) \ + X(INT, NONE, ctrlaltkeys) \ + X(STR, NONE, wintitle) /* initial window title */ \ + /* Terminal options */ \ + X(INT, NONE, savelines) \ + X(INT, NONE, dec_om) \ + X(INT, NONE, wrap_mode) \ + X(INT, NONE, lfhascr) \ + X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \ + X(INT, NONE, blink_cur) \ + X(INT, NONE, beep) \ + X(INT, NONE, beep_ind) \ + X(INT, NONE, bellovl) /* bell overload protection active? */ \ + X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \ + X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \ + X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \ + X(FILENAME, NONE, bell_wavefile) \ + X(INT, NONE, scrollbar) \ + X(INT, NONE, scrollbar_in_fullscreen) \ + X(INT, NONE, resize_action) \ + X(INT, NONE, bce) \ + X(INT, NONE, blinktext) \ + X(INT, NONE, win_name_always) \ + X(INT, NONE, width) \ + X(INT, NONE, height) \ + X(FONT, NONE, font) \ + X(INT, NONE, font_quality) \ + X(FILENAME, NONE, logfilename) \ + X(INT, NONE, logtype) \ + X(INT, NONE, logxfovr) \ + X(INT, NONE, logflush) \ + X(INT, NONE, logomitpass) \ + X(INT, NONE, logomitdata) \ + X(INT, NONE, hide_mouseptr) \ + X(INT, NONE, sunken_edge) \ + X(INT, NONE, window_border) \ + X(STR, NONE, answerback) \ + X(STR, NONE, printer) \ + X(INT, NONE, arabicshaping) \ + X(INT, NONE, bidi) \ + /* Colour options */ \ + X(INT, NONE, ansi_colour) \ + X(INT, NONE, xterm_256_colour) \ + X(INT, NONE, system_colour) \ + X(INT, NONE, try_palette) \ + X(INT, NONE, bold_style) \ + X(INT, INT, colours) \ + /* Selection options */ \ + X(INT, NONE, mouse_is_xterm) \ + X(INT, NONE, rect_select) \ + X(INT, NONE, rawcnp) \ + X(INT, NONE, rtf_paste) \ + X(INT, NONE, mouse_override) \ + X(INT, INT, wordness) \ + /* translations */ \ + X(INT, NONE, vtmode) \ + X(STR, NONE, line_codepage) \ + X(INT, NONE, cjk_ambig_wide) \ + X(INT, NONE, utf8_override) \ + X(INT, NONE, xlat_capslockcyr) \ + /* X11 forwarding */ \ + X(INT, NONE, x11_forward) \ + X(STR, NONE, x11_display) \ + X(INT, NONE, x11_auth) \ + X(FILENAME, NONE, xauthfile) \ + /* port forwarding */ \ + X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \ + X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \ + /* \ + * Subkeys for 'portfwd' can have the following forms: \ + * \ + * [LR]localport \ + * [LR]localaddr:localport \ + * \ + * Dynamic forwardings are indicated by an 'L' key, and the \ + * special value "D". For all other forwardings, the value \ + * should be of the form 'host:port'. \ + */ \ + X(STR, STR, portfwd) \ + /* SSH bug compatibility modes */ \ + X(INT, NONE, sshbug_ignore1) \ + X(INT, NONE, sshbug_plainpw1) \ + X(INT, NONE, sshbug_rsa1) \ + X(INT, NONE, sshbug_hmac2) \ + X(INT, NONE, sshbug_derivekey2) \ + X(INT, NONE, sshbug_rsapad2) \ + X(INT, NONE, sshbug_pksessid2) \ + X(INT, NONE, sshbug_rekey2) \ + X(INT, NONE, sshbug_maxpkt2) \ + X(INT, NONE, sshbug_ignore2) \ + X(INT, NONE, sshbug_winadj) \ + /* \ + * 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. \ + */ \ + X(INT, NONE, ssh_simple) \ + /* Options for pterm. Should split out into platform-dependent part. */ \ + X(INT, NONE, stamp_utmp) \ + X(INT, NONE, login_shell) \ + X(INT, NONE, scrollbar_on_left) \ + X(INT, NONE, shadowbold) \ + X(FONT, NONE, boldfont) \ + X(FONT, NONE, widefont) \ + X(FONT, NONE, wideboldfont) \ + X(INT, NONE, shadowboldoffset) \ + X(INT, NONE, crhaslf) \ + X(STR, NONE, winclass) \ + +/* Now define the actual enum of option keywords using that macro. */ +#define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword, +enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS }; +#undef CONF_ENUM_DEF + +#define NCFGCOLOURS 22 /* number of colours in CONF_colours above */ + +/* Functions handling configuration structures. */ +Conf *conf_new(void); /* create an empty configuration */ +void conf_free(Conf *conf); +Conf *conf_copy(Conf *oldconf); +void conf_copy_into(Conf *dest, Conf *src); +/* Mandatory accessor functions: enforce by assertion that keys exist. */ +int conf_get_int(Conf *conf, int key); +int conf_get_int_int(Conf *conf, int key, int subkey); +char *conf_get_str(Conf *conf, int key); /* result still owned by conf */ +char *conf_get_str_str(Conf *conf, int key, const char *subkey); +Filename *conf_get_filename(Conf *conf, int key); +FontSpec *conf_get_fontspec(Conf *conf, int key); /* still owned by conf */ +/* Optional accessor function: return NULL if key does not exist. */ +char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey); +/* Accessor function to step through a string-subkeyed list. + * Returns the next subkey after the provided one, or the first if NULL. + * Returns NULL if there are none left. + * Both the return value and *subkeyout are still owned by conf. */ +char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout); +/* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */ +char *conf_get_str_nthstrkey(Conf *conf, int key, int n); +/* Functions to set entries in configuration. Always copy their inputs. */ +void conf_set_int(Conf *conf, int key, int value); +void conf_set_int_int(Conf *conf, int key, int subkey, int value); +void conf_set_str(Conf *conf, int key, const char *value); +void conf_set_str_str(Conf *conf, int key, + const char *subkey, const char *val); +void conf_del_str_str(Conf *conf, int key, const char *subkey); +void conf_set_filename(Conf *conf, int key, const Filename *val); +void conf_set_fontspec(Conf *conf, int key, const FontSpec *val); +/* Serialisation functions for Duplicate Session */ +int conf_serialised_size(Conf *conf); +void conf_serialise(Conf *conf, void *data); +int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/ + +/* + * Functions to copy, free, serialise and deserialise FontSpecs. + * Provided per-platform, to go with the platform's idea of a + * FontSpec's contents. + * + * fontspec_serialise returns the number of bytes written, and can + * handle data==NULL without crashing. So you can call it once to find + * out a size, then again once you've allocated a buffer. + */ +FontSpec *fontspec_copy(const FontSpec *f); +void fontspec_free(FontSpec *f); +int fontspec_serialise(FontSpec *f, void *data); +FontSpec *fontspec_deserialise(void *data, int maxsize, int *used); + +/* * Exports from noise.c. */ void noise_get_heavy(void (*func) (void *, int)); @@ -853,13 +927,13 @@ void random_destroy_seed(void); */ Backend *backend_from_name(const char *name); Backend *backend_from_proto(int proto); -int get_remote_username(Config *cfg, char *user, size_t len); -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); +char *get_remote_username(Conf *conf); /* dynamically allocated */ +char *save_settings(char *section, Conf *conf); +void save_open_settings(void *sesskey, Conf *conf); +void load_settings(char *section, Conf *conf); +void load_open_settings(void *sesskey, Conf *conf); void get_sesslist(struct sesslist *, int allocate); -void do_defaults(char *, Config *); +void do_defaults(char *, Conf *); void registry_cleanup(void); /* @@ -872,17 +946,21 @@ void registry_cleanup(void); * function is perfectly all right returning NULL, of course. The * Filename and FontSpec functions are _not allowed_ to fail to * return, since these defaults _must_ be per-platform.) + * + * The 'Filename *' returned by platform_default_filename, and the + * 'FontSpec *' returned by platform_default_fontspec, have ownership + * transferred to the caller, and must be freed. */ char *platform_default_s(const char *name); int platform_default_i(const char *name, int def); -Filename platform_default_filename(const char *name); -FontSpec platform_default_fontspec(const char *name); +Filename *platform_default_filename(const char *name); +FontSpec *platform_default_fontspec(const char *name); /* * Exports from terminal.c. */ -Terminal *term_init(Config *, struct unicode_data *, void *); +Terminal *term_init(Conf *, struct unicode_data *, void *); void term_free(Terminal *); void term_size(Terminal *, int, int, int); void term_paint(Terminal *, Context, int, int, int, int, int); @@ -904,7 +982,7 @@ void term_paste(Terminal *); void term_nopaste(Terminal *); int term_ldisc(Terminal *, int option); void term_copyall(Terminal *); -void term_reconfig(Terminal *, Config *); +void term_reconfig(Terminal *, Conf *); void term_seen_key_event(Terminal *); int term_data(Terminal *, int is_stderr, const char *data, int len); int term_data_untrusted(Terminal *, const char *data, int len); @@ -922,9 +1000,9 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl); /* * Exports from logging.c. */ -void *log_init(void *frontend, Config *cfg); +void *log_init(void *frontend, Conf *conf); void log_free(void *logctx); -void log_reconfig(void *logctx, Config *cfg); +void log_reconfig(void *logctx, Conf *conf); void logfopen(void *logctx); void logfclose(void *logctx); void logtraffic(void *logctx, unsigned char c, int logmode); @@ -975,7 +1053,8 @@ extern Backend ssh_backend; /* * Exports from ldisc.c. */ -void *ldisc_create(Config *, Terminal *, Backend *, void *, void *); +void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *); +void ldisc_configure(void *, Conf *); void ldisc_free(void *); void ldisc_send(void *handle, char *buf, int len, int interactive); @@ -1003,8 +1082,8 @@ void random_unref(void); * Exports from pinger.c. */ typedef struct pinger_tag *Pinger; -Pinger pinger_new(Config *cfg, Backend *back, void *backhandle); -void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg); +Pinger pinger_new(Conf *conf, Backend *back, void *backhandle); +void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf); void pinger_free(Pinger); /* @@ -1012,8 +1091,8 @@ void pinger_free(Pinger); */ #include "misc.h" -int cfg_launchable(const Config *cfg); -char const *cfg_dest(const Config *cfg); +int conf_launchable(Conf *conf); +char const *conf_dest(Conf *conf); /* * Exports from sercfg.c. @@ -1034,9 +1113,9 @@ extern char ver[]; #endif /* void init_ucs(void); -- this is now in platform-specific headers */ int is_dbcs_leadbyte(int codepage, char byte); -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen); -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata); wchar_t xlat_uskbd2cyrllic(int ch); @@ -1049,10 +1128,10 @@ void get_unitab(int codepage, wchar_t * unitab, int ftype); /* * Exports from wcwidth.c */ -int mk_wcwidth(wchar_t ucs); -int mk_wcswidth(const wchar_t *pwcs, size_t n); -int mk_wcwidth_cjk(wchar_t ucs); -int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n); +int mk_wcwidth(unsigned int ucs); +int mk_wcswidth(const unsigned int *pwcs, size_t n); +int mk_wcwidth_cjk(unsigned int ucs); +int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n); /* * Exports from mscrypto.c @@ -1118,7 +1197,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * - 0 means cancel logging for this session * - -1 means please wait. */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); /* @@ -1147,8 +1226,8 @@ void printer_finish_job(printer_job *); * defined differently in various places and required _by_ * cmdline.c). */ -int cmdline_process_param(char *, char *, int, Config *); -void cmdline_run_saved(Config *); +int cmdline_process_param(char *, char *, int, Conf *); +void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen); #define TOOLTYPE_FILETRANSFER 1 @@ -1161,6 +1240,18 @@ void cmdline_error(char *, ...); * Exports from config.c. */ struct controlbox; +union control; +void conf_radiobutton_handler(union control *ctrl, void *dlg, + void *data, int event); +#define CHECKBOX_INVERT (1<<30) +void conf_checkbox_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_editbox_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_filesel_handler(union control *ctrl, void *dlg, + void *data, int event); +void conf_fontsel_handler(union control *ctrl, void *dlg, + void *data, int event); void setup_config_box(struct controlbox *b, int midsession, int protocol, int protcfginfo); @@ -1168,7 +1259,7 @@ void setup_config_box(struct controlbox *b, int midsession, * Exports from minibidi.c. */ typedef struct bidi_char { - wchar_t origwc, wc; + unsigned int origwc, wc; unsigned short index; } bidi_char; int do_bidi(bidi_char *line, int count); @@ -1188,11 +1279,18 @@ extern const char *const x11_authnames[]; /* declared in x11fwd.c */ /* * Miscellaneous exports from the platform-specific code. + * + * filename_serialise and filename_deserialise have the same semantics + * as fontspec_serialise and fontspec_deserialise above. */ -Filename filename_from_str(const char *string); +Filename *filename_from_str(const char *string); const char *filename_to_str(const Filename *fn); -int filename_equal(Filename f1, Filename f2); -int filename_is_null(Filename fn); +int filename_equal(const Filename *f1, const Filename *f2); +int filename_is_null(const Filename *fn); +Filename *filename_copy(const Filename *fn); +void filename_free(Filename *fn); +int filename_serialise(const Filename *f, void *data); +Filename *filename_deserialise(void *data, int maxsize, int *used); char *get_username(void); /* return value needs freeing */ char *get_random_data(int bytes); /* used in cmdgen.c */ @@ -1286,11 +1384,11 @@ char *get_random_data(int bytes); /* used in cmdgen.c */ * GETTICKCOUNT() and compare the result with the returned `next' * value to find out how long you have to make your next wait().) */ -typedef void (*timer_fn_t)(void *ctx, long now); -long schedule_timer(int ticks, timer_fn_t fn, void *ctx); +typedef void (*timer_fn_t)(void *ctx, unsigned long now); +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx); void expire_timer_context(void *ctx); -int run_timers(long now, long *next); -void timer_change_notify(long next); +int run_timers(unsigned long now, unsigned long *next); +void timer_change_notify(unsigned long next); /* * Define no-op macros for the jump list functions, on platforms that @@ -1303,6 +1401,31 @@ void timer_change_notify(long next); #define remove_session_from_jumplist(x) ((void)0) #endif +/* SURROGATE PAIR */ +#ifndef IS_HIGH_SURROGATE +#define HIGH_SURROGATE_START 0xd800 +#define HIGH_SURROGATE_END 0xdbff +#define LOW_SURROGATE_START 0xdc00 +#define LOW_SURROGATE_END 0xdfff + +#define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ + ((wch) <= HIGH_SURROGATE_END)) +#define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && \ + ((wch) <= LOW_SURROGATE_END)) +#define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && \ + IS_LOW_SURROGATE(ls)) +#endif + + +#define IS_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ + ((wch) <= LOW_SURROGATE_END)) +#define HIGH_SURROGATE_OF(codept) \ + (HIGH_SURROGATE_START + (((codept) - 0x10000) >> 10)) +#define LOW_SURROGATE_OF(codept) \ + (LOW_SURROGATE_START + (((codept) - 0x10000) & 0x3FF)) +#define FROM_SURROGATES(wch1, wch2) \ + (0x10000 + (((wch1) & 0x3FF) << 10) + ((wch2) & 0x3FF)) + // FFFTP #include "mbswrapper.h" #include "iowrapper.h" diff --git a/putty/PUTTYMEM.H b/putty/PUTTYMEM.H index d478f79..cf1afda 100644 --- a/putty/PUTTYMEM.H +++ b/putty/PUTTYMEM.H @@ -34,9 +34,19 @@ void safefree(void *); * possible, in favour of these type-casting macros which ensure * you don't mistakenly allocate enough space for one sort of * structure and assign it to a different sort of pointer. + * + * The nasty trick in sresize with sizeof arranges for the compiler, + * in passing, to type-check the expression ((type *)0 == (ptr)), i.e. + * to type-check that the input pointer is a pointer to the correct + * type. The construction sizeof(stuff) ? (b) : (b) looks like a + * violation of the first principle of safe macros, but in fact it's + * OK - although it _expands_ the macro parameter more than once, it + * only _evaluates_ it once, so it's still side-effect safe. */ #define snew(type) ((type *)snmalloc(1, sizeof(type))) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) -#define sresize(ptr, n, type) ((type *)snrealloc((ptr), (n), sizeof(type))) +#define sresize(ptr, n, type) \ + ((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ + (n), sizeof(type))) #endif diff --git a/putty/PuTTY.vc90.vcproj b/putty/PuTTY.vc90.vcproj index eb20588..de12854 100644 --- a/putty/PuTTY.vc90.vcproj +++ b/putty/PuTTY.vc90.vcproj @@ -330,6 +330,10 @@ > + + diff --git a/putty/PuTTY.vcproj b/putty/PuTTY.vcproj index 0d895c2..5d3701a 100644 --- a/putty/PuTTY.vcproj +++ b/putty/PuTTY.vcproj @@ -329,6 +329,10 @@ > + + diff --git a/putty/RAW.C b/putty/RAW.C index ea51d74..480a8f9 100644 --- a/putty/RAW.C +++ b/putty/RAW.C @@ -4,6 +4,7 @@ #include #include +#include #include "putty.h" @@ -21,8 +22,10 @@ typedef struct raw_backend_data { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; int bufsize; void *frontend; + int sent_console_eof, sent_socket_eof; } *Raw; static void raw_size(void *handle, int width, int height); @@ -47,6 +50,22 @@ static void raw_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(raw->frontend, msg); + sfree(msg); +} + +static void raw_check_close(Raw raw) +{ + /* + * Called after we send EOF on either the socket or the console. + * Its job is to wind up the session once we have sent EOF on both. + */ + if (raw->sent_console_eof && raw->sent_socket_eof) { + if (raw->s) { + sk_close(raw->s); + raw->s = NULL; + notify_remote_exit(raw->frontend); + } + } } static int raw_closing(Plug plug, const char *error_msg, int error_code, @@ -54,16 +73,32 @@ static int raw_closing(Plug plug, const char *error_msg, int error_code, { Raw raw = (Raw) plug; - if (raw->s) { - sk_close(raw->s); - raw->s = NULL; - notify_remote_exit(raw->frontend); - } if (error_msg) { - /* A socket error has occurred. */ - logevent(raw->frontend, error_msg); - connection_fatal(raw->frontend, "%s", error_msg); - } /* Otherwise, the remote side closed the connection normally. */ + /* A socket error has occurred. */ + if (raw->s) { + sk_close(raw->s); + raw->s = NULL; + raw->closed_on_socket_error = TRUE; + notify_remote_exit(raw->frontend); + } + logevent(raw->frontend, error_msg); + connection_fatal(raw->frontend, "%s", error_msg); + } else { + /* Otherwise, the remote side closed the connection normally. */ + if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) { + /* + * The front end wants us to close the outgoing side of the + * connection as soon as we see EOF from the far end. + */ + if (!raw->sent_socket_eof) { + if (raw->s) + sk_write_eof(raw->s); + raw->sent_socket_eof= TRUE; + } + } + raw->sent_console_eof = TRUE; + raw_check_close(raw); + } return 0; } @@ -89,7 +124,7 @@ static void raw_sent(Plug plug, int bufsize) * freed by the caller. */ static const char *raw_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -102,27 +137,32 @@ static const char *raw_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Raw raw; + int addressfamily; + char *loghost; raw = snew(struct raw_backend_data); raw->fn = &fn_table; raw->s = NULL; + raw->closed_on_socket_error = FALSE; *backend_handle = raw; + raw->sent_console_eof = raw->sent_socket_eof = FALSE; raw->frontend = frontend_handle; + addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(raw->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -135,15 +175,16 @@ static const char *raw_init(void *frontend_handle, void **backend_handle, * Open socket. */ raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive, - (Plug) raw, cfg); + (Plug) raw, conf); if ((err = sk_socket_error(raw->s)) != NULL) return err; - if (*cfg->loghost) { + loghost = conf_get_str(conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(cfg->loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -170,7 +211,7 @@ static void raw_free(void *handle) /* * Stub routine (we don't have any need to reconfigure this backend). */ -static void raw_reconfig(void *handle, Config *cfg) +static void raw_reconfig(void *handle, Conf *conf) { } @@ -208,11 +249,17 @@ static void raw_size(void *handle, int width, int height) } /* - * Send raw special codes. + * Send raw special codes. We only handle outgoing EOF here. */ static void raw_special(void *handle, Telnet_Special code) { - /* Do nothing! */ + Raw raw = (Raw) handle; + if (code == TS_EOF && raw->s) { + sk_write_eof(raw->s); + raw->sent_socket_eof= TRUE; + raw_check_close(raw); + } + return; } @@ -264,6 +311,8 @@ static int raw_exitcode(void *handle) Raw raw = (Raw) handle; if (raw->s != NULL) return -1; /* still connected */ + else if (raw->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* Exit codes are a meaningless concept in the Raw protocol */ return 0; diff --git a/putty/README b/putty/README index 9cca5c0..97aed71 100644 --- a/putty/README +++ b/putty/README @@ -40,7 +40,7 @@ For building on Windows: Makefile.bor' while in the `windows' subdirectory to build all the PuTTY binaries. - - windows/Makefile.cyg is for Cygwin / mingw32 installations. Type + - windows/Makefile.cyg is for Cygwin / MinGW installations. Type `make -f Makefile.cyg' while in the `windows' subdirectory to build all the PuTTY binaries. @@ -65,8 +65,12 @@ For building on Unix: - unix/configure is for Unix and GTK. If you don't have GTK, you should still be able to build the command-line utilities (PSCP, - PSFTP, Plink, PuTTYgen) using this script. To use it, change - into the `unix' subdirectory, run `./configure' and then `make'. + PSFTP, Plink, PuTTYgen) using this script. To use it, change into + the `unix' subdirectory, run `./configure' and then `make'. Or you + can do the same in the top-level directory (we provide a little + wrapper that invokes configure one level down), which is more like + a normal Unix source archive but doesn't do so well at keeping the + per-platform stuff in each platform's subdirectory; it's up to you. Note that Unix PuTTY has mostly only been tested on Linux so far; portability problems such as BSD-style ptys or different header file @@ -80,22 +84,46 @@ For building on Unix: utilities and has no Gtk dependence. - For the graphical utilities, Gtk+-1.2 and Gtk+-2.0 should both be - supported. - - - Both Unix Makefiles have an `install' target. Note that by default - it tries to install `man' pages, which you may need to have built - using Halibut first -- see below. + supported. If you have both installed, you can manually specify + which one you want by giving the option '--with-gtk=1' or + '--with-gtk=2' to the configure script. (2 is the default, of + course.) In the absence of either, the configure script will + automatically construct a Makefile which builds only the + command-line utilities; you can manually create this condition by + giving configure the option '--without-gtk'. + + - pterm would like to be setuid or setgid, as appropriate, to permit + it to write records of user logins to /var/run/utmp and + /var/log/wtmp. (Of course it will not use this privilege for + anything else, and in particular it will drop all privileges before + starting up complex subsystems like GTK.) By default the makefile + will not attempt to add privileges to the pterm executable at 'make + install' time, but you can ask it to do so by running configure + with the option '--enable-setuid=USER' or '--enable-setgid=GROUP'. + + - The Unix Makefiles have an `install' target. Note that by default + it tries to install `man' pages; if you have fetched the source via + Subversion then you will need to have built these using Halibut + first - see below. + + - It's also possible to build the Windows version of PuTTY to run + on Unix by using Winelib. To do this, change to the `windows' + directory and run `make -f Makefile.cyg CC=winegcc RC=wrc'. All of the Makefiles are generated automatically from the file -`Recipe' by the Perl script `mkfiles.pl'. Additions and corrections -to Recipe and the mkfiles.pl are much more useful than additions and -corrections to the alternative Makefiles themselves. +`Recipe' by the Perl script `mkfiles.pl' (except for the Unix one, +which is generated by the `configure' script; mkfiles.pl only +generates the input to automake). Additions and corrections to Recipe, +mkfiles.pl and/or configure.ac are much more useful than additions and +corrections to the actual Makefiles, Makefile.am or Makefile.in. The Unix `configure' script and its various requirements are generated by the shell script `mkauto.sh', which requires GNU Autoconf, GNU Automake, and Gtk; if you've got the source from Subversion rather than using one of our source snapshots, you'll need to run this -yourself. +yourself. The input file to Automake is generated by mkfiles.pl along +with all the rest of the makefiles, so you will need to run mkfiles.pl +and then mkauto.sh. Documentation (in various formats including Windows Help and Unix `man' pages) is built from the Halibut (`.but') files in the `doc' diff --git a/putty/RECIPE b/putty/RECIPE index cfc7ac9..e1d2c18 100644 --- a/putty/RECIPE +++ b/putty/RECIPE @@ -19,7 +19,7 @@ !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile unix unix/Makefile.ux -!makefile ac unix/Makefile.in +!makefile am unix/Makefile.am !makefile osx macosx/Makefile !makefile devcppproj windows/DEVCPP # Source directories. @@ -113,6 +113,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -169,6 +174,21 @@ version.o: FORCE fi !end !specialobj gtk version +# In the automake build, we have to do the whole job by supplying +# extra CFLAGS, so we have to put the if statement inside one big +# backtick expression. We also force rebuilding via a -D option that +# makes version.o include empty.h, which we construct ourselves and +# touch whenever any source file is updated. +!cflags am version $(VER) -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; md5sum -c manifest >/dev/null 2>&1); then cat $(srcdir)/../version.def; else echo "$(VER)"; fi` +!begin am +BUILT_SOURCES = empty.h +empty.h: $(allsources) + echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ + +!end +!begin >empty.h +/* Empty file touched by automake makefile to force rebuild of version.o */ +!end # Add VER to Windows resource targets, and force them to be rebuilt every # time, on the assumption that they will contain version information. @@ -177,6 +197,7 @@ CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32 RCFLAGS = $(RCFLAGS) $(VER) !end !begin cygwin vars +CFLAGS += -DSECURITY_WIN32 # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) !end @@ -228,6 +249,21 @@ install-strip: CFLAGS += -DMACOSX !end +# List the man pages for the automake makefile. +!begin am +man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \ + ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1 +!end + +# In automake, chgrp/chmod pterm after installation, if configured to. +!begin am +if HAVE_SETID_CMD +install-exec-local: + @SETID_CMD@ $(bindir)/pterm + chmod @SETID_MODE@ $(bindir)/pterm +endif +!end + # Random symbols. !begin cygwin vars # _WIN32_IE is required to expose identifiers that only make sense on @@ -245,7 +281,7 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 # Terminal emulator and its (platform-independent) dependencies. TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi - + config dialog + + config dialog conf # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint @@ -272,7 +308,7 @@ SFTP = sftp int64 logging # Miscellaneous objects appearing in all the network utilities (not # Pageant or PuTTYgen). -MISC = timing misc version settings tree234 proxy +MISC = timing misc version settings tree234 proxy conf WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy + wintime UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time @@ -315,12 +351,12 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils - + winmisc winhelp pageant.res LIBS + + winmisc winhelp conf pageant.res LIBS puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res - + tree234 notiming winhelp winnojmp LIBS wintime + + tree234 notiming winhelp winnojmp conf LIBS wintime pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg @@ -338,7 +374,7 @@ 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 sshsh256 sshsh512 import puttygen.res time tree234 - + uxgen notiming + + uxgen notiming conf pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC diff --git a/putty/RLOGIN.C b/putty/RLOGIN.C index 0abf8cd..ec1b21b 100644 --- a/putty/RLOGIN.C +++ b/putty/RLOGIN.C @@ -4,6 +4,7 @@ #include #include +#include #include #include "putty.h" @@ -22,13 +23,14 @@ typedef struct rlogin_tag { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; int bufsize; int firstbyte; int cansize; int term_width, term_height; void *frontend; - Config cfg; + Conf *conf; /* In case we need to read a username from the terminal before starting */ prompts_t *prompt; @@ -56,15 +58,25 @@ static void rlogin_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(rlogin->frontend, msg); + sfree(msg); } static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; + + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; + if (error_msg) + rlogin->closed_on_socket_error = TRUE; notify_remote_exit(rlogin->frontend); } if (error_msg) { @@ -122,18 +134,18 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser) { char z = 0; char *p; + sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, rlogin->cfg.localusername, - strlen(rlogin->cfg.localusername)); + p = conf_get_str(rlogin->conf, CONF_localusername); + sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, ruser, - strlen(ruser)); + sk_write(rlogin->s, ruser, strlen(ruser)); sk_write(rlogin->s, &z, 1); - sk_write(rlogin->s, rlogin->cfg.termtype, - strlen(rlogin->cfg.termtype)); + p = conf_get_str(rlogin->conf, CONF_termtype); + sk_write(rlogin->s, p, strlen(p)); sk_write(rlogin->s, "/", 1); - for (p = rlogin->cfg.termspeed; isdigit((unsigned char)*p); p++) continue; - sk_write(rlogin->s, rlogin->cfg.termspeed, p - rlogin->cfg.termspeed); + p = conf_get_str(rlogin->conf, CONF_termspeed); + sk_write(rlogin->s, p, strspn(p, "0123456789")); rlogin->bufsize = sk_write(rlogin->s, &z, 1); rlogin->prompt = NULL; @@ -148,7 +160,7 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser) * freed by the caller. */ static const char *rlogin_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -161,33 +173,37 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Rlogin rlogin; - char ruser[sizeof(cfg->username)]; + char *ruser; + int addressfamily; + char *loghost; rlogin = snew(struct rlogin_tag); rlogin->fn = &fn_table; rlogin->s = NULL; + rlogin->closed_on_socket_error = FALSE; rlogin->frontend = frontend_handle; - rlogin->term_width = cfg->width; - rlogin->term_height = cfg->height; + rlogin->term_width = conf_get_int(conf, CONF_width); + rlogin->term_height = conf_get_int(conf, CONF_height); rlogin->firstbyte = 1; rlogin->cansize = 0; rlogin->prompt = NULL; - rlogin->cfg = *cfg; /* STRUCTURE COPY */ + rlogin->conf = conf_copy(conf); *backend_handle = rlogin; + addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(rlogin->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -200,15 +216,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, * Open socket. */ rlogin->s = new_connection(addr, *realhost, port, 1, 0, - nodelay, keepalive, (Plug) rlogin, cfg); + nodelay, keepalive, (Plug) rlogin, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) return err; - if (*cfg->loghost) { + loghost = conf_get_str(conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(cfg->loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -226,16 +243,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, * in which case we prompt for it and may end up deferring doing * anything else until the local prompt mechanism returns. */ - if (get_remote_username(cfg, ruser, sizeof(ruser))) { + if ((ruser = get_remote_username(conf)) != NULL) { rlogin_startup(rlogin, ruser); + sfree(ruser); } else { int ret; rlogin->prompt = new_prompts(rlogin->frontend); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); - add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE, - sizeof(cfg->username)); + add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); ret = get_userpass_input(rlogin->prompt, NULL, 0); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); @@ -253,13 +270,14 @@ static void rlogin_free(void *handle) free_prompts(rlogin->prompt); if (rlogin->s) sk_close(rlogin->s); + conf_free(rlogin->conf); sfree(rlogin); } /* * Stub routine (we don't have any need to reconfigure this backend). */ -static void rlogin_reconfig(void *handle, Config *cfg) +static void rlogin_reconfig(void *handle, Conf *conf) { } @@ -380,6 +398,8 @@ static int rlogin_exitcode(void *handle) Rlogin rlogin = (Rlogin) handle; if (rlogin->s != NULL) return -1; /* still connected */ + else if (rlogin->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* If we ever implement RSH, we'll probably need to do this properly */ return 0; diff --git a/putty/Release/PuTTY.dll b/putty/Release/PuTTY.dll index 894acf2966233993be023bbb74232cf586231a87..7b2613957d7856b5410c27c9c851c96399fd8655 100644 GIT binary patch delta 228140 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 z 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%0D?aX%WMyDFdEC}rp! zN{?xhdxD*hk5z7LEAaZ*Fj=oNG-GfK%^2cdRm~We58Di~4G{QFisAswE|q1|q=HLA z`*^!(!#)TtiIA+9J)gSv%rt3*z7y`|N|R%0DROTuyG)t2Lh7X~Tq#+)t0$e?w*z8? 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 WF8p3lK{;J2CPn^Wn3b?P>Hh%NzIp!u delta 217653 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 F{|BEROW^listbox.context.i; int i, j; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { - int oldparity = cfg->serparity;/* preserve past reentrant calls */ + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldparity = conf_get_int(conf, CONF_serparity); + dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(parities); i++) { @@ -56,14 +60,14 @@ static void serial_parity_handler(union control *ctrl, void *dlg, oldparity = SER_PAR_NONE; } dlg_update_done(ctrl, dlg); - cfg->serparity = oldparity; /* restore */ + conf_set_int(conf, CONF_serparity, oldparity); /* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_PAR_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); - cfg->serparity = i; + conf_set_int(conf, CONF_serparity, i); } } @@ -81,10 +85,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg, }; int mask = ctrl->listbox.context.i; int i, j; - Config *cfg = (Config *)data; + Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { - int oldflow = cfg->serflow; /* preserve past reentrant calls */ + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldflow = conf_get_int(conf, CONF_serflow); + dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(flows); i++) { @@ -105,14 +113,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg, oldflow = SER_FLOW_NONE; } dlg_update_done(ctrl, dlg); - cfg->serflow = oldflow; /* restore */ + conf_set_int(conf, CONF_serflow, oldflow);/* restore */ } else if (event == EVENT_SELCHANGE) { int i = dlg_listbox_index(ctrl, dlg); if (i < 0) i = SER_FLOW_NONE; else i = dlg_listbox_getid(ctrl, dlg, i); - cfg->serflow = i; + conf_set_int(conf, CONF_serflow, i); } } @@ -173,23 +181,22 @@ void ser_setup_config_box(struct controlbox *b, int midsession, "Select a serial line"); ctrl_editbox(s, "Serial line to connect to", 'l', 40, HELPCTX(serial_line), - dlg_stdeditbox_handler, I(offsetof(Config,serline)), - I(sizeof(((Config *)0)->serline))); + conf_editbox_handler, I(CONF_serline), I(1)); } s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); ctrl_editbox(s, "Speed (baud)", 's', 40, HELPCTX(serial_speed), - dlg_stdeditbox_handler, I(offsetof(Config,serspeed)), I(-1)); + conf_editbox_handler, I(CONF_serspeed), I(-1)); ctrl_editbox(s, "Data bits", 'b', 40, HELPCTX(serial_databits), - dlg_stdeditbox_handler,I(offsetof(Config,serdatabits)),I(-1)); + conf_editbox_handler, I(CONF_serdatabits), I(-1)); /* * Stop bits come in units of one half. */ ctrl_editbox(s, "Stop bits", 't', 40, HELPCTX(serial_stopbits), - dlg_stdeditbox_handler,I(offsetof(Config,serstopbits)),I(-2)); + conf_editbox_handler, I(CONF_serstopbits), I(-2)); ctrl_droplist(s, "Parity", 'p', 40, HELPCTX(serial_parity), serial_parity_handler, I(parity_mask)); diff --git a/putty/SETTINGS.C b/putty/SETTINGS.C index 9a62f7e..9a55781 100644 --- a/putty/SETTINGS.C +++ b/putty/SETTINGS.C @@ -70,67 +70,68 @@ Backend *backend_from_proto(int proto) return NULL; } -int get_remote_username(Config *cfg, char *user, size_t len) +char *get_remote_username(Conf *conf) { - if (*cfg->username) { - strncpy(user, cfg->username, len); - user[len-1] = '\0'; + char *username = conf_get_str(conf, CONF_username); + if (*username) { + return dupstr(username); + } else if (conf_get_int(conf, CONF_username_from_env)) { + /* Use local username. */ + return get_username(); /* might still be NULL */ } else { - if (cfg->username_from_env) { - /* Use local username. */ - char *luser = get_username(); - if (luser) { - strncpy(user, luser, len); - user[len-1] = '\0'; - sfree(luser); - } else { - *user = '\0'; - } - } else { - *user = '\0'; - } + return NULL; } - return (*user != '\0'); } -static void gpps(void *handle, const char *name, const char *def, - char *val, int len) +static char *gpps_raw(void *handle, const char *name, const char *def) { - if (!read_setting_s(handle, name, val, len)) { - char *pdef; - - pdef = platform_default_s(name); - if (pdef) { - strncpy(val, pdef, len); - sfree(pdef); - } else { - strncpy(val, def, len); - } + char *ret = read_setting_s(handle, name); + if (!ret) + ret = platform_default_s(name); + if (!ret) + ret = def ? dupstr(def) : NULL; /* permit NULL as final fallback */ + return ret; +} - val[len - 1] = '\0'; - } +static void gpps(void *handle, const char *name, const char *def, + Conf *conf, int primary) +{ + char *val = gpps_raw(handle, name, def); + conf_set_str(conf, primary, val); + sfree(val); } /* * gppfont and gppfile cannot have local defaults, since the very - * format of a Filename or Font is platform-dependent. So the + * format of a Filename or FontSpec is platform-dependent. So the * platform-dependent functions MUST return some sort of value. */ -static void gppfont(void *handle, const char *name, FontSpec *result) +static void gppfont(void *handle, const char *name, Conf *conf, int primary) { - if (!read_setting_fontspec(handle, name, result)) - *result = platform_default_fontspec(name); + FontSpec *result = read_setting_fontspec(handle, name); + if (!result) + result = platform_default_fontspec(name); + conf_set_fontspec(conf, primary, result); + fontspec_free(result); } -static void gppfile(void *handle, const char *name, Filename *result) +static void gppfile(void *handle, const char *name, Conf *conf, int primary) { - if (!read_setting_filename(handle, name, result)) - *result = platform_default_filename(name); + Filename *result = read_setting_filename(handle, name); + if (!result) + result = platform_default_filename(name); + conf_set_filename(conf, primary, result); + filename_free(result); } -static void gppi(void *handle, char *name, int def, int *i) +static int gppi_raw(void *handle, char *name, int def) { def = platform_default_i(name, def); - *i = read_setting_i(handle, name, def); + return read_setting_i(handle, name, def); +} + +static void gppi(void *handle, char *name, int def, Conf *conf, int primary) +{ + conf_set_int(conf, primary, gppi_raw(handle, name, def)); } /* @@ -139,52 +140,126 @@ static void gppi(void *handle, char *name, int def, int *i) * NAME=VALUE,NAME=VALUE, in storage * `def' is in the storage format. */ -static void gppmap(void *handle, char *name, char *def, char *val, int len) +static int gppmap(void *handle, char *name, Conf *conf, int primary) { - char *buf = snewn(2*len, char), *p, *q; - gpps(handle, name, def, buf, 2*len); + char *buf, *p, *q, *key, *val; + + /* + * Start by clearing any existing subkeys of this key from conf. + */ + while ((key = conf_get_str_nthstrkey(conf, primary, 0)) != NULL) + conf_del_str_str(conf, primary, key); + + /* + * Now read a serialised list from the settings and unmarshal it + * into its components. + */ + buf = gpps_raw(handle, name, NULL); + if (!buf) + return FALSE; + p = buf; - q = val; while (*p) { + q = buf; + val = NULL; while (*p && *p != ',') { int c = *p++; if (c == '=') - c = '\t'; + c = '\0'; if (c == '\\') c = *p++; *q++ = c; + if (!c) + val = q; } if (*p == ',') p++; - *q++ = '\0'; + if (!val) + val = q; + *q = '\0'; + + if (primary == CONF_portfwd && buf[0] == 'D') { + /* + * Backwards-compatibility hack: dynamic forwardings are + * indexed in the data store as a third type letter in the + * key, 'D' alongside 'L' and 'R' - but really, they + * should be filed under 'L' with a special _value_, + * because local and dynamic forwardings both involve + * _listening_ on a local port, and are hence mutually + * exclusive on the same port number. So here we translate + * the legacy storage format into the sensible internal + * form. + */ + char *newkey = dupcat("L", buf+1, NULL); + conf_set_str_str(conf, primary, newkey, "D"); + sfree(newkey); + } else { + conf_set_str_str(conf, primary, buf, val); + } } - *q = '\0'; sfree(buf); + + return TRUE; } /* * Write a set of name/value pairs in the above format. */ -static void wmap(void *handle, char const *key, char const *value, int len) +static void wmap(void *handle, char const *outkey, Conf *conf, int primary) { - char *buf = snewn(2*len, char), *p; - const char *q; + char *buf, *p, *q, *key, *realkey, *val; + int len; + + len = 1; /* allow for NUL */ + + for (val = conf_get_str_strs(conf, primary, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, primary, key, &key)) + len += 2 + 2 * (strlen(key) + strlen(val)); /* allow for escaping */ + + buf = snewn(len, char); p = buf; - q = value; - while (*q) { - while (*q) { - int c = *q++; - if (c == '=' || c == ',' || c == '\\') + + for (val = conf_get_str_strs(conf, primary, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, primary, key, &key)) { + + if (primary == CONF_portfwd && !strcmp(val, "D")) { + /* + * Backwards-compatibility hack, as above: translate from + * the sensible internal representation of dynamic + * forwardings (key "L", value "D") to the + * conceptually incoherent legacy storage format (key + * "D", value empty). + */ + realkey = key; /* restore it at end of loop */ + val = ""; + key = dupcat("D", key+1, NULL); + } else { + realkey = NULL; + } + + if (p != buf) + *p++ = ','; + for (q = key; *q; q++) { + if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; - if (c == '\t') - c = '='; - *p++ = c; + *p++ = *q; } - *p++ = ','; - q++; + *p++ = '='; + for (q = val; *q; q++) { + if (*q == '=' || *q == ',' || *q == '\\') + *p++ = '\\'; + *p++ = *q; + } + + if (realkey) { + free(key); + key = realkey; + } } *p = '\0'; - write_setting_s(handle, key, buf); + write_setting_s(handle, outkey, buf); sfree(buf); } @@ -214,9 +289,9 @@ static const char *val2key(const struct keyvalwhere *mapping, */ static void gprefs(void *sesskey, char *name, char *def, const struct keyvalwhere *mapping, int nvals, - int *array) + Conf *conf, int primary) { - char commalist[256]; + char *commalist; char *p, *q; int i, j, n, v, pos; unsigned long seen = 0; /* bitmap for weeding dups etc */ @@ -224,7 +299,7 @@ static void gprefs(void *sesskey, char *name, char *def, /* * Fetch the string which we'll parse as a comma-separated list. */ - gpps(sesskey, name, def, commalist, sizeof(commalist)); + commalist = gpps_raw(sesskey, name, def); /* * Go through that list and convert it into values. @@ -243,10 +318,13 @@ static void gprefs(void *sesskey, char *name, char *def, v = key2val(mapping, nvals, q); if (v != -1 && !(seen & (1 << v))) { seen |= (1 << v); - array[n++] = v; + conf_set_int_int(conf, primary, n, v); + n++; } } + sfree(commalist); + /* * Now go through 'mapping' and add values that weren't mentioned * in the list we fetched. We may have to loop over it multiple @@ -272,7 +350,8 @@ static void gprefs(void *sesskey, char *name, char *def, pos = (mapping[i].where < 0 ? n : 0); } else { for (j = 0; j < n; j++) - if (array[j] == mapping[i].vrel) + if (conf_get_int_int(conf, primary, j) == + mapping[i].vrel) break; assert(j < n); /* implied by (seen & (1<= pos; j--) - array[j+1] = array[j]; - array[pos] = mapping[i].v; + conf_set_int_int(conf, primary, j+1, + conf_get_int_int(conf, primary, j)); + conf_set_int_int(conf, primary, pos, mapping[i].v); n++; } } @@ -295,13 +375,14 @@ static void gprefs(void *sesskey, char *name, char *def, */ static void wprefs(void *sesskey, char *name, const struct keyvalwhere *mapping, int nvals, - int *array) + Conf *conf, int primary) { char *buf, *p; int i, maxlen; for (maxlen = i = 0; i < nvals; i++) { - const char *s = val2key(mapping, nvals, array[i]); + const char *s = val2key(mapping, nvals, + conf_get_int_int(conf, primary, i)); if (s) { maxlen += (maxlen > 0 ? 1 : 0) + strlen(s); } @@ -311,7 +392,8 @@ static void wprefs(void *sesskey, char *name, p = buf; for (i = 0; i < nvals; i++) { - const char *s = val2key(mapping, nvals, array[i]); + const char *s = val2key(mapping, nvals, + conf_get_int_int(conf, primary, i)); if (s) { p += sprintf(p, "%s%s", (p > buf ? "," : ""), s); } @@ -325,7 +407,7 @@ static void wprefs(void *sesskey, char *name, sfree(buf); } -char *save_settings(char *section, Config * cfg) +char *save_settings(char *section, Conf *conf) { void *sesskey; char *errmsg; @@ -333,169 +415,169 @@ char *save_settings(char *section, Config * cfg) sesskey = open_settings_w(section, &errmsg); if (!sesskey) return errmsg; - save_open_settings(sesskey, cfg); + save_open_settings(sesskey, conf); close_settings_w(sesskey); return NULL; } -void save_open_settings(void *sesskey, Config *cfg) +void save_open_settings(void *sesskey, Conf *conf) { int i; char *p; write_setting_i(sesskey, "Present", 1); - 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); - write_setting_i(sesskey, "LogFlush", cfg->logflush); - write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass); - write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata); + write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host)); + write_setting_filename(sesskey, "LogFileName", conf_get_filename(conf, CONF_logfilename)); + write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype)); + write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr)); + write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush)); + write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass)); + write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata)); p = "raw"; { - const Backend *b = backend_from_proto(cfg->protocol); + const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (b) p = b->name; } write_setting_s(sesskey, "Protocol", p); - write_setting_i(sesskey, "PortNumber", cfg->port); + write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port)); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ - write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3); - write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close); - write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */ - write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60); /* seconds */ - write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay); - write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives); - write_setting_s(sesskey, "TerminalType", cfg->termtype); - write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed); - wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes)); + write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3); + write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close)); + write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */ + write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */ + write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay)); + write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives)); + write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype)); + write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed)); + wmap(sesskey, "TerminalModes", conf, CONF_ttymodes); /* Address family selection */ - write_setting_i(sesskey, "AddressFamily", cfg->addressfamily); + write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily)); /* proxy settings */ - write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list); - write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3); - write_setting_i(sesskey, "ProxyLocalhost", cfg->even_proxy_localhost); - write_setting_i(sesskey, "ProxyMethod", cfg->proxy_type); - write_setting_s(sesskey, "ProxyHost", cfg->proxy_host); - write_setting_i(sesskey, "ProxyPort", cfg->proxy_port); - write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username); - write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password); - write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command); - wmap(sesskey, "Environment", cfg->environmt, lenof(cfg->environmt)); - write_setting_s(sesskey, "UserName", cfg->username); - write_setting_i(sesskey, "UserNameFromEnvironment", cfg->username_from_env); - write_setting_s(sesskey, "LocalUserName", cfg->localusername); - write_setting_i(sesskey, "NoPTY", cfg->nopty); - write_setting_i(sesskey, "Compression", cfg->compression); - write_setting_i(sesskey, "TryAgent", cfg->tryagent); - write_setting_i(sesskey, "AgentFwd", cfg->agentfwd); - write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd); - write_setting_i(sesskey, "ChangeUsername", cfg->change_username); - wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, - cfg->ssh_cipherlist); - wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist); - write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time); - write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data); - write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth); - write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner); - write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth); - write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth); - write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth); + write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list)); + write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3); + write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost)); + write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type)); + write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host)); + write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port)); + write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username)); + write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password)); + write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command)); + wmap(sesskey, "Environment", conf, CONF_environmt); + write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); + write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env)); + write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); + write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty)); + write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression)); + write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent)); + write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd)); + write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd)); + write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username)); + wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); + wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); + write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); + write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); + write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth)); + write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner)); + write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth)); + write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth)); + write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth)); #ifndef NO_GSSAPI - wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, - cfg->ssh_gsslist); - write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom); + wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); + write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom)); #endif - write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell); - write_setting_i(sesskey, "SshProt", cfg->sshprot); - write_setting_s(sesskey, "LogHost", cfg->loghost); - write_setting_i(sesskey, "SSH2DES", cfg->ssh2_des_cbc); - write_setting_filename(sesskey, "PublicKeyFile", cfg->keyfile); - write_setting_s(sesskey, "RemoteCommand", cfg->remote_cmd); - write_setting_i(sesskey, "RFCEnviron", cfg->rfc_environ); - write_setting_i(sesskey, "PassiveTelnet", cfg->passive_telnet); - write_setting_i(sesskey, "BackspaceIsDelete", cfg->bksp_is_delete); - write_setting_i(sesskey, "RXVTHomeEnd", cfg->rxvt_homeend); - write_setting_i(sesskey, "LinuxFunctionKeys", cfg->funky_type); - write_setting_i(sesskey, "NoApplicationKeys", cfg->no_applic_k); - write_setting_i(sesskey, "NoApplicationCursors", cfg->no_applic_c); - write_setting_i(sesskey, "NoMouseReporting", cfg->no_mouse_rep); - write_setting_i(sesskey, "NoRemoteResize", cfg->no_remote_resize); - write_setting_i(sesskey, "NoAltScreen", cfg->no_alt_screen); - write_setting_i(sesskey, "NoRemoteWinTitle", cfg->no_remote_wintitle); - write_setting_i(sesskey, "RemoteQTitleAction", cfg->remote_qtitle_action); - write_setting_i(sesskey, "NoDBackspace", cfg->no_dbackspace); - write_setting_i(sesskey, "NoRemoteCharset", cfg->no_remote_charset); - write_setting_i(sesskey, "ApplicationCursorKeys", cfg->app_cursor); - write_setting_i(sesskey, "ApplicationKeypad", cfg->app_keypad); - write_setting_i(sesskey, "NetHackKeypad", cfg->nethack_keypad); - write_setting_i(sesskey, "AltF4", cfg->alt_f4); - write_setting_i(sesskey, "AltSpace", cfg->alt_space); - write_setting_i(sesskey, "AltOnly", cfg->alt_only); - write_setting_i(sesskey, "ComposeKey", cfg->compose_key); - write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys); - write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard); - write_setting_i(sesskey, "TelnetRet", cfg->telnet_newline); - write_setting_i(sesskey, "LocalEcho", cfg->localecho); - write_setting_i(sesskey, "LocalEdit", cfg->localedit); - write_setting_s(sesskey, "Answerback", cfg->answerback); - write_setting_i(sesskey, "AlwaysOnTop", cfg->alwaysontop); - write_setting_i(sesskey, "FullScreenOnAltEnter", cfg->fullscreenonaltenter); - write_setting_i(sesskey, "HideMousePtr", cfg->hide_mouseptr); - write_setting_i(sesskey, "SunkenEdge", cfg->sunken_edge); - write_setting_i(sesskey, "WindowBorder", cfg->window_border); - write_setting_i(sesskey, "CurType", cfg->cursor_type); - write_setting_i(sesskey, "BlinkCur", cfg->blink_cur); - write_setting_i(sesskey, "Beep", cfg->beep); - write_setting_i(sesskey, "BeepInd", cfg->beep_ind); - write_setting_filename(sesskey, "BellWaveFile", cfg->bell_wavefile); - write_setting_i(sesskey, "BellOverload", cfg->bellovl); - write_setting_i(sesskey, "BellOverloadN", cfg->bellovl_n); - write_setting_i(sesskey, "BellOverloadT", cfg->bellovl_t + write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell)); + write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot)); + write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost)); + write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc)); + write_setting_filename(sesskey, "PublicKeyFile", conf_get_filename(conf, CONF_keyfile)); + write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd)); + write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ)); + write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet)); + write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete)); + write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend)); + write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type)); + write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k)); + write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c)); + write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep)); + write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize)); + write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen)); + write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle)); + write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action)); + write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace)); + write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset)); + write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor)); + write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad)); + write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad)); + write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4)); + write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space)); + write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only)); + write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key)); + write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys)); + write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard)); + write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline)); + write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho)); + write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit)); + write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback)); + write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop)); + write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter)); + write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr)); + write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge)); + write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border)); + write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type)); + write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur)); + write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep)); + write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind)); + write_setting_filename(sesskey, "BellWaveFile", conf_get_filename(conf, CONF_bell_wavefile)); + write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl)); + write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n)); + write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t) #ifdef PUTTY_UNIX_H * 1000 #endif ); - write_setting_i(sesskey, "BellOverloadS", cfg->bellovl_s + write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s) #ifdef PUTTY_UNIX_H * 1000 #endif ); - write_setting_i(sesskey, "ScrollbackLines", cfg->savelines); - 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); - write_setting_s(sesskey, "WinTitle", cfg->wintitle); - write_setting_i(sesskey, "TermWidth", cfg->width); - write_setting_i(sesskey, "TermHeight", cfg->height); - write_setting_fontspec(sesskey, "Font", cfg->font); - write_setting_i(sesskey, "FontQuality", cfg->font_quality); - write_setting_i(sesskey, "FontVTMode", cfg->vtmode); - write_setting_i(sesskey, "UseSystemColours", cfg->system_colour); - write_setting_i(sesskey, "TryPalette", cfg->try_palette); - write_setting_i(sesskey, "ANSIColour", cfg->ansi_colour); - write_setting_i(sesskey, "Xterm256Colour", cfg->xterm_256_colour); - write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour); + write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines)); + write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om)); + write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode)); + write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr)); + write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf)); + write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping)); + write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi)); + write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always)); + write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle)); + write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width)); + write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height)); + write_setting_fontspec(sesskey, "Font", conf_get_fontspec(conf, CONF_font)); + write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality)); + write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode)); + write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour)); + write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette)); + write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour)); + write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour)); + write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_style)-1); for (i = 0; i < 22; i++) { char buf[20], buf2[30]; sprintf(buf, "Colour%d", i); - sprintf(buf2, "%d,%d,%d", cfg->colours[i][0], - cfg->colours[i][1], cfg->colours[i][2]); + sprintf(buf2, "%d,%d,%d", + conf_get_int_int(conf, CONF_colours, i*3+0), + conf_get_int_int(conf, CONF_colours, i*3+1), + conf_get_int_int(conf, CONF_colours, i*3+2)); write_setting_s(sesskey, buf, buf2); } - write_setting_i(sesskey, "RawCNP", cfg->rawcnp); - write_setting_i(sesskey, "PasteRTF", cfg->rtf_paste); - write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm); - write_setting_i(sesskey, "RectSelect", cfg->rect_select); - write_setting_i(sesskey, "MouseOverride", cfg->mouse_override); + write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp)); + write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste)); + write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); + write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select)); + write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override)); for (i = 0; i < 256; i += 32) { char buf[20], buf2[256]; int j; @@ -503,305 +585,288 @@ void save_open_settings(void *sesskey, Config *cfg) *buf2 = '\0'; for (j = i; j < i + 32; j++) { sprintf(buf2 + strlen(buf2), "%s%d", - (*buf2 ? "," : ""), cfg->wordness[j]); + (*buf2 ? "," : ""), + conf_get_int_int(conf, CONF_wordness, j)); } write_setting_s(sesskey, buf, buf2); } - write_setting_s(sesskey, "LineCodePage", cfg->line_codepage); - write_setting_i(sesskey, "CJKAmbigWide", cfg->cjk_ambig_wide); - write_setting_i(sesskey, "UTF8Override", cfg->utf8_override); - write_setting_s(sesskey, "Printer", cfg->printer); - write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr); - write_setting_i(sesskey, "ScrollBar", cfg->scrollbar); - write_setting_i(sesskey, "ScrollBarFullScreen", cfg->scrollbar_in_fullscreen); - write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key); - write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp); - write_setting_i(sesskey, "EraseToScrollback", cfg->erase_to_scrollback); - write_setting_i(sesskey, "LockSize", cfg->resize_action); - write_setting_i(sesskey, "BCE", cfg->bce); - write_setting_i(sesskey, "BlinkText", cfg->blinktext); - write_setting_i(sesskey, "X11Forward", cfg->x11_forward); - write_setting_s(sesskey, "X11Display", cfg->x11_display); - write_setting_i(sesskey, "X11AuthType", cfg->x11_auth); - write_setting_filename(sesskey, "X11AuthFile", cfg->xauthfile); - write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall); - write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall); - wmap(sesskey, "PortForwardings", cfg->portfwd, lenof(cfg->portfwd)); - write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1); - write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1); - write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1); - write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2); - write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2); - write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2); - 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); - write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont); - write_setting_fontspec(sesskey, "WideFont", cfg->widefont); - write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont); - write_setting_i(sesskey, "ShadowBold", cfg->shadowbold); - write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset); - write_setting_s(sesskey, "SerialLine", cfg->serline); - write_setting_i(sesskey, "SerialSpeed", cfg->serspeed); - write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits); - write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits); - write_setting_i(sesskey, "SerialParity", cfg->serparity); - write_setting_i(sesskey, "SerialFlowControl", cfg->serflow); - write_setting_s(sesskey, "WindowClass", cfg->winclass); + write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage)); + write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide)); + write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override)); + write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer)); + write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr)); + write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar)); + write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen)); + write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key)); + write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp)); + write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback)); + write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action)); + write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce)); + write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext)); + write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward)); + write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display)); + write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth)); + write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile)); + write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall)); + write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall)); + wmap(sesskey, "PortForwardings", conf, CONF_portfwd); + write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1)); + write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1)); + write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1)); + write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2)); + write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2)); + write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2)); + write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2)); + write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2)); + write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2)); + write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2)); + write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); + write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp)); + write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell)); + write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left)); + write_setting_fontspec(sesskey, "BoldFont", conf_get_fontspec(conf, CONF_boldfont)); + write_setting_fontspec(sesskey, "WideFont", conf_get_fontspec(conf, CONF_widefont)); + write_setting_fontspec(sesskey, "WideBoldFont", conf_get_fontspec(conf, CONF_wideboldfont)); + write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold)); + write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset)); + write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline)); + write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed)); + write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits)); + write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits)); + write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity)); + write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow)); + write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass)); } -void load_settings(char *section, Config * cfg) +void load_settings(char *section, Conf *conf) { void *sesskey; sesskey = open_settings_r(section); - load_open_settings(sesskey, cfg); + load_open_settings(sesskey, conf); close_settings_r(sesskey); - if (cfg_launchable(cfg)) + if (conf_launchable(conf)) add_session_to_jumplist(section); } -void load_open_settings(void *sesskey, Config *cfg) +void load_open_settings(void *sesskey, Conf *conf) { int i; - char prot[10]; - - cfg->ssh_subsys = 0; /* FIXME: load this properly */ - cfg->remote_cmd_ptr = NULL; - cfg->remote_cmd_ptr2 = NULL; - cfg->ssh_nc_host[0] = '\0'; - - 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); - gppi(sesskey, "LogFlush", 1, &cfg->logflush); - gppi(sesskey, "SSHLogOmitPasswords", 1, &cfg->logomitpass); - gppi(sesskey, "SSHLogOmitData", 0, &cfg->logomitdata); - - gpps(sesskey, "Protocol", "default", prot, 10); - cfg->protocol = default_protocol; - cfg->port = default_port; + char *prot; + + conf_set_int(conf, CONF_ssh_subsys, 0); /* FIXME: load this properly */ + conf_set_str(conf, CONF_remote_cmd, ""); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_str(conf, CONF_ssh_nc_host, ""); + + gpps(sesskey, "HostName", "", conf, CONF_host); + gppfile(sesskey, "LogFileName", conf, CONF_logfilename); + gppi(sesskey, "LogType", 0, conf, CONF_logtype); + gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr); + gppi(sesskey, "LogFlush", 1, conf, CONF_logflush); + gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass); + gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata); + + prot = gpps_raw(sesskey, "Protocol", "default"); + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); { const Backend *b = backend_from_name(prot); if (b) { - cfg->protocol = b->protocol; - gppi(sesskey, "PortNumber", default_port, &cfg->port); + conf_set_int(conf, CONF_protocol, b->protocol); + gppi(sesskey, "PortNumber", default_port, conf, CONF_port); } } + sfree(prot); /* Address family selection */ - gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily); + gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ - gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3; - gppi(sesskey, "WarnOnClose", 1, &cfg->warn_on_close); + i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3); + gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close); { /* This is two values for backward compatibility with 0.50/0.51 */ int pingmin, pingsec; - gppi(sesskey, "PingInterval", 0, &pingmin); - gppi(sesskey, "PingIntervalSecs", 0, &pingsec); - cfg->ping_interval = pingmin * 60 + pingsec; + pingmin = gppi_raw(sesskey, "PingInterval", 0); + pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0); + conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec); } - gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay); - gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives); - gpps(sesskey, "TerminalType", "xterm", cfg->termtype, - sizeof(cfg->termtype)); - gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed, - sizeof(cfg->termspeed)); - { + gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay); + gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives); + gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype); + gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed); + if (!gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) { /* This hardcodes a big set of defaults in any new saved * sessions. Let's hope we don't change our mind. */ - int i; - char *def = dupstr(""); - /* Default: all set to "auto" */ - for (i = 0; ttymodes[i]; i++) { - char *def2 = dupprintf("%s%s=A,", def, ttymodes[i]); - sfree(def); - def = def2; - } - gppmap(sesskey, "TerminalModes", def, - cfg->ttymodes, lenof(cfg->ttymodes)); - sfree(def); + for (i = 0; ttymodes[i]; i++) + conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A"); } /* proxy settings */ - gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list, - sizeof(cfg->proxy_exclude_list)); - gppi(sesskey, "ProxyDNS", 1, &i); cfg->proxy_dns = (i+1)%3; - gppi(sesskey, "ProxyLocalhost", 0, &cfg->even_proxy_localhost); - gppi(sesskey, "ProxyMethod", -1, &cfg->proxy_type); - if (cfg->proxy_type == -1) { + gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list); + i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3); + gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost); + gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type); + if (conf_get_int(conf, CONF_proxy_type) == -1) { int i; - gppi(sesskey, "ProxyType", 0, &i); + i = gppi_raw(sesskey, "ProxyType", 0); if (i == 0) - cfg->proxy_type = PROXY_NONE; + conf_set_int(conf, CONF_proxy_type, PROXY_NONE); else if (i == 1) - cfg->proxy_type = PROXY_HTTP; + conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); else if (i == 3) - cfg->proxy_type = PROXY_TELNET; + conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); else if (i == 4) - cfg->proxy_type = PROXY_CMD; + conf_set_int(conf, CONF_proxy_type, PROXY_CMD); else { - gppi(sesskey, "ProxySOCKSVersion", 5, &i); + i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); if (i == 5) - cfg->proxy_type = PROXY_SOCKS5; + conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5); else - cfg->proxy_type = PROXY_SOCKS4; + conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4); } } - gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host, - sizeof(cfg->proxy_host)); - gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port); - gpps(sesskey, "ProxyUsername", "", cfg->proxy_username, - sizeof(cfg->proxy_username)); - gpps(sesskey, "ProxyPassword", "", cfg->proxy_password, - sizeof(cfg->proxy_password)); + gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host); + gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port); + gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username); + gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password); gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n", - cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command)); - gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt)); - gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username)); - gppi(sesskey, "UserNameFromEnvironment", 0, &cfg->username_from_env); - gpps(sesskey, "LocalUserName", "", cfg->localusername, - sizeof(cfg->localusername)); - gppi(sesskey, "NoPTY", 0, &cfg->nopty); - gppi(sesskey, "Compression", 0, &cfg->compression); - gppi(sesskey, "TryAgent", 1, &cfg->tryagent); - gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd); - gppi(sesskey, "ChangeUsername", 0, &cfg->change_username); - gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd); + conf, CONF_proxy_telnet_command); + gppmap(sesskey, "Environment", conf, CONF_environmt); + gpps(sesskey, "UserName", "", conf, CONF_username); + gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env); + gpps(sesskey, "LocalUserName", "", conf, CONF_localusername); + gppi(sesskey, "NoPTY", 0, conf, CONF_nopty); + gppi(sesskey, "Compression", 0, conf, CONF_compression); + gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent); + gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd); + gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username); + gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd); gprefs(sesskey, "Cipher", "\0", - ciphernames, CIPHER_MAX, cfg->ssh_cipherlist); + ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); { /* Backward-compatibility: we used to have an option to * disable gex under the "bugs" panel after one report of * a server which offered it then choked, but we never got * a server version string or any other reports. */ char *default_kexes; - gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i; + i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0); if (i == FORCE_ON) 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,rsa,WARN"; gprefs(sesskey, "KEX", default_kexes, - kexnames, KEX_MAX, cfg->ssh_kexlist); + kexnames, KEX_MAX, conf, CONF_ssh_kexlist); } - gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time); - gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data, - sizeof(cfg->ssh_rekey_data)); - gppi(sesskey, "SshProt", 2, &cfg->sshprot); - gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost)); - gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc); - gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth); - gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner); - gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth); - gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth); - gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth); + gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); + gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); + gppi(sesskey, "SshProt", 2, conf, CONF_sshprot); + gpps(sesskey, "LogHost", "", conf, CONF_loghost); + gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc); + gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth); + gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner); + gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth); + gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth); + gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth); #ifndef NO_GSSAPI gprefs(sesskey, "GSSLibs", "\0", - gsslibkeywords, ngsslibs, cfg->ssh_gsslist); - gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom); + gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); + gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif - gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell); - gppfile(sesskey, "PublicKeyFile", &cfg->keyfile); - gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd, - sizeof(cfg->remote_cmd)); - gppi(sesskey, "RFCEnviron", 0, &cfg->rfc_environ); - gppi(sesskey, "PassiveTelnet", 0, &cfg->passive_telnet); - gppi(sesskey, "BackspaceIsDelete", 1, &cfg->bksp_is_delete); - gppi(sesskey, "RXVTHomeEnd", 0, &cfg->rxvt_homeend); - gppi(sesskey, "LinuxFunctionKeys", 0, &cfg->funky_type); - gppi(sesskey, "NoApplicationKeys", 0, &cfg->no_applic_k); - gppi(sesskey, "NoApplicationCursors", 0, &cfg->no_applic_c); - gppi(sesskey, "NoMouseReporting", 0, &cfg->no_mouse_rep); - gppi(sesskey, "NoRemoteResize", 0, &cfg->no_remote_resize); - gppi(sesskey, "NoAltScreen", 0, &cfg->no_alt_screen); - gppi(sesskey, "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle); + gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell); + gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile); + gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd); + gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ); + gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet); + gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete); + gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend); + gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type); + gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k); + gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c); + gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep); + gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize); + gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen); + gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle); { /* Backward compatibility */ - int no_remote_qtitle; - gppi(sesskey, "NoRemoteQTitle", 1, &no_remote_qtitle); + int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1); /* We deliberately interpret the old setting of "no response" as * "empty string". This changes the behaviour, but hopefully for * the better; the user can always recover the old behaviour. */ gppi(sesskey, "RemoteQTitleAction", no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL, - &cfg->remote_qtitle_action); + conf, CONF_remote_qtitle_action); } - gppi(sesskey, "NoDBackspace", 0, &cfg->no_dbackspace); - gppi(sesskey, "NoRemoteCharset", 0, &cfg->no_remote_charset); - gppi(sesskey, "ApplicationCursorKeys", 0, &cfg->app_cursor); - gppi(sesskey, "ApplicationKeypad", 0, &cfg->app_keypad); - gppi(sesskey, "NetHackKeypad", 0, &cfg->nethack_keypad); - gppi(sesskey, "AltF4", 1, &cfg->alt_f4); - gppi(sesskey, "AltSpace", 0, &cfg->alt_space); - gppi(sesskey, "AltOnly", 0, &cfg->alt_only); - gppi(sesskey, "ComposeKey", 0, &cfg->compose_key); - gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys); - gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard); - gppi(sesskey, "TelnetRet", 1, &cfg->telnet_newline); - gppi(sesskey, "LocalEcho", AUTO, &cfg->localecho); - gppi(sesskey, "LocalEdit", AUTO, &cfg->localedit); - gpps(sesskey, "Answerback", "PuTTY", cfg->answerback, - sizeof(cfg->answerback)); - gppi(sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop); - gppi(sesskey, "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter); - gppi(sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr); - gppi(sesskey, "SunkenEdge", 0, &cfg->sunken_edge); - gppi(sesskey, "WindowBorder", 1, &cfg->window_border); - gppi(sesskey, "CurType", 0, &cfg->cursor_type); - gppi(sesskey, "BlinkCur", 0, &cfg->blink_cur); - /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */ - gppi(sesskey, "Beep", 1, &cfg->beep); - gppi(sesskey, "BeepInd", 0, &cfg->beep_ind); - gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile); - gppi(sesskey, "BellOverload", 1, &cfg->bellovl); - gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n); - gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC + gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace); + gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset); + gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor); + gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad); + gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad); + gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4); + gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space); + gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only); + gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key); + gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys); + gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard); + gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline); + gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho); + gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit); + gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback); + gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop); + gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter); + gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr); + gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge); + gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); + gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); + gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur); + /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */ + gppi(sesskey, "Beep", 1, conf, CONF_beep); + gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind); + gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile); + gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl); + gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n); + i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif - , &i); - cfg->bellovl_t = i + ); + conf_set_int(conf, CONF_bellovl_t, i #ifdef PUTTY_UNIX_H - / 1000 + / 1000 #endif - ; - gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC + ); + i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif - , &i); - cfg->bellovl_s = i + ); + conf_set_int(conf, CONF_bellovl_s, i #ifdef PUTTY_UNIX_H - / 1000 + / 1000 #endif - ; - gppi(sesskey, "ScrollbackLines", 200, &cfg->savelines); - 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); - gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle)); - gppi(sesskey, "TermWidth", 80, &cfg->width); - gppi(sesskey, "TermHeight", 24, &cfg->height); - gppfont(sesskey, "Font", &cfg->font); - gppi(sesskey, "FontQuality", FQ_DEFAULT, &cfg->font_quality); - gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode); - gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour); - gppi(sesskey, "TryPalette", 0, &cfg->try_palette); - gppi(sesskey, "ANSIColour", 1, &cfg->ansi_colour); - gppi(sesskey, "Xterm256Colour", 1, &cfg->xterm_256_colour); - gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour); + ); + gppi(sesskey, "ScrollbackLines", 2000, conf, CONF_savelines); + gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om); + gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode); + gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr); + gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf); + gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping); + gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi); + gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always); + gpps(sesskey, "WinTitle", "", conf, CONF_wintitle); + gppi(sesskey, "TermWidth", 80, conf, CONF_width); + gppi(sesskey, "TermHeight", 24, conf, CONF_height); + gppfont(sesskey, "Font", conf, CONF_font); + gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality); + gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode); + gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour); + gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette); + gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour); + gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour); + i = gppi_raw(sesskey, "BoldAsColour", 0); conf_set_int(conf, CONF_bold_style, i+1); for (i = 0; i < 22; i++) { static const char *const defaults[] = { @@ -811,21 +876,22 @@ void load_open_settings(void *sesskey, Config *cfg) "85,85,255", "187,0,187", "255,85,255", "0,187,187", "85,255,255", "187,187,187", "255,255,255" }; - char buf[20], buf2[30]; + char buf[20], *buf2; int c0, c1, c2; sprintf(buf, "Colour%d", i); - gpps(sesskey, buf, defaults[i], buf2, sizeof(buf2)); + buf2 = gpps_raw(sesskey, buf, defaults[i]); if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) { - cfg->colours[i][0] = c0; - cfg->colours[i][1] = c1; - cfg->colours[i][2] = c2; + conf_set_int_int(conf, CONF_colours, i*3+0, c0); + conf_set_int_int(conf, CONF_colours, i*3+1, c1); + conf_set_int_int(conf, CONF_colours, i*3+2, c2); } + sfree(buf2); } - gppi(sesskey, "RawCNP", 0, &cfg->rawcnp); - gppi(sesskey, "PasteRTF", 0, &cfg->rtf_paste); - gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm); - gppi(sesskey, "RectSelect", 0, &cfg->rect_select); - gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override); + gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp); + gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste); + gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); + gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select); + gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override); for (i = 0; i < 256; i += 32) { static const char *const defaults[] = { "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", @@ -837,10 +903,10 @@ void load_open_settings(void *sesskey, Config *cfg) "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2" }; - char buf[20], buf2[256], *p; + char buf[20], *buf2, *p; int j; sprintf(buf, "Wordness%d", i); - gpps(sesskey, buf, defaults[i / 32], buf2, sizeof(buf2)); + buf2 = gpps_raw(sesskey, buf, defaults[i / 32]); p = buf2; for (j = i; j < i + 32; j++) { char *q = p; @@ -848,75 +914,75 @@ void load_open_settings(void *sesskey, Config *cfg) p++; if (*p == ',') *p++ = '\0'; - cfg->wordness[j] = atoi(q); + conf_set_int_int(conf, CONF_wordness, j, atoi(q)); } + sfree(buf2); } /* * The empty default for LineCodePage will be converted later * into a plausible default for the locale. */ - gpps(sesskey, "LineCodePage", "", cfg->line_codepage, - sizeof(cfg->line_codepage)); - gppi(sesskey, "CJKAmbigWide", 0, &cfg->cjk_ambig_wide); - gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override); - gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer)); - gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr); - gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar); - gppi(sesskey, "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen); - gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key); - gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp); - gppi(sesskey, "EraseToScrollback", 1, &cfg->erase_to_scrollback); - gppi(sesskey, "LockSize", 0, &cfg->resize_action); - gppi(sesskey, "BCE", 1, &cfg->bce); - gppi(sesskey, "BlinkText", 0, &cfg->blinktext); - gppi(sesskey, "X11Forward", 0, &cfg->x11_forward); - gpps(sesskey, "X11Display", "", cfg->x11_display, - sizeof(cfg->x11_display)); - gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth); - gppfile(sesskey, "X11AuthFile", &cfg->xauthfile); - - gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall); - gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall); - gppmap(sesskey, "PortForwardings", "", cfg->portfwd, lenof(cfg->portfwd)); - gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i; - gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i; - gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i; - gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i; + gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage); + gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide); + gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override); + gpps(sesskey, "Printer", "", conf, CONF_printer); + gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr); + gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar); + gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen); + gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key); + gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp); + gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback); + gppi(sesskey, "LockSize", 0, conf, CONF_resize_action); + gppi(sesskey, "BCE", 1, conf, CONF_bce); + gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext); + gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward); + gpps(sesskey, "X11Display", "", conf, CONF_x11_display); + gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth); + gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile); + + gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall); + gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall); + gppmap(sesskey, "PortForwardings", conf, CONF_portfwd); + i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i); + i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i); + i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i); + i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i); { int i; - gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i; - if (cfg->sshbug_hmac2 == AUTO) { - gppi(sesskey, "BuggyMAC", 0, &i); + i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i); + if (2-i == AUTO) { + i = gppi_raw(sesskey, "BuggyMAC", 0); if (i == 1) - cfg->sshbug_hmac2 = FORCE_ON; + conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON); } } - gppi(sesskey, "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i; - 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); - gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold); - gppfont(sesskey, "BoldFont", &cfg->boldfont); - gppfont(sesskey, "WideFont", &cfg->widefont); - gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont); - gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset); - gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline)); - gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed); - gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits); - gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits); - gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity); - gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow); - gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass)); + i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i); + i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i); + i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i); + i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i); + i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i); + i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); + conf_set_int(conf, CONF_ssh_simple, FALSE); + gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp); + gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell); + gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left); + gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold); + gppfont(sesskey, "BoldFont", conf, CONF_boldfont); + gppfont(sesskey, "WideFont", conf, CONF_widefont); + gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont); + gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset); + gpps(sesskey, "SerialLine", "", conf, CONF_serline); + gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed); + gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits); + gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits); + gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity); + gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow); + gpps(sesskey, "WindowClass", "", conf, CONF_winclass); } -void do_defaults(char *session, Config * cfg) +void do_defaults(char *session, Conf *conf) { - load_settings(session, cfg); + load_settings(session, conf); } static int sessioncmp(const void *av, const void *bv) diff --git a/putty/SFTP.C b/putty/SFTP.C index e665dfb..3e6bc6c 100644 --- a/putty/SFTP.C +++ b/putty/SFTP.C @@ -45,6 +45,13 @@ static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte) { sftp_pkt_adddata(pkt, &byte, 1); } +static void sftp_pkt_adduint32(struct sftp_packet *pkt, + unsigned long value) +{ + unsigned char x[4]; + PUT_32BIT(x, value); + sftp_pkt_adddata(pkt, x, 4); +} static struct sftp_packet *sftp_pkt_init(int pkt_type) { struct sftp_packet *pkt; @@ -53,6 +60,7 @@ static struct sftp_packet *sftp_pkt_init(int pkt_type) pkt->savedpos = -1; pkt->length = 0; pkt->maxlen = 0; + sftp_pkt_adduint32(pkt, 0); /* length field will be filled in later */ sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); return pkt; } @@ -62,13 +70,6 @@ static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) sftp_pkt_adddata(pkt, &value, 1); } */ -static void sftp_pkt_adduint32(struct sftp_packet *pkt, - unsigned long value) -{ - unsigned char x[4]; - PUT_32BIT(x, value); - sftp_pkt_adddata(pkt, x, 4); -} static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value) { unsigned char x[8]; @@ -149,7 +150,7 @@ static int sftp_pkt_getstring(struct sftp_packet *pkt, *p = NULL; if (pkt->length - pkt->savedpos < 4) return 0; - *length = GET_32BIT(pkt->data + pkt->savedpos); + *length = toint(GET_32BIT(pkt->data + pkt->savedpos)); pkt->savedpos += 4; if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) { *length = 0; @@ -215,9 +216,8 @@ static void sftp_pkt_free(struct sftp_packet *pkt) int sftp_send(struct sftp_packet *pkt) { int ret; - char x[4]; - PUT_32BIT(x, pkt->length); - ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length)); + PUT_32BIT(pkt->data, pkt->length - 4); + ret = sftp_senddata(pkt->data, pkt->length); sftp_pkt_free(pkt); return ret; } @@ -366,7 +366,6 @@ struct sftp_request *sftp_find_request(struct sftp_packet *pktin) if (!req || !req->registered) { fxp_internal_error("request ID mismatch\n"); - sftp_pkt_free(pktin); return NULL; } @@ -548,7 +547,8 @@ char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req) /* * Open a file. */ -struct sftp_request *fxp_open_send(char *path, int type) +struct sftp_request *fxp_open_send(char *path, int type, + struct fxp_attrs *attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; @@ -557,7 +557,10 @@ struct sftp_request *fxp_open_send(char *path, int type) sftp_pkt_adduint32(pktout, req->id); sftp_pkt_addstring(pktout, path); sftp_pkt_adduint32(pktout, type); - sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ + if (attrs) + sftp_pkt_addattrs(pktout, *attrs); + else + sftp_pkt_adduint32(pktout, 0); /* empty ATTRS structure */ sftp_send(pktout); return req; @@ -1193,15 +1196,23 @@ struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset) return xfer; } +/* + * Returns INT_MIN to indicate that it didn't even get as far as + * fxp_read_recv and hence has not freed pktin. + */ int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; struct req *rr; rreq = sftp_find_request(pktin); + if (!rreq) + return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); - if (!rr) - return 0; /* this packet isn't ours */ + if (!rr) { + fxp_internal_error("request ID is not part of the current download"); + return INT_MIN; /* this packet isn't ours */ + } rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len); #ifdef DEBUG_DOWNLOAD printf("read request %p has returned [%d]\n", rr, rr->retlen); @@ -1372,6 +1383,10 @@ void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len) #endif } +/* + * Returns INT_MIN to indicate that it didn't even get as far as + * fxp_write_recv and hence has not freed pktin. + */ int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) { struct sftp_request *rreq; @@ -1379,9 +1394,13 @@ int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin) int ret; rreq = sftp_find_request(pktin); + if (!rreq) + return INT_MIN; /* this packet doesn't even make sense */ rr = (struct req *)fxp_get_userdata(rreq); - if (!rr) - return 0; /* this packet isn't ours */ + if (!rr) { + fxp_internal_error("request ID is not part of the current upload"); + return INT_MIN; /* this packet isn't ours */ + } ret = fxp_write_recv(pktin, rreq); #ifdef DEBUG_UPLOAD printf("write request %p has returned [%d]\n", rr, ret); diff --git a/putty/SFTP.H b/putty/SFTP.H index 98368b3..2169603 100644 --- a/putty/SFTP.H +++ b/putty/SFTP.H @@ -82,6 +82,19 @@ struct fxp_attrs { unsigned long mtime; }; +/* + * Copy between the possibly-unused permissions field in an fxp_attrs + * and a possibly-negative integer containing the same permissions. + */ +#define PUT_PERMISSIONS(attrs, perms) \ + ((perms) >= 0 ? \ + ((attrs).flags |= SSH_FILEXFER_ATTR_PERMISSIONS, \ + (attrs).permissions = (perms)) : \ + ((attrs).flags &= ~SSH_FILEXFER_ATTR_PERMISSIONS)) +#define GET_PERMISSIONS(attrs) \ + ((attrs).flags & SSH_FILEXFER_ATTR_PERMISSIONS ? \ + (attrs).permissions : -1) + struct fxp_handle { char *hstring; int hlen; @@ -116,9 +129,11 @@ struct sftp_request *fxp_realpath_send(char *path); char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req); /* - * Open a file. + * Open a file. 'attrs' contains attributes to be applied to the file + * if it's being created. */ -struct sftp_request *fxp_open_send(char *path, int type); +struct sftp_request *fxp_open_send(char *path, int type, + struct fxp_attrs *attrs); struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, struct sftp_request *req); diff --git a/putty/SSH.C b/putty/SSH.C index 6b7eb8d..bffe277 100644 --- a/putty/SSH.C +++ b/putty/SSH.C @@ -196,6 +196,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_PK_SESSIONID 128 #define BUG_SSH2_MAXPKT 256 #define BUG_CHOKES_ON_SSH2_IGNORE 512 +#define BUG_CHOKES_ON_WINADJ 1024 /* * Codes for terminal modes. @@ -430,12 +431,16 @@ enum { * Database for Edit and Continue'. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; -#define crState(t) \ - struct t *s; \ - if (!ssh->t) ssh->t = snew(struct t); \ - s = ssh->t; +#define crBeginState crBegin(s->crLine) +#define crStateP(t, v) \ + struct t *s; \ + if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \ + s = (v); +#define crState(t) crStateP(t, ssh->t) #define crFinish(z) } *crLine = 0; return (z); } #define crFinishV } *crLine = 0; return; } +#define crFinishFree(z) } sfree(s); return (z); } +#define crFinishFreeV } sfree(s); return; } #define crReturn(z) \ do {\ *crLine =__LINE__; return (z); case __LINE__:;\ @@ -455,14 +460,14 @@ struct Packet; static struct Packet *ssh1_pkt_init(int pkt_type); static struct Packet *ssh2_pkt_init(int pkt_type); static void ssh_pkt_ensure(struct Packet *, int length); -static void ssh_pkt_adddata(struct Packet *, void *data, int len); +static void ssh_pkt_adddata(struct Packet *, const void *data, int len); static void ssh_pkt_addbyte(struct Packet *, unsigned char value); static void ssh2_pkt_addbool(struct Packet *, unsigned char value); static void ssh_pkt_adduint32(struct Packet *, unsigned long value); static void ssh_pkt_addstring_start(struct Packet *); -static void ssh_pkt_addstring_str(struct Packet *, char *data); -static void ssh_pkt_addstring_data(struct Packet *, char *data, int len); -static void ssh_pkt_addstring(struct Packet *, char *data); +static void ssh_pkt_addstring_str(struct Packet *, const char *data); +static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len); +static void ssh_pkt_addstring(struct Packet *, const char *data); static unsigned char *ssh2_mpint_fmt(Bignum b, int *len); static void ssh1_pkt_addmp(struct Packet *, Bignum b); static void ssh2_pkt_addmp(struct Packet *, Bignum b); @@ -473,6 +478,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin); +static void ssh2_channel_check_close(struct ssh_channel *c); +static void ssh_channel_destroy(struct ssh_channel *c); /* * Buffer management constants. There are several of these for @@ -515,13 +522,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, #define OUR_V2_MAXPKT 0x4000UL #define OUR_V2_PACKETLIMIT 0x9000UL -/* Maximum length of passwords/passphrases (arbitrary) */ -#define SSH_MAX_PASSWORD_LEN 100 - const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { - &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 + &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static struct ssh_mac *buggymacs[] = { &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 @@ -559,15 +563,29 @@ enum { /* channel types */ CHAN_X11, CHAN_AGENT, CHAN_SOCKDATA, - CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */ + CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */ + /* + * CHAN_ZOMBIE is used to indicate a channel for which we've + * already destroyed the local data source: for instance, if a + * forwarded port experiences a socket error on the local side, we + * immediately destroy its local socket and turn the SSH channel + * into CHAN_ZOMBIE. + */ + CHAN_ZOMBIE }; +typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); +typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); +typedef void (*cchandler_fn_t)(struct ssh_channel *, struct Packet *, void *); + /* - * little structure to keep track of outstanding WINDOW_ADJUSTs + * Each channel has a queue of outstanding CHANNEL_REQUESTS and their + * handlers. */ -struct winadj { - struct winadj *next; - unsigned size; +struct outstanding_channel_request { + cchandler_fn_t handler; + void *ctx; + struct outstanding_channel_request *next; }; /* @@ -588,18 +606,35 @@ struct ssh_channel { * 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. * * A channel is completely finished with when all four bits are set. + * + * In SSH-2, the four bits mean: + * + * 1 We have sent SSH2_MSG_CHANNEL_EOF. + * 2 We have sent SSH2_MSG_CHANNEL_CLOSE. + * 4 We have received SSH2_MSG_CHANNEL_EOF. + * 8 We have received SSH2_MSG_CHANNEL_CLOSE. + * + * A channel is completely finished with when we have both sent + * and received CLOSE. + * + * The symbolic constants below use the SSH-2 terminology, which + * is a bit confusing in SSH-1, but we have to use _something_. */ +#define CLOSES_SENT_EOF 1 +#define CLOSES_SENT_CLOSE 2 +#define CLOSES_RCVD_EOF 4 +#define CLOSES_RCVD_CLOSE 8 int closes; /* - * This flag indicates that a close is pending on the outgoing - * side of the channel: that is, wherever we're getting the data - * for this channel has sent us some data followed by EOF. We - * can't actually close the channel until we've finished sending - * the data, so we set this flag instead to remind us to - * initiate the closing process once our buffer is clear. + * This flag indicates that an EOF is pending on the outgoing side + * of the channel: that is, wherever we're getting the data for + * this channel has sent us some data followed by EOF. We can't + * actually send the EOF until we've finished sending the data, so + * we set this flag instead to remind us to do so once our buffer + * is clear. */ - int pending_close; + int pending_eof; /* * True if this channel is causing the underlying connection to be @@ -619,10 +654,10 @@ struct ssh_channel { */ int remlocwin; /* - * These store the list of window adjusts that haven't + * These store the list of channel requests that haven't * been acked. */ - struct winadj *winadj_head, *winadj_tail; + struct outstanding_channel_request *chanreq_head, *chanreq_tail; enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; @@ -631,6 +666,7 @@ struct ssh_channel { unsigned char *message; unsigned char msglen[4]; unsigned lensofar, totallen; + int outstanding_requests; } a; struct ssh_x11_channel { Socket s; @@ -736,9 +772,10 @@ static int ssh_do_close(Ssh ssh, int notify_exit); static unsigned long ssh_pkt_getuint32(struct Packet *pkt); static int ssh2_pkt_getbool(struct Packet *pkt); static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length); -static void ssh2_timer(void *ctx, long now); -static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, - struct Packet *pktin); +static void ssh2_timer(void *ctx, unsigned long now); +static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, + struct Packet *pktin); +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin); struct rdpkt1_state_tag { long len, pad, biglen, to_read; @@ -757,9 +794,6 @@ struct rdpkt2_state_tag { struct Packet *pktin; }; -typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); -typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); - struct queued_handler; struct queued_handler { int msg1, msg2; @@ -830,6 +864,8 @@ struct ssh_tag { } state; int size_needed, eof_needed; + int sent_console_eof; + int got_pty; /* affects EOF behaviour on main channel */ struct Packet **queue; int queuelen, queuesize; @@ -861,12 +897,8 @@ struct ssh_tag { int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; - int do_ssh_init_crstate; int ssh_gotdata_crstate; - int do_ssh1_login_crstate; int do_ssh1_connection_crstate; - int do_ssh2_transport_crstate; - int do_ssh2_authconn_crstate; void *do_ssh_init_state; void *do_ssh1_login_state; @@ -884,12 +916,26 @@ struct ssh_tag { struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen); /* - * We maintain a full _copy_ of a Config structure here, not - * merely a pointer to it. That way, when we're passed a new - * one for reconfiguration, we can check the differences and - * potentially reconfigure port forwardings etc in mid-session. + * We maintain our own copy of a Conf structure here. That way, + * when we're passed a new one for reconfiguration, we can check + * the differences and potentially reconfigure port forwardings + * etc in mid-session. + */ + Conf *conf; + + /* + * Values cached out of conf so as to avoid the tree234 lookup + * cost every time they're used. + */ + int logomitdata; + + /* + * Dynamically allocated username string created during SSH + * login. Stored in here rather than in the coroutine state so + * that it'll be reliably freed if we shut down the SSH session + * at some unexpected moment. */ - Config cfg; + char *username; /* * Used to transfer data back from async callbacks. @@ -921,6 +967,7 @@ struct ssh_tag { * indications from a request. */ struct queued_handler *qhead, *qtail; + handler_fn_t q_saved_handler1, q_saved_handler2; /* * This module deals with sending keepalives. @@ -934,7 +981,7 @@ struct ssh_tag { unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; unsigned long max_data_size; int kex_in_progress; - long next_rekey, last_rekey; + unsigned long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ /* @@ -965,26 +1012,27 @@ static void logeventf(Ssh ssh, const char *fmt, ...) sfree(buf); } -#define bombout(msg) \ - do { \ - char *text = dupprintf msg; \ - ssh_do_close(ssh, FALSE); \ - logevent(text); \ - connection_fatal(ssh->frontend, "%s", text); \ - sfree(text); \ - } while (0) +static void bomb_out(Ssh ssh, char *text) +{ + ssh_do_close(ssh, FALSE); + logevent(text); + connection_fatal(ssh->frontend, "%s", text); + sfree(text); +} + +#define bombout(msg) bomb_out(ssh, dupprintf msg) /* Functions to leave bits out of the SSH packet log file. */ static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype) { - if (ssh->cfg.logomitpass) + if (conf_get_int(ssh->conf, CONF_logomitpass)) pkt->logmode = blanktype; } static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype) { - if (ssh->cfg.logomitdata) + if (ssh->logomitdata) pkt->logmode = blanktype; } @@ -993,26 +1041,29 @@ static void end_log_omission(Ssh ssh, struct Packet *pkt) pkt->logmode = PKTLOG_EMIT; } -/* Helper function for common bits of parsing cfg.ttymodes. */ -static void parse_ttymodes(Ssh ssh, char *modes, +/* Helper function for common bits of parsing ttymodes. */ +static void parse_ttymodes(Ssh ssh, void (*do_mode)(void *data, char *mode, char *val), void *data) { - while (*modes) { - char *t = strchr(modes, '\t'); - char *m = snewn(t-modes+1, char); - char *val; - strncpy(m, modes, t-modes); - m[t-modes] = '\0'; - if (*(t+1) == 'A') - val = get_ttymode(ssh->frontend, m); - else - val = dupstr(t+2); - if (val) - do_mode(data, m, val); - sfree(m); - sfree(val); - modes += strlen(modes) + 1; + char *key, *val; + + for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key); + val != NULL; + val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) { + /* + * val[0] is either 'V', indicating that an explicit value + * follows it, or 'A' indicating that we should pass the + * value through from the local environment via get_ttymode. + */ + if (val[0] == 'A') { + val = get_ttymode(ssh->frontend, key); + if (val) { + do_mode(data, key, val); + sfree(val); + } + } else + do_mode(data, key, val + 1); /* skip the 'V' */ } } @@ -1300,7 +1351,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) if (ssh->logctx) { int nblanks = 0; struct logblank_t blank; - if (ssh->cfg.logomitdata) { + if (ssh->logomitdata) { int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) || @@ -1398,7 +1449,8 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* See if that gives us a valid packet. */ if (ssh->scmac->verresult(ssh->sc_mac_ctx, st->pktin->data + st->packetlen) && - (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen) + ((st->len = toint(GET_32BIT(st->pktin->data))) == + st->packetlen-4)) break; if (st->packetlen >= OUR_V2_PACKETLIMIT) { bombout(("No valid incoming packet found")); @@ -1431,7 +1483,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* * Now get the length figure. */ - st->len = GET_32BIT(st->pktin->data); + st->len = toint(GET_32BIT(st->pktin->data)); /* * _Completely_ silly lengths should be stomped on before they @@ -1533,7 +1585,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) if (ssh->logctx) { int nblanks = 0; struct logblank_t blank; - if (ssh->cfg.logomitdata) { + if (ssh->logomitdata) { int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) { @@ -1777,7 +1829,7 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length) if (body) pkt->body = pkt->data + offset; } } -static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len) +static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len) { if (pkt->logmode != PKTLOG_EMIT) { pkt->nblanks++; @@ -1811,17 +1863,18 @@ static void ssh_pkt_addstring_start(struct Packet *pkt) ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } -static void ssh_pkt_addstring_str(struct Packet *pkt, char *data) +static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data) { ssh_pkt_adddata(pkt, data, strlen(data)); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len) +static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data, + int len) { ssh_pkt_adddata(pkt, data, len); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring(struct Packet *pkt, char *data) +static void ssh_pkt_addstring(struct Packet *pkt, const char *data) { ssh_pkt_addstring_start(pkt); ssh_pkt_addstring_str(pkt, data); @@ -2262,7 +2315,7 @@ static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length) *length = 0; if (pkt->length - pkt->savedpos < 4) return; - len = GET_32BIT(pkt->body + pkt->savedpos); + len = toint(GET_32BIT(pkt->body + pkt->savedpos)); if (len < 0) return; *length = len; @@ -2346,7 +2399,7 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, * See if this is in fact an ssh-rsa signature and a buggy * server; otherwise we can just do this the easy way. */ - if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && + if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 && (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) { int pos, len, siglen; @@ -2355,8 +2408,15 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, */ pos = 4+7; /* skip over "ssh-rsa" */ - pos += 4 + GET_32BIT(pkblob+pos); /* skip over exponent */ - len = GET_32BIT(pkblob+pos); /* find length of modulus */ + len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */ + if (len < 0 || len > pkblob_len - pos - 4) + goto give_up; + pos += 4 + len; /* skip over exponent */ + if (pkblob_len - pos < 4) + goto give_up; + len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */ + if (len < 0 || len > pkblob_len - pos - 4) + goto give_up; pos += 4; /* find modulus itself */ while (len > 0 && pkblob[pos] == 0) len--, pos++; @@ -2366,7 +2426,11 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, * Now find the signature integer. */ pos = 4+7; /* skip over "ssh-rsa" */ - siglen = GET_32BIT(sigblob+pos); + if (sigblob_len < pos+4) + goto give_up; + siglen = toint(GET_32BIT(sigblob+pos)); + if (siglen != sigblob_len - pos - 4) + goto give_up; /* debug(("signature length is %d\n", siglen)); */ if (len != siglen) { @@ -2388,7 +2452,10 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt, return; } - /* Otherwise fall through and do it the easy way. */ + /* Otherwise fall through and do it the easy way. We also come + * here as a fallback if we discover above that the key blob + * is misformatted in some way. */ + give_up:; } ssh2_pkt_addstring_start(pkt); @@ -2417,8 +2484,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * with SSH1_MSG_IGNOREs -- but this string never seems to change, * so we can't distinguish them. */ - if (ssh->cfg.sshbug_ignore1 == FORCE_ON || - (ssh->cfg.sshbug_ignore1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") || @@ -2432,8 +2499,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-1 ignore bug"); } - if (ssh->cfg.sshbug_plainpw1 == FORCE_ON || - (ssh->cfg.sshbug_plainpw1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO && (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) { /* * These versions need a plain password sent; they can't @@ -2444,8 +2511,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version needs a plain SSH-1 password"); } - if (ssh->cfg.sshbug_rsa1 == FORCE_ON || - (ssh->cfg.sshbug_rsa1 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO && (!strcmp(imp, "Cisco-1.25")))) { /* * These versions apparently have no clue whatever about @@ -2456,8 +2523,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version can't handle SSH-1 RSA authentication"); } - if (ssh->cfg.sshbug_hmac2 == FORCE_ON || - (ssh->cfg.sshbug_hmac2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) || wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) || @@ -2469,8 +2536,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 HMAC bug"); } - if (ssh->cfg.sshbug_derivekey2 == FORCE_ON || - (ssh->cfg.sshbug_derivekey2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO && !wc_match("* VShell", imp) && (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) { /* @@ -2482,8 +2549,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 key-derivation bug"); } - if (ssh->cfg.sshbug_rsapad2 == FORCE_ON || - (ssh->cfg.sshbug_rsapad2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO && (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp)))) { /* @@ -2493,8 +2560,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 RSA padding bug"); } - if (ssh->cfg.sshbug_pksessid2 == FORCE_ON || - (ssh->cfg.sshbug_pksessid2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO && wc_match("OpenSSH_2.[0-2]*", imp))) { /* * These versions have the SSH-2 session-ID bug in @@ -2504,8 +2571,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 public-key-session-ID bug"); } - if (ssh->cfg.sshbug_rekey2 == FORCE_ON || - (ssh->cfg.sshbug_rekey2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO && (wc_match("DigiSSH_2.0", imp) || wc_match("OpenSSH_2.[0-4]*", imp) || wc_match("OpenSSH_2.5.[0-3]*", imp) || @@ -2520,8 +2587,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 rekey bug"); } - if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || - (ssh->cfg.sshbug_maxpkt2 == AUTO && + if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO && (wc_match("1.36_sshlib GlobalSCAPE", imp) || wc_match("1.36 sshlib: GlobalScape", imp)))) { /* @@ -2531,7 +2598,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version ignores SSH-2 maximum packet size"); } - if (ssh->cfg.sshbug_ignore2 == FORCE_ON) { + if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) { /* * Servers that don't support SSH2_MSG_IGNORE. Currently, * none detected automatically. @@ -2539,6 +2606,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; logevent("We believe remote version has SSH-2 ignore bug"); } + + if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) { + /* + * Servers that don't support our winadj request for one + * reason or another. Currently, none detected automatically. + */ + ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ; + logevent("We believe remote version has winadj bug"); + } } /* @@ -2608,6 +2684,7 @@ static void ssh_send_verstring(Ssh ssh, char *svers) static int do_ssh_init(Ssh ssh, unsigned char c) { struct do_ssh_init_state { + int crLine; int vslen; char version[10]; char *vstring; @@ -2616,8 +2693,8 @@ static int do_ssh_init(Ssh ssh, unsigned char c) int proto1, proto2; }; crState(do_ssh_init_state); - - crBegin(ssh->do_ssh_init_crstate); + + crBeginState; /* Search for a line beginning with the string "SSH-" in the input. */ for (;;) { @@ -2674,16 +2751,16 @@ static int do_ssh_init(Ssh ssh, unsigned char c) /* Anything greater or equal to "1.99" means protocol 2 is supported. */ s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0; - if (ssh->cfg.sshprot == 0 && !s->proto1) { + if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) { bombout(("SSH protocol version 1 required by user but not provided by server")); crStop(0); } - if (ssh->cfg.sshprot == 3 && !s->proto2) { + if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) { bombout(("SSH protocol version 2 required by user but not provided by server")); crStop(0); } - if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) + if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1)) ssh->version = 2; else ssh->version = 1; @@ -2691,7 +2768,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) logeventf(ssh, "Using SSH protocol version %d", ssh->version); /* Send the version string, if we haven't already */ - if (ssh->cfg.sshprot != 3) + if (conf_get_int(ssh->conf, CONF_sshprot) != 3) ssh_send_verstring(ssh, s->version); if (ssh->version == 2) { @@ -2723,7 +2800,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) update_specials_menu(ssh->frontend); ssh->state = SSH_STATE_BEFORE_SIZE; - ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh); + ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh); sfree(s->vstring); @@ -2976,11 +3053,14 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, SockAddr addr; const char *err; - - if (*ssh->cfg.loghost) { + char *loghost; + int addressfamily, sshprot; + + loghost = conf_get_str(ssh->conf, CONF_loghost); + if (*loghost) { char *colon; - ssh->savedhost = dupstr(ssh->cfg.loghost); + ssh->savedhost = dupstr(loghost); ssh->savedport = 22; /* default ssh port */ /* @@ -3005,11 +3085,11 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, /* * Try to find host. */ + addressfamily = conf_get_int(ssh->conf, CONF_addressfamily); logeventf(ssh, "Looking up host \"%s\"%s", host, - (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); - addr = name_lookup(host, port, realhost, &ssh->cfg, - ssh->cfg.addressfamily); + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); + addr = name_lookup(host, port, realhost, ssh->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -3021,7 +3101,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, */ ssh->fn = &fn_table; ssh->s = new_connection(addr, *realhost, port, - 0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg); + 0, 1, nodelay, keepalive, (Plug) ssh, ssh->conf); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; notify_remote_exit(ssh->frontend); @@ -3032,9 +3112,10 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, * 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) + sshprot = conf_get_int(ssh->conf, CONF_sshprot); + if (sshprot == 0) ssh->version = 1; - if (ssh->cfg.sshprot == 3) { + if (sshprot == 3) { ssh->version = 2; ssh_send_verstring(ssh, NULL); } @@ -3042,9 +3123,9 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, /* * loghost, if configured, overrides realhost. */ - if (*ssh->cfg.loghost) { + if (*loghost) { sfree(*realhost); - *realhost = dupstr(ssh->cfg.loghost); + *realhost = dupstr(loghost); } return NULL; @@ -3137,6 +3218,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) Ssh ssh = c->ssh; void *sentreply = reply; + c->u.a.outstanding_requests--; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ sentreply = "\0\0\0\1\5"; @@ -3156,6 +3238,12 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) } if (reply) sfree(reply); + /* + * If we've already seen an incoming EOF but haven't sent an + * outgoing one, this may be the moment to send it. + */ + if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF)) + sshfwd_write_eof(c); } /* @@ -3199,9 +3287,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, { int i, j, ret; unsigned char cookie[8], *ptr; - struct RSAKey servkey, hostkey; struct MD5Context md5c; struct do_ssh1_login_state { + int crLine; int len; unsigned char *rsabuf, *keystr1, *keystr2; unsigned long supported_ciphers_mask, supported_auths_mask; @@ -3209,7 +3297,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int tis_auth_refused, ccard_auth_refused; unsigned char session_id[16]; int cipher_type; - char username[100]; void *publickey_blob; int publickey_bloblen; char *publickey_comment; @@ -3226,10 +3313,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char *commentp; int commentlen; int dlgret; + Filename *keyfile; + struct RSAKey servkey, hostkey; }; crState(do_ssh1_login_state); - crBegin(ssh->do_ssh1_login_crstate); + crBeginState; if (!pktin) crWaitUntil(pktin); @@ -3248,8 +3337,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } memcpy(cookie, ptr, 8); - if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) || - !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) { + if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) || + !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) { bombout(("Failed to read SSH-1 public keys from public key packet")); crStop(0); } @@ -3261,9 +3350,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char logmsg[80]; logevent("Host key fingerprint is:"); strcpy(logmsg, " "); - hostkey.comment = NULL; + s->hostkey.comment = NULL; rsa_fingerprint(logmsg + strlen(logmsg), - sizeof(logmsg) - strlen(logmsg), &hostkey); + sizeof(logmsg) - strlen(logmsg), &s->hostkey); logevent(logmsg); } @@ -3278,8 +3367,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER; MD5Init(&md5c); - MD5Update(&md5c, s->keystr2, hostkey.bytes); - MD5Update(&md5c, s->keystr1, servkey.bytes); + MD5Update(&md5c, s->keystr2, s->hostkey.bytes); + MD5Update(&md5c, s->keystr1, s->servkey.bytes); MD5Update(&md5c, cookie, 8); MD5Final(s->session_id, &md5c); @@ -3289,13 +3378,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Verify that the `bits' and `bytes' parameters match. */ - if (hostkey.bits > hostkey.bytes * 8 || - servkey.bits > servkey.bytes * 8) { + if (s->hostkey.bits > s->hostkey.bytes * 8 || + s->servkey.bits > s->servkey.bytes * 8) { bombout(("SSH-1 public keys were badly formatted")); crStop(0); } - s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes); + s->len = (s->hostkey.bytes > s->servkey.bytes ? + s->hostkey.bytes : s->servkey.bytes); s->rsabuf = snewn(s->len, unsigned char); @@ -3306,11 +3396,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * First format the key into a string. */ - int len = rsastr_len(&hostkey); + int len = rsastr_len(&s->hostkey); char fingerprint[100]; char *keystr = snewn(len, char); - rsastr_fmt(keystr, &hostkey); - rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey); + rsastr_fmt(keystr, &s->hostkey); + rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); ssh_set_frozen(ssh, 1); s->dlgret = verify_ssh_host_key(ssh->frontend, @@ -3344,14 +3434,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->rsabuf[i] ^= s->session_id[i]; } - if (hostkey.bytes > servkey.bytes) { - ret = rsaencrypt(s->rsabuf, 32, &servkey); + if (s->hostkey.bytes > s->servkey.bytes) { + ret = rsaencrypt(s->rsabuf, 32, &s->servkey); if (ret) - ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey); + ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey); } else { - ret = rsaencrypt(s->rsabuf, 32, &hostkey); + ret = rsaencrypt(s->rsabuf, 32, &s->hostkey); if (ret) - ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey); + ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey); } if (!ret) { bombout(("SSH-1 public key encryptions failed due to bad formatting")); @@ -3365,7 +3455,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char *cipher_string = NULL; int i; for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) { - int next_cipher = ssh->cfg.ssh_cipherlist[i]; + int next_cipher = conf_get_int_int(ssh->conf, + CONF_ssh_cipherlist, i); if (next_cipher == CIPHER_WARN) { /* If/when we choose a cipher, warn about it */ warn = 1; @@ -3453,21 +3544,21 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->crcda_ctx = crcda_make_context(); logevent("Installing CRC compensation attack detector"); - if (servkey.modulus) { - sfree(servkey.modulus); - servkey.modulus = NULL; + if (s->servkey.modulus) { + sfree(s->servkey.modulus); + s->servkey.modulus = NULL; } - if (servkey.exponent) { - sfree(servkey.exponent); - servkey.exponent = NULL; + if (s->servkey.exponent) { + sfree(s->servkey.exponent); + s->servkey.exponent = NULL; } - if (hostkey.modulus) { - sfree(hostkey.modulus); - hostkey.modulus = NULL; + if (s->hostkey.modulus) { + sfree(s->hostkey.modulus); + s->hostkey.modulus = NULL; } - if (hostkey.exponent) { - sfree(hostkey.exponent); - hostkey.exponent = NULL; + if (s->hostkey.exponent) { + sfree(s->hostkey.exponent); + s->hostkey.exponent = NULL; } crWaitUntil(pktin); @@ -3480,14 +3571,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, fflush(stdout); /* FIXME eh? */ { - if (!get_remote_username(&ssh->cfg, s->username, - sizeof(s->username))) { + if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, - lenof(s->username)); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3503,14 +3592,13 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStop(0); } - memcpy(s->username, s->cur_prompt->prompts[0]->result, - lenof(s->username)); + ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } - send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END); + send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END); { - char *userlog = dupprintf("Sent username \"%s\"", s->username); + char *userlog = dupprintf("Sent username \"%s\"", ssh->username); logevent(userlog); if (flags & FLAG_INTERACTIVE && (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) { @@ -3533,24 +3621,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Load the public half of any configured keyfile for later use. */ - if (!filename_is_null(ssh->cfg.keyfile)) { + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", - filename_to_str(&ssh->cfg.keyfile)); - keytype = key_type(&ssh->cfg.keyfile); + filename_to_str(s->keyfile)); + keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH1) { const char *error; - if (rsakey_pubblob(&ssh->cfg.keyfile, + if (rsakey_pubblob(s->keyfile, &s->publickey_blob, &s->publickey_bloblen, &s->publickey_comment, &error)) { - s->publickey_encrypted = rsakey_encrypted(&ssh->cfg.keyfile, + s->publickey_encrypted = rsakey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -3562,7 +3651,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -3574,7 +3663,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, while (pktin->type == SSH1_SMSG_FAILURE) { s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; - if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) { + if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) { /* * Attempt RSA authentication using Pageant. */ @@ -3604,7 +3693,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, if (s->response && s->responselen >= 5 && s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { s->p = s->response + 5; - s->nkeys = GET_32BIT(s->p); + s->nkeys = toint(GET_32BIT(s->p)); + if (s->nkeys < 0) { + logeventf(ssh, "Pageant reported negative key count %d", + s->nkeys); + s->nkeys = 0; + } s->p += 4; logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys); for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { @@ -3614,22 +3708,23 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int n, ok = FALSE; do { /* do while (0) to make breaking easy */ n = ssh1_read_bignum - (s->p, s->responselen-(s->p-s->response), + (s->p, toint(s->responselen-(s->p-s->response)), &s->key.exponent); if (n < 0) break; s->p += n; n = ssh1_read_bignum - (s->p, s->responselen-(s->p-s->response), + (s->p, toint(s->responselen-(s->p-s->response)), &s->key.modulus); if (n < 0) - break; + break; s->p += n; if (s->responselen - (s->p-s->response) < 4) break; - s->commentlen = GET_32BIT(s->p); + s->commentlen = toint(GET_32BIT(s->p)); s->p += 4; - if (s->responselen - (s->p-s->response) < + if (s->commentlen < 0 || + toint(s->responselen - (s->p-s->response)) < s->commentlen) break; s->commentp = (char *)s->p; @@ -3758,8 +3853,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int got_passphrase; /* need not be kept over crReturn */ if (flags & FLAG_VERBOSE) c_write_str(ssh, "Trying public key authentication.\r\n"); + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); logeventf(ssh, "Trying public key \"%s\"", - filename_to_str(&ssh->cfg.keyfile)); + filename_to_str(s->keyfile)); s->tried_publickey = 1; got_passphrase = FALSE; while (!got_passphrase) { @@ -3779,8 +3875,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", - s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + s->publickey_comment), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3801,10 +3896,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Try decrypting key with passphrase. */ - ret = loadrsakey(&ssh->cfg.keyfile, &s->key, passphrase, + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + ret = loadrsakey(s->keyfile, &s->key, passphrase, &error); if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (ret == 1) { @@ -3812,7 +3908,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, got_passphrase = TRUE; } else if (ret == 0) { c_write_str(ssh, "Couldn't load private key from "); - c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile)); + c_write_str(ssh, filename_to_str(s->keyfile)); c_write_str(ssh, " ("); c_write_str(ssh, error); c_write_str(ssh, ").\r\n"); @@ -3895,7 +3991,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, */ s->cur_prompt = new_prompts(ssh->frontend); - if (ssh->cfg.try_tis_auth && + if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && !s->tis_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; @@ -3934,11 +4030,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } - if (ssh->cfg.try_tis_auth && + if (conf_get_int(ssh->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) && !s->ccard_auth_refused) { s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; @@ -3977,7 +4073,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } @@ -3988,9 +4084,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); - add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", - s->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", + ssh->username, ssh->savedhost), + FALSE); } /* @@ -4167,70 +4263,65 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crFinish(1); } -void sshfwd_close(struct ssh_channel *c) +static void ssh_channel_try_eof(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + assert(c->pending_eof); /* precondition for calling us */ + if (c->halfopen) + return; /* can't close: not even opened yet */ + if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0) + return; /* can't send EOF: pending outgoing data */ + + c->pending_eof = FALSE; /* we're about to send it */ + if (ssh->version == 1) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + c->closes |= CLOSES_SENT_EOF; + } else { + struct Packet *pktout; + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes |= CLOSES_SENT_EOF; + ssh2_channel_check_close(c); + } +} + +void sshfwd_write_eof(struct ssh_channel *c) { Ssh ssh = c->ssh; if (ssh->state == SSH_STATE_CLOSED) return; - if (!c->closes) { - /* - * If halfopen is true, we have sent - * CHANNEL_OPEN for this channel, but it hasn't even been - * acknowledged by the server. So we must set a close flag - * on it now, and then when the server acks the channel - * open, we can close it then. - */ - if (!c->halfopen) { - if (ssh->version == 1) { - send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, - PKT_END); - c->closes = 1; /* sent MSG_CLOSE */ - } else { - int bytes_to_send = bufchain_size(&c->v.v2.outbuffer); - if (bytes_to_send > 0) { - /* - * If we still have unsent data in our outgoing - * buffer for this channel, we can't actually - * initiate a close operation yet or that data - * will be lost. Instead, set the pending_close - * flag so that when we do clear the buffer - * we'll start closing the channel. - */ - char logmsg[160] = {'\0'}; - sprintf( - logmsg, - "Forwarded port pending to be closed : " - "%d bytes remaining", - bytes_to_send); - logevent(logmsg); - - c->pending_close = TRUE; - } else { - /* - * No locally buffered data, so we can send the - * close message immediately. - */ - struct Packet *pktout; - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - c->closes = 1; /* sent MSG_CLOSE */ - logevent("Nothing left to send, closing channel"); - } - } - } + if (c->closes & CLOSES_SENT_EOF) + return; - if (c->type == CHAN_X11) { - c->u.x11.s = NULL; - logevent("Forwarded X11 connection terminated"); - } else if (c->type == CHAN_SOCKDATA || - c->type == CHAN_SOCKDATA_DORMANT) { - c->u.pfd.s = NULL; - logevent("Forwarded port closed"); - } + c->pending_eof = TRUE; + ssh_channel_try_eof(c); +} + +void sshfwd_unclean_close(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + + if (ssh->state == SSH_STATE_CLOSED) + return; + + switch (c->type) { + case CHAN_X11: + x11_close(c->u.x11.s); + logevent("Forwarded X11 connection terminated due to local error"); + break; + case CHAN_SOCKDATA: + case CHAN_SOCKDATA_DORMANT: + pfd_close(c->u.pfd.s); + logevent("Forwarded port closed due to local error"); + break; } + c->type = CHAN_ZOMBIE; + + ssh2_channel_check_close(c); } int sshfwd_write(struct ssh_channel *c, char *buf, int len) @@ -4289,27 +4380,26 @@ static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin) if (qh->msg1 > 0) { assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg1] = NULL; + ssh->packet_dispatch[qh->msg1] = ssh->q_saved_handler1; } if (qh->msg2 > 0) { assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg2] = NULL; + ssh->packet_dispatch[qh->msg2] = ssh->q_saved_handler2; } if (qh->next) { ssh->qhead = qh->next; if (ssh->qhead->msg1 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler; } if (ssh->qhead->msg2 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler; } } else { ssh->qhead = ssh->qtail = NULL; - ssh->packet_dispatch[pktin->type] = NULL; } qh->handler(ssh, pktin, qh->ctx); @@ -4333,11 +4423,11 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2, ssh->qhead = qh; if (qh->msg1 > 0) { - assert(ssh->packet_dispatch[qh->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler; } if (qh->msg2 > 0) { - assert(ssh->packet_dispatch[qh->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler; } } else { @@ -4365,11 +4455,11 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx) } } -static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) +static void ssh_setup_portfwd(Ssh ssh, Conf *conf) { - const char *portfwd_strptr = cfg->portfwd; struct ssh_portfwd *epf; int i; + char *key, *val; if (!ssh->portfwds) { ssh->portfwds = newtree234(ssh_portcmp); @@ -4387,64 +4477,61 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) epf->status = DESTROY; } - while (*portfwd_strptr) { + for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) { + char *kp, *kp2, *vp, *vp2; char address_family, type; int sport,dport,sserv,dserv; - char sports[256], dports[256], saddr[256], host[256]; - int n; + char *sports, *dports, *saddr, *host; + + kp = key; address_family = 'A'; type = 'L'; - if (*portfwd_strptr == 'A' || - *portfwd_strptr == '4' || - *portfwd_strptr == '6') - address_family = *portfwd_strptr++; - if (*portfwd_strptr == 'L' || - *portfwd_strptr == 'R' || - *portfwd_strptr == 'D') - type = *portfwd_strptr++; - - saddr[0] = '\0'; - - n = 0; - while (*portfwd_strptr && *portfwd_strptr != '\t') { - if (*portfwd_strptr == ':') { - /* - * We've seen a colon in the middle of the - * source port number. This means that - * everything we've seen until now is the - * source _address_, so we'll move it into - * saddr and start sports from the beginning - * again. - */ - portfwd_strptr++; - sports[n] = '\0'; - if (ssh->version == 1 && type == 'R') { - logeventf(ssh, "SSH-1 cannot handle remote source address " - "spec \"%s\"; ignoring", sports); - } else - strcpy(saddr, sports); - n = 0; - } - if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++; + if (*kp == 'A' || *kp == '4' || *kp == '6') + address_family = *kp++; + if (*kp == 'L' || *kp == 'R') + type = *kp++; + + if ((kp2 = strchr(kp, ':')) != NULL) { + /* + * There's a colon in the middle of the source port + * string, which means that the part before it is + * actually a source address. + */ + saddr = dupprintf("%.*s", (int)(kp2 - kp), kp); + sports = kp2+1; + } else { + saddr = NULL; + sports = kp; } - sports[n] = 0; - if (type != 'D') { - if (*portfwd_strptr == '\t') - portfwd_strptr++; - n = 0; - while (*portfwd_strptr && *portfwd_strptr != ':') { - if (n < lenof(host)-1) host[n++] = *portfwd_strptr++; - } - host[n] = 0; - if (*portfwd_strptr == ':') - portfwd_strptr++; - n = 0; - while (*portfwd_strptr) { - if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++; + sport = atoi(sports); + sserv = 0; + if (sport == 0) { + sserv = 1; + sport = net_service_lookup(sports); + if (!sport) { + logeventf(ssh, "Service lookup failed for source" + " port \"%s\"", sports); } - dports[n] = 0; - portfwd_strptr++; + } + + if (type == 'L' && !strcmp(val, "D")) { + /* dynamic forwarding */ + host = NULL; + dports = NULL; + dport = -1; + dserv = 0; + type = 'D'; + } else { + /* ordinary forwarding */ + vp = val; + vp2 = vp + strcspn(vp, ":"); + host = dupprintf("%.*s", (int)(vp2 - vp), vp); + if (vp2) + vp2++; + dports = vp2; dport = atoi(dports); dserv = 0; if (dport == 0) { @@ -4455,33 +4542,18 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) " port \"%s\"", dports); } } - } else { - while (*portfwd_strptr) portfwd_strptr++; - host[0] = 0; - dports[0] = 0; - dport = dserv = -1; - portfwd_strptr++; /* eat the NUL and move to next one */ - } - sport = atoi(sports); - sserv = 0; - if (sport == 0) { - sserv = 1; - sport = net_service_lookup(sports); - if (!sport) { - logeventf(ssh, "Service lookup failed for source" - " port \"%s\"", sports); - } } + if (sport && dport) { /* Set up a description of the source port. */ struct ssh_portfwd *pfrec, *epfrec; pfrec = snew(struct ssh_portfwd); pfrec->type = type; - pfrec->saddr = *saddr ? dupstr(saddr) : NULL; + pfrec->saddr = saddr; pfrec->sserv = sserv ? dupstr(sports) : NULL; pfrec->sport = sport; - pfrec->daddr = *host ? dupstr(host) : NULL; + pfrec->daddr = host; pfrec->dserv = dserv ? dupstr(dports) : NULL; pfrec->dport = dport; pfrec->local = NULL; @@ -4509,6 +4581,9 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } else { pfrec->status = CREATE; } + } else { + sfree(saddr); + sfree(host); } } @@ -4562,13 +4637,13 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); - } else if (ssh->cfg.rport_acceptall) { - /* XXX: ssh->cfg.rport_acceptall may not represent + } else if (conf_get_int(conf, CONF_rport_acceptall)) { + /* XXX: rport_acceptall may not represent * what was used to open the original connection, * since it's reconfigurable. */ - ssh2_pkt_addstring(pktout, "0.0.0.0"); + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4612,7 +4687,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) if (epf->type == 'L') { const char *err = pfd_addforward(epf->daddr, epf->dport, epf->saddr, epf->sport, - ssh, cfg, + ssh, conf, &epf->local, epf->addressfamily); @@ -4624,7 +4699,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } else if (epf->type == 'D') { const char *err = pfd_addforward(NULL, -1, epf->saddr, epf->sport, - ssh, cfg, + ssh, conf, &epf->local, epf->addressfamily); @@ -4680,10 +4755,10 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) ssh2_pkt_addbool(pktout, 1);/* want reply */ if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); - } else if (cfg->rport_acceptall) { - ssh2_pkt_addstring(pktout, "0.0.0.0"); + } else if (conf_get_int(conf, CONF_rport_acceptall)) { + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4736,7 +4811,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->ssh = ssh; if (x11_init(&c->u.x11.s, ssh->x11disp, c, - NULL, -1, &ssh->cfg) != NULL) { + NULL, -1, ssh->conf) != NULL) { logevent("Opening X11 forward connection failed"); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, @@ -4748,7 +4823,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->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); @@ -4778,10 +4853,12 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.message = NULL; + c->u.a.outstanding_requests = 0; add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, @@ -4793,14 +4870,11 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) { /* Remote side is trying to open a channel to talk to a * forwarded port. Give them back a local channel number. */ - struct ssh_channel *c; struct ssh_rportfwd pf, *pfp; int remoteid; int hostsize, port; char *host; const char *e; - c = snew(struct ssh_channel); - c->ssh = ssh; remoteid = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &host, &hostsize); @@ -4819,10 +4893,13 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, PKT_INT, remoteid, PKT_END); } else { + struct ssh_channel *c = snew(struct ssh_channel); + c->ssh = ssh; + logeventf(ssh, "Received remote port open request for %s:%d", pf.dhost, port); e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port, - c, &ssh->cfg, pfp->pfrec->addressfamily); + c, ssh->conf, pfp->pfrec->addressfamily); if (e != NULL) { logeventf(ssh, "Port open failed: %s", e); sfree(c); @@ -4833,7 +4910,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->pending_close = FALSE; + c->pending_eof = FALSE; c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); @@ -4860,15 +4937,14 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) pfd_confirm(c->u.pfd.s); } - if (c && c->closes) { + if (c && c->pending_eof) { /* * We have a pending close on this channel, * which we decided on before the server acked * the channel open. So now we know the * remoteid, we can close it again. */ - send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, - PKT_INT, c->remoteid, PKT_END); + ssh_channel_try_eof(c); } } @@ -4893,34 +4969,62 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin) struct ssh_channel *c; c = find234(ssh->channels, &i, ssh_channelfind); if (c && !c->halfopen) { - int closetype; - closetype = - (pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2); - - if ((c->closes == 0) && (c->type == CHAN_X11)) { - logevent("Forwarded X11 connection terminated"); - assert(c->u.x11.s != NULL); - x11_close(c->u.x11.s); - c->u.x11.s = NULL; - } - if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) { - logevent("Forwarded port closed"); - assert(c->u.pfd.s != NULL); - pfd_close(c->u.pfd.s); - c->u.pfd.s = NULL; - } - c->closes |= (closetype << 2); /* seen this message */ - if (!(c->closes & closetype)) { - send_packet(ssh, pktin->type, PKT_INT, c->remoteid, - PKT_END); - c->closes |= closetype; /* sent it too */ - } + if (pktin->type == SSH1_MSG_CHANNEL_CLOSE && + !(c->closes & CLOSES_RCVD_EOF)) { + /* + * Received CHANNEL_CLOSE, which we translate into + * outgoing EOF. + */ + int send_close = FALSE; - if (c->closes == 15) { - del234(ssh->channels, c); - sfree(c); - } + c->closes |= CLOSES_RCVD_EOF; + + switch (c->type) { + case CHAN_X11: + if (c->u.x11.s) + x11_send_eof(c->u.x11.s); + else + send_close = TRUE; + break; + case CHAN_SOCKDATA: + if (c->u.pfd.s) + pfd_send_eof(c->u.pfd.s); + else + send_close = TRUE; + break; + case CHAN_AGENT: + send_close = TRUE; + break; + } + + if (send_close && !(c->closes & CLOSES_SENT_EOF)) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + c->closes |= CLOSES_SENT_EOF; + } + } + + if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION && + !(c->closes & CLOSES_RCVD_CLOSE)) { + + if (!(c->closes & CLOSES_SENT_EOF)) { + bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d" + " for which we never sent CHANNEL_CLOSE\n", i)); + } + + c->closes |= CLOSES_RCVD_CLOSE; + } + + if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) && + !(c->closes & CLOSES_SENT_CLOSE)) { + send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION, + PKT_INT, c->remoteid, PKT_END); + c->closes |= CLOSES_SENT_CLOSE; + } + + if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) + ssh_channel_destroy(c); } else { bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n", pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" : @@ -4980,6 +5084,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, @@ -5054,7 +5159,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data; ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status; - if (ssh->cfg.agentfwd && agent_exists()) { + if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) { logevent("Requesting agent forwarding"); send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END); do { @@ -5073,9 +5178,9 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - if (ssh->cfg.x11_forward && - (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, - ssh->cfg.x11_auth, &ssh->cfg))) { + if (conf_get_int(ssh->conf, CONF_x11_forward) && + (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), + conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) { logevent("Requesting X11 forwarding"); /* * Note that while we blank the X authentication data here, we don't @@ -5116,24 +5221,23 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - ssh_setup_portfwd(ssh, &ssh->cfg); + ssh_setup_portfwd(ssh, ssh->conf); ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open; - if (!ssh->cfg.nopty) { + if (!conf_get_int(ssh->conf, CONF_nopty)) { struct Packet *pkt; /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ - sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed); + sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); /* Send the pty request. */ pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY); - ssh_pkt_addstring(pkt, ssh->cfg.termtype); + ssh_pkt_addstring(pkt, conf_get_str(ssh->conf, CONF_termtype)); ssh_pkt_adduint32(pkt, ssh->term_height); ssh_pkt_adduint32(pkt, ssh->term_width); ssh_pkt_adduint32(pkt, 0); /* width in pixels */ ssh_pkt_adduint32(pkt, 0); /* height in pixels */ - parse_ttymodes(ssh, ssh->cfg.ttymodes, - ssh1_send_ttymode, (void *)pkt); + parse_ttymodes(ssh, ssh1_send_ttymode, (void *)pkt); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED); ssh_pkt_adduint32(pkt, ssh->ispeed); ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED); @@ -5151,14 +5255,16 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } else if (pktin->type == SSH1_SMSG_FAILURE) { c_write_str(ssh, "Server refused to allocate pty\r\n"); ssh->editing = ssh->echoing = 1; - } - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); + } else { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } } else { ssh->editing = ssh->echoing = 1; } - if (ssh->cfg.compression) { + if (conf_get_int(ssh->conf, CONF_compression)) { send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END); do { crReturnV; @@ -5186,12 +5292,11 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, * exists, we fall straight back to that. */ { - char *cmd = ssh->cfg.remote_cmd_ptr; - - if (!cmd) cmd = ssh->cfg.remote_cmd; + char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd); - if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) { - cmd = ssh->cfg.remote_cmd_ptr2; + if (conf_get_int(ssh->conf, CONF_ssh_subsys) && + conf_get_str(ssh->conf, CONF_remote_cmd2)) { + cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); ssh->fallback_cmd = TRUE; } if (*cmd) @@ -5396,11 +5501,12 @@ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, /* * Handle the SSH-2 transport layer. */ -static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, +static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin) { unsigned char *in = (unsigned char *)vin; struct do_ssh2_transport_state { + int crLine; int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher; Bignum p, g, e, f, K; void *our_kexinit; @@ -5434,7 +5540,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, }; crState(do_ssh2_transport_state); - crBegin(ssh->do_ssh2_transport_crstate); + crBeginState; s->cscipher_tobe = s->sccipher_tobe = NULL; s->csmac_tobe = s->scmac_tobe = NULL; @@ -5455,14 +5561,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, begin_key_exchange: ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { - int i, j, commalist_started; + int i, j, k, commalist_started; /* * Set up the preferred key exchange. (NULL => warn below here) */ s->n_preferred_kex = 0; for (i = 0; i < KEX_MAX; i++) { - switch (ssh->cfg.ssh_kexlist[i]) { + switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) { case KEX_DHGEX: s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_gex; @@ -5494,12 +5600,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ s->n_preferred_ciphers = 0; for (i = 0; i < CIPHER_MAX; i++) { - switch (ssh->cfg.ssh_cipherlist[i]) { + switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) { case CIPHER_BLOWFISH: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish; break; case CIPHER_DES: - if (ssh->cfg.ssh2_des_cbc) { + if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) { s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des; } break; @@ -5525,7 +5631,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, /* * Set up preferred compression. */ - if (ssh->cfg.compression) + if (conf_get_int(ssh->conf, CONF_compression)) s->preferred_comp = &ssh_zlib; else s->preferred_comp = &ssh_comp_none; @@ -5567,46 +5673,30 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (i < lenof(hostkey_algs) - 1) ssh2_pkt_addstring_str(s->pktout, ","); } - /* List client->server encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; + /* List encryption algorithms (client->server then server->client). */ + for (k = 0; k < 2; k++) { + ssh2_pkt_addstring_start(s->pktout); + commalist_started = 0; + for (i = 0; i < s->n_preferred_ciphers; i++) { + const struct ssh2_ciphers *c = s->preferred_ciphers[i]; + if (!c) continue; /* warning flag */ + for (j = 0; j < c->nciphers; j++) { + if (commalist_started) + ssh2_pkt_addstring_str(s->pktout, ","); + ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); + commalist_started = 1; + } } } - /* List server->client encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) + /* List MAC algorithms (client->server then server->client). */ + for (j = 0; j < 2; j++) { + ssh2_pkt_addstring_start(s->pktout); + for (i = 0; i < s->nmacs; i++) { + ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); + if (i < s->nmacs - 1) ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; } } - /* List client->server MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } - /* List server->client MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the * same set twice.) */ @@ -5652,7 +5742,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_send_noqueue(ssh, s->pktout); if (!pktin) - crWaitUntil(pktin); + crWaitUntilV(pktin); /* * Now examine the other side's KEXINIT to see what we're up @@ -5664,7 +5754,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (pktin->type != SSH2_MSG_KEXINIT) { bombout(("expected key exchange packet from server")); - crStop(0); + crStopV; } ssh->kex = NULL; ssh->hostkey = NULL; @@ -5699,7 +5789,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!ssh->kex) { bombout(("Couldn't agree a key exchange algorithm (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } /* * Note that the server's guess is considered wrong if it doesn't match @@ -5714,6 +5804,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } } + if (!ssh->hostkey) { + bombout(("Couldn't agree a host key algorithm (available: %s)", + str ? str : "(null)")); + crStopV; + } + s->guessok = s->guessok && first_in_commasep_string(hostkey_algs[0]->name, str, len); ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ @@ -5735,7 +5831,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->cscipher_tobe) { bombout(("Couldn't agree a client-to-server cipher (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */ @@ -5757,7 +5853,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->sccipher_tobe) { bombout(("Couldn't agree a server-to-client cipher (available: %s)", str ? str : "(null)")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* client->server mac */ @@ -5814,6 +5910,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; + ssh->exhash = ssh->kex->hash->init(); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); + hash_string(ssh->kex->hash, ssh->exhash, + s->our_kexinit, s->our_kexinitlen); + sfree(s->our_kexinit); + if (pktin->length > 5) + hash_string(ssh->kex->hash, ssh->exhash, + pktin->data + 5, pktin->length - 5); + if (s->warn_kex) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "key-exchange algorithm", @@ -5821,11 +5927,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5834,7 +5940,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at kex warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } @@ -5846,11 +5952,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5859,7 +5965,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } @@ -5871,11 +5977,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while" " waiting for user response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -5884,22 +5990,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at cipher warning", NULL, 0, TRUE); - crStop(0); + crStopV; } } - ssh->exhash = ssh->kex->hash->init(); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); - hash_string(ssh->kex->hash, ssh->exhash, - s->our_kexinit, s->our_kexinitlen); - sfree(s->our_kexinit); - if (pktin->length > 5) - hash_string(ssh->kex->hash, ssh->exhash, - pktin->data + 5, pktin->length - 5); - if (s->ignorepkt) /* first_kex_packet_follows */ - crWaitUntil(pktin); /* Ignore packet */ + crWaitUntilV(pktin); /* Ignore packet */ } if (ssh->kex->main_type == KEXTYPE_DH) { @@ -5936,16 +6032,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_adduint32(s->pktout, s->pbits); ssh2_pkt_send_noqueue(ssh, s->pktout); - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { bombout(("expected key exchange group packet from server")); - crStop(0); + crStopV; } 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); + crStopV; } ssh->kex_ctx = dh_setup_gex(s->p, s->g); s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; @@ -5971,10 +6067,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_send_noqueue(ssh, s->pktout); set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != s->kex_reply_value) { bombout(("expected key exchange reply packet from server")); - crStop(0); + crStopV; } set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); @@ -5982,7 +6078,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->f = ssh2_pkt_getmp(pktin); if (!s->f) { bombout(("unable to parse key exchange reply packet")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); @@ -6015,10 +6111,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { bombout(("expected RSA public key packet from server")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); @@ -6037,7 +6133,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (!s->rsakey) { sfree(s->rsakeydata); bombout(("unable to parse RSA public key from server")); - crStop(0); + crStopV; } hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen); @@ -6097,11 +6193,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_rsakex_freekey(s->rsakey); - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_KEXRSA_DONE) { sfree(s->rsakeydata); bombout(("expected signature packet from server")); - crStop(0); + crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); @@ -6125,7 +6221,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); - crStop(0); + crStopV; } /* @@ -6142,11 +6238,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_dialog_callback, ssh); if (s->dlgret < 0) { do { - crReturn(0); + crReturnV; if (pktin) { bombout(("Unexpected data from server while waiting" " for user host key response")); - crStop(0); + crStopV; } } while (pktin || inlen > 0); s->dlgret = ssh->user_response; @@ -6155,7 +6251,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (s->dlgret == 0) { ssh_disconnect(ssh, "User aborted at host key verification", NULL, 0, TRUE); - crStop(0); + crStopV; } if (!s->got_session_id) { /* don't bother logging this in rekeys */ logevent("Host key fingerprint is:"); @@ -6224,7 +6320,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->csmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s client->server encryption", @@ -6245,10 +6341,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, /* * Expect SSH2_MSG_NEWKEYS from server. */ - crWaitUntil(pktin); + crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_NEWKEYS) { bombout(("expected new-keys packet from server")); - crStop(0); + crStopV; } ssh->incoming_data_size = 0; /* start counting from here */ @@ -6290,7 +6386,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->scmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s server->client encryption", ssh->sccipher->text_name); @@ -6321,24 +6417,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ ssh->kex_in_progress = FALSE; ssh->last_rekey = GETTICKCOUNT(); - if (ssh->cfg.ssh_rekey_time != 0) - ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) + ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); /* - * If this is the first key exchange phase, we must pass the - * SSH2_MSG_NEWKEYS packet to the next layer, not because it - * wants to see it but because it will need time to initialise - * itself before it sees an actual packet. In subsequent key - * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because - * it would only confuse the layer above. - */ - if (s->activated_authconn) { - crReturn(0); - } - s->activated_authconn = TRUE; - - /* * Now we're encrypting. Begin returning 1 to the protocol main * function so that other things can run on top of the * transport. If we ever see a KEXINIT, we must go back to the @@ -6356,7 +6439,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) || (!pktin && inlen < 0))) { wait_for_rekey: - crReturn(1); + if (!ssh->protocol_initial_phase_done) { + ssh->protocol_initial_phase_done = TRUE; + /* + * Allow authconn to initialise itself. + */ + do_ssh2_authconn(ssh, NULL, 0, NULL); + } + crReturnV; } if (pktin) { logevent("Server initiated key re-exchange"); @@ -6403,9 +6493,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * hit the event log _too_ often. */ ssh->outgoing_data_size = 0; ssh->incoming_data_size = 0; - if (ssh->cfg.ssh_rekey_time != 0) { + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) { ssh->next_rekey = - schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, + schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC, ssh2_timer, ssh); } goto wait_for_rekey; /* this is still utterly horrid */ @@ -6415,7 +6505,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } goto begin_key_exchange; - crFinish(1); + crFinishV; } /* @@ -6434,6 +6524,7 @@ static int ssh2_try_send(struct ssh_channel *c) { Ssh ssh = c->ssh; struct Packet *pktout; + int ret; while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) { int len; @@ -6458,14 +6549,23 @@ static int ssh2_try_send(struct ssh_channel *c) * After having sent as much data as we can, return the amount * still buffered. */ - return bufchain_size(&c->v.v2.outbuffer); + ret = bufchain_size(&c->v.v2.outbuffer); + + /* + * And if there's no data pending but we need to send an EOF, send + * it. + */ + if (!ret && c->pending_eof) + ssh_channel_try_eof(c); + + return ret; } static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) { int bufsize; - if (c->closes) - return; /* don't send on closing channels */ + if (c->closes & CLOSES_SENT_EOF) + return; /* don't send on channels we've EOFed */ bufsize = ssh2_try_send(c); if (bufsize == 0) { switch (c->type) { @@ -6485,19 +6585,6 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) break; } } - - /* - * If we've emptied the channel's output buffer and there's a - * pending close event, start the channel-closing procedure. - */ - if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) { - struct Packet *pktout; - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - c->closes = 1; - c->pending_close = FALSE; - } } /* @@ -6508,28 +6595,95 @@ static void ssh2_channel_init(struct ssh_channel *c) Ssh ssh = c->ssh; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->pending_close = FALSE; + c->pending_eof = FALSE; 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; + conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.chanreq_head = NULL; c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); } /* - * Potentially enlarge the window on an SSH-2 channel. + * Construct the common parts of a CHANNEL_OPEN. */ -static void ssh2_set_window(struct ssh_channel *c, int newwin) +static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type) { - Ssh ssh = c->ssh; + struct Packet *pktout; + + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); + ssh2_pkt_addstring(pktout, type); + ssh2_pkt_adduint32(pktout, c->localid); + ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ + ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + return 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. + */ +static void ssh2_queue_chanreq_handler(struct ssh_channel *c, + cchandler_fn_t handler, void *ctx) +{ + struct outstanding_channel_request *ocr = + snew(struct outstanding_channel_request); + + assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); + ocr->handler = handler; + ocr->ctx = ctx; + ocr->next = NULL; + if (!c->v.v2.chanreq_head) + c->v.v2.chanreq_head = ocr; + else + c->v.v2.chanreq_tail->next = ocr; + c->v.v2.chanreq_tail = ocr; +} + +/* + * Construct the common parts of a CHANNEL_REQUEST. If handler is not + * NULL then a reply will be requested and the handler will be called + * when it arrives. The returned packet is ready to have any + * request-specific data added and be sent. Note that if a handler is + * provided, it's essential that the request actually be sent. + * + * The handler will usually be passed the response packet in pktin. + * If pktin is NULL, this means that no reply will ever be forthcoming + * (e.g. because the entire connection is being destroyed) and the + * handler should free any storage it's holding. + */ +static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type, + cchandler_fn_t handler, void *ctx) +{ + struct Packet *pktout; + + assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, type); + ssh2_pkt_addbool(pktout, handler != NULL); + if (handler != NULL) + ssh2_queue_chanreq_handler(c, handler, ctx); + return pktout; +} + +/* + * Potentially enlarge the window on an SSH-2 channel. + */ +static void ssh2_handle_winadj_response(struct ssh_channel *, struct Packet *, + void *); +static void ssh2_set_window(struct ssh_channel *c, int newwin) +{ + Ssh ssh = c->ssh; /* - * Never send WINDOW_ADJUST for a channel that the remote side - * already thinks it's closed; there's no point, since it won't - * be sending any more data anyway. + * Never send WINDOW_ADJUST for a channel that the remote side has + * already sent EOF on; there's no point, since it won't be + * sending any more data anyway. Ditto if _we've_ already sent + * CLOSE. */ - if (c->closes != 0) + if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE)) return; /* @@ -6539,7 +6693,6 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ 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 @@ -6550,7 +6703,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; - struct winadj *wa; + unsigned *up; /* * In order to keep track of how much window the client @@ -6561,33 +6714,15 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) * 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); + !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) { + up = snew(unsigned); + *up = newwin - c->v.v2.locwindow; + pktout = ssh2_chanreq_init(c, "winadj@putty.projects.tartarus.org", + ssh2_handle_winadj_response, up); 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 { @@ -6627,14 +6762,21 @@ static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) return c; } -static int ssh2_handle_winadj_response(struct ssh_channel *c) +static void ssh2_handle_winadj_response(struct ssh_channel *c, + struct Packet *pktin, void *ctx) { - struct winadj *wa = c->v.v2.winadj_head; - if (!wa) - return FALSE; - c->v.v2.winadj_head = wa->next; - c->v.v2.remlocwin += wa->size; - sfree(wa); + unsigned *sizep = ctx; + + /* + * Winadj responses should always be failures. However, at least + * one server ("boks_sshd") is known to return SUCCESS for channel + * requests it's never heard of, such as "winadj@putty". Raised + * with foxt.com as bug 090916-090424, but for the sake of a quiet + * life, we don't worry about what kind of response we got. + */ + + c->v.v2.remlocwin += *sizep; + sfree(sizep); /* * 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 @@ -6642,51 +6784,28 @@ static int ssh2_handle_winadj_response(struct ssh_channel *c) */ if (c->v.v2.throttle_state == UNTHROTTLING) c->v.v2.throttle_state = UNTHROTTLED; - return TRUE; } -static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin) { - /* - * This should never get called. All channel requests are either - * sent with want_reply false, are sent before this handler gets - * installed, or are "winadj@putty" requests, which servers should - * never respond to with success. - * - * However, at least one server ("boks_sshd") is known to return - * SUCCESS for channel requests it's never heard of, such as - * "winadj@putty". Raised with foxt.com as bug 090916-090424, but - * for the sake of a quiet life, we handle it just the same as the - * expected FAILURE. - */ - struct ssh_channel *c; + struct ssh_channel *c = ssh2_channel_msg(ssh, pktin); + struct outstanding_channel_request *ocr; - c = ssh2_channel_msg(ssh, pktin); - if (!c) + if (!c) return; + ocr = c->v.v2.chanreq_head; + if (!ocr) { + ssh2_msg_unexpected(ssh, pktin); return; - if (!ssh2_handle_winadj_response(c)) - 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) -{ + } + ocr->handler(c, pktin, ocr->ctx); + c->v.v2.chanreq_head = ocr->next; + sfree(ocr); /* - * 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. + * We may now initiate channel-closing procedures, if that + * CHANNEL_REQUEST was the last thing outstanding before we send + * CHANNEL_CLOSE. */ - struct ssh_channel *c; - - c = ssh2_channel_msg(ssh, pktin); - if (!c) - return; - if (!ssh2_handle_winadj_response(c)) - ssh_disconnect(ssh, NULL, - "Received unsolicited SSH_MSG_CHANNEL_FAILURE", - SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + ssh2_channel_check_close(c); } static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) @@ -6695,7 +6814,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) c = ssh2_channel_msg(ssh, pktin); if (!c) return; - if (!c->closes) { + if (!(c->closes & CLOSES_SENT_EOF)) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(ssh, c); } @@ -6761,12 +6880,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, ssh_agentf_callback, c)) ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); + c->u.a.message = NULL; c->u.a.lensofar = 0; } } @@ -6796,7 +6917,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) * throttle the whole channel. */ if ((bufsize > c->v.v2.locmaxwin || - (ssh->cfg.ssh_simple && bufsize > 0)) && + (conf_get_int(ssh->conf, CONF_ssh_simple) && bufsize > 0)) && !c->throttling_conn) { c->throttling_conn = 1; ssh_throttle_conn(ssh, +1); @@ -6804,93 +6925,202 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) } } -static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) +static void ssh_channel_destroy(struct ssh_channel *c) { - struct ssh_channel *c; + Ssh ssh = c->ssh; - c = ssh2_channel_msg(ssh, pktin); - if (!c) - return; + switch (c->type) { + case CHAN_MAINSESSION: + ssh->mainchan = NULL; + update_specials_menu(ssh->frontend); + break; + case CHAN_X11: + if (c->u.x11.s != NULL) + x11_close(c->u.x11.s); + logevent("Forwarded X11 connection terminated"); + break; + case CHAN_AGENT: + sfree(c->u.a.message); + break; + case CHAN_SOCKDATA: + if (c->u.pfd.s != NULL) + pfd_close(c->u.pfd.s); + logevent("Forwarded port closed"); + break; + } + + del234(ssh->channels, c); + if (ssh->version == 2) { + bufchain_clear(&c->v.v2.outbuffer); + assert(c->v.v2.chanreq_head == NULL); + } + sfree(c); + + /* + * See if that was the last channel left open. + * (This is only our termination condition if we're + * not running in -N mode.) + */ + if (ssh->version == 2 && + !conf_get_int(ssh->conf, CONF_ssh_no_shell) && + count234(ssh->channels) == 0) { + /* + * We used to send SSH_MSG_DISCONNECT here, + * because I'd believed that _every_ conforming + * SSH-2 connection had to end with a disconnect + * being sent by at least one side; apparently + * I was wrong and it's perfectly OK to + * unceremoniously slam the connection shut + * when you're done, and indeed OpenSSH feels + * this is more polite than sending a + * DISCONNECT. So now we don't. + */ + ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE); + } +} + +static void ssh2_channel_check_close(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + struct Packet *pktout; + + if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) || + c->type == CHAN_ZOMBIE) && + !c->v.v2.chanreq_head && + !(c->closes & CLOSES_SENT_CLOSE)) { + /* + * We have both sent and received EOF (or the channel is a + * zombie), and we have no outstanding channel requests, which + * means the channel is in final wind-up. But we haven't sent + * CLOSE, so let's do so now. + */ + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE; + } + + if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) { + assert(c->v.v2.chanreq_head == NULL); + /* + * We have both sent and received CLOSE, which means we're + * completely done with the channel. + */ + ssh_channel_destroy(c); + } +} + +static void ssh2_channel_got_eof(struct ssh_channel *c) +{ + if (c->closes & CLOSES_RCVD_EOF) + return; /* already seen EOF */ + c->closes |= CLOSES_RCVD_EOF; if (c->type == CHAN_X11) { - /* - * Remote EOF on an X11 channel means we should - * wrap up and close the channel ourselves. - */ - x11_close(c->u.x11.s); - c->u.x11.s = NULL; - sshfwd_close(c); + x11_send_eof(c->u.x11.s); } else if (c->type == CHAN_AGENT) { - sshfwd_close(c); + if (c->u.a.outstanding_requests == 0) { + /* Manufacture an outgoing EOF in response to the incoming one. */ + sshfwd_write_eof(c); + } } else if (c->type == CHAN_SOCKDATA) { - pfd_close(c->u.pfd.s); - c->u.pfd.s = NULL; - sshfwd_close(c); + pfd_send_eof(c->u.pfd.s); + } else if (c->type == CHAN_MAINSESSION) { + Ssh ssh = c->ssh; + + if (!ssh->sent_console_eof && + (from_backend_eof(ssh->frontend) || ssh->got_pty)) { + /* + * Either from_backend_eof told us that the front end + * wants us to close the outgoing side of the connection + * as soon as we see EOF from the far end, or else we've + * unilaterally decided to do that because we've allocated + * a remote pty and hence EOF isn't a particularly + * meaningful concept. + */ + sshfwd_write_eof(c); + } + ssh->sent_console_eof = TRUE; } + + ssh2_channel_check_close(c); +} + +static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) +{ + struct ssh_channel *c; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + ssh2_channel_got_eof(c); } static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; - struct Packet *pktout; c = ssh2_channel_msg(ssh, pktin); if (!c) return; - /* Do pre-close processing on the channel. */ - switch (c->type) { - case CHAN_MAINSESSION: - ssh->mainchan = NULL; - update_specials_menu(ssh->frontend); - break; - case CHAN_X11: - if (c->u.x11.s != NULL) - x11_close(c->u.x11.s); - sshfwd_close(c); - break; - case CHAN_AGENT: - sshfwd_close(c); - break; - case CHAN_SOCKDATA: - if (c->u.pfd.s != NULL) - pfd_close(c->u.pfd.s); - sshfwd_close(c); - break; - } - if (c->closes == 0) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); + + /* + * When we receive CLOSE on a channel, we assume it comes with an + * implied EOF if we haven't seen EOF yet. + */ + ssh2_channel_got_eof(c); + + /* + * And we also send an outgoing EOF, if we haven't already, on the + * assumption that CLOSE is a pretty forceful announcement that + * the remote side is doing away with the entire channel. (If it + * had wanted to send us EOF and continue receiving data from us, + * it would have just sent CHANNEL_EOF.) + */ + if (!(c->closes & CLOSES_SENT_EOF)) { + /* + * Make sure we don't read any more from whatever our local + * data source is for this channel. + */ + switch (c->type) { + case CHAN_MAINSESSION: + ssh->send_ok = 0; /* stop trying to read from stdin */ + break; + case CHAN_X11: + x11_override_throttle(c->u.x11.s, 1); + break; + case CHAN_SOCKDATA: + pfd_override_throttle(c->u.pfd.s, 1); + break; + } + + /* + * Abandon any buffered data we still wanted to send to this + * channel. Receiving a CHANNEL_CLOSE is an indication that + * the server really wants to get on and _destroy_ this + * channel, and it isn't going to send us any further + * WINDOW_ADJUSTs to permit us to send pending stuff. + */ + bufchain_clear(&c->v.v2.outbuffer); + + /* + * Send outgoing EOF. + */ + sshfwd_write_eof(c); } - del234(ssh->channels, c); - bufchain_clear(&c->v.v2.outbuffer); - sfree(c); /* - * See if that was the last channel left open. - * (This is only our termination condition if we're - * not running in -N mode.) + * Now process the actual close. */ - if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) { - /* - * We used to send SSH_MSG_DISCONNECT here, - * because I'd believed that _every_ conforming - * SSH-2 connection had to end with a disconnect - * being sent by at least one side; apparently - * I was wrong and it's perfectly OK to - * unceremoniously slam the connection shut - * when you're done, and indeed OpenSSH feels - * this is more polite than sending a - * DISCONNECT. So now we don't. - */ - ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE); + if (!(c->closes & CLOSES_RCVD_CLOSE)) { + c->closes |= CLOSES_RCVD_CLOSE; + ssh2_channel_check_close(c); } } static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { struct ssh_channel *c; - struct Packet *pktout; c = ssh2_channel_msg(ssh, pktin); if (!c) @@ -6904,17 +7134,8 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); if (c->u.pfd.s) pfd_confirm(c->u.pfd.s); - if (c->closes) { - /* - * We have a pending close on this channel, - * which we decided on before the server acked - * the channel open. So now we know the - * remoteid, we can close it again. - */ - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); - } + if (c->pending_eof) + ssh_channel_try_eof(c); } static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) @@ -7003,16 +7224,18 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) is_int = FALSE; } else { int maybe_int = FALSE, maybe_str = FALSE; -#define CHECK_HYPOTHESIS(offset, result) \ - do { \ - long q = offset; \ - if (q >= 0 && q+4 <= len) { \ - q = q + 4 + GET_32BIT(p+q); \ - if (q >= 0 && q+4 <= len && \ - ((q = q + 4 + GET_32BIT(p+q))!= 0) && q == len) \ - result = TRUE; \ - } \ - } while(0) +#define CHECK_HYPOTHESIS(offset, result) \ + do \ + { \ + int q = toint(offset); \ + if (q >= 0 && q+4 <= len) { \ + q = toint(q + 4 + GET_32BIT(p+q)); \ + if (q >= 0 && q+4 <= len && \ + ((q = toint(q + 4 + GET_32BIT(p+q))) != 0) && \ + q == len) \ + result = TRUE; \ + } \ + } while(0) CHECK_HYPOTHESIS(4+1, maybe_int); CHECK_HYPOTHESIS(4+num+1, maybe_str); #undef CHECK_HYPOTHESIS @@ -7186,7 +7409,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (!ssh->X11_fwd_enabled) error = "X11 forwarding is not enabled"; else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c, - addrstr, peerport, &ssh->cfg)) != NULL) { + addrstr, peerport, ssh->conf)) != NULL) { logeventf(ssh, "Local X11 connection failed: %s", x11err); error = "Unable to open an X11 connection"; } else { @@ -7213,7 +7436,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) const char *e = pfd_newconnect(&c->u.pfd.s, realpf->dhost, realpf->dport, c, - &ssh->cfg, + ssh->conf, realpf->pfrec->addressfamily); logeventf(ssh, "Attempting to forward remote port to " "%s:%d", realpf->dhost, realpf->dport); @@ -7232,6 +7455,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) else { c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.outstanding_requests = 0; } } else { error = "Unsupported channel type requested"; @@ -7269,7 +7493,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin) { /* Arbitrary limit to prevent unbounded inflation of buffer */ - if (ssh->cfg.ssh_show_banner && + if (conf_get_int(ssh->conf, CONF_ssh_show_banner) && bufchain_size(&ssh->banner) <= 131072) { char *banner = NULL; int size = 0; @@ -7299,13 +7523,220 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val) ssh2_pkt_adduint32(pktout, arg); } +static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_x11_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_x11_state, ctx); + + crBeginState; + + logevent("Requesting X11 forwarding"); + pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req", + ssh2_setup_x11, s); + ssh2_pkt_addbool(pktout, 0); /* many connections */ + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthprotoname); + /* + * Note that while we blank the X authentication data here, we don't + * take any special action to blank the start of an X11 channel, + * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection + * without having session blanking enabled is likely to leak your + * cookie into the log. + */ + dont_log_password(ssh, pktout, PKTLOG_BLANK); + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthdatastring); + end_log_omission(ssh, pktout); + ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum); + ssh2_pkt_send(ssh, pktout); + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logevent("X11 forwarding enabled"); + ssh->X11_fwd_enabled = TRUE; + } else + logevent("X11 forwarding refused"); + } + + crFinishFreeV; +} + +static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_agent_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_agent_state, ctx); + + crBeginState; + + logevent("Requesting OpenSSH-style agent forwarding"); + pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com", + ssh2_setup_agent, s); + ssh2_pkt_send(ssh, pktout); + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logevent("Agent forwarding enabled"); + ssh->agentfwd_enabled = TRUE; + } else + logevent("Agent forwarding refused"); + } + + crFinishFreeV; +} + +static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_pty_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_pty_state, ctx); + + crBeginState; + + /* Unpick the terminal-speed string. */ + /* XXX perhaps we should allow no speeds to be sent. */ + ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ + sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); + /* Build the pty request. */ + pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req", + ssh2_setup_pty, s); + ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype)); + ssh2_pkt_adduint32(pktout, ssh->term_width); + ssh2_pkt_adduint32(pktout, ssh->term_height); + ssh2_pkt_adduint32(pktout, 0); /* pixel width */ + ssh2_pkt_adduint32(pktout, 0); /* pixel height */ + ssh2_pkt_addstring_start(pktout); + parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED); + ssh2_pkt_adduint32(pktout, ssh->ispeed); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED); + ssh2_pkt_adduint32(pktout, ssh->ospeed); + ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */ + ssh2_pkt_send(ssh, pktout); + ssh->state = SSH_STATE_INTERMED; + + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; + + if (pktin) { + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } else { + c_write_str(ssh, "Server refused to allocate pty\r\n"); + ssh->editing = ssh->echoing = 1; + } + } + + crFinishFreeV; +} + +static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_env_state { + int crLine; + int num_env, env_left, env_ok; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_env_state, ctx); + + crBeginState; + + /* + * Send environment variables. + * + * Simplest thing here is to send all the requests at once, and + * then wait for a whole bunch of successes or failures. + */ + s->num_env = 0; + { + char *key, *val; + + for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { + pktout = ssh2_chanreq_init(ssh->mainchan, "env", ssh2_setup_env, s); + ssh2_pkt_addstring(pktout, key); + ssh2_pkt_addstring(pktout, val); + ssh2_pkt_send(ssh, pktout); + + s->num_env++; + } + if (s->num_env) + logeventf(ssh, "Sent %d environment variables", s->num_env); + } + + if (s->num_env) { + s->env_ok = 0; + s->env_left = s->num_env; + + while (s->env_left > 0) { + /* Wait to be called back with either a response packet, + * or NULL meaning clean up and free our data */ + crReturnV; + if (!pktin) goto out; + if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) + s->env_ok++; + s->env_left--; + } + + if (s->env_ok == s->num_env) { + logevent("All environment variables successfully set"); + } else if (s->env_ok == 0) { + logevent("All environment variables refused"); + c_write_str(ssh, "Server refused to set environment variables\r\n"); + } else { + logeventf(ssh, "%d environment variables refused", + s->num_env - s->env_ok); + c_write_str(ssh, "Server refused to set all environment variables\r\n"); + } + } + out:; + crFinishFreeV; +} + /* * Handle the SSH-2 userauth and connection layers. */ +static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_authconn(ssh, NULL, 0, pktin); +} + +static void ssh2_response_authconn(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + do_ssh2_authconn(c->ssh, NULL, 0, pktin); +} + static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { struct do_ssh2_authconn_state { + int crLine; enum { AUTH_TYPE_NONE, AUTH_TYPE_PUBLICKEY, @@ -7327,7 +7758,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int we_are_in, userauth_success; prompts_t *cur_prompt; int num_prompts; - char username[100]; + char *username; char *password; int got_username; void *publickey_blob; @@ -7344,8 +7775,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int siglen, retlen, len; char *q, *agentreq, *ret; int try_send; - int num_env, env_left, env_ok; struct Packet *pktout; + Filename *keyfile; #ifndef NO_GSSAPI struct ssh_gss_library *gsslib; Ssh_gss_ctx gss_ctx; @@ -7357,15 +7788,37 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, }; crState(do_ssh2_authconn_state); - crBegin(ssh->do_ssh2_authconn_crstate); - + crBeginState; + + /* Register as a handler for all the messages this coroutine handles. */ + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_authconn; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_authconn; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_authconn; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_authconn; + s->done_service_req = FALSE; s->we_are_in = s->userauth_success = FALSE; #ifndef NO_GSSAPI s->tried_gssapi = FALSE; #endif - if (!ssh->cfg.ssh_no_userauth) { + if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) { /* * Request userauth protocol, and await a response to it. */ @@ -7408,28 +7861,29 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Load the public half of any configured public key file * for later use. */ - if (!filename_is_null(ssh->cfg.keyfile)) { + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + if (!filename_is_null(s->keyfile)) { int keytype; logeventf(ssh, "Reading private key file \"%.150s\"", - filename_to_str(&ssh->cfg.keyfile)); - keytype = key_type(&ssh->cfg.keyfile); + filename_to_str(s->keyfile)); + keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH2) { const char *error; s->publickey_blob = - ssh2_userkey_loadpub(&ssh->cfg.keyfile, + ssh2_userkey_loadpub(s->keyfile, &s->publickey_algorithm, &s->publickey_bloblen, &s->publickey_comment, &error); if (s->publickey_blob) { s->publickey_encrypted = - ssh2_userkey_encrypted(&ssh->cfg.keyfile, NULL); + ssh2_userkey_encrypted(s->keyfile, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to load private key (%s)", error); msgbuf = dupprintf("Unable to load private key file " "\"%.150s\" (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), error); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -7440,7 +7894,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" " (%s)\r\n", - filename_to_str(&ssh->cfg.keyfile), + filename_to_str(s->keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -7455,7 +7909,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->nkeys = 0; s->agent_response = NULL; s->pkblob_in_agent = NULL; - if (ssh->cfg.tryagent && agent_exists()) { + if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) { void *r; @@ -7483,13 +7937,53 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int keyi; unsigned char *p; p = s->agent_response + 5; - s->nkeys = GET_32BIT(p); + s->nkeys = toint(GET_32BIT(p)); + + /* + * Vet the Pageant response to ensure that the key + * count and blob lengths make sense. + */ + if (s->nkeys < 0) { + logeventf(ssh, "Pageant response contained a negative" + " key count %d", s->nkeys); + s->nkeys = 0; + goto done_agent_query; + } else { + unsigned char *q = p + 4; + int lenleft = s->agent_responselen - 5 - 4; + + for (keyi = 0; keyi < s->nkeys; keyi++) { + int bloblen, commentlen; + if (lenleft < 4) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + bloblen = toint(GET_32BIT(q)); + if (bloblen < 0 || bloblen > lenleft) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + lenleft -= 4 + bloblen; + q += 4 + bloblen; + commentlen = toint(GET_32BIT(q)); + if (commentlen < 0 || commentlen > lenleft) { + logeventf(ssh, "Pageant response was truncated"); + s->nkeys = 0; + goto done_agent_query; + } + lenleft -= 4 + commentlen; + q += 4 + commentlen; + } + } + p += 4; logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys); if (s->publickey_blob) { /* See if configured key is in agent. */ for (keyi = 0; keyi < s->nkeys; keyi++) { - s->pklen = GET_32BIT(p); + s->pklen = toint(GET_32BIT(p)); if (s->pklen == s->publickey_bloblen && !memcmp(p+4, s->publickey_blob, s->publickey_bloblen)) { @@ -7500,7 +7994,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, break; } p += 4 + s->pklen; - p += GET_32BIT(p) + 4; /* comment */ + p += toint(GET_32BIT(p)) + 4; /* comment */ } if (!s->pkblob_in_agent) { logevent("Configured key file not in Pageant"); @@ -7510,6 +8004,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { logevent("Failed to get reply from Pageant"); } + done_agent_query:; } } @@ -7538,26 +8033,23 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * the username they will want to be able to get back and * retype it! */ - s->username[0] = '\0'; s->got_username = FALSE; while (!s->we_are_in) { /* * Get a username. */ - if (s->got_username && !ssh->cfg.change_username) { + if (s->got_username && !conf_get_int(ssh->conf, CONF_change_username)) { /* * We got a username last time round this loop, and * with change_username turned off we don't try to get * it again. */ - } else if (!get_remote_username(&ssh->cfg, s->username, - sizeof(s->username))) { + } else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, - lenof(s->username)); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -7574,13 +8066,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE); crStopV; } - memcpy(s->username, s->cur_prompt->prompts[0]->result, - lenof(s->username)); + ssh->username = dupstr(s->cur_prompt->prompts[0]->result); free_prompts(s->cur_prompt); } else { char *stuff; if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { - stuff = dupprintf("Using username \"%s\".\r\n", s->username); + stuff = dupprintf("Using username \"%s\".\r\n", ssh->username); c_write_str(ssh, stuff); sfree(stuff); } @@ -7595,7 +8086,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */ ssh2_pkt_addstring(s->pktout, "none"); /* method */ ssh2_pkt_send(ssh, s->pktout); @@ -7725,7 +8216,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, logevent("Password authentication failed"); c_write_str(ssh, "Access denied\r\n"); - if (ssh->cfg.change_username) { + if (conf_get_int(ssh->conf, CONF_change_username)) { /* XXX perhaps we should allow * keyboard-interactive to do this too? */ s->we_are_in = FALSE; @@ -7741,12 +8232,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("publickey", methods, methlen); s->can_passwd = in_commasep_string("password", methods, methlen); - s->can_keyb_inter = ssh->cfg.try_ki_auth && + s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) && in_commasep_string("keyboard-interactive", methods, methlen); #ifndef NO_GSSAPI if (!ssh->gsslibs) - ssh->gsslibs = ssh_gss_setup(&ssh->cfg); - s->can_gssapi = ssh->cfg.try_gssapi_auth && + ssh->gsslibs = ssh_gss_setup(ssh->conf); + s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) && in_commasep_string("gssapi-with-mic", methods, methlen) && ssh->gsslibs->nlibraries > 0; #endif @@ -7765,13 +8256,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, logeventf(ssh, "Trying Pageant key #%d", s->keyi); /* Unpack key from agent response */ - s->pklen = GET_32BIT(s->agentp); + s->pklen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->pkblob = (char *)s->agentp; s->agentp += s->pklen; - s->alglen = GET_32BIT(s->pkblob); + s->alglen = toint(GET_32BIT(s->pkblob)); s->alg = s->pkblob + 4; - s->commentlen = GET_32BIT(s->agentp); + s->commentlen = toint(GET_32BIT(s->agentp)); s->agentp += 4; s->commentp = (char *)s->agentp; s->agentp += s->commentlen; @@ -7779,7 +8270,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* See if server will accept it */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -7814,7 +8305,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Construct a SIGN_REQUEST. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -7875,7 +8366,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->ret = vret; sfree(s->agentreq); if (s->ret) { - if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) { + if (s->retlen >= 9 && + s->ret[4] == SSH2_AGENT_SIGN_RESPONSE && + GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) { logevent("Sending Pageant's response"); ssh2_add_sigblob(ssh, s->pktout, s->pkblob, s->pklen, @@ -7918,7 +8411,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * willing to accept it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); /* method */ @@ -7964,7 +8457,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -7992,11 +8485,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Try decrypting the key. */ - key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase, - &error); + s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile); + key = ssh2_load_userkey(s->keyfile, passphrase, &error); if (passphrase) { /* burn the evidence */ - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (key == SSH2_WRONG_PASSPHRASE || key == NULL) { @@ -8026,7 +8519,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Hallelujah. Generate a signature and send it. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "publickey"); @@ -8100,7 +8593,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int i, j; s->gsslib = NULL; for (i = 0; i < ngsslibs; i++) { - int want_id = ssh->cfg.ssh_gsslist[i]; + int want_id = conf_get_int_int(ssh->conf, + CONF_ssh_gsslist, i); for (j = 0; j < ssh->gsslibs->nlibraries; j++) if (ssh->gsslibs->libraries[j].id == want_id) { s->gsslib = &ssh->gsslibs->libraries[j]; @@ -8123,7 +8617,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); logevent("Attempting GSSAPI authentication"); @@ -8195,7 +8689,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, (s->gsslib, &s->gss_ctx, s->gss_srv_name, - ssh->cfg.gssapifwd, + conf_get_int(ssh->conf, CONF_gssapifwd), &s->gss_rcvtok, &s->gss_sndtok); @@ -8251,7 +8745,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_pkt_addstring_start(s->pktout); ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); - ssh_pkt_addstring(s->pktout, s->username); + ssh_pkt_addstring(s->pktout, ssh->username); ssh_pkt_addstring(s->pktout, "ssh-connection"); ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); @@ -8282,7 +8776,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "keyboard-interactive"); @@ -8343,7 +8837,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), - echo, SSH_MAX_PASSWORD_LEN); + echo); } if (name_len) { @@ -8444,10 +8938,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); - add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", - s->username, + add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", + ssh->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { @@ -8485,7 +8979,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); @@ -8549,11 +9043,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ add_prompt(s->cur_prompt, dupstr("Current password (blank for previously entered password): "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Enter new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); /* * Loop until the user manages to enter the same @@ -8574,7 +9068,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ /* burn the evidence */ free_prompts(s->cur_prompt); - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); ssh_disconnect(ssh, NULL, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER, @@ -8590,7 +9084,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * re-enter it if they louse up the new password.) */ if (s->cur_prompt->prompts[0]->result[0]) { - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); s->password = @@ -8614,7 +9108,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, ssh->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ ssh2_pkt_addstring(s->pktout, "password"); @@ -8657,7 +9151,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * We don't need the old password any more, in any * case. Burn the evidence. */ - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); } else { @@ -8719,67 +9213,32 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Create the main session channel. */ - if (ssh->cfg.ssh_no_shell) { + if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) { ssh->mainchan = NULL; - } else if (*ssh->cfg.ssh_nc_host) { - /* - * Just start a direct-tcpip channel and use it as the main - * channel. - */ + } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = 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); - 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); - ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port); - /* - * There's nothing meaningful to put in the originator - * fields, but some servers insist on syntactically correct - * information. - */ - ssh2_pkt_addstring(s->pktout, "0.0.0.0"); - ssh2_pkt_adduint32(s->pktout, 0); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a direct-tcpip channel")); - crStopV; - /* FIXME: error data comes back in FAILURE packet */ - } - if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) { - bombout(("Server's channel confirmation cited wrong channel")); - crStopV; + if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) { + /* + * Just start a direct-tcpip channel and use it as the main + * channel. + */ + ssh_send_port_open(ssh->mainchan, + conf_get_str(ssh->conf, CONF_ssh_nc_host), + conf_get_int(ssh->conf, CONF_ssh_nc_port), + "main channel"); + ssh->ncmode = TRUE; + } else { + s->pktout = ssh2_chanopen_init(ssh->mainchan, "session"); + logevent("Opening session as main channel"); + ssh2_pkt_send(ssh, s->pktout); + ssh->ncmode = FALSE; } - ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); - ssh->mainchan->halfopen = FALSE; - ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); - ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - add234(ssh->channels, ssh->mainchan); - update_specials_menu(ssh->frontend); - logevent("Opened direct-tcpip channel"); - ssh->ncmode = TRUE; - } else { - ssh->mainchan = snew(struct ssh_channel); - ssh->mainchan->ssh = 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); - 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); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a session")); + bombout(("Server refused to open channel")); crStopV; /* FIXME: error data comes back in FAILURE packet */ } @@ -8794,8 +9253,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); - logevent("Opened channel for session"); - ssh->ncmode = FALSE; + logevent("Opened main channel"); } /* @@ -8815,265 +9273,114 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_msg_channel_request; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response; - if (ssh->mainchan && ssh->cfg.ssh_simple) { + + if (ssh->mainchan && conf_get_int(ssh->conf, CONF_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 */ + s->pktout = ssh2_chanreq_init(ssh->mainchan, + "simple@putty.projects.tartarus.org", + NULL, NULL); ssh2_pkt_send(ssh, s->pktout); } /* - * Potentially enable X11 forwarding. + * Enable port forwardings. */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward && - (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, - ssh->cfg.x11_auth, &ssh->cfg))) { - logevent("Requesting X11 forwarding"); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "x11-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addbool(s->pktout, 0); /* many connections */ - ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname); + ssh_setup_portfwd(ssh, ssh->conf); + + if (ssh->mainchan && !ssh->ncmode) { /* - * Note that while we blank the X authentication data here, we don't - * take any special action to blank the start of an X11 channel, - * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection - * without having session blanking enabled is likely to leak your - * cookie into the log. + * Send the CHANNEL_REQUESTS for the main session channel. + * Each one is handled by its own little asynchronous + * co-routine. */ - dont_log_password(ssh, s->pktout, PKTLOG_BLANK); - ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring); - end_log_omission(ssh, s->pktout); - ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); + /* Potentially enable X11 forwarding. */ + if (conf_get_int(ssh->conf, CONF_x11_forward) && + (ssh->x11disp = + x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), + conf_get_int(ssh->conf, CONF_x11_auth), + ssh->conf))) + ssh2_setup_x11(ssh->mainchan, NULL, NULL); - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to X11 forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("X11 forwarding refused"); - } else { - logevent("X11 forwarding enabled"); - ssh->X11_fwd_enabled = TRUE; - } - } + /* Potentially enable agent forwarding. */ + if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) + ssh2_setup_agent(ssh->mainchan, NULL, NULL); - /* - * Enable port forwardings. - */ - ssh_setup_portfwd(ssh, &ssh->cfg); + /* Now allocate a pty for the session. */ + if (!conf_get_int(ssh->conf, CONF_nopty)) + ssh2_setup_pty(ssh->mainchan, NULL, NULL); - /* - * Potentially enable agent forwarding. - */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) { - logevent("Requesting OpenSSH-style agent forwarding"); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_send(ssh, s->pktout); + /* Send environment variables. */ + ssh2_setup_env(ssh->mainchan, NULL, NULL); - crWaitUntilV(pktin); + /* + * Start a shell or a remote command. We may have to attempt + * this twice if the config data has provided a second choice + * of command. + */ + while (1) { + int subsys; + char *cmd; - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to agent forwarding request:" - " packet type %d", pktin->type)); - crStopV; + if (ssh->fallback_cmd) { + subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2); + cmd = conf_get_str(ssh->conf, CONF_remote_cmd2); + } else { + subsys = conf_get_int(ssh->conf, CONF_ssh_subsys); + cmd = conf_get_str(ssh->conf, CONF_remote_cmd); } - logevent("Agent forwarding refused"); - } else { - logevent("Agent forwarding enabled"); - ssh->agentfwd_enabled = TRUE; - } - } - - /* - * Now allocate a pty for the session. - */ - if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) { - /* Unpick the terminal-speed string. */ - /* XXX perhaps we should allow no speeds to be sent. */ - ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ - sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed); - /* Build the pty request. */ - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */ - ssh2_pkt_addstring(s->pktout, "pty-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, ssh->cfg.termtype); - ssh2_pkt_adduint32(s->pktout, ssh->term_width); - ssh2_pkt_adduint32(s->pktout, ssh->term_height); - ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */ - ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */ - ssh2_pkt_addstring_start(s->pktout); - parse_ttymodes(ssh, ssh->cfg.ttymodes, - ssh2_send_ttymode, (void *)s->pktout); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ispeed); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ospeed); - ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */ - ssh2_pkt_send(ssh, s->pktout); - ssh->state = SSH_STATE_INTERMED; - - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to pty request:" - " packet type %d", pktin->type)); - crStopV; + if (subsys) { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem", + ssh2_response_authconn, NULL); + ssh2_pkt_addstring(s->pktout, cmd); + } else if (*cmd) { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec", + ssh2_response_authconn, NULL); + ssh2_pkt_addstring(s->pktout, cmd); + } else { + s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell", + ssh2_response_authconn, NULL); } - c_write_str(ssh, "Server refused to allocate pty\r\n"); - ssh->editing = ssh->echoing = 1; - } else { - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); - } - } else { - ssh->editing = ssh->echoing = 1; - } - - /* - * Send environment variables. - * - * Simplest thing here is to send all the requests at once, and - * then wait for a whole bunch of successes or failures. - */ - if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) { - char *e = ssh->cfg.environmt; - char *var, *varend, *val; - - s->num_env = 0; - - while (*e) { - var = e; - while (*e && *e != '\t') e++; - varend = e; - if (*e == '\t') e++; - val = e; - while (*e) e++; - e++; - - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "env"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring_start(s->pktout); - ssh2_pkt_addstring_data(s->pktout, var, varend-var); - ssh2_pkt_addstring(s->pktout, val); ssh2_pkt_send(ssh, s->pktout); - s->num_env++; - } - - logeventf(ssh, "Sent %d environment variables", s->num_env); - - s->env_ok = 0; - s->env_left = s->num_env; - - while (s->env_left > 0) { crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to environment request:" + bombout(("Unexpected response to shell/command request:" " packet type %d", pktin->type)); crStopV; } - } else { - s->env_ok++; - } - - s->env_left--; - } - - if (s->env_ok == s->num_env) { - logevent("All environment variables successfully set"); - } else if (s->env_ok == 0) { - logevent("All environment variables refused"); - c_write_str(ssh, "Server refused to set environment variables\r\n"); - } else { - logeventf(ssh, "%d environment variables refused", - s->num_env - s->env_ok); - c_write_str(ssh, "Server refused to set all environment variables\r\n"); - } - } - - /* - * Start a shell or a remote command. We may have to attempt - * this twice if the config data has provided a second choice - * of command. - */ - if (ssh->mainchan && !ssh->ncmode) while (1) { - int subsys; - char *cmd; - - if (ssh->fallback_cmd) { - subsys = ssh->cfg.ssh_subsys2; - cmd = ssh->cfg.remote_cmd_ptr2; - } else { - subsys = ssh->cfg.ssh_subsys; - cmd = ssh->cfg.remote_cmd_ptr; - if (!cmd) cmd = ssh->cfg.remote_cmd; - } - - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */ - if (subsys) { - ssh2_pkt_addstring(s->pktout, "subsystem"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, cmd); - } else if (*cmd) { - ssh2_pkt_addstring(s->pktout, "exec"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, cmd); - } else { - ssh2_pkt_addstring(s->pktout, "shell"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - } - ssh2_pkt_send(ssh, s->pktout); - - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to shell/command request:" - " packet type %d", pktin->type)); + /* + * We failed to start the command. If this is the + * fallback command, we really are finished; if it's + * not, and if the fallback command exists, try falling + * back to it before complaining. + */ + if (!ssh->fallback_cmd && + *conf_get_str(ssh->conf, CONF_remote_cmd2)) { + logevent("Primary command failed; attempting fallback"); + ssh->fallback_cmd = TRUE; + continue; + } + bombout(("Server refused to start a shell/command")); crStopV; + } else { + logevent("Started a shell/command"); } - /* - * We failed to start the command. If this is the - * fallback command, we really are finished; if it's - * not, and if the fallback command exists, try falling - * back to it before complaining. - */ - if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) { - logevent("Primary command failed; attempting fallback"); - ssh->fallback_cmd = TRUE; - continue; - } - bombout(("Server refused to start a shell/command")); - crStopV; - } else { - logevent("Started a shell/command"); + break; } - break; + } else { + ssh->editing = ssh->echoing = TRUE; } ssh->state = SSH_STATE_SESSION; @@ -9083,13 +9390,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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! */ if (ssh->ldisc) @@ -9175,6 +9475,25 @@ static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Remote debug message: %.*s", msglen, msg); } +static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_transport(ssh, NULL, 0, pktin); +} + +/* + * Called if we receive a packet that isn't allowed by the protocol. + * This only applies to packets whose meaning PuTTY understands. + * Entirely unknown packets are handled below. + */ +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin) +{ + char *buf = dupprintf("Server protocol violation: unexpected %s packet", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type)); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); +} + static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) { struct Packet *pktout; @@ -9201,60 +9520,62 @@ static void ssh2_protocol_setup(Ssh ssh) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* - * Any message we actually understand, we set to NULL so that - * the coroutines will get it. + * Initially, we only accept transport messages (and a few generic + * ones). do_ssh2_authconn will add more when it starts. + * Messages that are understood but not currently acceptable go to + * ssh2_msg_unexpected. */ - ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXINIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = NULL; + ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_KEXINIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = ssh2_msg_transport; + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = ssh2_msg_transport; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = ssh2_msg_transport; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_unexpected; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_unexpected; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_unexpected; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected; /* - * These special message types we install handlers for. + * These messages have a special handler from the start. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */ ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } -static void ssh2_timer(void *ctx, long now) +static void ssh2_timer(void *ctx, unsigned long now) { Ssh ssh = (Ssh)ctx; if (ssh->state == SSH_STATE_CLOSED) return; - if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 && - now - ssh->next_rekey >= 0) { + if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 && + now == ssh->next_rekey) { do_ssh2_transport(ssh, "timeout", -1, NULL); } } @@ -9274,24 +9595,17 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen, do_ssh2_transport(ssh, "too much data received", -1, NULL); } - if (pktin && ssh->packet_dispatch[pktin->type]) { + if (pktin) ssh->packet_dispatch[pktin->type](ssh, pktin); - return; - } - - if (!ssh->protocol_initial_phase_done || - (pktin && pktin->type >= 20 && pktin->type < 50)) { - if (do_ssh2_transport(ssh, in, inlen, pktin) && - !ssh->protocol_initial_phase_done) { - ssh->protocol_initial_phase_done = TRUE; - /* - * Allow authconn to initialise itself. - */ - do_ssh2_authconn(ssh, NULL, 0, NULL); - } - } else { + else if (!ssh->protocol_initial_phase_done) + do_ssh2_transport(ssh, in, inlen, pktin); + else do_ssh2_authconn(ssh, in, inlen, pktin); - } +} + +static void ssh_cache_conf_values(Ssh ssh) +{ + ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata); } /* @@ -9300,15 +9614,15 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen, * Returns an error message, or NULL on success. */ static const char *ssh_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive) + Conf *conf, char *host, int port, char **realhost, + int nodelay, int keepalive) { const char *p; Ssh ssh; ssh = snew(struct ssh_tag); - ssh->cfg = *cfg; /* STRUCTURE COPY */ + ssh->conf = conf_copy(conf); + ssh_cache_conf_values(ssh); ssh->version = 0; /* when not ready yet */ ssh->s = NULL; ssh->cipher = NULL; @@ -9348,12 +9662,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; ssh->ssh2_rdpkt_crstate = 0; - ssh->do_ssh_init_crstate = 0; ssh->ssh_gotdata_crstate = 0; ssh->do_ssh1_connection_crstate = 0; - ssh->do_ssh1_login_crstate = 0; - ssh->do_ssh2_transport_crstate = 0; - ssh->do_ssh2_authconn_crstate = 0; ssh->do_ssh_init_state = NULL; ssh->do_ssh1_login_state = NULL; ssh->do_ssh2_transport_state = NULL; @@ -9370,6 +9680,9 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->deferred_rekey_reason = NULL; bufchain_init(&ssh->queued_incoming_data); ssh->frozen = FALSE; + ssh->username = NULL; + ssh->sent_console_eof = FALSE; + ssh->got_pty = FALSE; *backend_handle = ssh; @@ -9379,8 +9692,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, #endif ssh->frontend = frontend_handle; - ssh->term_width = ssh->cfg.width; - ssh->term_height = ssh->cfg.height; + ssh->term_width = conf_get_int(ssh->conf, CONF_width); + ssh->term_height = conf_get_int(ssh->conf, CONF_height); ssh->channels = NULL; ssh->rportfwds = NULL; @@ -9401,7 +9714,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->incoming_data_size = ssh->outgoing_data_size = ssh->deferred_data_size = 0L; - ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data); + ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, + CONF_ssh_rekey_data)); ssh->kex_in_progress = FALSE; #ifndef NO_GSSAPI @@ -9456,7 +9770,7 @@ static void ssh_free(void *handle) while (ssh->qhead) { struct queued_handler *qh = ssh->qhead; ssh->qhead = qh->next; - sfree(ssh->qhead); + sfree(qh); } ssh->qhead = ssh->qtail = NULL; @@ -9473,6 +9787,17 @@ static void ssh_free(void *handle) pfd_close(c->u.pfd.s); break; } + if (ssh->version == 2) { + struct outstanding_channel_request *ocr, *nocr; + ocr = c->v.v2.chanreq_head; + while (ocr) { + ocr->handler(c, NULL, ocr->ctx); + nocr = ocr->next; + sfree(ocr); + ocr = nocr; + } + bufchain_clear(&c->v.v2.outbuffer); + } sfree(c); } freetree234(ssh->channels); @@ -9505,6 +9830,8 @@ static void ssh_free(void *handle) if (ssh->pinger) pinger_free(ssh->pinger); bufchain_clear(&ssh->queued_incoming_data); + sfree(ssh->username); + conf_free(ssh->conf); #ifndef NO_GSSAPI if (ssh->gsslibs) ssh_gss_cleanup(ssh->gsslibs); @@ -9517,22 +9844,24 @@ static void ssh_free(void *handle) /* * Reconfigure the SSH backend. */ -static void ssh_reconfig(void *handle, Config *cfg) +static void ssh_reconfig(void *handle, Conf *conf) { Ssh ssh = (Ssh) handle; char *rekeying = NULL, rekey_mandatory = FALSE; unsigned long old_max_data_size; + int i, rekey_time; - pinger_reconfig(ssh->pinger, &ssh->cfg, cfg); + pinger_reconfig(ssh->pinger, ssh->conf, conf); if (ssh->portfwds) - ssh_setup_portfwd(ssh, cfg); + ssh_setup_portfwd(ssh, conf); - if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time && - cfg->ssh_rekey_time != 0) { - long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC; - long now = GETTICKCOUNT(); + rekey_time = conf_get_int(conf, CONF_ssh_rekey_time); + if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time && + rekey_time != 0) { + unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC; + unsigned long now = GETTICKCOUNT(); - if (new_next - now < 0) { + if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) { rekeying = "timeout shortened"; } else { ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh); @@ -9540,7 +9869,8 @@ static void ssh_reconfig(void *handle, Config *cfg) } old_max_data_size = ssh->max_data_size; - ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data); + ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf, + CONF_ssh_rekey_data)); if (old_max_data_size != ssh->max_data_size && ssh->max_data_size != 0) { if (ssh->outgoing_data_size > ssh->max_data_size || @@ -9548,19 +9878,27 @@ static void ssh_reconfig(void *handle, Config *cfg) rekeying = "data limit lowered"; } - if (ssh->cfg.compression != cfg->compression) { + if (conf_get_int(ssh->conf, CONF_compression) != + conf_get_int(conf, CONF_compression)) { rekeying = "compression setting changed"; rekey_mandatory = TRUE; } - if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc || - memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist, - sizeof(ssh->cfg.ssh_cipherlist))) { + for (i = 0; i < CIPHER_MAX; i++) + if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) != + conf_get_int_int(conf, CONF_ssh_cipherlist, i)) { + rekeying = "cipher settings changed"; + rekey_mandatory = TRUE; + } + if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) != + conf_get_int(conf, CONF_ssh2_des_cbc)) { rekeying = "cipher settings changed"; rekey_mandatory = TRUE; } - ssh->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(ssh->conf); + ssh->conf = conf_copy(conf); + ssh_cache_conf_values(ssh); if (rekeying) { if (!ssh->kex_in_progress) { @@ -9608,7 +9946,7 @@ static int ssh_sendbuffer(void *handle) if (ssh->version == 1) { return override_value; } else if (ssh->version == 2) { - if (!ssh->mainchan || ssh->mainchan->closes > 0) + if (!ssh->mainchan) return override_value; else return (override_value + @@ -9638,17 +9976,15 @@ static void ssh_size(void *handle, int width, int height) ssh->size_needed = TRUE; /* buffer for later */ break; case SSH_STATE_SESSION: - if (!ssh->cfg.nopty) { + if (!conf_get_int(ssh->conf, CONF_nopty)) { if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_WINDOW_SIZE, PKT_INT, ssh->term_height, PKT_INT, ssh->term_width, PKT_INT, 0, PKT_INT, 0, PKT_END); } else if (ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "window-change"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "window-change", + NULL, NULL); ssh2_pkt_adduint32(pktout, ssh->term_width); ssh2_pkt_adduint32(pktout, ssh->term_height); ssh2_pkt_adduint32(pktout, 0); @@ -9757,9 +10093,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_EOF, PKT_END); } else if (ssh->mainchan) { - struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_send(ssh, pktout); + sshfwd_write_eof(ssh->mainchan); ssh->send_ok = 0; /* now stop trying to read from stdin */ } logevent("Sent EOF message"); @@ -9786,10 +10120,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (ssh->version == 1) { logevent("Unable to send BREAK signal in SSH-1"); } else if (ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "break"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "break", NULL, NULL); ssh2_pkt_adduint32(pktout, 0); /* default break length */ ssh2_pkt_send(ssh, pktout); } @@ -9814,10 +10145,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (signame) { /* It's a signal. */ if (ssh->version == 2 && ssh->mainchan) { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(pktout, "signal"); - ssh2_pkt_addbool(pktout, 0); + pktout = ssh2_chanreq_init(ssh->mainchan, "signal", NULL, NULL); ssh2_pkt_addstring(pktout, signame); ssh2_pkt_send(ssh, pktout); logeventf(ssh, "Sent signal SIG%s", signame); @@ -9862,7 +10190,7 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh2_set_window(ssh->mainchan, bufsize < ssh->mainchan->v.v2.locmaxwin ? ssh->mainchan->v.v2.locmaxwin - bufsize : 0); - if (ssh->cfg.ssh_simple) + if (conf_get_int(ssh->conf, CONF_ssh_simple)) buflimit = 0; else buflimit = ssh->mainchan->v.v2.locmaxwin; @@ -9872,6 +10200,12 @@ static void ssh_unthrottle(void *handle, int bufsize) } } } + + /* + * Now process any SSH connection data that was stashed in our + * queue while we were frozen. + */ + ssh_process_queued_incoming_data(ssh); } void ssh_send_port_open(void *channel, char *hostname, int port, char *org) @@ -9880,7 +10214,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) Ssh ssh = c->ssh; struct Packet *pktout; - logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port); + logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org); if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_PORT_OPEN, @@ -9890,11 +10224,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) /* PKT_STR, , */ PKT_END); } else { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); - ssh2_pkt_addstring(pktout, "direct-tcpip"); - ssh2_pkt_adduint32(pktout, c->localid); - ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ - ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + pktout = ssh2_chanopen_init(c, "direct-tcpip"); ssh2_pkt_addstring(pktout, hostname); ssh2_pkt_adduint32(pktout, port); /* diff --git a/putty/SSH.H b/putty/SSH.H index 605b608..55ed7d2 100644 --- a/putty/SSH.H +++ b/putty/SSH.H @@ -9,8 +9,9 @@ struct ssh_channel; -extern void sshfwd_close(struct ssh_channel *c); extern int sshfwd_write(struct ssh_channel *c, char *, int); +extern void sshfwd_write_eof(struct ssh_channel *c); +extern void sshfwd_unclean_close(struct ssh_channel *c); extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize); /* @@ -132,9 +133,9 @@ typedef struct { uint32 lenhi, lenlo; } SHA_State; void SHA_Init(SHA_State * s); -void SHA_Bytes(SHA_State * s, void *p, int len); +void SHA_Bytes(SHA_State * s, const void *p, int len); void SHA_Final(SHA_State * s, unsigned char *output); -void SHA_Simple(void *p, int len, unsigned char *output); +void SHA_Simple(const void *p, int len, unsigned char *output); void hmac_sha1_simple(void *key, int keylen, void *data, int datalen, unsigned char *output); @@ -296,6 +297,7 @@ extern const struct ssh_mac ssh_hmac_sha1; extern const struct ssh_mac ssh_hmac_sha1_buggy; extern const struct ssh_mac ssh_hmac_sha1_96; extern const struct ssh_mac ssh_hmac_sha1_96_buggy; +extern const struct ssh_mac ssh_hmac_sha256; void *aes_make_context(void); void aes_free_context(void *handle); @@ -334,16 +336,15 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org); /* Exports from portfwd.c */ extern const char *pfd_newconnect(Socket * s, char *hostname, int port, - void *c, const Config *cfg, - int addressfamily); + void *c, Conf *conf, int addressfamily); /* desthost == NULL indicates dynamic (SOCKS) port forwarding */ extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr, - int port, void *backhandle, - const Config *cfg, void **sockdata, - int address_family); + int port, void *backhandle, Conf *conf, + void **sockdata, int address_family); extern void pfd_close(Socket s); extern void pfd_terminate(void *sockdata); extern int pfd_send(Socket s, char *data, int len); +extern void pfd_send_eof(Socket s); extern void pfd_confirm(Socket s); extern void pfd_unthrottle(Socket s); extern void pfd_override_throttle(Socket s, int enable); @@ -393,18 +394,18 @@ struct X11Display { * details are looked up by calling platform_get_x11_auth. */ extern struct X11Display *x11_setup_display(char *display, int authtype, - const Config *); + Conf *); void x11_free_display(struct X11Display *disp); extern const char *x11_init(Socket *, struct X11Display *, void *, - const char *, int, const Config *); + const char *, int, Conf *); extern void x11_close(Socket); extern int x11_send(Socket, char *, int); +extern void x11_send_eof(Socket s); extern void x11_unthrottle(Socket s); extern void x11_override_throttle(Socket s, int enable); char *x11_display(const char *display); /* Platform-dependent X11 functions */ -extern void platform_get_x11_auth(struct X11Display *display, - const Config *); +extern void platform_get_x11_auth(struct X11Display *display, Conf *); /* examine a mostly-filled-in X11Display and fill in localauth* */ extern const int platform_uses_x11_unix_by_default; /* choose default X transport in the absence of a specified one */ @@ -550,7 +551,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam); Bignum primegen(int bits, int modulus, int residue, Bignum factor, - int phase, progfn_t pfn, void *pfnparam); + int phase, progfn_t pfn, void *pfnparam, unsigned firstbits); +void invent_firstbits(unsigned *one, unsigned *two); /* diff --git a/putty/SSHAES.C b/putty/SSHAES.C index 7684cd9..c4b1986 100644 --- a/putty/SSHAES.C +++ b/putty/SSHAES.C @@ -1157,7 +1157,7 @@ void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_encrypt_cbc(blk, len, &ctx); - memset(&ctx, 0, sizeof(ctx)); + smemclr(&ctx, sizeof(ctx)); } void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) @@ -1166,7 +1166,7 @@ void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) aes_setup(&ctx, 16, key, 32); memset(ctx.iv, 0, sizeof(ctx.iv)); aes_decrypt_cbc(blk, len, &ctx); - memset(&ctx, 0, sizeof(ctx)); + smemclr(&ctx, sizeof(ctx)); } static const struct ssh2_cipher ssh_aes128_ctr = { diff --git a/putty/SSHARCF.C b/putty/SSHARCF.C index e0b247e..3f4376c 100644 --- a/putty/SSHARCF.C +++ b/putty/SSHARCF.C @@ -75,7 +75,7 @@ static void arcfour_stir(ArcfourContext *ctx) unsigned char *junk = snewn(1536, unsigned char); memset(junk, 0, 1536); arcfour_block(ctx, junk, 1536); - memset(junk, 0, 1536); + smemclr(junk, 1536); sfree(junk); } diff --git a/putty/SSHBN.C b/putty/SSHBN.C index 51cecdf..f9657b7 100644 --- a/putty/SSHBN.C +++ b/putty/SSHBN.C @@ -6,6 +6,7 @@ #include #include #include +#include #include "misc.h" @@ -120,7 +121,11 @@ Bignum Zero = bnZero, One = bnOne; static Bignum newbn(int length) { - Bignum b = snewn(length + 1, BignumInt); + Bignum b; + + assert(length >= 0 && length < INT_MAX / BIGNUM_INT_BITS); + + b = snewn(length + 1, BignumInt); if (!b) abort(); /* FIXME */ memset(b, 0, (length + 1) * sizeof(*b)); @@ -148,13 +153,17 @@ void freebn(Bignum b) /* * Burn the evidence, just in case. */ - memset(b, 0, sizeof(b[0]) * (b[0] + 1)); + smemclr(b, sizeof(b[0]) * (b[0] + 1)); sfree(b); } Bignum bn_power_2(int n) { - Bignum ret = newbn(n / BIGNUM_INT_BITS + 1); + Bignum ret; + + assert(n >= 0); + + ret = newbn(n / BIGNUM_INT_BITS + 1); bignum_set_bit(ret, n, 1); return ret; } @@ -598,6 +607,7 @@ static void internal_add_shifted(BignumInt *number, addend = (BignumDblInt)n << bshift; while (addend) { + assert(word <= number[0]); addend += number[word]; number[word] = (BignumInt) addend & BIGNUM_INT_MASK; addend >>= BIGNUM_INT_BITS; @@ -624,6 +634,7 @@ static void internal_mod(BignumInt *a, int alen, int i, k; m0 = m[0]; + assert(m0 >> (BIGNUM_INT_BITS-1) == 1); if (mlen > 1) m1 = m[1]; else @@ -815,20 +826,15 @@ Bignum modpow_simple(Bignum base_in, Bignum exp, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < 2 * mlen; i++) - a[i] = 0; + smemclr(a, 2 * mlen * sizeof(*a)); sfree(a); - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * mlen; i++) - b[i] = 0; + smemclr(b, 2 * mlen * sizeof(*b)); sfree(b); - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < mlen; i++) - n[i] = 0; + smemclr(n, mlen * sizeof(*n)); sfree(n); freebn(base); @@ -873,6 +879,7 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod) len = mod[0]; r = bn_power_2(BIGNUM_INT_BITS * len); inv = modinv(mod, r); + assert(inv); /* cannot fail, since mod is odd and r is a power of 2 */ /* * Multiply the base by r mod n, to get it into Montgomery @@ -965,23 +972,17 @@ Bignum modpow(Bignum base_in, Bignum exp, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * len; i++) - a[i] = 0; + smemclr(a, 2 * len * sizeof(*a)); sfree(a); - for (i = 0; i < 2 * len; i++) - b[i] = 0; + smemclr(b, 2 * len * sizeof(*b)); sfree(b); - for (i = 0; i < len; i++) - mninv[i] = 0; + smemclr(mninv, len * sizeof(*mninv)); sfree(mninv); - for (i = 0; i < len; i++) - n[i] = 0; + smemclr(n, len * sizeof(*n)); sfree(n); - for (i = 0; i < len; i++) - x[i] = 0; + smemclr(x, len * sizeof(*x)); sfree(x); return result; @@ -999,6 +1000,12 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) int pqlen, mlen, rlen, i, j; Bignum result; + /* + * The most significant word of mod needs to be non-zero. It + * should already be, but let's make sure. + */ + assert(mod[mod[0]] != 0); + /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; @@ -1018,6 +1025,13 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) pqlen = (p[0] > q[0] ? p[0] : q[0]); + /* + * Make sure that we're allowing enough space. The shifting below + * will underflow the vectors we allocate if pqlen is too small. + */ + if (2*pqlen <= mlen) + pqlen = mlen/2 + 1; + /* Allocate n of size pqlen, copy p to n */ n = snewn(pqlen, BignumInt); i = pqlen - p[0]; @@ -1064,20 +1078,15 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) result[0]--; /* Free temporary arrays */ - for (i = 0; i < scratchlen; i++) - scratch[i] = 0; + smemclr(scratch, scratchlen * sizeof(*scratch)); sfree(scratch); - for (i = 0; i < 2 * pqlen; i++) - a[i] = 0; + smemclr(a, 2 * pqlen * sizeof(*a)); sfree(a); - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < pqlen; i++) - n[i] = 0; + smemclr(n, pqlen * sizeof(*n)); sfree(n); - for (i = 0; i < pqlen; i++) - o[i] = 0; + smemclr(o, pqlen * sizeof(*o)); sfree(o); return result; @@ -1096,6 +1105,12 @@ static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) int mshift; int plen, mlen, i, j; + /* + * The most significant word of mod needs to be non-zero. It + * should already be, but let's make sure. + */ + assert(mod[mod[0]] != 0); + /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; @@ -1147,11 +1162,9 @@ static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) } /* Free temporary arrays */ - for (i = 0; i < mlen; i++) - m[i] = 0; + smemclr(m, mlen * sizeof(*m)); sfree(m); - for (i = 0; i < plen; i++) - n[i] = 0; + smemclr(n, plen * sizeof(*n)); sfree(n); } @@ -1171,6 +1184,8 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes) Bignum result; int w, i; + assert(nbytes >= 0 && nbytes < INT_MAX/8); + w = (nbytes + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES; /* bytes->words */ result = newbn(w); @@ -1247,7 +1262,7 @@ int ssh2_bignum_length(Bignum bn) */ int bignum_byte(Bignum bn, int i) { - if (i >= (int)(BIGNUM_INT_BYTES * bn[0])) + if (i < 0 || i >= (int)(BIGNUM_INT_BYTES * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BYTES + 1] >> @@ -1259,7 +1274,7 @@ int bignum_byte(Bignum bn, int i) */ int bignum_bit(Bignum bn, int i) { - if (i >= (int)(BIGNUM_INT_BITS * bn[0])) + if (i < 0 || i >= (int)(BIGNUM_INT_BITS * bn[0])) return 0; /* beyond the end */ else return (bn[i / BIGNUM_INT_BITS + 1] >> (i % BIGNUM_INT_BITS)) & 1; @@ -1270,7 +1285,7 @@ int bignum_bit(Bignum bn, int i) */ void bignum_set_bit(Bignum bn, int bitnum, int value) { - if (bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) + if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) abort(); /* beyond the end */ else { int v = bitnum / BIGNUM_INT_BITS + 1; @@ -1306,7 +1321,18 @@ int ssh1_write_bignum(void *data, Bignum bn) int bignum_cmp(Bignum a, Bignum b) { int amax = a[0], bmax = b[0]; - int i = (amax > bmax ? amax : bmax); + int i; + + /* Annoyingly we have two representations of zero */ + if (amax == 1 && a[amax] == 0) + amax = 0; + if (bmax == 1 && b[bmax] == 0) + bmax = 0; + + assert(amax == 0 || a[amax] != 0); + assert(bmax == 0 || b[bmax] != 0); + + i = (amax > bmax ? amax : bmax); while (i) { BignumInt aval = (i > amax ? 0 : a[i]); BignumInt bval = (i > bmax ? 0 : b[i]); @@ -1328,6 +1354,8 @@ Bignum bignum_rshift(Bignum a, int shift) int i, shiftw, shiftb, shiftbb, bits; BignumInt ai, ai1; + assert(shift >= 0); + bits = bignum_bitcount(a) - shift; ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS); @@ -1398,8 +1426,7 @@ Bignum bigmuladd(Bignum a, Bignum b, Bignum addend) } ret[0] = maxspot; - for (i = 0; i < wslen; i++) - workspace[i] = 0; + smemclr(workspace, wslen * sizeof(*workspace)); sfree(workspace); return ret; } @@ -1629,9 +1656,26 @@ Bignum modinv(Bignum number, Bignum modulus) Bignum x = copybn(One); int sign = +1; + assert(number[number[0]] != 0); + assert(modulus[modulus[0]] != 0); + while (bignum_cmp(b, One) != 0) { - Bignum t = newbn(b[0]); - Bignum q = newbn(a[0]); + Bignum t, q; + + if (bignum_cmp(b, Zero) == 0) { + /* + * Found a common factor between the inputs, so we cannot + * return a modular inverse at all. + */ + freebn(b); + freebn(a); + freebn(xp); + freebn(x); + return NULL; + } + + t = newbn(b[0]); + q = newbn(a[0]); bigdivmod(a, b, t, q); while (t[0] > 1 && t[t[0]] == 0) t[0]--; @@ -1750,6 +1794,7 @@ char *bignum_decimal(Bignum x) /* * Done. */ + smemclr(workspace, x[0] * sizeof(*workspace)); sfree(workspace); return ret; } @@ -1761,7 +1806,7 @@ char *bignum_decimal(Bignum x) #include /* - * gcc -g -O0 -DTESTBN -o testbn sshbn.c misc.c -I unix -I charset + * gcc -Wall -g -O0 -DTESTBN -o testbn sshbn.c misc.c conf.c tree234.c unix/uxmisc.c -I. -I unix -I charset * * Then feed to this program's standard input the output of * testdata/bignum.py . @@ -1835,7 +1880,7 @@ int main(int argc, char **argv) Bignum a, b, c, p; if (ptrnum != 3) { - printf("%d: mul with %d parameters, expected 3\n", line); + printf("%d: mul with %d parameters, expected 3\n", line, ptrnum); exit(1); } a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); @@ -1864,11 +1909,49 @@ int main(int argc, char **argv) freebn(b); freebn(c); freebn(p); + } else if (!strcmp(buf, "modmul")) { + Bignum a, b, m, c, p; + + if (ptrnum != 4) { + printf("%d: modmul with %d parameters, expected 4\n", + line, ptrnum); + exit(1); + } + a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]); + b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]); + m = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]); + c = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]); + p = modmul(a, b, m); + + if (bignum_cmp(c, p) == 0) { + passes++; + } else { + char *as = bignum_decimal(a); + char *bs = bignum_decimal(b); + char *ms = bignum_decimal(m); + char *cs = bignum_decimal(c); + char *ps = bignum_decimal(p); + + printf("%d: fail: %s * %s mod %s gave %s expected %s\n", + line, as, bs, ms, ps, cs); + fails++; + + sfree(as); + sfree(bs); + sfree(ms); + sfree(cs); + sfree(ps); + } + freebn(a); + freebn(b); + freebn(m); + freebn(c); + freebn(p); } else if (!strcmp(buf, "pow")) { Bignum base, expt, modulus, expected, answer; if (ptrnum != 4) { - printf("%d: mul with %d parameters, expected 3\n", line); + printf("%d: mul with %d parameters, expected 4\n", line, ptrnum); exit(1); } diff --git a/putty/SSHDES.C b/putty/SSHDES.C index 0beb273..ad584f3 100644 --- a/putty/SSHDES.C +++ b/putty/SSHDES.C @@ -858,7 +858,7 @@ void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_decrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) @@ -871,7 +871,7 @@ void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]); des_3cbc_encrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, @@ -887,7 +887,7 @@ void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_decrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, @@ -903,7 +903,7 @@ void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv, ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv); ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4); des_cbc3_encrypt(blk, len, ourkeys); - memset(ourkeys, 0, sizeof(ourkeys)); + smemclr(ourkeys, sizeof(ourkeys)); } static void des_keysetup_xdmauth(unsigned char *keydata, DESContext *dc) diff --git a/putty/SSHDSS.C b/putty/SSHDSS.C index dba1db1..6ffcf6d 100644 --- a/putty/SSHDSS.C +++ b/putty/SSHDSS.C @@ -20,7 +20,7 @@ static void sha_mpint(SHA_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void sha512_mpint(SHA512_State * s, Bignum b) @@ -34,7 +34,7 @@ static void sha512_mpint(SHA512_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void getstring(char **data, int *datalen, char **p, int *length) @@ -42,7 +42,9 @@ static void getstring(char **data, int *datalen, char **p, int *length) *p = NULL; if (*datalen < 4) return; - *length = GET_32BIT(*data); + *length = toint(GET_32BIT(*data)); + if (*length < 0) + return; *datalen -= 4; *data += 4; if (*datalen < *length) @@ -70,6 +72,9 @@ static Bignum get160(char **data, int *datalen) { Bignum b; + if (*datalen < 20) + return NULL; + b = bignum_from_bytes((unsigned char *)*data, 20); *data += 20; *datalen -= 20; @@ -77,6 +82,8 @@ static Bignum get160(char **data, int *datalen) return b; } +static void dss_freekey(void *key); /* forward reference */ + static void *dss_newkey(char *data, int len) { char *p; @@ -84,8 +91,6 @@ static void *dss_newkey(char *data, int len) struct dss_key *dss; dss = snew(struct dss_key); - if (!dss) - return NULL; getstring(&data, &len, &p, &slen); #ifdef DEBUG_DSS @@ -98,7 +103,7 @@ static void *dss_newkey(char *data, int len) } #endif - if (!p || memcmp(p, "ssh-dss", 7)) { + if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { sfree(dss); return NULL; } @@ -106,6 +111,14 @@ static void *dss_newkey(char *data, int len) dss->q = getmp(&data, &len); dss->g = getmp(&data, &len); dss->y = getmp(&data, &len); + dss->x = NULL; + + if (!dss->p || !dss->q || !dss->g || !dss->y || + !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { + /* Invalid key. */ + dss_freekey(dss); + return NULL; + } return dss; } @@ -113,10 +126,16 @@ static void *dss_newkey(char *data, int len) static void dss_freekey(void *key) { struct dss_key *dss = (struct dss_key *) key; - freebn(dss->p); - freebn(dss->q); - freebn(dss->g); - freebn(dss->y); + if (dss->p) + freebn(dss->p); + if (dss->q) + freebn(dss->q); + if (dss->g) + freebn(dss->g); + if (dss->y) + freebn(dss->y); + if (dss->x) + freebn(dss->x); sfree(dss); } @@ -249,13 +268,29 @@ static int dss_verifysig(void *key, char *sig, int siglen, } r = get160(&sig, &siglen); s = get160(&sig, &siglen); - if (!r || !s) + if (!r || !s) { + if (r) + freebn(r); + if (s) + freebn(s); return 0; + } + + if (!bignum_cmp(s, Zero)) { + freebn(r); + freebn(s); + return 0; + } /* * Step 1. w <- s^-1 mod q. */ w = modinv(s, dss->q); + if (!w) { + freebn(r); + freebn(s); + return 0; + } /* * Step 2. u1 <- SHA(message) * w mod q. @@ -287,6 +322,8 @@ static int dss_verifysig(void *key, char *sig, int siglen, freebn(w); freebn(sha); + freebn(u1); + freebn(u2); freebn(gu1p); freebn(yu2p); freebn(gu1yu2p); @@ -377,7 +414,13 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, Bignum ytest; dss = dss_newkey((char *) pub_blob, pub_len); + if (!dss) + return NULL; dss->x = getmp(&pb, &priv_len); + if (!dss->x) { + dss_freekey(dss); + return NULL; + } /* * Check the obsolete hash in the old DSS key format. @@ -402,6 +445,7 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); + freebn(ytest); return NULL; } freebn(ytest); @@ -415,8 +459,6 @@ static void *dss_openssh_createkey(unsigned char **blob, int *len) struct dss_key *dss; dss = snew(struct dss_key); - if (!dss) - return NULL; dss->p = getmp(b, len); dss->q = getmp(b, len); @@ -424,14 +466,11 @@ static void *dss_openssh_createkey(unsigned char **blob, int *len) dss->y = getmp(b, len); dss->x = getmp(b, len); - if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x) { - sfree(dss->p); - sfree(dss->q); - sfree(dss->g); - sfree(dss->y); - sfree(dss->x); - sfree(dss); - return NULL; + if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x || + !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { + /* Invalid key. */ + dss_freekey(dss); + return NULL; } return dss; @@ -471,6 +510,8 @@ static int dss_pubkey_bits(void *blob, int len) int ret; dss = dss_newkey((char *) blob, len); + if (!dss) + return -1; ret = bignum_bitcount(dss->p); dss_freekey(dss); @@ -573,18 +614,34 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); SHA512_Bytes(&ss, digest, sizeof(digest)); - SHA512_Final(&ss, digest512); - memset(&ss, 0, sizeof(ss)); + while (1) { + SHA512_State ss2 = ss; /* structure copy */ + SHA512_Final(&ss2, digest512); + + smemclr(&ss2, sizeof(ss2)); + + /* + * Now convert the result into a bignum, and reduce it mod q. + */ + proto_k = bignum_from_bytes(digest512, 64); + k = bigmod(proto_k, dss->q); + freebn(proto_k); + kinv = modinv(k, dss->q); /* k^-1 mod q */ + if (!kinv) { /* very unlikely */ + freebn(k); + /* Perturb the hash to think of a different k. */ + SHA512_Bytes(&ss, "x", 1); + /* Go round and try again. */ + continue; + } + + break; + } - /* - * Now convert the result into a bignum, and reduce it mod q. - */ - proto_k = bignum_from_bytes(digest512, 64); - k = bigmod(proto_k, dss->q); - freebn(proto_k); + smemclr(&ss, sizeof(ss)); - memset(digest512, 0, sizeof(digest512)); + smemclr(digest512, sizeof(digest512)); /* * Now we have k, so just go ahead and compute the signature. @@ -594,11 +651,11 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) freebn(gkp); hash = bignum_from_bytes(digest, 20); - kinv = modinv(k, dss->q); /* k^-1 mod q */ hxr = bigmuladd(dss->x, r, hash); /* hash + x*r */ s = modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash + x*r) mod q */ freebn(hxr); freebn(kinv); + freebn(k); freebn(hash); /* diff --git a/putty/SSHDSSG.C b/putty/SSHDSSG.C index a19bc27..d0ed73c 100644 --- a/putty/SSHDSSG.C +++ b/putty/SSHDSSG.C @@ -9,6 +9,7 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam) { Bignum qm1, power, g, h, tmp; + unsigned pfirst, qfirst; int progress; /* @@ -70,15 +71,16 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, pfn(pfnparam, PROGFN_READY, 0, 0); + invent_firstbits(&pfirst, &qfirst); /* * Generate q: a prime of length 160. */ - key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam); + key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam, qfirst); /* * Now generate p: a prime of length `bits', such that p-1 is * divisible by q. */ - key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam); + key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam, pfirst); /* * Next we need g. Raise 2 to the power (p-1)/q modulo p, and diff --git a/putty/SSHGSS.H b/putty/SSHGSS.H index 5d8fca1..61f35a2 100644 --- a/putty/SSHGSS.H +++ b/putty/SSHGSS.H @@ -47,7 +47,7 @@ struct ssh_gss_liblist { struct ssh_gss_library *libraries; int nlibraries; }; -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg); +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf); void ssh_gss_cleanup(struct ssh_gss_liblist *list); /* diff --git a/putty/SSHMD5.C b/putty/SSHMD5.C index 80474df..b27d001 100644 --- a/putty/SSHMD5.C +++ b/putty/SSHMD5.C @@ -249,7 +249,7 @@ void hmacmd5_key(void *handle, void const *keyv, int len) MD5Init(&keys[1]); MD5Update(&keys[1], foo, 64); - memset(foo, 0, 64); /* burn the evidence */ + smemclr(foo, 64); /* burn the evidence */ } static void hmacmd5_key_16(void *handle, unsigned char *key) @@ -312,11 +312,7 @@ static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len, { unsigned char seqbuf[16]; - seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF); - seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF); - seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF); - seqbuf[3] = (unsigned char) ((seq) & 0xFF); - + PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac); } diff --git a/putty/SSHNOGSS.C b/putty/SSHNOGSS.C index 27ec760..2aff9c1 100644 --- a/putty/SSHNOGSS.C +++ b/putty/SSHNOGSS.C @@ -3,7 +3,7 @@ /* For platforms not supporting GSSAPI */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *); list->libraries = NULL; diff --git a/putty/SSHPRIME.C b/putty/SSHPRIME.C index e283b52..8cd2391 100644 --- a/putty/SSHPRIME.C +++ b/putty/SSHPRIME.C @@ -123,1061 +123,706 @@ * for i in list[1:]: sys.stdout.write("%d," % i) */ static const unsigned short primes[] = { - 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, - 71, 73, 79, 83, 89, 97, 101, - 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, - 277, 281, 283, 293, - 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, - 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, - 499, 503, 509, 521, - 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, - 617, 619, 631, 641, - 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, - 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, - 859, 863, 877, 881, - 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, - 991, 997, 1009, - 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, - 1091, 1093, - 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, - 1193, 1201, - 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, - 1291, 1297, - 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, - 1423, 1427, - 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, - 1493, 1499, - 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, - 1601, 1607, - 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, - 1699, 1709, - 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, - 1811, 1823, - 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, - 1931, 1933, - 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, - 2029, 2039, - 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, - 2137, 2141, - 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, - 2267, 2269, - 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, - 2357, 2371, - 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, - 2459, 2467, - 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, - 2593, 2609, - 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, - 2693, 2699, - 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, - 2791, 2797, - 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, - 2903, 2909, - 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, - 3023, 3037, - 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, - 3167, 3169, - 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, - 3271, 3299, - 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, - 3373, 3389, - 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, - 3511, 3517, - 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, - 3607, 3613, - 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, - 3709, 3719, - 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, - 3833, 3847, - 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, - 3931, 3943, - 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, - 4057, 4073, - 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, - 4177, 4201, - 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, - 4283, 4289, - 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, - 4423, 4441, - 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, - 4547, 4549, - 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, - 4657, 4663, - 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, - 4789, 4793, - 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, - 4931, 4933, - 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, - 5011, 5021, - 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, - 5147, 5153, - 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, - 5279, 5281, - 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, - 5413, 5417, - 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, - 5507, 5519, - 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, - 5647, 5651, - 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, - 5743, 5749, - 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, - 5857, 5861, - 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, - 6007, 6011, - 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, - 6121, 6131, - 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, - 6247, 6257, - 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, - 6343, 6353, - 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, - 6473, 6481, - 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, - 6607, 6619, - 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, - 6733, 6737, - 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, - 6857, 6863, - 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, - 6971, 6977, - 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, - 7103, 7109, - 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, - 7229, 7237, - 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, - 7369, 7393, - 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, - 7517, 7523, - 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, - 7603, 7607, - 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, - 7723, 7727, - 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, - 7873, 7877, - 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, - 8009, 8011, - 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, - 8123, 8147, - 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, - 8263, 8269, - 8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, - 8387, 8389, - 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, - 8537, 8539, - 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, - 8663, 8669, - 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, - 8753, 8761, - 8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, - 8867, 8887, - 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, - 9011, 9013, - 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, - 9151, 9157, - 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241, 9257, - 9277, 9281, - 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, - 9397, 9403, - 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, - 9491, 9497, - 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, - 9631, 9643, - 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 9739, 9743, 9749, - 9767, 9769, - 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, - 9871, 9883, + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, + 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, + 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, + 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, + 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, + 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, + 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, + 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, + 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, + 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, + 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, + 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, + 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, + 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, + 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, + 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, + 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, + 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, + 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, + 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, + 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, + 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, + 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, + 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, + 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, + 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, + 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, + 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, + 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, + 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, + 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, + 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, + 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, + 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, + 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, + 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, + 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, + 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, + 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, + 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, + 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, + 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, + 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, + 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, + 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, + 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, + 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, + 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, + 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, + 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, + 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, + 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, + 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, + 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, + 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, + 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, + 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, + 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, + 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, + 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, + 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, + 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, + 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, + 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, + 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, + 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, + 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, + 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, + 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, + 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, + 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, + 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, + 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, + 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, + 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, + 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, + 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, + 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, + 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, + 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, + 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, + 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, + 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, + 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, + 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, + 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, + 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, + 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, + 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, + 8647, 8663, 8669, 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, + 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, + 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, + 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, + 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, + 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, + 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, + 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, 9397, 9403, 9413, + 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, + 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, + 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, + 9719, 9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, + 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, - 10009, 10037, - 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, - 10133, 10139, - 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, - 10243, 10247, - 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, - 10331, 10333, - 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, - 10457, 10459, - 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, - 10589, 10597, - 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, - 10687, 10691, - 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, - 10799, 10831, - 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, - 10909, 10937, - 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, - 11057, 11059, - 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, - 11159, 11161, - 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, - 11273, 11279, - 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, - 11393, 11399, - 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, - 11497, 11503, - 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, - 11633, 11657, - 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, - 11779, 11783, - 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, - 11867, 11887, - 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, - 11969, 11971, - 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, - 12097, 12101, - 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, - 12203, 12211, - 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, - 12301, 12323, - 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, - 12421, 12433, - 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, - 12517, 12527, - 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, - 12613, 12619, - 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, - 12721, 12739, - 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, - 12841, 12853, - 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, - 12959, 12967, - 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, - 13049, 13063, - 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163, - 13171, 13177, - 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, - 13297, 13309, - 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, - 13417, 13421, - 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523, - 13537, 13553, - 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, - 13679, 13681, - 13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, - 13757, 13759, - 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, - 13877, 13879, - 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, - 13997, 13999, - 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, - 14107, 14143, - 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, - 14251, 14281, - 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, - 14401, 14407, - 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, - 14503, 14519, - 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, - 14621, 14627, - 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717, - 14723, 14731, - 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, - 14813, 14821, - 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897, - 14923, 14929, - 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, - 15061, 15073, - 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, - 15161, 15173, - 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, - 15271, 15277, - 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, - 15361, 15373, - 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, - 15467, 15473, - 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, - 15601, 15607, - 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, - 15683, 15727, - 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, - 15797, 15803, - 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, - 15913, 15919, - 15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057, - 16061, 16063, - 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, - 16141, 16183, - 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, - 16273, 16301, - 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, - 16421, 16427, - 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, - 16547, 16553, - 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651, - 16657, 16661, - 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, - 16787, 16811, - 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, - 16921, 16927, - 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021, - 17027, 17029, - 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, - 17137, 17159, - 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, - 17291, 17293, - 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, - 17387, 17389, - 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, - 17483, 17489, - 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, - 17597, 17599, - 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, - 17729, 17737, - 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, - 17851, 17863, - 17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, - 17959, 17971, - 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, - 18061, 18077, - 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, 18149, 18169, - 18181, 18191, - 18199, 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, - 18287, 18289, - 18301, 18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379, - 18397, 18401, - 18413, 18427, 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, - 18503, 18517, - 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, - 18661, 18671, - 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773, - 18787, 18793, - 18797, 18803, 18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, - 18947, 18959, - 18973, 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, - 19079, 19081, - 19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, - 19213, 19219, - 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, - 19333, 19373, - 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, - 19433, 19441, - 19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489, 19501, 19507, - 19531, 19541, - 19543, 19553, 19559, 19571, 19577, 19583, 19597, 19603, 19609, 19661, - 19681, 19687, - 19697, 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, - 19777, 19793, - 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, - 19913, 19919, - 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, 19993, 19997, - 20011, 20021, - 20023, 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113, - 20117, 20123, - 20129, 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, - 20231, 20233, - 20249, 20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, - 20353, 20357, - 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, - 20477, 20479, - 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593, - 20599, 20611, - 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719, 20731, - 20743, 20747, - 20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, - 20873, 20879, - 20887, 20897, 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, - 20981, 20983, - 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, - 21089, 21101, - 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, - 21191, 21193, - 21211, 21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319, - 21323, 21341, - 21347, 21377, 21379, 21383, 21391, 21397, 21401, 21407, 21419, 21433, - 21467, 21481, - 21487, 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, - 21559, 21563, - 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, - 21649, 21661, - 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, - 21773, 21787, - 21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871, - 21881, 21893, - 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003, 22013, - 22027, 22031, - 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, - 22111, 22123, - 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, - 22247, 22259, - 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, 22343, 22349, - 22367, 22369, - 22381, 22391, 22397, 22409, 22433, 22441, 22447, 22453, 22469, 22481, - 22483, 22501, - 22511, 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, - 22621, 22637, - 22639, 22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, - 22721, 22727, - 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, - 22853, 22859, - 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, - 22973, 22993, - 23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, - 23059, 23063, - 23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173, - 23189, 23197, - 23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, - 23311, 23321, - 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, - 23459, 23473, - 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, 23563, 23567, - 23581, 23593, - 23599, 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, - 23677, 23687, - 23689, 23719, 23741, 23743, 23747, 23753, 23761, 23767, 23773, 23789, - 23801, 23813, - 23819, 23827, 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, - 23899, 23909, - 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, - 24019, 24023, - 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, - 24107, 24109, - 24113, 24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203, - 24223, 24229, - 24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, - 24379, 24391, - 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, - 24509, 24517, - 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, - 24671, 24677, - 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793, - 24799, 24809, - 24821, 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917, 24919, - 24923, 24943, - 24953, 24967, 24971, 24977, 24979, 24989, 25013, 25031, 25033, 25037, - 25057, 25073, - 25087, 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, - 25171, 25183, - 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, - 25307, 25309, - 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, 25409, 25411, - 25423, 25439, - 25447, 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, - 25577, 25579, - 25583, 25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, - 25667, 25673, - 25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, - 25793, 25799, - 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, - 25919, 25931, - 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017, - 26021, 26029, - 26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119, 26141, 26153, - 26161, 26171, - 26177, 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261, - 26263, 26267, - 26293, 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, - 26393, 26399, - 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, - 26501, 26513, - 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, - 26669, 26681, - 26683, 26687, 26693, 26699, 26701, 26711, 26713, 26717, 26723, 26729, - 26731, 26737, - 26759, 26777, 26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861, - 26863, 26879, - 26881, 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, - 26981, 26987, - 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, - 27091, 27103, - 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241, - 27253, 27259, - 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397, - 27407, 27409, - 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527, - 27529, 27539, - 27541, 27551, 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, - 27689, 27691, - 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, - 27773, 27779, - 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847, 27851, - 27883, 27893, - 27901, 27917, 27919, 27941, 27943, 27947, 27953, 27961, 27967, 27983, - 27997, 28001, - 28019, 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, - 28109, 28111, - 28123, 28151, 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, - 28279, 28283, - 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, - 28409, 28411, - 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, - 28537, 28541, - 28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607, - 28619, 28621, - 28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687, 28697, - 28703, 28711, - 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, - 28817, 28837, - 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, - 28949, 28961, - 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, 29063, 29077, - 29101, 29123, - 29129, 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201, - 29207, 29209, - 29221, 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311, 29327, - 29333, 29339, - 29347, 29363, 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, - 29437, 29443, - 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, - 29581, 29587, - 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717, - 29723, 29741, - 29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863, - 29867, 29873, - 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, - 30013, 30029, - 30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, - 30133, 30137, - 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, - 30253, 30259, - 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367, - 30389, 30391, - 30403, 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509, - 30517, 30529, - 30539, 30553, 30557, 30559, 30577, 30593, 30631, 30637, 30643, 30649, - 30661, 30671, - 30677, 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, - 30781, 30803, - 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, - 30881, 30893, - 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, - 31033, 31039, - 31051, 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, - 31151, 31153, - 31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237, - 31247, 31249, - 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, - 31337, 31357, - 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, - 31513, 31517, - 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627, - 31643, 31649, - 31657, 31663, 31667, 31687, 31699, 31721, 31723, 31727, 31729, 31741, - 31751, 31769, - 31771, 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891, - 31907, 31957, - 31963, 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, - 32059, 32063, - 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, - 32173, 32183, - 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297, - 32299, 32303, - 32309, 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371, - 32377, 32381, - 32401, 32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, - 32497, 32503, - 32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, - 32603, 32609, - 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, - 32719, 32749, - 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833, 32839, - 32843, 32869, - 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, - 32983, 32987, - 32993, 32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073, - 33083, 33091, - 33107, 33113, 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, - 33203, 33211, - 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, - 33347, 33349, - 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461, - 33469, 33479, - 33487, 33493, 33503, 33521, 33529, 33533, 33547, 33563, 33569, 33577, - 33581, 33587, - 33589, 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, - 33647, 33679, - 33703, 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, - 33791, 33797, - 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, - 33911, 33923, - 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, - 34057, 34061, - 34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183, 34211, - 34213, 34217, - 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297, 34301, 34303, - 34313, 34319, - 34327, 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, - 34439, 34457, - 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, - 34543, 34549, - 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651, 34667, - 34673, 34679, - 34687, 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763, - 34781, 34807, - 34819, 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913, - 34919, 34939, - 34949, 34961, 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, - 35081, 35083, - 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, - 35171, 35201, - 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317, - 35323, 35327, - 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437, - 35447, 35449, - 35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, - 35569, 35573, - 35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, - 35753, 35759, - 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, - 35869, 35879, - 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, - 35993, 35999, - 36007, 36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097, - 36107, 36109, - 36131, 36137, 36151, 36161, 36187, 36191, 36209, 36217, 36229, 36241, - 36251, 36263, - 36269, 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, - 36373, 36383, - 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, - 36523, 36527, - 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, - 36629, 36637, - 36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, - 36739, 36749, - 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833, - 36847, 36857, - 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, - 36943, 36947, - 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, - 37061, 37087, - 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199, 37201, - 37217, 37223, - 37243, 37253, 37273, 37277, 37307, 37309, 37313, 37321, 37337, 37339, - 37357, 37361, - 37363, 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, - 37489, 37493, - 37501, 37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, - 37571, 37573, - 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, - 37691, 37693, - 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, - 37853, 37861, - 37871, 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, - 37991, 37993, - 37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, - 38153, 38167, - 38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, - 38273, 38281, - 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, - 38377, 38393, - 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, 38557, 38561, - 38567, 38569, - 38593, 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, - 38677, 38693, - 38699, 38707, 38711, 38713, 38723, 38729, 38737, 38747, 38749, 38767, - 38783, 38791, - 38803, 38821, 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, - 38917, 38921, - 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, - 39043, 39047, - 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157, - 39161, 39163, - 39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, - 39251, 39293, - 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, - 39383, 39397, - 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, - 39521, 39541, - 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, - 39671, 39679, - 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, - 39799, 39821, - 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883, - 39887, 39901, - 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, - 40037, 40039, - 40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, - 40163, 40169, - 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, - 40289, 40343, - 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471, - 40483, 40487, - 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, - 40591, 40597, - 40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, - 40759, 40763, - 40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, - 40853, 40867, - 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, - 40993, 41011, - 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, - 41131, 41141, - 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, - 41221, 41227, - 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, - 41351, 41357, - 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, - 41491, 41507, - 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, - 41609, 41611, - 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, - 41719, 41729, - 41737, 41759, 41761, 41771, 41777, 41801, 41809, 41813, 41843, 41849, - 41851, 41863, - 41879, 41887, 41893, 41897, 41903, 41911, 41927, 41941, 41947, 41953, - 41957, 41959, - 41969, 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, - 42071, 42073, - 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, - 42193, 42197, - 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299, - 42307, 42323, - 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407, - 42409, 42433, - 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491, - 42499, 42509, - 42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, - 42667, 42677, - 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, - 42751, 42767, - 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853, 42859, - 42863, 42899, - 42901, 42923, 42929, 42937, 42943, 42953, 42961, 42967, 42979, 42989, - 43003, 43013, - 43019, 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, - 43151, 43159, - 43177, 43189, 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, - 43313, 43319, - 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, - 43457, 43481, - 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, - 43607, 43609, - 43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, - 43721, 43753, - 43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867, - 43889, 43891, - 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, - 43997, 44017, - 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, - 44111, 44119, - 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, 44203, 44207, - 44221, 44249, - 44257, 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357, - 44371, 44381, - 44383, 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501, 44507, - 44519, 44531, - 44533, 44537, 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, - 44633, 44641, - 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, - 44753, 44771, - 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867, - 44879, 44887, - 44893, 44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983, - 44987, 45007, - 45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, - 45139, 45161, - 45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, - 45293, 45307, - 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, - 45413, 45427, - 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553, - 45557, 45569, - 45587, 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677, - 45691, 45697, - 45707, 45737, 45751, 45757, 45763, 45767, 45779, 45817, 45821, 45823, - 45827, 45833, - 45841, 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, - 45971, 45979, - 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, - 46103, 46133, - 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, 46219, 46229, - 46237, 46261, - 46271, 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, - 46381, 46399, - 46411, 46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499, - 46507, 46511, - 46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, - 46639, 46643, - 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, - 46751, 46757, - 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861, - 46867, 46877, - 46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017, 47041, 47051, - 47057, 47059, - 47087, 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149, - 47161, 47189, - 47207, 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, - 47309, 47317, - 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, - 47431, 47441, - 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543, - 47563, 47569, - 47581, 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657, 47659, - 47681, 47699, - 47701, 47711, 47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, - 47797, 47807, - 47809, 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, - 47933, 47939, - 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, - 48073, 48079, - 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193, - 48197, 48221, - 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, - 48353, 48371, - 48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479, - 48481, 48487, - 48491, 48497, 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, - 48593, 48611, - 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, - 48751, 48757, - 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823, - 48847, 48857, - 48859, 48869, 48871, 48883, 48889, 48907, 48947, 48953, 48973, 48989, - 48991, 49003, - 49009, 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103, - 49109, 49117, - 49121, 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, - 49207, 49211, - 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, - 49363, 49367, - 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, - 49463, 49477, - 49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597, - 49603, 49613, - 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711, 49727, - 49739, 49741, - 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, - 49843, 49853, - 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, - 49991, 49993, - 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, 50077, 50087, - 50093, 50101, - 50111, 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207, - 50221, 50227, - 50231, 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333, - 50341, 50359, - 50363, 50377, 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, - 50497, 50503, - 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, - 50599, 50627, - 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773, - 50777, 50789, - 50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, - 50923, 50929, - 50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, - 51059, 51061, - 51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, - 51199, 51203, - 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, - 51341, 51343, - 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, - 51437, 51439, - 51449, 51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521, - 51539, 51551, - 51563, 51577, 51581, 51593, 51599, 51607, 51613, 51631, 51637, 51647, - 51659, 51673, - 51679, 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, - 51797, 51803, - 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, - 51907, 51913, - 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, - 52051, 52057, - 52067, 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, - 52181, 52183, - 52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291, - 52301, 52313, - 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, - 52489, 52501, - 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, - 52583, 52609, - 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, 52711, 52721, - 52727, 52733, - 52747, 52757, 52769, 52783, 52807, 52813, 52817, 52837, 52859, 52861, - 52879, 52883, - 52889, 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, - 52981, 52999, - 53003, 53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, - 53113, 53117, - 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, - 53233, 53239, - 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359, - 53377, 53381, - 53401, 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, - 53527, 53549, - 53551, 53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, - 53633, 53639, - 53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, - 53777, 53783, - 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, - 53897, 53899, - 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001, 54011, - 54013, 54037, - 54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, - 54167, 54181, - 54193, 54217, 54251, 54269, 54277, 54287, 54293, 54311, 54319, 54323, - 54331, 54347, - 54361, 54367, 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, - 54437, 54443, - 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, - 54547, 54559, - 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647, - 54667, 54673, - 54679, 54709, 54713, 54721, 54727, 54751, 54767, 54773, 54779, 54787, - 54799, 54829, - 54833, 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, - 54959, 54973, - 54979, 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, - 55079, 55103, - 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, - 55219, 55229, - 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343, - 55351, 55373, - 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501, 55511, - 55529, 55541, - 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631, 55633, 55639, - 55661, 55663, - 55667, 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, - 55787, 55793, - 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, - 55871, 55889, - 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, 55967, 55987, - 55997, 56003, - 56009, 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, - 56123, 56131, - 56149, 56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249, - 56263, 56267, - 56269, 56299, 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, - 56417, 56431, - 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, - 56509, 56519, - 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, - 56633, 56659, - 56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747, - 56767, 56773, - 56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873, - 56891, 56893, - 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, - 56983, 56989, - 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, - 57107, 57119, - 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, - 57221, 57223, - 57241, 57251, 57259, 57269, 57271, 57283, 57287, 57301, 57329, 57331, - 57347, 57349, - 57367, 57373, 57383, 57389, 57397, 57413, 57427, 57457, 57467, 57487, - 57493, 57503, - 57527, 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, - 57649, 57653, - 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, - 57751, 57773, - 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853, - 57859, 57881, - 57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, - 58027, 58031, - 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129, - 58147, 58151, - 58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, - 58231, 58237, - 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, - 58391, 58393, - 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477, 58481, - 58511, 58537, - 58543, 58549, 58567, 58573, 58579, 58601, 58603, 58613, 58631, 58657, - 58661, 58679, - 58687, 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, - 58787, 58789, - 58831, 58889, 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, - 58963, 58967, - 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, - 59063, 59069, - 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, - 59167, 59183, - 59197, 59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, - 59281, 59333, - 59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407, - 59417, 59419, - 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, - 59539, 59557, - 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, - 59663, 59669, - 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, 59753, 59771, - 59779, 59791, - 59797, 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957, - 59971, 59981, - 59999, 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089, 60091, - 60101, 60103, - 60107, 60127, 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, - 60223, 60251, - 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, - 60373, 60383, - 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, 60509, 60521, - 60527, 60539, - 60589, 60601, 60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649, - 60659, 60661, - 60679, 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, - 60773, 60779, - 60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, - 60917, 60919, - 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, - 61051, 61057, - 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223, - 61231, 61253, - 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363, - 61379, 61381, - 61403, 61409, 61417, 61441, 61463, 61469, 61471, 61483, 61487, 61493, - 61507, 61511, - 61519, 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, - 61627, 61631, - 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, - 61723, 61729, - 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, 61871, 61879, - 61909, 61927, - 61933, 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, - 62017, 62039, - 62047, 62053, 62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137, - 62141, 62143, - 62171, 62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, - 62299, 62303, - 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, - 62467, 62473, - 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581, - 62591, 62597, - 62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687, 62701, - 62723, 62731, - 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861, - 62869, 62873, - 62897, 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, - 62987, 62989, - 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, - 63131, 63149, - 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311, - 63313, 63317, - 63331, 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391, 63397, - 63409, 63419, - 63421, 63439, 63443, 63463, 63467, 63473, 63487, 63493, 63499, 63521, - 63527, 63533, - 63541, 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, - 63629, 63647, - 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, - 63727, 63737, - 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839, - 63841, 63853, - 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, - 64013, 64019, - 64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153, - 64157, 64171, - 64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, - 64303, 64319, - 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, - 64483, 64489, - 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613, - 64621, 64627, - 64633, 64661, 64663, 64667, 64679, 64693, 64709, 64717, 64747, 64763, - 64781, 64783, - 64793, 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901, - 64919, 64921, - 64927, 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, - 65053, 65063, - 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, - 65167, 65171, - 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, - 65293, 65309, - 65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419, - 65423, 65437, - 65447, 65449, 65479, 65497, 65519, 65521, + 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, + 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, + 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, + 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, + 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, + 10429, 10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499, + 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, + 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, + 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, + 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, + 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, + 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, + 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113, 11117, + 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, + 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, + 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, + 11393, 11399, 11411, 11423, 11437, 11443, 11447, 11467, 11471, + 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, + 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677, + 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, + 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, + 11833, 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, + 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, + 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, + 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, + 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, + 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, + 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, + 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, + 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, + 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, + 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, + 12721, 12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, + 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, + 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 12973, + 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, + 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, + 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, + 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, + 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, + 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, + 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, + 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, + 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, + 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, + 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903, 13907, + 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, + 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, + 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, + 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, + 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, + 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, + 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, + 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, + 14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, + 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, + 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869, 14879, + 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, + 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, + 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, + 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, + 15259, 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, + 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, + 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, + 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, + 15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, + 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, + 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, + 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, + 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, + 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, + 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, + 16141, 16183, 16187, 16189, 16193, 16217, 16223, 16229, 16231, + 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, + 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 16433, + 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, + 16547, 16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, + 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, + 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, 16823, + 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, + 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, + 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, + 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, + 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, + 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, + 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417, 17419, + 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, + 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, + 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, + 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, + 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, + 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, + 17959, 17971, 17977, 17981, 17987, 17989, 18013, 18041, 18043, + 18047, 18049, 18059, 18061, 18077, 18089, 18097, 18119, 18121, + 18127, 18131, 18133, 18143, 18149, 18169, 18181, 18191, 18199, + 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, + 18287, 18289, 18301, 18307, 18311, 18313, 18329, 18341, 18353, + 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, + 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, + 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, + 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, + 18749, 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, + 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973, + 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, + 19079, 19081, 19087, 19121, 19139, 19141, 19157, 19163, 19181, + 19183, 19207, 19211, 19213, 19219, 19231, 19237, 19249, 19259, + 19267, 19273, 19289, 19301, 19309, 19319, 19333, 19373, 19379, + 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, + 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, 19483, + 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, + 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, + 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, + 19777, 19793, 19801, 19813, 19819, 19841, 19843, 19853, 19861, + 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961, + 19963, 19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, + 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113, + 20117, 20123, 20129, 20143, 20147, 20149, 20161, 20173, 20177, + 20183, 20201, 20219, 20231, 20233, 20249, 20261, 20269, 20287, + 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, 20359, + 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, + 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, + 20551, 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, + 20681, 20693, 20707, 20717, 20719, 20731, 20743, 20747, 20749, + 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, + 20873, 20879, 20887, 20897, 20899, 20903, 20921, 20929, 20939, + 20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, + 21019, 21023, 21031, 21059, 21061, 21067, 21089, 21101, 21107, + 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, + 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, 21283, + 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, + 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, + 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, + 21559, 21563, 21569, 21577, 21587, 21589, 21599, 21601, 21611, + 21613, 21617, 21647, 21649, 21661, 21673, 21683, 21701, 21713, + 21727, 21737, 21739, 21751, 21757, 21767, 21773, 21787, 21799, + 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871, + 21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, + 21997, 22003, 22013, 22027, 22031, 22037, 22039, 22051, 22063, + 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, 22129, + 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, + 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, + 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, + 22433, 22441, 22447, 22453, 22469, 22481, 22483, 22501, 22511, + 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, + 22621, 22637, 22639, 22643, 22651, 22669, 22679, 22691, 22697, + 22699, 22709, 22717, 22721, 22727, 22739, 22741, 22751, 22769, + 22777, 22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861, + 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, + 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, 23039, + 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, + 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, + 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, + 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, + 23417, 23431, 23447, 23459, 23473, 23497, 23509, 23531, 23537, + 23539, 23549, 23557, 23561, 23563, 23567, 23581, 23593, 23599, + 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, + 23677, 23687, 23689, 23719, 23741, 23743, 23747, 23753, 23761, + 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833, + 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, 23911, + 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, + 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, + 24091, 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, + 24151, 24169, 24179, 24181, 24197, 24203, 24223, 24229, 24239, + 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, + 24379, 24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, + 24473, 24481, 24499, 24509, 24517, 24527, 24533, 24547, 24551, + 24571, 24593, 24611, 24623, 24631, 24659, 24671, 24677, 24683, + 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793, + 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889, + 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, + 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, + 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, + 25171, 25183, 25189, 25219, 25229, 25237, 25243, 25247, 25253, + 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343, 25349, + 25357, 25367, 25373, 25391, 25409, 25411, 25423, 25439, 25447, + 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, + 25577, 25579, 25583, 25589, 25601, 25603, 25609, 25621, 25633, + 25639, 25643, 25657, 25667, 25673, 25679, 25693, 25703, 25717, + 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, 25801, + 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, + 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, + 25999, 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, + 26107, 26111, 26113, 26119, 26141, 26153, 26161, 26171, 26177, + 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261, + 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339, 26347, + 26357, 26371, 26387, 26393, 26399, 26407, 26417, 26423, 26431, + 26437, 26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539, + 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, + 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, 26713, + 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, + 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, + 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, + 26981, 26987, 26993, 27011, 27017, 27031, 27043, 27059, 27061, + 27067, 27073, 27077, 27091, 27103, 27107, 27109, 27127, 27143, + 27179, 27191, 27197, 27211, 27239, 27241, 27253, 27259, 27271, + 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397, + 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, + 27487, 27509, 27527, 27529, 27539, 27541, 27551, 27581, 27583, + 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, 27697, + 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, + 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, + 27827, 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, + 27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019, + 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, + 28109, 28111, 28123, 28151, 28163, 28181, 28183, 28201, 28211, + 28219, 28229, 28277, 28279, 28283, 28289, 28297, 28307, 28309, + 28319, 28349, 28351, 28387, 28393, 28403, 28409, 28411, 28429, + 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, + 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, 28591, + 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, + 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, + 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, + 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909, + 28921, 28927, 28933, 28949, 28961, 28979, 29009, 29017, 29021, + 29023, 29027, 29033, 29059, 29063, 29077, 29101, 29123, 29129, + 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201, + 29207, 29209, 29221, 29231, 29243, 29251, 29269, 29287, 29297, + 29303, 29311, 29327, 29333, 29339, 29347, 29363, 29383, 29387, + 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453, + 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, + 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, + 29671, 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, + 29803, 29819, 29833, 29837, 29851, 29863, 29867, 29873, 29879, + 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, + 30013, 30029, 30047, 30059, 30071, 30089, 30091, 30097, 30103, + 30109, 30113, 30119, 30133, 30137, 30139, 30161, 30169, 30181, + 30187, 30197, 30203, 30211, 30223, 30241, 30253, 30259, 30269, + 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367, + 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, 30491, + 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, + 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, + 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, + 30781, 30803, 30809, 30817, 30829, 30839, 30841, 30851, 30853, + 30859, 30869, 30871, 30881, 30893, 30911, 30931, 30937, 30941, + 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051, + 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, + 31151, 31153, 31159, 31177, 31181, 31183, 31189, 31193, 31219, + 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267, 31271, + 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, 31379, + 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, + 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, + 31601, 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, + 31699, 31721, 31723, 31727, 31729, 31741, 31751, 31769, 31771, + 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891, + 31907, 31957, 31963, 31973, 31981, 31991, 32003, 32009, 32027, + 32029, 32051, 32057, 32059, 32063, 32069, 32077, 32083, 32089, + 32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, + 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297, + 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359, + 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, + 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, + 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, + 32603, 32609, 32611, 32621, 32633, 32647, 32653, 32687, 32693, + 32707, 32713, 32717, 32719, 32749, 32771, 32779, 32783, 32789, + 32797, 32801, 32803, 32831, 32833, 32839, 32843, 32869, 32887, + 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, + 32983, 32987, 32993, 32999, 33013, 33023, 33029, 33037, 33049, + 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, + 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223, + 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, + 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, + 33427, 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, + 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, + 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, + 33647, 33679, 33703, 33713, 33721, 33739, 33749, 33751, 33757, + 33767, 33769, 33773, 33791, 33797, 33809, 33811, 33827, 33829, + 33851, 33857, 33863, 33871, 33889, 33893, 33911, 33923, 33931, + 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, + 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159, + 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, + 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, + 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, + 34439, 34457, 34469, 34471, 34483, 34487, 34499, 34501, 34511, + 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, + 34607, 34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, + 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763, + 34781, 34807, 34819, 34841, 34843, 34847, 34849, 34871, 34877, + 34883, 34897, 34913, 34919, 34939, 34949, 34961, 34963, 34981, + 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089, + 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, + 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, + 35291, 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, + 35393, 35401, 35407, 35419, 35423, 35437, 35447, 35449, 35461, + 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, + 35569, 35573, 35591, 35593, 35597, 35603, 35617, 35671, 35677, + 35729, 35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, + 35809, 35831, 35837, 35839, 35851, 35863, 35869, 35879, 35897, + 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, + 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, 36067, + 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, + 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, + 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, + 36373, 36383, 36389, 36433, 36451, 36457, 36467, 36469, 36473, + 36479, 36493, 36497, 36523, 36527, 36529, 36541, 36551, 36559, + 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637, 36643, + 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, + 36739, 36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, + 36809, 36821, 36833, 36847, 36857, 36871, 36877, 36887, 36899, + 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973, + 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, + 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, + 37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, + 37307, 37309, 37313, 37321, 37337, 37339, 37357, 37361, 37363, + 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, + 37489, 37493, 37501, 37507, 37511, 37517, 37529, 37537, 37547, + 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591, 37607, + 37619, 37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, + 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, + 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957, + 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, + 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, + 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, + 38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, + 38333, 38351, 38371, 38377, 38393, 38431, 38447, 38449, 38453, + 38459, 38461, 38501, 38543, 38557, 38561, 38567, 38569, 38593, + 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, + 38677, 38693, 38699, 38707, 38711, 38713, 38723, 38729, 38737, + 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, + 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, 38923, + 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, + 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, + 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, + 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, + 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, + 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, + 39503, 39509, 39511, 39521, 39541, 39551, 39563, 39569, 39581, + 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, + 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, + 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, + 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, + 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, + 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, + 40163, 40169, 40177, 40189, 40193, 40213, 40231, 40237, 40241, + 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, + 40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, + 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, + 40591, 40597, 40609, 40627, 40637, 40639, 40693, 40697, 40699, + 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, + 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, + 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, + 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, + 41081, 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, + 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, + 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, + 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, + 41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, + 41543, 41549, 41579, 41593, 41597, 41603, 41609, 41611, 41617, + 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, + 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, 41809, + 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, + 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, + 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, + 42071, 42073, 42083, 42089, 42101, 42131, 42139, 42157, 42169, + 42179, 42181, 42187, 42193, 42197, 42209, 42221, 42223, 42227, + 42239, 42257, 42281, 42283, 42293, 42299, 42307, 42323, 42331, + 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407, + 42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, + 42473, 42487, 42491, 42499, 42509, 42533, 42557, 42569, 42571, + 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, 42683, + 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, + 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, + 42841, 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, + 42943, 42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019, + 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, + 43151, 43159, 43177, 43189, 43201, 43207, 43223, 43237, 43261, + 43271, 43283, 43291, 43313, 43319, 43321, 43331, 43391, 43397, + 43399, 43403, 43411, 43427, 43441, 43451, 43457, 43481, 43487, + 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, + 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, 43669, + 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, + 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, + 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, + 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, + 44087, 44089, 44101, 44111, 44119, 44123, 44129, 44131, 44159, + 44171, 44179, 44189, 44201, 44203, 44207, 44221, 44249, 44257, + 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357, + 44371, 44381, 44383, 44389, 44417, 44449, 44453, 44483, 44491, + 44497, 44501, 44507, 44519, 44531, 44533, 44537, 44543, 44549, + 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647, + 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, + 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, + 44843, 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, + 44939, 44953, 44959, 44963, 44971, 44983, 44987, 45007, 45013, + 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, + 45139, 45161, 45179, 45181, 45191, 45197, 45233, 45247, 45259, + 45263, 45281, 45289, 45293, 45307, 45317, 45319, 45329, 45337, + 45341, 45343, 45361, 45377, 45389, 45403, 45413, 45427, 45433, + 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553, + 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659, + 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, + 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, + 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, + 45971, 45979, 45989, 46021, 46027, 46049, 46051, 46061, 46073, + 46091, 46093, 46099, 46103, 46133, 46141, 46147, 46153, 46171, + 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271, + 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, + 46381, 46399, 46411, 46439, 46441, 46447, 46451, 46457, 46471, + 46477, 46489, 46499, 46507, 46511, 46523, 46549, 46559, 46567, + 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, 46649, + 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, + 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, + 46831, 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, + 46957, 46993, 46997, 47017, 47041, 47051, 47057, 47059, 47087, + 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149, + 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279, 47287, + 47293, 47297, 47303, 47309, 47317, 47339, 47351, 47353, 47363, + 47381, 47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, + 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543, + 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, 47639, + 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, + 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, + 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, + 47933, 47939, 47947, 47951, 47963, 47969, 47977, 47981, 48017, + 48023, 48029, 48049, 48073, 48079, 48091, 48109, 48119, 48121, + 48131, 48157, 48163, 48179, 48187, 48193, 48197, 48221, 48239, + 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, + 48353, 48371, 48383, 48397, 48407, 48409, 48413, 48437, 48449, + 48463, 48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, + 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, 48619, + 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, + 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, + 48817, 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, + 48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, + 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103, + 49109, 49117, 49121, 49123, 49139, 49157, 49169, 49171, 49177, + 49193, 49199, 49201, 49207, 49211, 49223, 49253, 49261, 49277, + 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367, 49369, + 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, + 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, 49547, + 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, + 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, + 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, + 49843, 49853, 49871, 49877, 49891, 49919, 49921, 49927, 49937, + 49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, + 50047, 50051, 50053, 50069, 50077, 50087, 50093, 50101, 50111, + 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207, + 50221, 50227, 50231, 50261, 50263, 50273, 50287, 50291, 50311, + 50321, 50329, 50333, 50341, 50359, 50363, 50377, 50383, 50387, + 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, + 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, + 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, + 50753, 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, + 50857, 50867, 50873, 50891, 50893, 50909, 50923, 50929, 50951, + 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, + 51059, 51061, 51071, 51109, 51131, 51133, 51137, 51151, 51157, + 51169, 51193, 51197, 51199, 51203, 51217, 51229, 51239, 51241, + 51257, 51263, 51283, 51287, 51307, 51329, 51341, 51343, 51347, + 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, + 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, 51503, + 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, + 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, + 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, + 51797, 51803, 51817, 51827, 51829, 51839, 51853, 51859, 51869, + 51871, 51893, 51899, 51907, 51913, 51929, 51941, 51949, 51971, + 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067, + 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, + 52181, 52183, 52189, 52201, 52223, 52237, 52249, 52253, 52259, + 52267, 52289, 52291, 52301, 52313, 52321, 52361, 52363, 52369, + 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, 52511, + 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, + 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, + 52709, 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, + 52807, 52813, 52817, 52837, 52859, 52861, 52879, 52883, 52889, + 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, + 52981, 52999, 53003, 53017, 53047, 53051, 53069, 53077, 53087, + 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161, + 53171, 53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, + 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359, + 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, 53453, + 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, + 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, + 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, + 53777, 53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, + 53881, 53887, 53891, 53897, 53899, 53917, 53923, 53927, 53939, + 53951, 53959, 53987, 53993, 54001, 54011, 54013, 54037, 54049, + 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, + 54167, 54181, 54193, 54217, 54251, 54269, 54277, 54287, 54293, + 54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, + 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, 54449, + 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, + 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, + 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, + 54727, 54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, + 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, + 54959, 54973, 54979, 54983, 55001, 55009, 55021, 55049, 55051, + 55057, 55061, 55073, 55079, 55103, 55109, 55117, 55127, 55147, + 55163, 55171, 55201, 55207, 55213, 55217, 55219, 55229, 55243, + 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343, + 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, + 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, + 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, + 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, + 55787, 55793, 55799, 55807, 55813, 55817, 55819, 55823, 55829, + 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, + 55927, 55931, 55933, 55949, 55967, 55987, 55997, 56003, 56009, + 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, + 56123, 56131, 56149, 56167, 56171, 56179, 56197, 56207, 56209, + 56237, 56239, 56249, 56263, 56267, 56269, 56299, 56311, 56333, + 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437, + 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, + 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, + 56599, 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, + 56701, 56711, 56713, 56731, 56737, 56747, 56767, 56773, 56779, + 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873, + 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, + 56951, 56957, 56963, 56983, 56989, 56993, 56999, 57037, 57041, + 57047, 57059, 57073, 57077, 57089, 57097, 57107, 57119, 57131, + 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, + 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, 57287, + 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, + 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, + 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, + 57649, 57653, 57667, 57679, 57689, 57697, 57709, 57713, 57719, + 57727, 57731, 57737, 57751, 57773, 57781, 57787, 57791, 57793, + 57803, 57809, 57829, 57839, 57847, 57853, 57859, 57881, 57899, + 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, + 58027, 58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, + 58109, 58111, 58129, 58147, 58151, 58153, 58169, 58171, 58189, + 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, 58243, + 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, + 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, + 58453, 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, + 58579, 58601, 58603, 58613, 58631, 58657, 58661, 58679, 58687, + 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, + 58787, 58789, 58831, 58889, 58897, 58901, 58907, 58909, 58913, + 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997, 59009, + 59011, 59021, 59023, 59029, 59051, 59053, 59063, 59069, 59077, + 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, + 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, 59239, + 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, + 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, + 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, + 59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, + 59629, 59651, 59659, 59663, 59669, 59671, 59693, 59699, 59707, + 59723, 59729, 59743, 59747, 59753, 59771, 59779, 59791, 59797, + 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957, + 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041, 60077, + 60083, 60089, 60091, 60101, 60103, 60107, 60127, 60133, 60139, + 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257, + 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, + 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, + 60497, 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, + 60617, 60623, 60631, 60637, 60647, 60649, 60659, 60661, 60679, + 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, + 60773, 60779, 60793, 60811, 60821, 60859, 60869, 60887, 60889, + 60899, 60901, 60913, 60917, 60919, 60923, 60937, 60943, 60953, + 60961, 61001, 61007, 61027, 61031, 61043, 61051, 61057, 61091, + 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223, + 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, + 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, + 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, + 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, + 61627, 61631, 61637, 61643, 61651, 61657, 61667, 61673, 61681, + 61687, 61703, 61717, 61723, 61729, 61751, 61757, 61781, 61813, + 61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933, + 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, + 62017, 62039, 62047, 62053, 62057, 62071, 62081, 62099, 62119, + 62129, 62131, 62137, 62141, 62143, 62171, 62189, 62191, 62201, + 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, 62311, + 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, + 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, + 62549, 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, + 62639, 62653, 62659, 62683, 62687, 62701, 62723, 62731, 62743, + 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861, + 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969, + 62971, 62981, 62983, 62987, 62989, 63029, 63031, 63059, 63067, + 63073, 63079, 63097, 63103, 63113, 63127, 63131, 63149, 63179, + 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311, + 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, 63377, + 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, + 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, + 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, + 63629, 63647, 63649, 63659, 63667, 63671, 63689, 63691, 63697, + 63703, 63709, 63719, 63727, 63737, 63743, 63761, 63773, 63781, + 63793, 63799, 63803, 63809, 63823, 63839, 63841, 63853, 63857, + 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, + 64013, 64019, 64033, 64037, 64063, 64067, 64081, 64091, 64109, + 64123, 64151, 64153, 64157, 64171, 64187, 64189, 64217, 64223, + 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, 64327, + 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, + 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, + 64601, 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, + 64679, 64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, + 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901, + 64919, 64921, 64927, 64937, 64951, 64969, 64997, 65003, 65011, + 65027, 65029, 65033, 65053, 65063, 65071, 65089, 65099, 65101, + 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173, + 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, + 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, 65393, + 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, + 65519, 65521, }; #define NPRIMES (sizeof(primes) / sizeof(*primes)) @@ -1193,11 +838,19 @@ static const unsigned short primes[] = { * more than a multiple of a dirty great bignum. In this case * `bits' gives the size of the factor by which we _multiply_ * that bignum, rather than the size of the whole number. + * + * - for the basically cosmetic purposes of generating keys of the + * length actually specified rather than off by one bit, we permit + * the caller to provide an unsigned integer 'firstbits' which will + * match the top few bits of the returned prime. (That is, there + * will exist some n such that (returnvalue >> n) == firstbits.) If + * 'firstbits' is not needed, specifying it to either 0 or 1 is + * an adequate no-op. */ Bignum primegen(int bits, int modulus, int residue, Bignum factor, - int phase, progfn_t pfn, void *pfnparam) + int phase, progfn_t pfn, void *pfnparam, unsigned firstbits) { - int i, k, v, byte, bitsleft, check, checks; + int i, k, v, byte, bitsleft, check, checks, fbsize; unsigned long delta; unsigned long moduli[NPRIMES + 1]; unsigned long residues[NPRIMES + 1]; @@ -1208,6 +861,10 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, byte = 0; bitsleft = 0; + fbsize = 0; + while (firstbits >> fbsize) /* work out how to align this */ + fbsize++; + STARTOVER: pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress); @@ -1220,9 +877,11 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, */ p = bn_power_2(bits - 1); for (i = 0; i < bits; i++) { - if (i == 0 || i == bits - 1) + if (i == 0 || i == bits - 1) { v = (i != 0 || !factor) ? 1 : 0; - else { + } else if (i >= bits - fbsize) { + v = (firstbits >> (i - (bits - fbsize))) & 1; + } else { if (bitsleft <= 0) bitsleft = 8, byte = random_byte(); v = byte & 1; @@ -1396,3 +1055,32 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor, freebn(pm1); return p; } + +/* + * Invent a pair of values suitable for use as 'firstbits' in the + * above function, such that their product is at least 2. + * + * This is used for generating both RSA and DSA keys which have + * exactly the specified number of bits rather than one fewer - if you + * generate an a-bit and a b-bit number completely at random and + * multiply them together, you could end up with either an (ab-1)-bit + * number or an (ab)-bit number. The former happens log(2)*2-1 of the + * time (about 39%) and, though actually harmless, every time it + * occurs it has a non-zero probability of sparking a user email along + * the lines of 'Hey, I asked PuTTYgen for a 2048-bit key and I only + * got 2047 bits! Bug!' + */ +void invent_firstbits(unsigned *one, unsigned *two) +{ + /* + * Our criterion is that any number in the range [one,one+1) + * multiplied by any number in the range [two,two+1) should have + * the highest bit set. It should be clear that we can trivially + * test this by multiplying the smallest values in each interval, + * i.e. the ones we actually invented. + */ + do { + *one = 0x100 | random_byte(); + *two = 0x100 | random_byte(); + } while (*one * *two < 0x20000); +} diff --git a/putty/SSHPUBK.C b/putty/SSHPUBK.C index 7b5a690..f13e33b 100644 --- a/putty/SSHPUBK.C +++ b/putty/SSHPUBK.C @@ -67,14 +67,15 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, i += 4; /* Now the serious stuff. An ordinary SSH-1 public key. */ - i += makekey(buf + i, len, key, NULL, 1); - if (i < 0) + j = makekey(buf + i, len, key, NULL, 1); + if (j < 0) goto end; /* overran */ + i += j; /* Next, the comment field. */ - j = GET_32BIT(buf + i); + j = toint(GET_32BIT(buf + i)); i += 4; - if (len - i < j) + if (j < 0 || len - i < j) goto end; comment = snewn(j + 1, char); if (comment) { @@ -108,7 +109,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7); - memset(keybuf, 0, sizeof(keybuf)); /* burn the evidence */ + smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* @@ -150,7 +151,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, ret = 1; end: - memset(buf, 0, sizeof(buf)); /* burn the evidence */ + smemclr(buf, sizeof(buf)); /* burn the evidence */ return ret; } @@ -162,7 +163,7 @@ int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase, int ret = 0; const char *error = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; @@ -203,7 +204,7 @@ int rsakey_encrypted(const Filename *filename, char **comment) FILE *fp; char buf[64]; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) return 0; /* doesn't even exist */ @@ -241,7 +242,7 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen, *bloblen = 0; ret = 0; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto end; @@ -257,8 +258,8 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen, *blob = rsa_public_blob(&key, bloblen); freersakey(&key); ret = 1; - fp = NULL; } + fp = NULL; /* loadrsakey_main unconditionally closes fp */ } else { error = "not an SSH-1 RSA file"; } @@ -358,13 +359,13 @@ int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase) MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); MD5Final(keybuf, &md5c); des3_encrypt_pubkey(keybuf, estart, p - estart); - memset(keybuf, 0, sizeof(keybuf)); /* burn the evidence */ + smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } /* * Done. Write the result to the file. */ - fp = f_open(*filename, "wb", TRUE); + fp = f_open(filename, "wb", TRUE); if (fp) { int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf)); if (fclose(fp)) @@ -462,7 +463,7 @@ static int read_header(FILE * fp, char *header) int len = 39; int c; - while (len > 0) { + while (1) { c = fgetc(fp); if (c == '\n' || c == '\r' || c == EOF) return 0; /* failure */ @@ -632,7 +633,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, encryption = comment = mac = NULL; public_blob = private_blob = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; @@ -647,6 +648,11 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, /* this is an old key file; warn and then continue */ old_keyfile_warning(); old_fmt = 1; + } else if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) { + /* this is a key file FROM THE FUTURE; refuse it, but with a + * more specific error message than the generic one below */ + error = "PuTTY key format too new"; + goto error; } else { error = "not a PuTTY SSH-2 private key"; goto error; @@ -674,7 +680,6 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, cipher = 0; cipherblk = 1; } else { - sfree(encryption); goto error; } @@ -794,14 +799,14 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename, hmac_sha1_simple(mackey, 20, macdata, maclen, binary); - memset(mackey, 0, sizeof(mackey)); - memset(&s, 0, sizeof(s)); + smemclr(mackey, sizeof(mackey)); + smemclr(&s, sizeof(s)); } else { SHA_Simple(macdata, maclen, binary); } if (free_macdata) { - memset(macdata, 0, maclen); + smemclr(macdata, maclen); sfree(macdata); } @@ -881,7 +886,7 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm, public_blob = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) { error = "can't open file"; goto error; @@ -891,7 +896,10 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm, if (!read_header(fp, header) || (0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { - error = "not a PuTTY SSH-2 private key"; + if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) + error = "PuTTY key format too new"; + else + error = "not a PuTTY SSH-2 private key"; goto error; } error = "file format error"; @@ -962,7 +970,7 @@ int ssh2_userkey_encrypted(const Filename *filename, char **commentptr) if (commentptr) *commentptr = NULL; - fp = f_open(*filename, "rb", FALSE); + fp = f_open(filename, "rb", FALSE); if (!fp) return 0; if (!read_header(fp, header) @@ -1000,6 +1008,8 @@ int ssh2_userkey_encrypted(const Filename *filename, char **commentptr) if (commentptr) *commentptr = comment; + else + sfree(comment); fclose(fp); if (!strcmp(b, "aes256-cbc")) @@ -1116,10 +1126,10 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, SHA_Bytes(&s, passphrase, strlen(passphrase)); SHA_Final(&s, mackey); hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac); - memset(macdata, 0, maclen); + smemclr(macdata, maclen); sfree(macdata); - memset(mackey, 0, sizeof(mackey)); - memset(&s, 0, sizeof(s)); + smemclr(mackey, sizeof(mackey)); + smemclr(&s, sizeof(s)); } if (passphrase) { @@ -1139,11 +1149,11 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, aes256_encrypt_pubkey(key, priv_blob_encrypted, priv_encrypted_len); - memset(key, 0, sizeof(key)); - memset(&s, 0, sizeof(s)); + smemclr(key, sizeof(key)); + smemclr(&s, sizeof(s)); } - fp = f_open(*filename, "w", TRUE); + fp = f_open(filename, "w", TRUE); if (!fp) return 0; fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name); @@ -1160,7 +1170,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key, fclose(fp); sfree(pub_blob); - memset(priv_blob, 0, priv_blob_len); + smemclr(priv_blob, priv_blob_len); sfree(priv_blob); sfree(priv_blob_encrypted); return 1; @@ -1179,7 +1189,7 @@ int key_type(const Filename *filename) const char openssh_sig[] = "-----BEGIN "; int i; - fp = f_open(*filename, "r", FALSE); + fp = f_open(filename, "r", FALSE); if (!fp) return SSH_KEYTYPE_UNOPENABLE; i = fread(buf, 1, sizeof(buf), fp); diff --git a/putty/SSHRAND.C b/putty/SSHRAND.C index 91d9b37..e5f3186 100644 --- a/putty/SSHRAND.C +++ b/putty/SSHRAND.C @@ -49,6 +49,10 @@ static struct RandPool pool; int random_active = 0; long next_noise_collection; +#ifdef RANDOM_DIAGNOSTICS +int random_diagnostics = 0; +#endif + static void random_stir(void) { word32 block[HASHINPUT / sizeof(word32)]; @@ -65,6 +69,30 @@ static void random_stir(void) noise_get_light(random_add_noise); +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir starting\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics++; + } +#endif + SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); pool.incomingpos = 0; @@ -116,6 +144,29 @@ static void random_stir(void) for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) ((word32 *) (pool.pool + j))[k] = digest[k]; } + +#ifdef RANDOM_DIAGNOSTICS + if (i == 0) { + int p, q; + printf("random stir midpoint\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + } +#endif } /* @@ -128,6 +179,30 @@ static void random_stir(void) pool.poolpos = sizeof(pool.incoming); pool.stir_pending = FALSE; + +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir done\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics--; + } +#endif } void random_add_noise(void *noise, int length) @@ -199,9 +274,9 @@ static void random_add_heavynoise_bitbybit(void *noise, int length) pool.poolpos = i; } -static void random_timer(void *ctx, long now) +static void random_timer(void *ctx, unsigned long now) { - if (random_active > 0 && now - next_noise_collection >= 0) { + if (random_active > 0 && now == next_noise_collection) { noise_regular(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); @@ -213,27 +288,30 @@ void random_ref(void) if (!random_active) { memset(&pool, 0, sizeof(pool)); /* just to start with */ + random_active++; + noise_get_heavy(random_add_heavynoise_bitbybit); random_stir(); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); } - - random_active++; } void random_unref(void) { + assert(random_active > 0); + if (random_active == 1) { + random_save_seed(); + expire_timer_context(&pool); + } random_active--; - assert(random_active >= 0); - if (random_active) return; - - expire_timer_context(&pool); } int random_byte(void) { + assert(random_active); + if (pool.poolpos >= POOLSIZE) random_stir(); diff --git a/putty/SSHRSA.C b/putty/SSHRSA.C index ea6440b..02b87d0 100644 --- a/putty/SSHRSA.C +++ b/putty/SSHRSA.C @@ -110,7 +110,7 @@ static void sha512_mpint(SHA512_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } /* @@ -273,9 +273,18 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key) bignum_cmp(random, key->modulus) >= 0) { freebn(random); continue; - } else { - break; } + + /* + * Also, make sure it has an inverse mod modulus. + */ + random_inverse = modinv(random, key->modulus); + if (!random_inverse) { + freebn(random); + continue; + } + + break; } /* @@ -294,7 +303,6 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key) */ random_encrypted = crt_modpow(random, key->exponent, key->modulus, key->p, key->q, key->iqmp); - random_inverse = modinv(random, key->modulus); input_blinded = modmul(input, random_encrypted, key->modulus); ret_blinded = crt_modpow(input_blinded, key->private_exponent, key->modulus, key->p, key->q, key->iqmp); @@ -413,16 +421,18 @@ int rsa_verify(struct RSAKey *key) pm1 = copybn(key->p); decbn(pm1); ed = modmul(key->exponent, key->private_exponent, pm1); + freebn(pm1); cmp = bignum_cmp(ed, One); - sfree(ed); + freebn(ed); if (cmp != 0) return 0; qm1 = copybn(key->q); decbn(qm1); ed = modmul(key->exponent, key->private_exponent, qm1); + freebn(qm1); cmp = bignum_cmp(ed, One); - sfree(ed); + freebn(ed); if (cmp != 0) return 0; @@ -441,6 +451,8 @@ int rsa_verify(struct RSAKey *key) freebn(key->iqmp); key->iqmp = modinv(key->q, key->p); + if (!key->iqmp) + return 0; } /* @@ -448,7 +460,7 @@ int rsa_verify(struct RSAKey *key) */ n = modmul(key->iqmp, key->q, key->p); cmp = bignum_cmp(n, One); - sfree(n); + freebn(n); if (cmp != 0) return 0; @@ -525,7 +537,9 @@ static void getstring(char **data, int *datalen, char **p, int *length) *p = NULL; if (*datalen < 4) return; - *length = GET_32BIT(*data); + *length = toint(GET_32BIT(*data)); + if (*length < 0) + return; *datalen -= 4; *data += 4; if (*datalen < *length) @@ -547,6 +561,8 @@ static Bignum getmp(char **data, int *datalen) return b; } +static void rsa2_freekey(void *key); /* forward reference */ + static void *rsa2_newkey(char *data, int len) { char *p; @@ -554,8 +570,6 @@ static void *rsa2_newkey(char *data, int len) struct RSAKey *rsa; rsa = snew(struct RSAKey); - if (!rsa) - return NULL; getstring(&data, &len, &p, &slen); if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { @@ -568,6 +582,11 @@ static void *rsa2_newkey(char *data, int len) rsa->p = rsa->q = rsa->iqmp = NULL; rsa->comment = NULL; + if (!rsa->exponent || !rsa->modulus) { + rsa2_freekey(rsa); + return NULL; + } + return rsa; } @@ -690,8 +709,6 @@ static void *rsa2_openssh_createkey(unsigned char **blob, int *len) struct RSAKey *rsa; rsa = snew(struct RSAKey); - if (!rsa) - return NULL; rsa->comment = NULL; rsa->modulus = getmp(b, len); @@ -703,13 +720,12 @@ static void *rsa2_openssh_createkey(unsigned char **blob, int *len) if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent || !rsa->iqmp || !rsa->p || !rsa->q) { - sfree(rsa->modulus); - sfree(rsa->exponent); - sfree(rsa->private_exponent); - sfree(rsa->iqmp); - sfree(rsa->p); - sfree(rsa->q); - sfree(rsa); + rsa2_freekey(rsa); + return NULL; + } + + if (!rsa_verify(rsa)) { + rsa2_freekey(rsa); return NULL; } @@ -838,6 +854,8 @@ static int rsa2_verifysig(void *key, char *sig, int siglen, return 0; } in = getmp(&sig, &siglen); + if (!in) + return 0; out = modpow(in, rsa->exponent, rsa->modulus); freebn(in); diff --git a/putty/SSHRSAG.C b/putty/SSHRSAG.C index b19d3c1..9392a9f 100644 --- a/putty/SSHRSAG.C +++ b/putty/SSHRSAG.C @@ -2,6 +2,8 @@ * RSA key generation. */ +#include + #include "ssh.h" #define RSA_EXPONENT 37 /* we like this prime */ @@ -10,6 +12,7 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam) { Bignum pm1, qm1, phi_n; + unsigned pfirst, qfirst; /* * Set up the phase limits for the progress report. We do this @@ -59,10 +62,11 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, * general that's slightly more fiddly to arrange. By choosing * a prime e, we can simplify the criterion.) */ + invent_firstbits(&pfirst, &qfirst); key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL, - 1, pfn, pfnparam); + 1, pfn, pfnparam, pfirst); key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL, - 2, pfn, pfnparam); + 2, pfn, pfnparam, qfirst); /* * Ensure p > q, by swapping them if not. @@ -90,8 +94,10 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, freebn(pm1); freebn(qm1); key->private_exponent = modinv(key->exponent, phi_n); + assert(key->private_exponent); pfn(pfnparam, PROGFN_PROGRESS, 3, 4); key->iqmp = modinv(key->q, key->p); + assert(key->iqmp); pfn(pfnparam, PROGFN_PROGRESS, 3, 5); /* diff --git a/putty/SSHSH256.C b/putty/SSHSH256.C index 538982a..49fb842 100644 --- a/putty/SSHSH256.C +++ b/putty/SSHSH256.C @@ -218,6 +218,116 @@ const struct ssh_hash ssh_sha256 = { sha256_init, sha256_bytes, sha256_final, 32, "SHA-256" }; +/* ---------------------------------------------------------------------- + * The above is the SHA-256 algorithm itself. Now we implement the + * HMAC wrapper on it. + */ + +static void *sha256_make_context(void) +{ + return snewn(3, SHA256_State); +} + +static void sha256_free_context(void *handle) +{ + sfree(handle); +} + +static void sha256_key_internal(void *handle, unsigned char *key, int len) +{ + SHA256_State *keys = (SHA256_State *)handle; + unsigned char foo[64]; + int i; + + memset(foo, 0x36, 64); + for (i = 0; i < len && i < 64; i++) + foo[i] ^= key[i]; + SHA256_Init(&keys[0]); + SHA256_Bytes(&keys[0], foo, 64); + + memset(foo, 0x5C, 64); + for (i = 0; i < len && i < 64; i++) + foo[i] ^= key[i]; + SHA256_Init(&keys[1]); + SHA256_Bytes(&keys[1], foo, 64); + + smemclr(foo, 64); /* burn the evidence */ +} + +static void sha256_key(void *handle, unsigned char *key) +{ + sha256_key_internal(handle, key, 32); +} + +static void hmacsha256_start(void *handle) +{ + SHA256_State *keys = (SHA256_State *)handle; + + keys[2] = keys[0]; /* structure copy */ +} + +static void hmacsha256_bytes(void *handle, unsigned char const *blk, int len) +{ + SHA256_State *keys = (SHA256_State *)handle; + SHA256_Bytes(&keys[2], (void *)blk, len); +} + +static void hmacsha256_genresult(void *handle, unsigned char *hmac) +{ + SHA256_State *keys = (SHA256_State *)handle; + SHA256_State s; + unsigned char intermediate[32]; + + s = keys[2]; /* structure copy */ + SHA256_Final(&s, intermediate); + s = keys[1]; /* structure copy */ + SHA256_Bytes(&s, intermediate, 32); + SHA256_Final(&s, hmac); +} + +static void sha256_do_hmac(void *handle, unsigned char *blk, int len, + unsigned long seq, unsigned char *hmac) +{ + unsigned char seqbuf[4]; + + PUT_32BIT_MSB_FIRST(seqbuf, seq); + hmacsha256_start(handle); + hmacsha256_bytes(handle, seqbuf, 4); + hmacsha256_bytes(handle, blk, len); + hmacsha256_genresult(handle, hmac); +} + +static void sha256_generate(void *handle, unsigned char *blk, int len, + unsigned long seq) +{ + sha256_do_hmac(handle, blk, len, seq, blk + len); +} + +static int hmacsha256_verresult(void *handle, unsigned char const *hmac) +{ + unsigned char correct[32]; + hmacsha256_genresult(handle, correct); + return !memcmp(correct, hmac, 32); +} + +static int sha256_verify(void *handle, unsigned char *blk, int len, + unsigned long seq) +{ + unsigned char correct[32]; + sha256_do_hmac(handle, blk, len, seq, correct); + return !memcmp(correct, blk + len, 32); +} + +const struct ssh_mac ssh_hmac_sha256 = { + sha256_make_context, sha256_free_context, sha256_key, + sha256_generate, sha256_verify, + hmacsha256_start, hmacsha256_bytes, + hmacsha256_genresult, hmacsha256_verresult, + "hmac-sha2-256", + 32, + "HMAC-SHA-256" +}; + #ifdef TEST #include diff --git a/putty/SSHSHA.C b/putty/SSHSHA.C index d1c7981..8d8b5e2 100644 --- a/putty/SSHSHA.C +++ b/putty/SSHSHA.C @@ -28,6 +28,21 @@ void SHATransform(word32 * digest, word32 * block) word32 a, b, c, d, e; int t; +#ifdef RANDOM_DIAGNOSTICS + { + extern int random_diagnostics; + if (random_diagnostics) { + int i; + printf("SHATransform:"); + for (i = 0; i < 5; i++) + printf(" %08x", digest[i]); + printf(" +"); + for (i = 0; i < 16; i++) + printf(" %08x", block[i]); + } + } +#endif + for (t = 0; t < 16; t++) w[t] = block[t]; @@ -83,6 +98,19 @@ void SHATransform(word32 * digest, word32 * block) digest[2] += c; digest[3] += d; digest[4] += e; + +#ifdef RANDOM_DIAGNOSTICS + { + extern int random_diagnostics; + if (random_diagnostics) { + int i; + printf(" ="); + for (i = 0; i < 5; i++) + printf(" %08x", digest[i]); + printf("\n"); + } + } +#endif } /* ---------------------------------------------------------------------- @@ -98,9 +126,9 @@ void SHA_Init(SHA_State * s) s->lenhi = s->lenlo = 0; } -void SHA_Bytes(SHA_State * s, void *p, int len) +void SHA_Bytes(SHA_State * s, const void *p, int len) { - unsigned char *q = (unsigned char *) p; + const unsigned char *q = (const unsigned char *) p; uint32 wordblock[16]; uint32 lenw = len; int i; @@ -179,7 +207,7 @@ void SHA_Final(SHA_State * s, unsigned char *output) } } -void SHA_Simple(void *p, int len, unsigned char *output) +void SHA_Simple(const void *p, int len, unsigned char *output) { SHA_State s; @@ -253,7 +281,7 @@ static void sha1_key_internal(void *handle, unsigned char *key, int len) SHA_Init(&keys[1]); SHA_Bytes(&keys[1], foo, 64); - memset(foo, 0, 64); /* burn the evidence */ + smemclr(foo, 64); /* burn the evidence */ } static void sha1_key(void *handle, unsigned char *key) @@ -297,11 +325,7 @@ static void sha1_do_hmac(void *handle, unsigned char *blk, int len, { unsigned char seqbuf[4]; - seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF); - seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF); - seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF); - seqbuf[3] = (unsigned char) ((seq) & 0xFF); - + PUT_32BIT_MSB_FIRST(seqbuf, seq); hmacsha1_start(handle); hmacsha1_bytes(handle, seqbuf, 4); hmacsha1_bytes(handle, blk, len); diff --git a/putty/SSHZLIB.C b/putty/SSHZLIB.C index 9c780a4..7a2f613 100644 --- a/putty/SSHZLIB.C +++ b/putty/SSHZLIB.C @@ -38,6 +38,7 @@ */ #include +#include #include #ifdef ZLIB_STANDALONE diff --git a/putty/STORAGE.H b/putty/STORAGE.H index 0e0a7c0..e7963ec 100644 --- a/putty/STORAGE.H +++ b/putty/STORAGE.H @@ -9,9 +9,9 @@ /* ---------------------------------------------------------------------- * Functions to save and restore PuTTY sessions. Note that this is * only the low-level code to do the reading and writing. The - * higher-level code that translates a Config structure into a set - * of (key,value) pairs is elsewhere, since it doesn't (mostly) - * change between platforms. + * higher-level code that translates an internal Conf structure into + * a set of (key,value) pairs in their external storage format is + * elsewhere, since it doesn't (mostly) change between platforms. */ /* @@ -31,8 +31,8 @@ void *open_settings_w(const char *sessionname, char **errmsg); void write_setting_s(void *handle, const char *key, const char *value); void write_setting_i(void *handle, const char *key, int value); -void write_setting_filename(void *handle, const char *key, Filename value); -void write_setting_fontspec(void *handle, const char *key, FontSpec font); +void write_setting_filename(void *handle, const char *key, Filename *value); +void write_setting_fontspec(void *handle, const char *key, FontSpec *font); void close_settings_w(void *handle); /* @@ -41,22 +41,21 @@ void close_settings_w(void *handle); * number of calls to read_setting_s() and read_setting_i(), and * then close it using close_settings_r(). * - * read_setting_s() writes into the provided buffer and returns a - * pointer to the same buffer. + * read_setting_s() returns a dynamically allocated string which the + * caller must free. read_setting_filename() and + * read_setting_fontspec() likewise return dynamically allocated + * structures. * * If a particular string setting is not present in the session, * read_setting_s() can return NULL, in which case the caller * should invent a sensible default. If an integer setting is not * present, read_setting_i() returns its provided default. - * - * read_setting_filename() and read_setting_fontspec() each read into - * the provided buffer, and return zero if they failed to. */ void *open_settings_r(const char *sessionname); -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen); +char *read_setting_s(void *handle, const char *key); int read_setting_i(void *handle, const char *key, int defvalue); -int read_setting_filename(void *handle, const char *key, Filename *value); -int read_setting_fontspec(void *handle, const char *key, FontSpec *font); +Filename *read_setting_filename(void *handle, const char *key); +FontSpec *read_setting_fontspec(void *handle, const char *key); void close_settings_r(void *handle); /* diff --git a/putty/TELNET.C b/putty/TELNET.C index 8fbe886..0d630c7 100644 --- a/putty/TELNET.C +++ b/putty/TELNET.C @@ -4,6 +4,7 @@ #include #include +#include #include "putty.h" @@ -181,6 +182,7 @@ typedef struct telnet_tag { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; void *frontend; void *ldisc; @@ -201,7 +203,7 @@ typedef struct telnet_tag { SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; - Config cfg; + Conf *conf; Pinger pinger; } *Telnet; @@ -363,42 +365,46 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option) static void process_subneg(Telnet telnet) { - unsigned char b[2048], *p, *q; - int var, value, n; - char *e; + unsigned char *b, *p, *q; + int var, value, n, bsize; + char *e, *eval, *ekey, *user; switch (telnet->sb_opt) { case TELOPT_TSPEED: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termspeed = conf_get_str(telnet->conf, CONF_termspeed); + b = snewn(20 + strlen(termspeed), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED; b[3] = TELQUAL_IS; - strcpy((char *)(b + 4), telnet->cfg.termspeed); - n = 4 + strlen(telnet->cfg.termspeed); + strcpy((char *)(b + 4), termspeed); + n = 4 + strlen(termspeed); b[n] = IAC; b[n + 1] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2); logevent(telnet->frontend, "server:\tSB TSPEED SEND"); - logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed); + logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TSPEED "); break; case TELOPT_TTYPE: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termtype = conf_get_str(telnet->conf, CONF_termtype); + b = snewn(20 + strlen(termtype), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE; b[3] = TELQUAL_IS; - for (n = 0; telnet->cfg.termtype[n]; n++) - b[n + 4] = (telnet->cfg.termtype[n] >= 'a' - && telnet->cfg.termtype[n] <= - 'z' ? telnet->cfg.termtype[n] + 'A' - - 'a' : telnet->cfg.termtype[n]); + for (n = 0; termtype[n]; n++) + b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ? + termtype[n] + 'A' - 'a' : + termtype[n]); b[n + 4] = IAC; b[n + 5] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6); @@ -407,6 +413,7 @@ static void process_subneg(Telnet telnet) logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TTYPE \r\n"); break; @@ -421,7 +428,7 @@ static void process_subneg(Telnet telnet) logevent(telnet->frontend, logbuf); sfree(logbuf); if (telnet->sb_opt == TELOPT_OLD_ENVIRON) { - if (telnet->cfg.rfc_environ) { + if (conf_get_int(telnet->conf, CONF_rfc_environ)) { value = RFC_VALUE; var = RFC_VAR; } else { @@ -449,50 +456,75 @@ static void process_subneg(Telnet telnet) value = RFC_VALUE; var = RFC_VAR; } + bsize = 20; + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) + bsize += strlen(ekey) + strlen(eval) + 2; + user = get_remote_username(telnet->conf); + if (user) + bsize += 6 + strlen(user); + + b = snewn(bsize, unsigned char); b[0] = IAC; b[1] = SB; b[2] = telnet->sb_opt; b[3] = TELQUAL_IS; n = 4; - e = telnet->cfg.environmt; - while (*e) { + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { b[n++] = var; - while (*e && *e != '\t') - b[n++] = *e++; - if (*e == '\t') - e++; + for (e = ekey; *e; e++) + b[n++] = *e; b[n++] = value; - while (*e) - b[n++] = *e++; - e++; + for (e = eval; *e; e++) + b[n++] = *e; } - { - char user[sizeof(telnet->cfg.username)]; - (void) get_remote_username(&telnet->cfg, user, sizeof(user)); - if (*user) { - b[n++] = var; - b[n++] = 'U'; - b[n++] = 'S'; - b[n++] = 'E'; - b[n++] = 'R'; - b[n++] = value; - e = user; - while (*e) - b[n++] = *e++; - } - b[n++] = IAC; - b[n++] = SE; - telnet->bufsize = sk_write(telnet->s, (char *)b, n); - logbuf = dupprintf("client:\tSB %s IS %s%s%s%s", - telopt(telnet->sb_opt), - *user ? "USER=" : "", - user, - *user ? " " : "", - n == 6 ? "" : - (*telnet->cfg.environmt ? "" : "")); + if (user) { + b[n++] = var; + b[n++] = 'U'; + b[n++] = 'S'; + b[n++] = 'E'; + b[n++] = 'R'; + b[n++] = value; + for (e = user; *e; e++) + b[n++] = *e; + } + b[n++] = IAC; + b[n++] = SE; + telnet->bufsize = sk_write(telnet->s, (char *)b, n); + if (n == 6) { + logbuf = dupprintf("client:\tSB %s IS ", + telopt(telnet->sb_opt)); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } else { + logbuf = dupprintf("client:\tSB %s IS:", + telopt(telnet->sb_opt)); logevent(telnet->frontend, logbuf); sfree(logbuf); + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { + logbuf = dupprintf("\t%s=%s", ekey, eval); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } + if (user) { + logbuf = dupprintf("\tUSER=%s", user); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } } + sfree(b); + sfree(user); } break; } @@ -630,6 +662,7 @@ static void telnet_log(Plug plug, int type, SockAddr addr, int port, msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); logevent(telnet->frontend, msg); + sfree(msg); } static int telnet_closing(Plug plug, const char *error_msg, int error_code, @@ -637,9 +670,17 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code, { Telnet telnet = (Telnet) plug; + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + if (telnet->s) { sk_close(telnet->s); telnet->s = NULL; + if (error_msg) + telnet->closed_on_socket_error = TRUE; notify_remote_exit(telnet->frontend); } if (error_msg) { @@ -674,9 +715,8 @@ static void telnet_sent(Plug plug, int bufsize) * freed by the caller. */ static const char *telnet_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, - int nodelay, int keepalive) + Conf *conf, char *host, int port, + char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { telnet_log, @@ -687,19 +727,22 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Telnet telnet; + char *loghost; + int addressfamily; telnet = snew(struct telnet_tag); telnet->fn = &fn_table; - telnet->cfg = *cfg; /* STRUCTURE COPY */ + telnet->conf = conf_copy(conf); telnet->s = NULL; + telnet->closed_on_socket_error = FALSE; telnet->echoing = TRUE; telnet->editing = TRUE; telnet->activated = FALSE; telnet->sb_buf = NULL; telnet->sb_size = 0; telnet->frontend = frontend_handle; - telnet->term_width = telnet->cfg.width; - telnet->term_height = telnet->cfg.height; + telnet->term_width = conf_get_int(telnet->conf, CONF_width); + telnet->term_height = conf_get_int(telnet->conf, CONF_height); telnet->state = TOP_LEVEL; telnet->ldisc = NULL; telnet->pinger = NULL; @@ -710,14 +753,15 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, */ { char *buf; + addressfamily = conf_get_int(telnet->conf, CONF_addressfamily); buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(telnet->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, telnet->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -730,16 +774,16 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, * Open socket. */ telnet->s = new_connection(addr, *realhost, port, 0, 1, - nodelay, keepalive, (Plug) telnet, &telnet->cfg); + nodelay, keepalive, (Plug) telnet, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) return err; - telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet); + telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet); /* * Initialise option states. */ - if (telnet->cfg.passive_telnet) { + if (conf_get_int(telnet->conf, CONF_passive_telnet)) { const struct Opt *const *o; for (o = opts; *o; o++) @@ -768,11 +812,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, /* * loghost overrides realhost, if specified. */ - if (*telnet->cfg.loghost) { + loghost = conf_get_str(telnet->conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(telnet->cfg.loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -796,6 +841,7 @@ static void telnet_free(void *handle) sk_close(telnet->s); if (telnet->pinger) pinger_free(telnet->pinger); + conf_free(telnet->conf); sfree(telnet); } /* @@ -803,11 +849,12 @@ static void telnet_free(void *handle) * necessary, in this backend: we just save the fresh config for * any subsequent negotiations. */ -static void telnet_reconfig(void *handle, Config *cfg) +static void telnet_reconfig(void *handle, Conf *conf) { Telnet telnet = (Telnet) handle; - pinger_reconfig(telnet->pinger, &telnet->cfg, cfg); - telnet->cfg = *cfg; /* STRUCTURE COPY */ + pinger_reconfig(telnet->pinger, telnet->conf, conf); + conf_free(telnet->conf); + telnet->conf = conf_copy(conf); } /* @@ -1055,6 +1102,8 @@ static int telnet_exitcode(void *handle) Telnet telnet = (Telnet) handle; if (telnet->s != NULL) return -1; /* still connected */ + else if (telnet->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* Telnet doesn't transmit exit codes back to the client */ return 0; diff --git a/putty/TERMINAL.C b/putty/TERMINAL.C index 77ddfb1..7af66ae 100644 --- a/putty/TERMINAL.C +++ b/putty/TERMINAL.C @@ -989,7 +989,7 @@ static void resizeline(Terminal *term, termline *line, int cols) static int sblines(Terminal *term) { int sblines = count234(term->scrollback); - if (term->cfg.erase_to_scrollback && + if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { sblines += term->alt_sblines; } @@ -1015,7 +1015,7 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) assert(!screen); - if (term->cfg.erase_to_scrollback && + if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { altlines = term->alt_sblines; } @@ -1065,32 +1065,32 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); -static void term_timer(void *ctx, long now) +static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; int update = FALSE; - if (term->tblink_pending && now - term->next_tblink >= 0) { + if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = FALSE; term_schedule_tblink(term); update = TRUE; } - if (term->cblink_pending && now - term->next_cblink >= 0) { + if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = FALSE; term_schedule_cblink(term); update = TRUE; } - if (term->in_vbell && now - term->vbell_end >= 0) { + if (term->in_vbell && now == term->vbell_end) { term->in_vbell = FALSE; update = TRUE; } if (update || - (term->window_update_pending && now - term->next_update >= 0)) + (term->window_update_pending && now == term->next_update)) term_update(term); } @@ -1133,7 +1133,7 @@ static void term_schedule_tblink(Terminal *term) */ static void term_schedule_cblink(Terminal *term) { - if (term->cfg.blink_cur && term->has_focus) { + if (term->blink_cur && term->has_focus) { if (!term->cblink_pending) term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term); term->cblink_pending = TRUE; @@ -1197,11 +1197,11 @@ static void power_on(Terminal *term, int clear) for (i = 0; i < term->cols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } - term->alt_om = term->dec_om = term->cfg.dec_om; + term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); term->alt_ins = term->insert = FALSE; term->alt_wnext = term->wrapnext = term->save_wnext = term->alt_save_wnext = FALSE; - term->alt_wrap = term->wrap = term->cfg.wrap_mode; + term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0; term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0; term->utf_state = 0; @@ -1216,19 +1216,22 @@ static void power_on(Terminal *term, int clear) term->default_attr = term->save_attr = term->alt_save_attr = term->curr_attr = ATTR_DEFAULT; term->term_editing = term->term_echoing = FALSE; - term->app_cursor_keys = term->cfg.app_cursor; - term->app_keypad_keys = term->cfg.app_keypad; - term->use_bce = term->cfg.bce; - term->blink_is_real = term->cfg.blinktext; + term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor); + term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad); + term->use_bce = conf_get_int(term->conf, CONF_bce); + term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); term->xterm_mouse = 0; + term->xterm_extended_mouse = 0; + term->urxvt_extended_mouse = 0; set_raw_mouse_mode(term->frontend, FALSE); + term->bracketed_paste = FALSE; { int i; for (i = 0; i < 256; i++) - term->wordness[i] = term->cfg.wordness[i]; + term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); } if (term->screen) { swap_screen(term, 1, FALSE, FALSE); @@ -1261,7 +1264,7 @@ void term_update(Terminal *term) ctx = get_ctx(term->frontend); if (ctx) { int need_sbar_update = term->seen_disp_event; - if (term->seen_disp_event && term->cfg.scroll_on_disp) { + if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = 0; need_sbar_update = TRUE; @@ -1300,7 +1303,7 @@ void term_seen_key_event(Terminal *term) /* * Reset the scrollback on keypress, if we're doing that. */ - if (term->cfg.scroll_on_key) { + if (term->scroll_on_key) { term->disptop = 0; /* return to main screen */ seen_disp_event(term); } @@ -1328,12 +1331,82 @@ static void set_erase_char(Terminal *term) } /* + * We copy a bunch of stuff out of the Conf structure into local + * fields in the Terminal structure, to avoid the repeated tree234 + * lookups which would be involved in fetching them from the former + * every time. + */ +void term_copy_stuff_from_conf(Terminal *term) +{ + term->ansi_colour = conf_get_int(term->conf, CONF_ansi_colour); + term->arabicshaping = conf_get_int(term->conf, CONF_arabicshaping); + term->beep = conf_get_int(term->conf, CONF_beep); + term->bellovl = conf_get_int(term->conf, CONF_bellovl); + term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n); + term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s); + term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t); + term->bidi = conf_get_int(term->conf, CONF_bidi); + term->bksp_is_delete = conf_get_int(term->conf, CONF_bksp_is_delete); + term->blink_cur = conf_get_int(term->conf, CONF_blink_cur); + term->blinktext = conf_get_int(term->conf, CONF_blinktext); + term->cjk_ambig_wide = conf_get_int(term->conf, CONF_cjk_ambig_wide); + term->conf_height = conf_get_int(term->conf, CONF_height); + term->conf_width = conf_get_int(term->conf, CONF_width); + term->crhaslf = conf_get_int(term->conf, CONF_crhaslf); + term->erase_to_scrollback = conf_get_int(term->conf, CONF_erase_to_scrollback); + term->funky_type = conf_get_int(term->conf, CONF_funky_type); + term->lfhascr = conf_get_int(term->conf, CONF_lfhascr); + term->logflush = conf_get_int(term->conf, CONF_logflush); + term->logtype = conf_get_int(term->conf, CONF_logtype); + term->mouse_override = conf_get_int(term->conf, CONF_mouse_override); + term->nethack_keypad = conf_get_int(term->conf, CONF_nethack_keypad); + term->no_alt_screen = conf_get_int(term->conf, CONF_no_alt_screen); + term->no_applic_c = conf_get_int(term->conf, CONF_no_applic_c); + term->no_applic_k = conf_get_int(term->conf, CONF_no_applic_k); + term->no_dbackspace = conf_get_int(term->conf, CONF_no_dbackspace); + term->no_mouse_rep = conf_get_int(term->conf, CONF_no_mouse_rep); + term->no_remote_charset = conf_get_int(term->conf, CONF_no_remote_charset); + term->no_remote_resize = conf_get_int(term->conf, CONF_no_remote_resize); + term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle); + term->rawcnp = conf_get_int(term->conf, CONF_rawcnp); + term->rect_select = conf_get_int(term->conf, CONF_rect_select); + term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); + term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend); + term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp); + term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key); + term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour); + + /* + * Parse the control-character escapes in the configured + * answerback string. + */ + { + char *answerback = conf_get_str(term->conf, CONF_answerback); + int maxlen = strlen(answerback); + + term->answerback = snewn(maxlen, char); + term->answerbacklen = 0; + + while (*answerback) { + char *n; + char c = ctrlparse(answerback, &n); + if (n) { + term->answerback[term->answerbacklen++] = c; + answerback = n; + } else { + term->answerback[term->answerbacklen++] = *answerback++; + } + } + } +} + +/* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the * user has disabled mouse reporting, and abandon a print job if * the user has disabled printing. */ -void term_reconfig(Terminal *term, Config *cfg) +void term_reconfig(Terminal *term, Conf *conf) { /* * Before adopting the new config, check all those terminal @@ -1345,21 +1418,28 @@ void term_reconfig(Terminal *term, Config *cfg) int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; int i; - reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode); - reset_decom = (term->cfg.dec_om != cfg->dec_om); - reset_bce = (term->cfg.bce != cfg->bce); - reset_tblink = (term->cfg.blinktext != cfg->blinktext); + reset_wrap = (conf_get_int(term->conf, CONF_wrap_mode) != + conf_get_int(conf, CONF_wrap_mode)); + reset_decom = (conf_get_int(term->conf, CONF_dec_om) != + conf_get_int(conf, CONF_dec_om)); + reset_bce = (conf_get_int(term->conf, CONF_bce) != + conf_get_int(conf, CONF_bce)); + reset_tblink = (conf_get_int(term->conf, CONF_blinktext) != + conf_get_int(conf, CONF_blinktext)); reset_charclass = 0; - for (i = 0; i < lenof(term->cfg.wordness); i++) - if (term->cfg.wordness[i] != cfg->wordness[i]) + for (i = 0; i < 256; i++) + if (conf_get_int_int(term->conf, CONF_wordness, i) != + conf_get_int_int(conf, CONF_wordness, i)) reset_charclass = 1; /* * If the bidi or shaping settings have changed, flush the bidi * cache completely. */ - if (term->cfg.arabicshaping != cfg->arabicshaping || - term->cfg.bidi != cfg->bidi) { + if (conf_get_int(term->conf, CONF_arabicshaping) != + conf_get_int(conf, CONF_arabicshaping) || + conf_get_int(term->conf, CONF_bidi) != + conf_get_int(conf, CONF_bidi)) { for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); @@ -1370,39 +1450,41 @@ void term_reconfig(Terminal *term, Config *cfg) } } - term->cfg = *cfg; /* STRUCTURE COPY */ + conf_free(term->conf); + term->conf = conf_copy(conf); if (reset_wrap) - term->alt_wrap = term->wrap = term->cfg.wrap_mode; + term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); if (reset_decom) - term->alt_om = term->dec_om = term->cfg.dec_om; + term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); if (reset_bce) { - term->use_bce = term->cfg.bce; + term->use_bce = conf_get_int(term->conf, CONF_bce); set_erase_char(term); } if (reset_tblink) { - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); } if (reset_charclass) for (i = 0; i < 256; i++) - term->wordness[i] = term->cfg.wordness[i]; + term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); - if (term->cfg.no_alt_screen) + if (conf_get_int(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, FALSE, FALSE); - if (term->cfg.no_mouse_rep) { + if (conf_get_int(term->conf, CONF_no_mouse_rep)) { term->xterm_mouse = 0; set_raw_mouse_mode(term->frontend, 0); } - if (term->cfg.no_remote_charset) { + if (conf_get_int(term->conf, CONF_no_remote_charset)) { term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII; term->sco_acs = term->alt_sco_acs = 0; term->utf = 0; } - if (!*term->cfg.printer) { + if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } term_schedule_tblink(term); term_schedule_cblink(term); + term_copy_stuff_from_conf(term); } /* @@ -1423,7 +1505,7 @@ void term_clrsb(Terminal *term) /* * Initialise the terminal. */ -Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, +Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, void *frontend) { Terminal *term; @@ -1435,7 +1517,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, term = snew(Terminal); term->frontend = frontend; term->ucsdata = ucsdata; - term->cfg = *mycfg; /* STRUCTURE COPY */ + term->conf = conf_copy(myconf); term->logctx = NULL; term->compatibility_level = TM_PUTTY; strcpy(term->id_string, "\033[?6c"); @@ -1459,6 +1541,8 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata, term->selstate = NO_SELECTION; term->curstype = 0; + term_copy_stuff_from_conf(term); + term->screen = term->alt_screen = term->scrollback = NULL; term->tempsblines = 0; term->alt_sblines = 0; @@ -1537,12 +1621,18 @@ void term_free(Terminal *term) for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); + sfree(term->post_bidi_cache[i].forward); + sfree(term->post_bidi_cache[i].backward); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); + sfree(term->tabs); + expire_timer_context(term); + conf_free(term->conf); + sfree(term); } @@ -1627,7 +1717,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) while (term->rows > newrows) { if (term->curs.y < term->rows - 1) { /* delete bottom row, unless it contains the cursor */ - sfree(delpos234(term->screen, term->rows - 1)); + line = delpos234(term->screen, term->rows - 1); + freeline(line); } else { /* push top row to scrollback */ line = delpos234(term->screen, 0); @@ -1889,7 +1980,7 @@ static void check_selection(Terminal *term, pos from, pos to) static void scroll(Terminal *term, int topline, int botline, int lines, int sb) { termline *line; - int i, seltop; + int i, seltop, scrollwinsize; #ifdef OPTIMISE_SCROLL int olddisptop, shift; #endif /* OPTIMISE_SCROLL */ @@ -1901,8 +1992,14 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) olddisptop = term->disptop; shift = lines; #endif /* OPTIMISE_SCROLL */ + + scrollwinsize = botline - topline + 1; + if (lines < 0) { - while (lines < 0) { + lines = -lines; + if (lines > scrollwinsize) + lines = scrollwinsize; + while (lines-- > 0) { line = delpos234(term->screen, botline); resizeline(term, line, term->cols); for (i = 0; i < term->cols; i++) @@ -1924,11 +2021,11 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) term->selend.x = 0; } } - - lines++; } } else { - while (lines > 0) { + if (lines > scrollwinsize) + lines = scrollwinsize; + while (lines-- > 0) { line = delpos234(term->screen, topline); #ifdef TERM_CC_DIAGS cc_check(line); @@ -2014,8 +2111,6 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) } } } - - lines--; } } #ifdef OPTIMISE_SCROLL @@ -2243,7 +2338,7 @@ static void erase_lots(Terminal *term, if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; - if (term->cfg.erase_to_scrollback && erasing_lines_from_top) { + if (term->erase_to_scrollback && erasing_lines_from_top) { /* If it's a whole number of lines, starting at the top, and * we're fully erasing them, erase by scrolling and keep the * lines in the scrollback. */ @@ -2334,13 +2429,13 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = term->blinktext; } term_schedule_tblink(term); break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, state ? 132 : 80, term->rows); term->reset_132 = state; term->alt_t = term->marg_t = 0; @@ -2387,7 +2482,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 47: /* alternate screen */ compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE); + swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE); term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ @@ -2398,28 +2493,37 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) term->xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(term->frontend, state); break; + case 1006: /* xterm extended mouse */ + term->xterm_extended_mouse = state ? 1 : 0; + break; + case 1015: /* urxvt extended mouse */ + term->urxvt_extended_mouse = state ? 1 : 0; + break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE); + swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE); term->disptop = 0; break; case 1048: /* save/restore cursor */ - if (!term->cfg.no_alt_screen) + if (!term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); break; case 1049: /* cursor & alternate screen */ - if (state && !term->cfg.no_alt_screen) + if (state && !term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); compatibility(OTHER); deselect(term); - swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE); - if (!state && !term->cfg.no_alt_screen) + swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE); + if (!state && !term->no_alt_screen) save_cursor(term, state); term->disptop = 0; break; + case 2004: /* xterm bracketed paste */ + term->bracketed_paste = state ? TRUE : FALSE; + break; } else switch (mode) { case 4: /* IRM: set insert mode */ @@ -2454,14 +2558,14 @@ static void do_osc(Terminal *term) switch (term->esc_args[0]) { case 0: case 1: - if (!term->cfg.no_remote_wintitle) + if (!term->no_remote_wintitle) set_icon(term->frontend, term->osc_string); if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: - if (!term->cfg.no_remote_wintitle) + if (!term->no_remote_wintitle) set_title(term->frontend, term->osc_string); break; } @@ -2471,10 +2575,10 @@ static void do_osc(Terminal *term) /* * ANSI printing routines. */ -static void term_print_setup(Terminal *term) +static void term_print_setup(Terminal *term, char *printer) { bufchain_clear(&term->printer_buf); - term->print_job = printer_start_job(term->cfg.printer); + term->print_job = printer_start_job(printer); } static void term_print_flush(Terminal *term) { @@ -2549,7 +2653,7 @@ static void term_out(Terminal *term) * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ - if (term->cfg.logtype == LGTYP_DEBUG && term->logctx) + if (term->logtype == LGTYP_DEBUG && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG); } else { c = unget; @@ -2741,7 +2845,7 @@ static void term_out(Terminal *term) term->curs.x--; term->wrapnext = FALSE; /* destructive backspace might be disabled */ - if (!term->cfg.no_dbackspace) { + if (!term->no_dbackspace) { check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); copy_termchar(scrlineptr(term->curs.y), @@ -2761,19 +2865,8 @@ static void term_out(Terminal *term) */ compatibility(ANSIMIN); if (term->ldisc) { - char abuf[lenof(term->cfg.answerback)], *s, *d; - for (s = term->cfg.answerback, d = abuf; *s;) { - char *n; - char c = ctrlparse(s, &n); - if (n) { - *d++ = c; - s = n; - } else { - *d++ = *s++; - } - } lpage_send(term->ldisc, DEFAULT_CODEPAGE, - abuf, d - abuf, 0); + term->answerback, term->answerbacklen, 0); } break; case '\007': /* BEL: Bell */ @@ -2800,7 +2893,7 @@ static void term_out(Terminal *term) * t seconds ago. */ while (term->beephead && - term->beephead->ticks < ticks - term->cfg.bellovl_t) { + term->beephead->ticks < ticks - term->bellovl_t) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); @@ -2809,16 +2902,16 @@ static void term_out(Terminal *term) term->nbeeps--; } - if (term->cfg.bellovl && term->beep_overloaded && - ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) { + if (term->bellovl && term->beep_overloaded && + ticks - term->lastbeep >= (unsigned)term->bellovl_s) { /* * If we're currently overloaded and the * last beep was more than s seconds ago, * leave overload mode. */ term->beep_overloaded = FALSE; - } else if (term->cfg.bellovl && !term->beep_overloaded && - term->nbeeps >= term->cfg.bellovl_n) { + } else if (term->bellovl && !term->beep_overloaded && + term->nbeeps >= term->bellovl_n) { /* * Now, if we have n or more beeps * remaining in the queue, go into overload @@ -2831,10 +2924,10 @@ static void term_out(Terminal *term) /* * Perform an actual beep if we're not overloaded. */ - if (!term->cfg.bellovl || !term->beep_overloaded) { - do_beep(term->frontend, term->cfg.beep); + if (!term->bellovl || !term->beep_overloaded) { + do_beep(term->frontend, term->beep); - if (term->cfg.beep == BELL_VISUAL) { + if (term->beep == BELL_VISUAL) { term_schedule_vbell(term, FALSE, 0); } } @@ -2876,12 +2969,12 @@ static void term_out(Terminal *term) 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->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; @@ -2901,7 +2994,7 @@ static void term_out(Terminal *term) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; - if (term->cfg.lfhascr) + if (term->lfhascr) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); @@ -2943,9 +3036,9 @@ static void term_out(Terminal *term) if (DIRECT_CHAR(c)) width = 1; if (!width) - width = (term->cfg.cjk_ambig_wide ? - mk_wcwidth_cjk((wchar_t) c) : - mk_wcwidth((wchar_t) c)); + width = (term->cjk_ambig_wide ? + mk_wcwidth_cjk((unsigned int) c) : + mk_wcwidth((unsigned int) c)); if (term->wrapnext && term->wrap && width > 0) { cline->lattr |= LATTR_WRAPPED; @@ -3166,7 +3259,7 @@ static void term_out(Terminal *term) if (term->ldisc) /* cause ldisc to notice changes */ ldisc_send(term->ldisc, NULL, 0, 0); if (term->reset_132) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); term->reset_132 = 0; } @@ -3231,55 +3324,55 @@ static void term_out(Terminal *term) /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_GBCHR; break; case ANSI('B', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_ASCII; break; case ANSI('0', '('): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_LINEDRW; break; case ANSI('U', '('): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[0] = CSET_SCOACS; break; /* G1D4: G1-designate 94-set */ case ANSI('A', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_GBCHR; break; case ANSI('B', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_ASCII; break; case ANSI('0', ')'): compatibility(VT100); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_LINEDRW; break; case ANSI('U', ')'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->cset_attr[1] = CSET_SCOACS; break; /* DOCS: Designate other coding system */ case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->utf = 1; break; case ANSI('@', '%'): compatibility(OTHER); - if (!term->cfg.no_remote_charset) + if (!term->no_remote_charset) term->utf = 0; break; } @@ -3463,12 +3556,15 @@ static void term_out(Terminal *term) case ANSI_QUE('i'): compatibility(VT100); { + char *printer; if (term->esc_nargs != 1) break; - if (term->esc_args[0] == 5 && *term->cfg.printer) { + if (term->esc_args[0] == 5 && + (printer = conf_get_str(term->conf, + CONF_printer))[0]) { term->printing = TRUE; term->only_printing = !term->esc_query; term->print_state = 0; - term_print_setup(term); + term_print_setup(term, printer); } else if (term->esc_args[0] == 4 && term->printing) { term_print_finish(term); @@ -3591,15 +3687,15 @@ static void term_out(Terminal *term) break; case 10: /* SCO acs off */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 0; break; case 11: /* SCO acs on */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 1; break; case 12: /* SCO acs on, |0x80 */ compatibility(SCOANSI); - if (term->cfg.no_remote_charset) break; + if (term->no_remote_charset) break; term->sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); @@ -3722,7 +3818,7 @@ static void term_out(Terminal *term) && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], 24)); deselect(term); @@ -3742,7 +3838,7 @@ static void term_out(Terminal *term) break; case 3: if (term->esc_nargs >= 3) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) move_window(term->frontend, def(term->esc_args[1], 0), def(term->esc_args[2], 0)); @@ -3767,10 +3863,10 @@ static void term_out(Terminal *term) break; case 8: if (term->esc_nargs >= 3) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, - def(term->esc_args[2], term->cfg.width), - def(term->esc_args[1], term->cfg.height)); + def(term->esc_args[2], term->conf_width), + def(term->esc_args[1], term->conf_height)); } break; case 9: @@ -3825,8 +3921,8 @@ static void term_out(Terminal *term) break; case 20: if (term->ldisc && - term->cfg.remote_qtitle_action != TITLE_NONE) { - if(term->cfg.remote_qtitle_action == TITLE_REAL) + term->remote_qtitle_action != TITLE_NONE) { + if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, TRUE); else p = EMPTY_WINDOW_TITLE; @@ -3838,8 +3934,8 @@ static void term_out(Terminal *term) break; case 21: if (term->ldisc && - term->cfg.remote_qtitle_action != TITLE_NONE) { - if(term->cfg.remote_qtitle_action == TITLE_REAL) + term->remote_qtitle_action != TITLE_NONE) { + if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, FALSE); else p = EMPTY_WINDOW_TITLE; @@ -3875,10 +3971,10 @@ static void term_out(Terminal *term) */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], - term->cfg.height)); + term->conf_height)); deselect(term); } break; @@ -3890,10 +3986,11 @@ static void term_out(Terminal *term) */ compatibility(VT340TEXT); if (term->esc_nargs <= 1) { - if (!term->cfg.no_remote_resize) + if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[0], - term->cfg.width), term->rows); + term->conf_width), + term->rows); deselect(term); } break; @@ -4095,7 +4192,7 @@ static void term_out(Terminal *term) * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { - if (!term->cfg.no_remote_resize) { + if (!term->no_remote_resize) { if (term->reset_132) request_resize(132, 24); else @@ -4330,7 +4427,7 @@ static void term_out(Terminal *term) * emulation. */ term->vt52_mode = FALSE; - term->blink_is_real = term->cfg.blinktext; + term->blink_is_real = term->blinktext; term_schedule_tblink(term); break; #if 0 @@ -4474,7 +4571,7 @@ static void term_out(Terminal *term) } term_print_flush(term); - if (term->cfg.logflush) + if (term->logflush) logflush(term->logctx); } @@ -4577,7 +4674,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int it; /* Do Arabic shaping and bidi. */ - if(!term->cfg.bidi || !term->cfg.arabicshaping) { + if(!term->bidi || !term->arabicshaping) { if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) { @@ -4595,7 +4692,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, switch (uc & CSET_MASK) { case CSET_LINEDRW: - if (!term->cfg.rawcnp) { + if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } @@ -4616,19 +4713,19 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, } term->wcFrom[it].origwc = term->wcFrom[it].wc = - (wchar_t)uc; + (unsigned int)uc; term->wcFrom[it].index = it; } - if(!term->cfg.bidi) + if(!term->bidi) do_bidi(term->wcFrom, term->cols); /* this is saved iff done from inside the shaping */ - if(!term->cfg.bidi && term->cfg.arabicshaping) + if(!term->bidi && term->arabicshaping) for(it=0; itcols; it++) term->wcTo[it] = term->wcFrom[it]; - if(!term->cfg.arabicshaping) + if(!term->arabicshaping) do_shape(term->wcFrom, term->wcTo, term->cols); if (term->ltemp_size < ldata->size) { @@ -4690,14 +4787,14 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) /* Depends on: * screen array, disptop, scrtop, * selection, rv, - * cfg.blinkpc, blink_is_real, tblinker, - * curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext + * blinkpc, blink_is_real, tblinker, + * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext */ /* Has the cursor position or type changed ? */ if (term->cursor_on) { if (term->has_focus) { - if (term->cblinker || !term->cfg.blink_cur) + if (term->cblinker || !term->blink_cur) cursor = TATTR_ACTCURS; else cursor = 0; @@ -4805,11 +4902,11 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) tchar = d->chr; tattr = d->attr; - if (!term->cfg.ansi_colour) + if (!term->ansi_colour) tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | ATTR_DEFFG | ATTR_DEFBG; - if (!term->cfg.xterm_256_colour) { + if (!term->xterm_256_colour) { int colour; colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT; if (colour >= 16 && colour < 256) @@ -4939,11 +5036,13 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) break_run = ((tattr ^ attr) & term->attr_mask) != 0; +#ifdef USES_VTLINE_HACK /* Special hack for VT100 Linedraw glyphs */ if ((tchar >= 0x23BA && tchar <= 0x23BD) || (j > 0 && (newline[j-1].chr >= 0x23BA && newline[j-1].chr <= 0x23BD))) break_run = TRUE; +#endif /* * Separate out sequences of characters that have the @@ -4991,10 +5090,17 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) dirty_run = TRUE; } - if (ccount >= chlen) { + if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } + +#ifdef PLATFORM_IS_UTF16 + if (tchar > 0x10000 && tchar < 0x110000) { + ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(tchar); + ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(tchar); + } else +#endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) tchar; if (d->cc_next) { @@ -5018,10 +5124,17 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) break; } - if (ccount >= chlen) { + if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } + +#ifdef PLATFORM_IS_UTF16 + if (schar > 0x10000 && schar < 0x110000) { + ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(schar); + ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(schar); + } else +#endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) schar; } @@ -5267,7 +5380,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) switch (uc & CSET_MASK) { case CSET_LINEDRW: - if (!term->cfg.rawcnp) { + if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } @@ -5521,7 +5634,8 @@ static pos sel_spread_half(Terminal *term, pos p, int dir) else break; } else { - if (ldata->lattr & LATTR_WRAPPED) { + if (p.y+1 < term->rows && + (ldata->lattr & LATTR_WRAPPED)) { termline *ldata2; ldata2 = lineptr(p.y+1); if (wordtype(term, UCSGET(ldata2->chars, 0)) @@ -5606,7 +5720,12 @@ void term_do_paste(Terminal *term) if (term->paste_buffer) sfree(term->paste_buffer); term->paste_pos = term->paste_hold = term->paste_len = 0; - term->paste_buffer = snewn(len, wchar_t); + term->paste_buffer = snewn(len + 12, wchar_t); + + if (term->bracketed_paste) { + memcpy(term->paste_buffer, L"\033[200~", 6 * sizeof(wchar_t)); + term->paste_len += 6; + } p = q = data; while (p < data + len) { @@ -5630,6 +5749,12 @@ void term_do_paste(Terminal *term) q = p; } + if (term->bracketed_paste) { + memcpy(term->paste_buffer + term->paste_len, + L"\033[201~", 6 * sizeof(wchar_t)); + term->paste_len += 6; + } + /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) @@ -5649,8 +5774,8 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, pos selpoint; termline *ldata; int raw_mouse = (term->xterm_mouse && - !term->cfg.no_mouse_rep && - !(term->cfg.mouse_override && shift)); + !term->no_mouse_rep && + !(term->mouse_override && shift)); int default_seltype; if (y < 0) { @@ -5701,25 +5826,26 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c; - char abuf[16]; + char abuf[32]; + int len = 0; if (term->ldisc) { switch (braw) { case MBT_LEFT: - encstate = 0x20; /* left button down */ + encstate = 0x00; /* left button down */ break; case MBT_MIDDLE: - encstate = 0x21; + encstate = 0x01; break; case MBT_RIGHT: - encstate = 0x22; + encstate = 0x02; break; case MBT_WHEEL_UP: - encstate = 0x60; + encstate = 0x40; break; case MBT_WHEEL_DOWN: - encstate = 0x61; + encstate = 0x41; break; default: break; /* placate gcc warning about enum use */ } @@ -5730,7 +5856,9 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate += 0x20; break; case MA_RELEASE: - encstate = 0x23; + /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */ + if (!term->xterm_extended_mouse) + encstate = 0x03; term->mouse_is_down = 0; break; case MA_CLICK: @@ -5744,11 +5872,18 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate += 0x04; if (ctrl) encstate += 0x10; - r = y + 33; - c = x + 33; - - sprintf(abuf, "\033[M%c%c%c", encstate, c, r); - ldisc_send(term->ldisc, abuf, 6, 0); + r = y + 1; + c = x + 1; + + /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */ + if (term->xterm_extended_mouse) { + len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M'); + } else if (term->urxvt_extended_mouse) { + len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r); + } else if (c <= 223 && r <= 223) { + len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32); + } + ldisc_send(term->ldisc, abuf, len, 0); } return; } @@ -5757,7 +5892,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, * Set the selection type (rectangular or normal) at the start * of a selection attempt, from the state of Alt. */ - if (!alt ^ !term->cfg.rect_select) + if (!alt ^ !term->rect_select) default_seltype = RECTANGULAR; else default_seltype = LEXICOGRAPHIC; @@ -5867,6 +6002,13 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, request_paste(term->frontend); } + /* + * Since terminal output is suppressed during drag-selects, we + * should make sure to write any pending output if one has just + * finished. + */ + if (term->selstate != DRAGGING) + term_out(term); term_update(term); } @@ -5877,7 +6019,7 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", xkey); else { - int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c); + int app_flg = (term->app_cursor_keys && !term->no_applic_c); #if 0 /* * RDB: VT100 & VT102 manuals both state the app cursor @@ -5906,433 +6048,6 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) return p - buf; } -void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen, - unsigned int modifiers, unsigned int flags) -{ - char output[10]; - char *p = output; - int prependesc = FALSE; -#if 0 - int i; - - fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen); - for (i = 0; i < tlen; i++) - fprintf(stderr, " %04x", (unsigned)text[i]); - fprintf(stderr, "\n"); -#endif - - /* XXX Num Lock */ - if ((flags & PKF_REPEAT) && term->repeat_off) - return; - - /* Currently, Meta always just prefixes everything with ESC. */ - if (modifiers & PKM_META) - prependesc = TRUE; - modifiers &= ~PKM_META; - - /* - * Alt is only used for Alt+keypad, which isn't supported yet, so - * ignore it. - */ - modifiers &= ~PKM_ALT; - - /* Standard local function keys */ - switch (modifiers & (PKM_SHIFT | PKM_CONTROL)) { - case PKM_SHIFT: - if (keysym == PK_PAGEUP) - /* scroll up one page */; - if (keysym == PK_PAGEDOWN) - /* scroll down on page */; - if (keysym == PK_INSERT) - term_do_paste(term); - break; - case PKM_CONTROL: - if (keysym == PK_PAGEUP) - /* scroll up one line */; - if (keysym == PK_PAGEDOWN) - /* scroll down one line */; - /* Control-Numlock for app-keypad mode switch */ - if (keysym == PK_PF1) - term->app_keypad_keys ^= 1; - break; - } - - if (modifiers & PKM_ALT) { - /* Alt+F4 (close) */ - /* Alt+Return (full screen) */ - /* Alt+Space (system menu) */ - } - - if (keysym == PK_NULL && (modifiers & PKM_CONTROL) && tlen == 1 && - text[0] >= 0x20 && text[0] <= 0x7e) { - /* ASCII chars + Control */ - if ((text[0] >= 0x40 && text[0] <= 0x5f) || - (text[0] >= 0x61 && text[0] <= 0x7a)) - text[0] &= 0x1f; - else { - /* - * Control-2 should return ^@ (0x00), Control-6 should return - * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since - * the DOS keyboard handling did it, and we have nothing better - * to do with the key combo in question, we'll also map - * Control-Backquote to ^\ (0x1C). - */ - switch (text[0]) { - case ' ': text[0] = 0x00; break; - case '-': text[0] = 0x1f; break; - case '/': text[0] = 0x1f; break; - case '2': text[0] = 0x00; break; - case '3': text[0] = 0x1b; break; - case '4': text[0] = 0x1c; break; - case '5': text[0] = 0x1d; break; - case '6': text[0] = 0x1e; break; - case '7': text[0] = 0x1f; break; - case '8': text[0] = 0x7f; break; - case '`': text[0] = 0x1c; break; - } - } - } - - /* Nethack keypad */ - if (term->cfg.nethack_keypad) { - char c = 0; - switch (keysym) { - case PK_KP1: c = 'b'; break; - case PK_KP2: c = 'j'; break; - case PK_KP3: c = 'n'; break; - case PK_KP4: c = 'h'; break; - case PK_KP5: c = '.'; break; - case PK_KP6: c = 'l'; break; - case PK_KP7: c = 'y'; break; - case PK_KP8: c = 'k'; break; - case PK_KP9: c = 'u'; break; - default: break; /* else gcc warns `enum value not used' */ - } - if (c != 0) { - if (c != '.') { - if (modifiers & PKM_CONTROL) - c &= 0x1f; - else if (modifiers & PKM_SHIFT) - c = toupper((unsigned char)c); - } - *p++ = c; - goto done; - } - } - - /* Numeric Keypad */ - if (PK_ISKEYPAD(keysym)) { - int xkey = 0; - - /* - * In VT400 mode, PFn always emits an escape sequence. In - * Linux and tilde modes, this only happens in app keypad mode. - */ - if (term->cfg.funky_type == FUNKY_VT400 || - ((term->cfg.funky_type == FUNKY_LINUX || - term->cfg.funky_type == FUNKY_TILDE) && - term->app_keypad_keys && !term->cfg.no_applic_k)) { - switch (keysym) { - case PK_PF1: xkey = 'P'; break; - case PK_PF2: xkey = 'Q'; break; - case PK_PF3: xkey = 'R'; break; - case PK_PF4: xkey = 'S'; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - if (term->app_keypad_keys && !term->cfg.no_applic_k) { - switch (keysym) { - case PK_KP0: xkey = 'p'; break; - case PK_KP1: xkey = 'q'; break; - case PK_KP2: xkey = 'r'; break; - case PK_KP3: xkey = 's'; break; - case PK_KP4: xkey = 't'; break; - case PK_KP5: xkey = 'u'; break; - case PK_KP6: xkey = 'v'; break; - case PK_KP7: xkey = 'w'; break; - case PK_KP8: xkey = 'x'; break; - case PK_KP9: xkey = 'y'; break; - case PK_KPDECIMAL: xkey = 'n'; break; - case PK_KPENTER: xkey = 'M'; break; - default: break; /* else gcc warns `enum value not used' */ - } - if (term->cfg.funky_type == FUNKY_XTERM && tlen > 0) { - /* - * xterm can't see the layout of the keypad, so it has - * to rely on the X keysyms returned by the keys. - * Hence, we look at the strings here, not the PuTTY - * keysyms (which describe the layout). - */ - switch (text[0]) { - case '+': - if (modifiers & PKM_SHIFT) - xkey = 'l'; - else - xkey = 'k'; - break; - case '/': xkey = 'o'; break; - case '*': xkey = 'j'; break; - case '-': xkey = 'm'; break; - } - } else { - /* - * In all other modes, we try to retain the layout of - * the DEC keypad in application mode. - */ - switch (keysym) { - case PK_KPBIGPLUS: - /* This key covers the '-' and ',' keys on a VT220 */ - if (modifiers & PKM_SHIFT) - xkey = 'm'; /* VT220 '-' */ - else - xkey = 'l'; /* VT220 ',' */ - break; - case PK_KPMINUS: xkey = 'm'; break; - case PK_KPCOMMA: xkey = 'l'; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - } - if (xkey) { - if (term->vt52_mode) { - if (xkey >= 'P' && xkey <= 'S') - p += sprintf((char *) p, "\x1B%c", xkey); - else - p += sprintf((char *) p, "\x1B?%c", xkey); - } else - p += sprintf((char *) p, "\x1BO%c", xkey); - goto done; - } - /* Not in application mode -- treat the number pad as arrow keys? */ - if ((flags & PKF_NUMLOCK) == 0) { - switch (keysym) { - case PK_KP0: keysym = PK_INSERT; break; - case PK_KP1: keysym = PK_END; break; - case PK_KP2: keysym = PK_DOWN; break; - case PK_KP3: keysym = PK_PAGEDOWN; break; - case PK_KP4: keysym = PK_LEFT; break; - case PK_KP5: keysym = PK_REST; break; - case PK_KP6: keysym = PK_RIGHT; break; - case PK_KP7: keysym = PK_HOME; break; - case PK_KP8: keysym = PK_UP; break; - case PK_KP9: keysym = PK_PAGEUP; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - } - - /* Miscellaneous keys */ - switch (keysym) { - case PK_ESCAPE: - *p++ = 0x1b; - goto done; - case PK_BACKSPACE: - if (modifiers == 0) - *p++ = (term->cfg.bksp_is_delete ? 0x7F : 0x08); - else if (modifiers == PKM_SHIFT) - /* We do the opposite of what is configured */ - *p++ = (term->cfg.bksp_is_delete ? 0x08 : 0x7F); - else break; - goto done; - case PK_TAB: - if (modifiers == 0) - *p++ = 0x09; - else if (modifiers == PKM_SHIFT) - *p++ = 0x1B, *p++ = '[', *p++ = 'Z'; - else break; - goto done; - /* XXX window.c has ctrl+shift+space sending 0xa0 */ - case PK_PAUSE: - if (modifiers == PKM_CONTROL) - *p++ = 26; - else break; - goto done; - case PK_RETURN: - case PK_KPENTER: /* Odd keypad modes handled above */ - if (modifiers == 0) { - *p++ = 0x0d; - if (term->cr_lf_return) - *p++ = 0x0a; - goto done; - } - default: break; /* else gcc warns `enum value not used' */ - } - - /* SCO function keys and editing keys */ - if (term->cfg.funky_type == FUNKY_SCO) { - if (PK_ISFKEY(keysym) && keysym <= PK_F12) { - static char const codes[] = - "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{"; - int index = keysym - PK_F1; - - if (modifiers & PKM_SHIFT) index += 12; - if (modifiers & PKM_CONTROL) index += 24; - p += sprintf((char *) p, "\x1B[%c", codes[index]); - goto done; - } - if (PK_ISEDITING(keysym)) { - int xkey = 0; - - switch (keysym) { - case PK_DELETE: *p++ = 0x7f; goto done; - case PK_HOME: xkey = 'H'; break; - case PK_INSERT: xkey = 'L'; break; - case PK_END: xkey = 'F'; break; - case PK_PAGEUP: xkey = 'I'; break; - case PK_PAGEDOWN: xkey = 'G'; break; - default: break; /* else gcc warns `enum value not used' */ - } - p += sprintf((char *) p, "\x1B[%c", xkey); - } - } - - if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) { - int code; - - if (term->cfg.funky_type == FUNKY_XTERM) { - /* Xterm shuffles these keys, apparently. */ - switch (keysym) { - case PK_HOME: keysym = PK_INSERT; break; - case PK_INSERT: keysym = PK_HOME; break; - case PK_DELETE: keysym = PK_END; break; - case PK_END: keysym = PK_PAGEUP; break; - case PK_PAGEUP: keysym = PK_DELETE; break; - case PK_PAGEDOWN: keysym = PK_PAGEDOWN; break; - default: break; /* else gcc warns `enum value not used' */ - } - } - - /* RXVT Home/End */ - if (term->cfg.rxvt_homeend && - (keysym == PK_HOME || keysym == PK_END)) { - p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw"); - goto done; - } - - if (term->vt52_mode) { - int xkey; - - /* - * A real VT52 doesn't have these, and a VT220 doesn't - * send anything for them in VT52 mode. - */ - switch (keysym) { - case PK_HOME: xkey = 'H'; break; - case PK_INSERT: xkey = 'L'; break; - case PK_DELETE: xkey = 'M'; break; - case PK_END: xkey = 'E'; break; - case PK_PAGEUP: xkey = 'I'; break; - case PK_PAGEDOWN: xkey = 'G'; break; - default: xkey=0; break; /* else gcc warns `enum value not used'*/ - } - p += sprintf((char *) p, "\x1B%c", xkey); - goto done; - } - - switch (keysym) { - case PK_HOME: code = 1; break; - case PK_INSERT: code = 2; break; - case PK_DELETE: code = 3; break; - case PK_END: code = 4; break; - case PK_PAGEUP: code = 5; break; - case PK_PAGEDOWN: code = 6; break; - default: code = 0; break; /* else gcc warns `enum value not used' */ - } - p += sprintf((char *) p, "\x1B[%d~", code); - goto done; - } - - if (PK_ISFKEY(keysym)) { - /* Map Shift+F1-F10 to F11-F20 */ - if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT)) - keysym += 10; - if ((term->vt52_mode || term->cfg.funky_type == FUNKY_VT100P) && - keysym <= PK_F14) { - /* XXX This overrides the XTERM/VT52 mode below */ - int offt = 0; - if (keysym >= PK_F6) offt++; - if (keysym >= PK_F12) offt++; - p += sprintf((char *) p, term->vt52_mode ? "\x1B%c" : "\x1BO%c", - 'P' + keysym - PK_F1 - offt); - goto done; - } - if (term->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) { - p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1); - goto done; - } - if (term->cfg.funky_type == FUNKY_XTERM && keysym <= PK_F4) { - if (term->vt52_mode) - p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1); - else - p += sprintf((char *) p, "\x1BO%c", 'P' + keysym - PK_F1); - goto done; - } - p += sprintf((char *) p, "\x1B[%d~", 11 + keysym - PK_F1); - goto done; - } - - if (PK_ISCURSOR(keysym)) { - int xkey; - - switch (keysym) { - case PK_UP: xkey = 'A'; break; - case PK_DOWN: xkey = 'B'; break; - case PK_RIGHT: xkey = 'C'; break; - case PK_LEFT: xkey = 'D'; break; - case PK_REST: xkey = 'G'; break; /* centre key on number pad */ - default: xkey = 0; break; /* else gcc warns `enum value not used' */ - } - p += format_arrow_key(p, term, xkey, modifiers == PKM_CONTROL); - goto done; - } - - done: - if (p > output || tlen > 0) { - /* - * Interrupt an ongoing paste. I'm not sure - * this is sensible, but for the moment it's - * preferable to having to faff about buffering - * things. - */ - term_nopaste(term); - - /* - * We need not bother about stdin backlogs - * here, because in GUI PuTTY we can't do - * anything about it anyway; there's no means - * of asking Windows to hold off on KEYDOWN - * messages. We _have_ to buffer everything - * we're sent. - */ - term_seen_key_event(term); - - if (prependesc) { -#if 0 - fprintf(stderr, "sending ESC\n"); -#endif - ldisc_send(term->ldisc, "\x1b", 1, 1); - } - - if (p > output) { -#if 0 - fprintf(stderr, "sending %d bytes:", p - output); - for (i = 0; i < p - output; i++) - fprintf(stderr, " %02x", output[i]); - fprintf(stderr, "\n"); -#endif - ldisc_send(term->ldisc, output, p - output, 1); - } else if (tlen > 0) { -#if 0 - fprintf(stderr, "sending %d unichars:", tlen); - for (i = 0; i < tlen; i++) - fprintf(stderr, " %04x", (unsigned) text[i]); - fprintf(stderr, "\n"); -#endif - luni_send(term->ldisc, text, tlen, 1); - } - } -} - void term_nopaste(Terminal *term) { if (term->paste_len == 0) @@ -6393,6 +6108,14 @@ void term_deselect(Terminal *term) { deselect(term); term_update(term); + + /* + * Since terminal output is suppressed during drag-selects, we + * should make sure to write any pending output if one has just + * finished. + */ + if (term->selstate != DRAGGING) + term_out(term); } int term_ldisc(Terminal *term, int option) @@ -6480,9 +6203,9 @@ char *term_get_ttymode(Terminal *term, const char *mode) { char *val = NULL; if (strcmp(mode, "ERASE") == 0) { - val = term->cfg.bksp_is_delete ? "^?" : "^H"; + val = term->bksp_is_delete ? "^?" : "^H"; } - /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */ + /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */ /* FIXME: or ECHO and friends based on local echo state? */ return dupstr(val); } @@ -6528,7 +6251,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } } @@ -6555,8 +6278,8 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, case 10: case 13: term_data(term, 0, "\r\n", 2); + prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos] = '\0'; - pr->result[pr->result_len - 1] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = 0; @@ -6591,10 +6314,9 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, * when we're doing password input, because some people * have control characters in their passwords. */ - if ((!pr->echo || - (c >= ' ' && c <= '~') || - ((unsigned char) c >= 160)) - && s->pos < pr->result_len - 1) { + if (!pr->echo || (c >= ' ' && c <= '~') || + ((unsigned char) c >= 160)) { + prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos++] = c; if (pr->echo) term_data(term, 0, &c, 1); diff --git a/putty/TERMINAL.H b/putty/TERMINAL.H index 6d3b1c5..2c61d17 100644 --- a/putty/TERMINAL.H +++ b/putty/TERMINAL.H @@ -152,8 +152,12 @@ struct terminal_tag { int big_cursor; int xterm_mouse; /* send mouse messages to host */ + int xterm_extended_mouse; + int urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ + int bracketed_paste; + int cset_attr[2]; /* @@ -233,13 +237,13 @@ struct terminal_tag { struct unicode_data *ucsdata; /* - * We maintain a full _copy_ of a Config structure here, not - * merely a pointer to it. That way, when we're passed a new - * one for reconfiguration, we can check the differences and - * adjust the _current_ setting of (e.g.) auto wrap mode rather - * than only the default. + * We maintain a full copy of a Conf here, not merely a pointer + * to it. That way, when we're passed a new one for + * reconfiguration, we can check the differences and adjust the + * _current_ setting of (e.g.) auto wrap mode rather than only + * the default. */ - Config cfg; + Conf *conf; /* * from_backend calls term_out, but it can also be called from @@ -273,6 +277,52 @@ struct terminal_tag { int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; + + /* + * We copy a bunch of stuff out of the Conf structure into local + * fields in the Terminal structure, to avoid the repeated + * tree234 lookups which would be involved in fetching them from + * the former every time. + */ + int ansi_colour; + char *answerback; + int answerbacklen; + int arabicshaping; + int beep; + int bellovl; + int bellovl_n; + int bellovl_s; + int bellovl_t; + int bidi; + int bksp_is_delete; + int blink_cur; + int blinktext; + int cjk_ambig_wide; + int conf_height; + int conf_width; + int crhaslf; + int erase_to_scrollback; + int funky_type; + int lfhascr; + int logflush; + int logtype; + int mouse_override; + int nethack_keypad; + int no_alt_screen; + int no_applic_c; + int no_applic_k; + int no_dbackspace; + int no_mouse_rep; + int no_remote_charset; + int no_remote_resize; + int no_remote_wintitle; + int rawcnp; + int rect_select; + int remote_qtitle_action; + int rxvt_homeend; + int scroll_on_disp; + int scroll_on_key; + int xterm_256_colour; }; #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8) diff --git a/putty/TESTBACK.C b/putty/TESTBACK.C index 0dd26d9..5f9f1e8 100644 --- a/putty/TESTBACK.C +++ b/putty/TESTBACK.C @@ -1,4 +1,4 @@ -/* $Id: testback.c 7628 2007-06-30 21:56:44Z jacob $ */ +/* $Id: testback.c 9214 2011-07-14 18:52:21Z simon $ */ /* * Copyright (c) 1999 Simon Tatham * Copyright (c) 1999 Ben Harris @@ -33,13 +33,13 @@ #include "putty.h" -static const char *null_init(void *, void **, Config *, char *, int, char **, +static const char *null_init(void *, void **, Conf *, char *, int, char **, int, int); -static const char *loop_init(void *, void **, Config *, char *, int, char **, +static const char *loop_init(void *, void **, Conf *, char *, int, char **, int, int); static void null_free(void *); static void loop_free(void *); -static void null_reconfig(void *, Config *); +static void null_reconfig(void *, Conf *); static int null_send(void *, char *, int); static int loop_send(void *, char *, int); static int null_sendbuffer(void *); @@ -74,14 +74,14 @@ struct loop_state { }; static const char *null_init(void *frontend_handle, void **backend_handle, - Config *cfg, char *host, int port, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { return NULL; } static const char *loop_init(void *frontend_handle, void **backend_handle, - Config *cfg, char *host, int port, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { struct loop_state *st = snew(struct loop_state); @@ -101,7 +101,7 @@ static void loop_free(void *handle) sfree(handle); } -static void null_reconfig(void *handle, Config *cfg) { +static void null_reconfig(void *handle, Conf *conf) { } diff --git a/putty/TESTDATA/BIGNUM.PY b/putty/TESTDATA/BIGNUM.PY index 0a24780..b2e2e7f 100644 --- a/putty/TESTDATA/BIGNUM.PY +++ b/putty/TESTDATA/BIGNUM.PY @@ -103,6 +103,15 @@ for i in range(1,4200): a, b, p = findprod((1< @@ -18,12 +36,13 @@ struct timer { timer_fn_t fn; void *ctx; - long now; + unsigned long now; + unsigned long when_set; }; static tree234 *timers = NULL; static tree234 *timer_contexts = NULL; -static long now = 0L; +static unsigned long now = 0L; static int compare_timers(void *av, void *bv) { @@ -41,14 +60,12 @@ static int compare_timers(void *av, void *bv) * Failing that, compare on the other two fields, just so that * we don't get unwanted equality. */ -#ifdef __LCC__ +#if defined(__LCC__) || defined(__clang__) /* lcc won't let us compare function pointers. Legal, but annoying. */ { int c = memcmp(&a->fn, &b->fn, sizeof(a->fn)); - if (c < 0) - return -1; - else if (c > 0) - return +1; + if (c) + return c; } #else if (a->fn < b->fn) @@ -89,14 +106,15 @@ static void init_timers(void) } } -long schedule_timer(int ticks, timer_fn_t fn, void *ctx) +unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { - long when; + unsigned long when; struct timer *t, *first; init_timers(); - when = ticks + GETTICKCOUNT(); + now = GETTICKCOUNT(); + when = ticks + now; /* * Just in case our various defences against timing skew fail @@ -110,6 +128,7 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx) t->fn = fn; t->ctx = ctx; t->now = when; + t->when_set = now; if (t != add234(timers, t)) { sfree(t); /* identical timer already exists */ @@ -134,65 +153,13 @@ long schedule_timer(int ticks, timer_fn_t fn, void *ctx) * Returns the time (in ticks) expected until the next timer after * that triggers. */ -int run_timers(long anow, long *next) +int run_timers(unsigned long anow, unsigned long *next) { struct timer *first; init_timers(); -#ifdef TIMING_SYNC - /* - * In this ifdef I put some code which deals with the - * possibility that `anow' disagrees with GETTICKCOUNT by a - * significant margin. Our strategy for dealing with it differs - * depending on platform, because on some platforms - * GETTICKCOUNT is more likely to be right whereas on others - * `anow' is a better gold standard. - */ - { - long tnow = GETTICKCOUNT(); - - if (tnow + TICKSPERSEC/50 - anow < 0 || - anow + TICKSPERSEC/50 - tnow < 0 - ) { -#if defined TIMING_SYNC_ANOW - /* - * If anow is accurate and the tick count is wrong, - * this is likely to be because the tick count is - * derived from the system clock which has changed (as - * can occur on Unix). Therefore, we resolve this by - * inventing an offset which is used to adjust all - * future output from GETTICKCOUNT. - * - * A platform which defines TIMING_SYNC_ANOW is - * expected to have also defined this offset variable - * in (its platform-specific adjunct to) putty.h. - * Therefore we can simply reference it here and assume - * that it will exist. - */ - tickcount_offset += anow - tnow; -#elif defined TIMING_SYNC_TICKCOUNT - /* - * If the tick count is more likely to be accurate, we - * simply use that as our time value, which may mean we - * run no timers in this call (because we got called - * early), or alternatively it may mean we run lots of - * timers in a hurry because we were called late. - */ - anow = tnow; -#else -/* - * Any platform which defines TIMING_SYNC must also define one of the two - * auxiliary symbols TIMING_SYNC_ANOW and TIMING_SYNC_TICKCOUNT, to - * indicate which measurement to trust when the two disagree. - */ -#error TIMING_SYNC definition incomplete -#endif - } - } -#endif - - now = anow; + now = GETTICKCOUNT(); while (1) { first = (struct timer *)index234(timers, 0); @@ -207,7 +174,8 @@ int run_timers(long anow, long *next) */ delpos234(timers, 0); sfree(first); - } else if (first->now - now <= 0) { + } else if (now - (first->when_set - 10) > + first->now - (first->when_set - 10)) { /* * This timer is active and has reached its running * time. Run it. diff --git a/putty/TREE234.C b/putty/TREE234.C index 4e2da9d..b2a80a3 100644 --- a/putty/TREE234.C +++ b/putty/TREE234.C @@ -29,12 +29,18 @@ #include #include -#include "puttymem.h" #include "tree234.h" #ifdef TEST #define LOG(x) (printf x) +#define snew(type) ((type *)malloc(sizeof(type))) +#define snewn(n, type) ((type *)malloc((n) * sizeof(type))) +#define sresize(ptr, n, type) \ + ((type *)realloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ + (n) * sizeof(type))) +#define sfree(ptr) free(ptr) #else +#include "puttymem.h" #define LOG(x) #endif @@ -220,7 +226,7 @@ static void *add234_internal(tree234 * t, void *e, int index) n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d [%p] %p/%d at position %d\n", - left, lcount, e, right, rcount, np - n->kids)); + left, lcount, e, right, rcount, (int)(np - n->kids))); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. @@ -1469,7 +1475,8 @@ int main(void) printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); - printf("deleting string %s from index %d\n", array[j], j); + printf("deleting string %s from index %d\n", + (const char *)array[j], j); delpostest(j); } diff --git a/putty/UNIX/CONFIGUR.AC b/putty/UNIX/CONFIGUR.AC index 382bf2a..40bbd0e 100644 --- a/putty/UNIX/CONFIGUR.AC +++ b/putty/UNIX/CONFIGUR.AC @@ -4,18 +4,41 @@ # * Automake (for aclocal) # If you've got them, running "autoreconf" should work. -AC_INIT +# Version number is substituted by Buildscr for releases, snapshots +# and custom builds out of svn; X.XX shows up in ad-hoc developer +# builds, which shouldn't matter +AC_INIT(putty, X.XX) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_INSTALL -AC_PROG_CC -if test "X$GCC" = Xyes; then - PUTTYCFLAGS="-Wall -Werror" -else - PUTTYCFLAGS="" -fi -AC_SUBST(PUTTYCFLAGS) +AC_PROG_RANLIB + +# Mild abuse of the '--enable' option format to allow manual +# specification of setuid or setgid setup in pterm. +setidtype=none +AC_ARG_ENABLE([setuid], + [AS_HELP_STRING([--enable-setuid=USER], + [make pterm setuid to a given user])], + [case "$enableval" in + no) setidtype=none;; + *) setidtype=setuid; setidval="$enableval";; + esac]) +AC_ARG_ENABLE([setgid], + [AS_HELP_STRING([--enable-setgid=GROUP], + [make pterm setgid to a given group])], + [case "$enableval" in + no) setidtype=none;; + *) setidtype=setgid; setidval="$enableval";; + esac]) +AM_CONDITIONAL(HAVE_SETID_CMD, [test "$setidtype" != "none"]) +AS_IF([test "x$setidtype" = "xsetuid"], + [SETID_CMD="chown $setidval"; SETID_MODE="4755"]) +AS_IF([test "x$setidtype" = "xsetgid"], + [SETID_CMD="chgrp $setidval"; SETID_MODE="2755"]) +AC_SUBST(SETID_CMD) +AC_SUBST(SETID_MODE) AC_ARG_WITH([gssapi], [AS_HELP_STRING([--without-gssapi], @@ -27,18 +50,55 @@ WITH_GSSAPI= AS_IF([test "x$with_gssapi" != xno], [AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])]) +AC_ARG_WITH([gtk], + [AS_HELP_STRING([--with-gtk=VER], + [specify GTK version to use (`1' or `2')]) +AS_HELP_STRING([--without-gtk], + [do not use GTK (build command-line tools only)])], + [gtk_version_desired="$withval"], + [gtk_version_desired="any"]) + +case "$gtk_version_desired" in + 1 | 2 | any | no) ;; + yes) gtk_version_desired="any" ;; + *) AC_ERROR([Invalid GTK version specified]) +esac + AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[ #include #include ]) -# Look for both GTK 1 and GTK 2. -AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none]) -AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) -if test "$gtk" = "none"; then - all_targets="all-cli" -else - all_targets="all-cli all-gtk" -fi +# Look for both GTK 2 and GTK 1, in descending order of preference. If +# we can't find either, have the makefile only build the CLI programs. + +gtk=none + +case "$gtk_version_desired:$gtk" in + 2:none | any:none) + ifdef([AM_PATH_GTK_2_0],[ + AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) + ],[AC_WARNING([generating configure script without GTK 2 autodetection])]) + ;; +esac + +case "$gtk_version_desired:$gtk" in + 1:none | any:none) + ifdef([AM_PATH_GTK],[ + AM_PATH_GTK([1.2.0], [gtk=1], []) + ],[ + # manual check for gtk1 + AC_PATH_PROG(GTK1_CONFIG, gtk-config, absent) + if test "$GTK1_CONFIG" != "absent"; then + GTK_CFLAGS=`"$GTK1_CONFIG" --cflags` + GTK_LIBS=`"$GTK1_CONFIG" --libs` + gtk=1 + fi + ]) + ;; +esac + +AM_CONDITIONAL(HAVE_GTK, [test "$gtk" != "none"]) + if test "$gtk" = "2"; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" @@ -48,7 +108,6 @@ if test "$gtk" = "2"; then CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi -AC_SUBST([all_targets]) AC_SEARCH_LIBS([socket], [xnet]) @@ -63,12 +122,42 @@ AS_IF([test "x$with_gssapi" != xno], [], [AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])]) -AC_CHECK_LIB(X11, XOpenDisplay) +AC_CHECK_LIB(X11, XOpenDisplay, + [GTK_LIBS="-lX11 $GTK_LIBS" + AC_DEFINE([HAVE_LIBX11],[],[Define if libX11.a is available])]) -AC_CHECK_FUNCS([getaddrinfo ptsname setresuid strsignal updwtmpx]) +AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx]) +AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include ]]) +AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])]) + +if test "x$GCC" = "xyes"; then + : + AC_SUBST(WARNINGOPTS, ['-Wall -Werror']) +else + : + AC_SUBST(WARNINGOPTS, []) +fi AC_OUTPUT +if test "$gtk_version_desired" = "no"; then cat <ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && - c->generic.context.i == offsetof(Config,scrollbar)) { + c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-on-left one. @@ -89,29 +89,29 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) "Fonts for displaying non-bold text"); ctrl_fontsel(s, "Font used for ordinary text", 'f', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,font))); + conf_fontsel_handler, I(CONF_font)); ctrl_fontsel(s, "Font used for wide (CJK) text", 'w', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,widefont))); + conf_fontsel_handler, I(CONF_widefont)); s = ctrl_getset(b, "Window/Fonts", "fontbold", "Fonts for displaying bolded text"); ctrl_fontsel(s, "Font used for bolded text", 'b', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,boldfont))); + conf_fontsel_handler, I(CONF_boldfont)); ctrl_fontsel(s, "Font used for bold wide text", 'i', HELPCTX(no_help), - dlg_stdfontsel_handler, I(offsetof(Config,wideboldfont))); + conf_fontsel_handler, I(CONF_wideboldfont)); ctrl_checkbox(s, "Use shadow bold instead of bold fonts", 'u', HELPCTX(no_help), - dlg_stdcheckbox_handler, - I(offsetof(Config,shadowbold))); + conf_checkbox_handler, + I(CONF_shadowbold)); ctrl_text(s, "(Note that bold fonts or shadow bolding are only" " used if you have not requested bolding to be done by" " changing the text colour.)", HELPCTX(no_help)); ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20, - HELPCTX(no_help), dlg_stdeditbox_handler, - I(offsetof(Config,shadowboldoffset)), I(-1)); + HELPCTX(no_help), conf_editbox_handler, + I(CONF_shadowboldoffset), I(-1)); /* * Markus Kuhn feels, not totally unreasonably, that it's good @@ -125,8 +125,8 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) "Character set translation on received data"); ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l', HELPCTX(translation_utf8_override), - dlg_stdcheckbox_handler, - I(offsetof(Config,utf8_override))); + conf_checkbox_handler, + I(CONF_utf8_override)); if (!midsession) { /* @@ -137,8 +137,7 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win) s = ctrl_getset(b, "Window/Behaviour", "x11", "X Window System settings"); ctrl_editbox(s, "Window class name:", 'z', 50, - HELPCTX(no_help), dlg_stdeditbox_handler, - I(offsetof(Config,winclass)), - I(sizeof(((Config *)0)->winclass))); + HELPCTX(no_help), conf_editbox_handler, + I(CONF_winclass), I(1)); } } diff --git a/putty/UNIX/GTKCOLS.H b/putty/UNIX/GTKCOLS.H index 295e650..8061266 100644 --- a/putty/UNIX/GTKCOLS.H +++ b/putty/UNIX/GTKCOLS.H @@ -8,7 +8,7 @@ #define COLUMNS_H #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/putty/UNIX/GTKDLG.C b/putty/UNIX/GTKDLG.C index fc25e78..764c3e9 100644 --- a/putty/UNIX/GTKDLG.C +++ b/putty/UNIX/GTKDLG.C @@ -330,11 +330,10 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) * The first call to "changed", if allowed to proceed normally, * will cause an EVENT_VALCHANGE event on the edit box, causing * a call to dlg_editbox_get() which will read the empty string - * out of the GtkEntry - and promptly write it straight into - * the Config structure, which is precisely where our `text' - * pointer is probably pointing, so the second editing - * operation will insert that instead of the string we - * originally asked for. + * out of the GtkEntry - and promptly write it straight into the + * Conf structure, which is precisely where our `text' pointer + * is probably pointing, so the second editing operation will + * insert that instead of the string we originally asked for. * * Hence, we must take our own copy of the text before we do * this. @@ -344,7 +343,7 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) sfree(tmpstring); } -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) +char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); @@ -353,25 +352,16 @@ void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) #if GTK_CHECK_VERSION(2,4,0) if (uc->combo) { #if GTK_CHECK_VERSION(2,6,0) - strncpy(buffer, - gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)), - length); + return dupstr(gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo))); #else - strncpy(buffer, - gtk_entry_get_text - (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))), - length); + return dupstr(gtk_entry_get_text + (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo))))); #endif - buffer[length-1] = '\0'; - return; } #endif if (uc->entry) { - strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - length); - buffer[length-1] = '\0'; - return; + return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } assert(!"We shouldn't get here"); @@ -916,44 +906,48 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text) } } -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); + /* We must copy fn->path before passing it to gtk_entry_set_text. + * See comment in dlg_editbox_set() for the reasons. */ + char *duppath = dupstr(fn->path); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); - gtk_entry_set_text(GTK_ENTRY(uc->entry), fn.path); + gtk_entry_set_text(GTK_ENTRY(uc->entry), duppath); + sfree(duppath); } -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn) +Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FILESELECT); assert(uc->entry != NULL); - strncpy(fn->path, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - lenof(fn->path)); - fn->path[lenof(fn->path)-1] = '\0'; + return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs) +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); + /* We must copy fs->name before passing it to gtk_entry_set_text. + * See comment in dlg_editbox_set() for the reasons. */ + char *dupname = dupstr(fs->name); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); - gtk_entry_set_text(GTK_ENTRY(uc->entry), fs.name); + gtk_entry_set_text(GTK_ENTRY(uc->entry), dupname); + sfree(dupname); } -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs) +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_FONTSELECT); assert(uc->entry != NULL); - strncpy(fs->name, gtk_entry_get_text(GTK_ENTRY(uc->entry)), - lenof(fs->name)); - fs->name[lenof(fs->name)-1] = '\0'; + return fontspec_new(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } /* @@ -2826,12 +2820,12 @@ void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w) #endif } -int do_config_box(const char *title, Config *cfg, int midsession, +int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo) { GtkWidget *window, *hbox, *vbox, *cols, *label, *tree, *treescroll, *panels, *panelvbox; - int index, level; + int index, level, protocol; struct controlbox *ctrlbox; char *path; #if GTK_CHECK_VERSION(2,0,0) @@ -2859,8 +2853,9 @@ int do_config_box(const char *title, Config *cfg, int midsession, window = gtk_dialog_new(); ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo); - unix_setup_config_box(ctrlbox, midsession, cfg->protocol); + protocol = conf_get_int(conf, CONF_protocol); + setup_config_box(ctrlbox, midsession, protocol, protcfginfo); + unix_setup_config_box(ctrlbox, midsession, protocol); gtk_setup_config_box(ctrlbox, midsession, window); gtk_window_set_title(GTK_WINDOW(window), title); @@ -3095,7 +3090,7 @@ int do_config_box(const char *title, Config *cfg, int midsession, } #endif - dp.data = cfg; + dp.data = conf; dlg_refresh(NULL, &dp); dp.shortcuts = &selparams[0].shortcuts; @@ -3267,7 +3262,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) return dp.retval; } -static int string_width(char *text) +int string_width(char *text) { GtkWidget *label = gtk_label_new(text); GtkRequisition req; @@ -3394,6 +3389,13 @@ void fatal_message_box(void *window, char *msg) "OK", 'o', 1, 1, NULL); } +void nonfatal_message_box(void *window, char *msg) +{ + messagebox(window, "PuTTY Error", msg, + string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), + "OK", 'o', 1, 1, NULL); +} + void fatalbox(char *p, ...) { va_list ap; @@ -3406,6 +3408,17 @@ void fatalbox(char *p, ...) cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + char *msg; + va_start(ap, p); + msg = dupvprintf(p, ap); + va_end(ap); + fatal_message_box(NULL, msg); + sfree(msg); +} + static GtkWidget *aboutbox = NULL; static void about_close_clicked(GtkButton *button, gpointer data) @@ -3419,7 +3432,7 @@ static void licence_clicked(GtkButton *button, gpointer data) char *title; char *licence = - "Copyright 1997-2011 Simon Tatham.\n\n" + "Copyright 1997-2013 Simon Tatham.\n\n" "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " @@ -3500,7 +3513,7 @@ void about_box(void *window) w, FALSE, FALSE, 5); gtk_widget_show(w); - w = gtk_label_new("Copyright 1997-2011 Simon Tatham. All rights reserved"); + w = gtk_label_new("Copyright 1997-2013 Simon Tatham. All rights reserved"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 5); gtk_widget_show(w); @@ -3752,7 +3765,7 @@ void logevent_dlg(void *estuff, const char *string) es->nevents++; } -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -3764,7 +3777,7 @@ int askappend(void *frontend, Filename filename, char *mbtitle; int mbret; - message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = messagebox(get_window(frontend), mbtitle, message, diff --git a/putty/UNIX/GTKFONT.C b/putty/UNIX/GTKFONT.C index e382183..cf983e4 100644 --- a/putty/UNIX/GTKFONT.C +++ b/putty/UNIX/GTKFONT.C @@ -30,12 +30,6 @@ * and in particular whether it's client- or server-side, * during the progress of the font selector. * - * - all the GDK font functions used in the x11font subclass are - * deprecated, so one day they may go away. When this happens - - * or before, if I'm feeling proactive - it oughtn't to be too - * difficult in principle to convert the whole thing to use - * actual Xlib font calls. - * * - it would be nice if we could move the processing of * underline and VT100 double width into this module, so that * instead of using the ghastly pixmap-stretching technique @@ -47,6 +41,11 @@ * I haven't the energy. */ +#if !GLIB_CHECK_VERSION(1,3,7) +#define g_ascii_strcasecmp g_strcasecmp +#define g_ascii_strncasecmp g_strncasecmp +#endif + /* * Ad-hoc vtable mechanism to allow font structures to be * polymorphic. @@ -77,9 +76,12 @@ struct unifont_vtable { */ unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); + unifont *(*create_fallback)(GtkWidget *widget, int height, int wide, + int bold, int shadowoffset, int shadowalways); void (*destroy)(unifont *font); + int (*has_glyph)(unifont *font, wchar_t glyph); void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, int wide, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); void (*enum_fonts)(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); @@ -94,11 +96,12 @@ struct unifont_vtable { }; /* ---------------------------------------------------------------------- - * GDK-based X11 font implementation. + * X11 font implementation, directly using Xlib calls. */ +static int x11font_has_glyph(unifont *font, wchar_t glyph); static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *x11font_create(GtkWidget *widget, const char *name, int wide, int bold, @@ -124,12 +127,12 @@ struct x11font { * failed, so that we don't keep trying and failing * subsequently). */ - GdkFont *fonts[4]; + XFontStruct *fonts[4]; int allocated[4]; /* * `sixteen_bit' is true iff the font object is indexed by * values larger than a byte. That is, this flag tells us - * whether we use gdk_draw_text_wc() or gdk_draw_text(). + * whether we use XDrawString or XDrawString16, etc. */ int sixteen_bit; /* @@ -139,6 +142,14 @@ struct x11font { */ int variable; /* + * real_charset is the charset used when translating text into the + * font's internal encoding inside draw_text(). This need not be + * the same as the public_charset provided to the client; for + * example, public_charset might be CS_ISO8859_1 while + * real_charset is CS_ISO8859_1_X11. + */ + int real_charset; + /* * Data passed in to unifont_create(). */ int wide, bold, shadowoffset, shadowalways; @@ -146,7 +157,9 @@ struct x11font { static const struct unifont_vtable x11font_vtable = { x11font_create, + NULL, /* no fallback fonts in X11 */ x11font_destroy, + x11font_has_glyph, x11font_draw_text, x11font_enum_fonts, x11font_canonify_fontname, @@ -154,10 +167,9 @@ static const struct unifont_vtable x11font_vtable = { "server", }; -char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) +static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide) { - XFontStruct *xfs = GDK_FONT_XFONT(font); - Display *disp = GDK_FONT_XDISPLAY(font); + Display *disp = GDK_DISPLAY(); Atom fontprop = XInternAtom(disp, "FONT", False); unsigned long ret; if (XGetFontProperty(xfs, fontprop, &ret)) { @@ -179,8 +191,10 @@ char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) p++; } - if (nstr < lenof(strings)) + if (nstr < lenof(strings)) { + sfree(dupname); return NULL; /* XLFD was malformed */ + } if (bold) strings[2] = "bold"; @@ -206,16 +220,78 @@ char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide) return NULL; } -static int x11_font_width(GdkFont *font, int sixteen_bit) +static int x11_font_width(XFontStruct *xfs, int sixteen_bit) { if (sixteen_bit) { XChar2b space; space.byte1 = 0; space.byte2 = '0'; - return gdk_text_width(font, (const gchar *)&space, 2); + return XTextWidth16(xfs, &space, 1); + } else { + return XTextWidth(xfs, "0", 1); + } +} + +static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2) +{ + int index; + + /* + * Not to be confused with x11font_has_glyph, which is a method of + * the x11font 'class' and hence takes a unifont as argument. This + * is the low-level function which grubs about in an actual + * XFontStruct to see if a given glyph exists. + * + * We must do this ourselves rather than letting Xlib's + * XTextExtents16 do the job, because XTextExtents will helpfully + * substitute the font's default_char for any missing glyph and + * not tell us it did so, which precisely won't help us find out + * which glyphs _are_ missing. + * + * The man page for XQueryFont is rather confusing about how the + * per_char array in the XFontStruct is laid out, because it gives + * formulae for determining the two-byte X character code _from_ + * an index into the per_char array. Going the other way, it's + * rather simpler: + * + * The valid character codes have byte1 between min_byte1 and + * max_byte1 inclusive, and byte2 between min_char_or_byte2 and + * max_char_or_byte2 inclusive. This gives a rectangle of size + * (max_byte2-min_byte1+1) by + * (max_char_or_byte2-min_char_or_byte2+1), which is precisely the + * rectangle encoded in the per_char array. Hence, given a + * character code which is valid in the sense that it falls + * somewhere in that rectangle, its index in per_char is given by + * setting + * + * x = byte2 - min_char_or_byte2 + * y = byte1 - min_byte1 + * index = y * (max_char_or_byte2-min_char_or_byte2+1) + x + * + * If min_byte1 and min_byte2 are both zero, that's a special case + * which can be treated as if min_byte2 was 1 instead, i.e. the + * per_char array just runs from min_char_or_byte2 to + * max_char_or_byte2 inclusive, and byte1 should always be zero. + */ + + if (byte2 < xfs->min_char_or_byte2 || byte2 > xfs->max_char_or_byte2) + return FALSE; + + if (xfs->min_byte1 == 0 && xfs->max_byte1 == 0) { + index = byte2 - xfs->min_char_or_byte2; } else { - return gdk_char_width(font, '0'); + if (byte1 < xfs->min_byte1 || byte1 > xfs->max_byte1) + return FALSE; + index = ((byte2 - xfs->min_char_or_byte2) + + ((byte1 - xfs->min_byte1) * + (xfs->max_char_or_byte2 - xfs->min_char_or_byte2 + 1))); } + + if (!xfs->per_char) /* per_char NULL => everything in range exists */ + return TRUE; + + return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 || + xfs->per_char[index].width > 0); } static unifont *x11font_create(GtkWidget *widget, const char *name, @@ -223,21 +299,17 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, int shadowoffset, int shadowalways) { struct x11font *xfont; - GdkFont *font; XFontStruct *xfs; - Display *disp; + Display *disp = GDK_DISPLAY(); Atom charset_registry, charset_encoding, spacing; unsigned long registry_ret, encoding_ret, spacing_ret; int pubcs, realcs, sixteen_bit, variable; int i; - font = gdk_font_load(name); - if (!font) + xfs = XLoadQueryFont(disp, name); + if (!xfs) return NULL; - xfs = GDK_FONT_XFONT(font); - disp = GDK_FONT_XDISPLAY(font); - charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False); charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False); @@ -266,27 +338,19 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, } /* - * Hack for X line-drawing characters: if the primary - * font is encoded as ISO-8859-1, and has valid glyphs - * in the first 32 char positions, it is assumed that - * those glyphs are the VT100 line-drawing character - * set. - * - * Actually, we'll hack even harder by only checking - * position 0x19 (vertical line, VT100 linedrawing - * `x'). Then we can check it easily by seeing if the - * ascent and descent differ. + * Hack for X line-drawing characters: if the primary font + * is encoded as ISO-8859-1, and has valid glyphs in the + * low character positions, it is assumed that those + * glyphs are the VT100 line-drawing character set. */ if (pubcs == CS_ISO8859_1) { - int lb, rb, wid, asc, desc; - gchar text[2]; - - text[1] = '\0'; - text[0] = '\x12'; - gdk_string_extents(font, text, &lb, &rb, &wid, &asc, &desc); - if (asc != desc) - realcs = CS_ISO8859_1_X11; - } + int ch; + for (ch = 1; ch < 32; ch++) + if (!x11_font_has_glyph(xfs, 0, ch)) + break; + if (ch == 32) + realcs = CS_ISO8859_1_X11; + } sfree(encoding); } @@ -303,13 +367,14 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, xfont = snew(struct x11font); xfont->u.vt = &x11font_vtable; - xfont->u.width = x11_font_width(font, sixteen_bit); - xfont->u.ascent = font->ascent; - xfont->u.descent = font->descent; + xfont->u.width = x11_font_width(xfs, sixteen_bit); + xfont->u.ascent = xfs->ascent; + xfont->u.descent = xfs->descent; xfont->u.height = xfont->u.ascent + xfont->u.descent; xfont->u.public_charset = pubcs; - xfont->u.real_charset = realcs; - xfont->fonts[0] = font; + xfont->u.want_fallback = TRUE; + xfont->real_charset = realcs; + xfont->fonts[0] = xfs; xfont->allocated[0] = TRUE; xfont->sixteen_bit = sixteen_bit; xfont->variable = variable; @@ -328,63 +393,150 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, static void x11font_destroy(unifont *font) { + Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; int i; for (i = 0; i < lenof(xfont->fonts); i++) if (xfont->fonts[i]) - gdk_font_unref(xfont->fonts[i]); + XFreeFont(disp, xfont->fonts[i]); sfree(font); } static void x11_alloc_subfont(struct x11font *xfont, int sfid) { + Display *disp = GDK_DISPLAY(); char *derived_name = x11_guess_derived_font_name (xfont->fonts[0], sfid & 1, !!(sfid & 2)); - xfont->fonts[sfid] = gdk_font_load(derived_name); /* may be NULL */ + xfont->fonts[sfid] = XLoadQueryFont(disp, derived_name); xfont->allocated[sfid] = TRUE; sfree(derived_name); + /* Note that xfont->fonts[sfid] may still be NULL, if XLQF failed. */ +} + +static int x11font_has_glyph(unifont *font, wchar_t glyph) +{ + struct x11font *xfont = (struct x11font *)font; + + if (xfont->sixteen_bit) { + /* + * This X font has 16-bit character indices, which means + * we can directly use our Unicode input value. + */ + return x11_font_has_glyph(xfont->fonts[0], glyph >> 8, glyph & 0xFF); + } else { + /* + * This X font has 8-bit indices, so we must convert to the + * appropriate character set. + */ + char sbstring[2]; + int sblen = wc_to_mb(xfont->real_charset, 0, &glyph, 1, + sbstring, 2, "", NULL, NULL); + if (sblen == 0 || !sbstring[0]) + return FALSE; /* not even in the charset */ + + return x11_font_has_glyph(xfont->fonts[0], 0, + (unsigned char)sbstring[0]); + } } -static void x11font_really_draw_text(GdkDrawable *target, GdkFont *font, - GdkGC *gc, int x, int y, - const gchar *string, int clen, int nchars, - int shadowbold, int shadowoffset, - int fontvariable, int cellwidth) +#if !GTK_CHECK_VERSION(2,0,0) +#define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */ +#endif + +static void x11font_really_draw_text_16(GdkDrawable *target, XFontStruct *xfs, + GC gc, int x, int y, + const XChar2b *string, int nchars, + int shadowoffset, + int fontvariable, int cellwidth) { - int step = clen * nchars, nsteps = 1, centre = FALSE; + Display *disp = GDK_DISPLAY(); + int step, nsteps, centre; if (fontvariable) { /* * In a variable-pitch font, we draw one character at a * time, and centre it in the character cell. */ - step = clen; + step = 1; nsteps = nchars; centre = TRUE; + } else { + /* + * In a fixed-pitch font, we can draw the whole lot in one go. + */ + step = nchars; + nsteps = 1; + centre = FALSE; } while (nsteps-- > 0) { int X = x; if (centre) - X += (cellwidth - gdk_text_width(font, string, step)) / 2; + X += (cellwidth - XTextWidth16(xfs, string, step)) / 2; - gdk_draw_text(target, font, gc, X, y, string, step); - if (shadowbold) - gdk_draw_text(target, font, gc, X + shadowoffset, y, string, step); + XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, + X, y, string, step); + if (shadowoffset) + XDrawString16(disp, GDK_DRAWABLE_XID(target), gc, + X + shadowoffset, y, string, step); x += cellwidth; string += step; } } -static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, +static void x11font_really_draw_text(GdkDrawable *target, XFontStruct *xfs, + GC gc, int x, int y, + const char *string, int nchars, + int shadowoffset, + int fontvariable, int cellwidth) +{ + Display *disp = GDK_DISPLAY(); + int step, nsteps, centre; + + if (fontvariable) { + /* + * In a variable-pitch font, we draw one character at a + * time, and centre it in the character cell. + */ + step = 1; + nsteps = nchars; + centre = TRUE; + } else { + /* + * In a fixed-pitch font, we can draw the whole lot in one go. + */ + step = nchars; + nsteps = 1; + centre = FALSE; + } + + while (nsteps-- > 0) { + int X = x; + if (centre) + X += (cellwidth - XTextWidth(xfs, string, step)) / 2; + + XDrawString(disp, GDK_DRAWABLE_XID(target), gc, + X, y, string, step); + if (shadowoffset) + XDrawString(disp, GDK_DRAWABLE_XID(target), gc, + X + shadowoffset, y, string, step); + + x += cellwidth; + string += step; + } +} + +static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { + Display *disp = GDK_DISPLAY(); struct x11font *xfont = (struct x11font *)font; + GC gc = GDK_GC_XGC(gdkgc); int sfid; - int shadowbold = FALSE; + int shadowoffset = 0; int mult = (wide ? 2 : 1); wide -= xfont->wide; @@ -395,7 +547,7 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, * use shadow bold. */ if (xfont->shadowalways && bold) { - shadowbold = TRUE; + shadowoffset = xfont->shadowoffset; bold = 0; } sfid = 2 * wide + bold; @@ -403,7 +555,7 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, x11_alloc_subfont(xfont, sfid); if (bold && !xfont->fonts[sfid]) { bold = 0; - shadowbold = TRUE; + shadowoffset = xfont->shadowoffset; sfid = 2 * wide + bold; if (!xfont->allocated[sfid]) x11_alloc_subfont(xfont, sfid); @@ -412,46 +564,38 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, if (!xfont->fonts[sfid]) return; /* we've tried our best, but no luck */ + XSetFont(disp, gc, xfont->fonts[sfid]->fid); + if (xfont->sixteen_bit) { /* * This X font has 16-bit character indices, which means - * we expect our string to have been passed in UTF-8. + * we can directly use our Unicode input string. */ XChar2b *xcs; - wchar_t *wcs; - int nchars, maxchars, i; + int i; - /* - * Convert the input string to wide-character Unicode. - */ - maxchars = 0; - for (i = 0; i < len; i++) - if ((unsigned char)string[i] <= 0x7F || - (unsigned char)string[i] >= 0xC0) - maxchars++; - wcs = snewn(maxchars+1, wchar_t); - nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars, - CS_UTF8, NULL, NULL, 0); - assert(nchars <= maxchars); - wcs[nchars] = L'\0'; - - xcs = snewn(nchars, XChar2b); - for (i = 0; i < nchars; i++) { - xcs[i].byte1 = wcs[i] >> 8; - xcs[i].byte2 = wcs[i]; + xcs = snewn(len, XChar2b); + for (i = 0; i < len; i++) { + xcs[i].byte1 = string[i] >> 8; + xcs[i].byte2 = string[i]; } - x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y, - (gchar *)xcs, 2, nchars, - shadowbold, xfont->shadowoffset, - xfont->variable, cellwidth * mult); + x11font_really_draw_text_16(target, xfont->fonts[sfid], gc, x, y, + xcs, len, shadowoffset, + xfont->variable, cellwidth * mult); sfree(xcs); - sfree(wcs); } else { + /* + * This X font has 8-bit indices, so we must convert to the + * appropriate character set. + */ + char *sbstring = snewn(len+1, char); + int sblen = wc_to_mb(xfont->real_charset, 0, string, len, + sbstring, len+1, ".", NULL, NULL); x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y, - string, 1, len, - shadowbold, xfont->shadowoffset, + sbstring, sblen, shadowoffset, xfont->variable, cellwidth * mult); + sfree(sbstring); } } @@ -524,21 +668,21 @@ static void x11font_enum_fonts(GtkWidget *widget, style = p; p += sprintf(p, "%s", components[2][0] ? components[2] : "regular"); - if (!g_strcasecmp(components[3], "i")) + if (!g_ascii_strcasecmp(components[3], "i")) p += sprintf(p, " italic"); - else if (!g_strcasecmp(components[3], "o")) + else if (!g_ascii_strcasecmp(components[3], "o")) p += sprintf(p, " oblique"); - else if (!g_strcasecmp(components[3], "ri")) + else if (!g_ascii_strcasecmp(components[3], "ri")) p += sprintf(p, " reverse italic"); - else if (!g_strcasecmp(components[3], "ro")) + else if (!g_ascii_strcasecmp(components[3], "ro")) p += sprintf(p, " reverse oblique"); - else if (!g_strcasecmp(components[3], "ot")) + else if (!g_ascii_strcasecmp(components[3], "ot")) p += sprintf(p, " other-slant"); - if (components[4][0] && g_strcasecmp(components[4], "normal")) + if (components[4][0] && g_ascii_strcasecmp(components[4], "normal")) p += sprintf(p, " %s", components[4]); - if (!g_strcasecmp(components[10], "m")) + if (!g_ascii_strcasecmp(components[10], "m")) p += sprintf(p, " [M]"); - if (!g_strcasecmp(components[10], "c")) + if (!g_ascii_strcasecmp(components[10], "c")) p += sprintf(p, " [C]"); if (components[5][0]) p += sprintf(p, " %s", components[5]); @@ -550,23 +694,23 @@ static void x11font_enum_fonts(GtkWidget *widget, */ p++; stylekey = p; - if (!g_strcasecmp(components[2], "medium") || - !g_strcasecmp(components[2], "regular") || - !g_strcasecmp(components[2], "normal") || - !g_strcasecmp(components[2], "book")) + if (!g_ascii_strcasecmp(components[2], "medium") || + !g_ascii_strcasecmp(components[2], "regular") || + !g_ascii_strcasecmp(components[2], "normal") || + !g_ascii_strcasecmp(components[2], "book")) weightkey = 0; - else if (!g_strncasecmp(components[2], "demi", 4) || - !g_strncasecmp(components[2], "semi", 4)) + else if (!g_ascii_strncasecmp(components[2], "demi", 4) || + !g_ascii_strncasecmp(components[2], "semi", 4)) weightkey = 1; else weightkey = 2; - if (!g_strcasecmp(components[3], "r")) + if (!g_ascii_strcasecmp(components[3], "r")) slantkey = 0; - else if (!g_strncasecmp(components[3], "r", 1)) + else if (!g_ascii_strncasecmp(components[3], "r", 1)) slantkey = 2; else slantkey = 1; - if (!g_strcasecmp(components[4], "normal")) + if (!g_ascii_strcasecmp(components[4], "normal")) setwidthkey = 0; else setwidthkey = 1; @@ -638,19 +782,16 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, * _aliases_, unless specifically asked to, because the font * selector treats them as worthwhile in their own right. */ - GdkFont *font = gdk_font_load(name); XFontStruct *xfs; - Display *disp; + Display *disp = GDK_DISPLAY(); Atom fontprop, fontprop2; unsigned long ret; - if (!font) - return NULL; /* didn't make sense to us, sorry */ + xfs = XLoadQueryFont(disp, name); - gdk_font_ref(font); + if (!xfs) + return NULL; /* didn't make sense to us, sorry */ - xfs = GDK_FONT_XFONT(font); - disp = GDK_FONT_XDISPLAY(font); fontprop = XInternAtom(disp, "FONT", False); if (XGetFontProperty(xfs, fontprop, &ret)) { @@ -661,7 +802,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False); if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) { *size = fsize; - gdk_font_unref(font); + XFreeFont(disp, xfs); if (flags) { if (name[0] == '-' || resolve_aliases) *flags = FONTFLAG_SERVERSIDE; @@ -674,7 +815,8 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, } } - gdk_font_unref(font); + XFreeFont(disp, xfs); + return NULL; /* something went wrong */ } @@ -694,12 +836,16 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name, #define PANGO_PRE_1POINT6 /* make life easier for pre-1.4 folk */ #endif +static int pangofont_has_glyph(unifont *font, wchar_t glyph); static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); static unifont *pangofont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); +static unifont *pangofont_create_fallback(GtkWidget *widget, int height, + int wide, int bold, + int shadowoffset, int shadowalways); static void pangofont_destroy(unifont *font); static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); @@ -728,7 +874,9 @@ struct pangofont { static const struct unifont_vtable pangofont_vtable = { pangofont_create, + pangofont_create_fallback, pangofont_destroy, + pangofont_has_glyph, pangofont_draw_text, pangofont_enum_fonts, pangofont_canonify_fontname, @@ -774,8 +922,8 @@ static int pangofont_check_desc_makes_sense(PangoContext *ctx, matched = FALSE; for (i = 0; i < nfamilies; i++) { - if (!g_strcasecmp(pango_font_family_get_name(families[i]), - pango_font_description_get_family(desc))) { + if (!g_ascii_strcasecmp(pango_font_family_get_name(families[i]), + pango_font_description_get_family(desc))) { matched = TRUE; break; } @@ -785,31 +933,19 @@ static int pangofont_check_desc_makes_sense(PangoContext *ctx, return matched; } -static unifont *pangofont_create(GtkWidget *widget, const char *name, - int wide, int bold, - int shadowoffset, int shadowalways) +static unifont *pangofont_create_internal(GtkWidget *widget, + PangoContext *ctx, + PangoFontDescription *desc, + int wide, int bold, + int shadowoffset, int shadowalways) { struct pangofont *pfont; - PangoContext *ctx; #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif - PangoFontDescription *desc; PangoFontset *fset; PangoFontMetrics *metrics; - desc = pango_font_description_from_string(name); - if (!desc) - return NULL; - ctx = gtk_widget_get_pango_context(widget); - if (!ctx) { - pango_font_description_free(desc); - return NULL; - } - if (!pangofont_check_desc_makes_sense(ctx, desc)) { - pango_font_description_free(desc); - return NULL; - } #ifndef PANGO_PRE_1POINT6 map = pango_context_get_font_map(ctx); if (!map) { @@ -841,9 +977,9 @@ static unifont *pangofont_create(GtkWidget *widget, const char *name, pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; + pfont->u.want_fallback = FALSE; /* The Pango API is hardwired to UTF-8 */ pfont->u.public_charset = CS_UTF8; - pfont->u.real_charset = CS_UTF8; pfont->desc = desc; pfont->fset = fset; pfont->widget = widget; @@ -856,6 +992,49 @@ static unifont *pangofont_create(GtkWidget *widget, const char *name, return (unifont *)pfont; } +static unifont *pangofont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + PangoContext *ctx; + PangoFontDescription *desc; + + desc = pango_font_description_from_string(name); + if (!desc) + return NULL; + ctx = gtk_widget_get_pango_context(widget); + if (!ctx) { + pango_font_description_free(desc); + return NULL; + } + if (!pangofont_check_desc_makes_sense(ctx, desc)) { + pango_font_description_free(desc); + return NULL; + } + return pangofont_create_internal(widget, ctx, desc, wide, bold, + shadowoffset, shadowalways); +} + +static unifont *pangofont_create_fallback(GtkWidget *widget, int height, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + PangoContext *ctx; + PangoFontDescription *desc; + + desc = pango_font_description_from_string("Monospace"); + if (!desc) + return NULL; + ctx = gtk_widget_get_pango_context(widget); + if (!ctx) { + pango_font_description_free(desc); + return NULL; + } + pango_font_description_set_absolute_size(desc, height * PANGO_SCALE); + return pangofont_create_internal(widget, ctx, desc, wide, bold, + shadowoffset, shadowalways); +} + static void pangofont_destroy(unifont *font) { struct pangofont *pfont = (struct pangofont *)font; @@ -864,13 +1043,21 @@ static void pangofont_destroy(unifont *font) sfree(font); } +static int pangofont_has_glyph(unifont *font, wchar_t glyph) +{ + /* Pango implements font fallback, so assume it has everything */ + return TRUE; +} + static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { struct pangofont *pfont = (struct pangofont *)font; PangoLayout *layout; PangoRectangle rect; + char *utfstring, *utfptr; + int utflen; int shadowbold = FALSE; if (wide) @@ -891,7 +1078,16 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, } } - while (len > 0) { + /* + * Pango always expects UTF-8, so convert the input wide character + * string to UTF-8. + */ + utfstring = snewn(len*6+1, char); /* UTF-8 has max 6 bytes/char */ + utflen = wc_to_mb(CS_UTF8, 0, string, len, + utfstring, len*6+1, ".", NULL, NULL); + + utfptr = utfstring; + while (utflen > 0) { int clen, n; /* @@ -923,42 +1119,50 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, * string. */ clen = 1; - while (clen < len && - (unsigned char)string[clen] >= 0x80 && - (unsigned char)string[clen] < 0xC0) + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) clen++; n = 1; - /* - * See if that character has the width we expect. - */ - pango_layout_set_text(layout, string, clen); - pango_layout_get_pixel_extents(layout, NULL, &rect); - - if (rect.width == cellwidth) { - /* - * Try extracting more characters, for as long as they - * stay well-behaved. - */ - while (clen < len) { - int oldclen = clen; - clen++; /* skip UTF-8 introducer byte */ - while (clen < len && - (unsigned char)string[clen] >= 0x80 && - (unsigned char)string[clen] < 0xC0) - clen++; - n++; - pango_layout_set_text(layout, string, clen); - pango_layout_get_pixel_extents(layout, NULL, &rect); - if (rect.width != n * cellwidth) { - clen = oldclen; - n--; - break; - } - } - } + /* + * If it's a right-to-left character, we must display it on + * its own, to stop Pango helpfully re-reversing our already + * reversed text. + */ + if (!is_rtl(string[0])) { - pango_layout_set_text(layout, string, clen); + /* + * See if that character has the width we expect. + */ + pango_layout_set_text(layout, utfptr, clen); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + if (rect.width == cellwidth) { + /* + * Try extracting more characters, for as long as they + * stay well-behaved. + */ + while (clen < utflen) { + int oldclen = clen; + clen++; /* skip UTF-8 introducer byte */ + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) + clen++; + n++; + pango_layout_set_text(layout, utfptr, clen); + pango_layout_get_pixel_extents(layout, NULL, &rect); + if (rect.width != n * cellwidth) { + clen = oldclen; + n--; + break; + } + } + } + } + + pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2, y + (pfont->u.height - rect.height)/2, layout); @@ -966,11 +1170,14 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset, y + (pfont->u.height - rect.height)/2, layout); - len -= clen; - string += clen; + utflen -= clen; + utfptr += clen; + string += n; x += n * cellwidth; } + sfree(utfstring); + g_object_unref(layout); } @@ -1233,6 +1440,8 @@ static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, * event that the same font name is valid as both a Pango and an * X11 font, it will be interpreted as the former in the absence * of an explicit type-disambiguating prefix.) + * + * The 'multifont' subclass is omitted here, as discussed above. */ static const struct unifont_vtable *unifont_types[] = { #if GTK_CHECK_VERSION(2,0,0) @@ -1309,13 +1518,131 @@ void unifont_destroy(unifont *font) } void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth) { font->vt->draw_text(target, gc, font, x, y, string, len, wide, bold, cellwidth); } +/* ---------------------------------------------------------------------- + * Multiple-font wrapper. This is a type of unifont which encapsulates + * up to two other unifonts, permitting missing glyphs in the main + * font to be filled in by a fallback font. + * + * This is a type of unifont just like the previous two, but it has a + * separate constructor which is manually called by the client, so it + * doesn't appear in the list of available font types enumerated by + * unifont_create. This means it's not used by unifontsel either, so + * it doesn't need to support any methods except draw_text and + * destroy. + */ + +static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth); +static void multifont_destroy(unifont *font); + +struct multifont { + struct unifont u; + unifont *main; + unifont *fallback; +}; + +static const struct unifont_vtable multifont_vtable = { + NULL, /* creation is done specially */ + NULL, + multifont_destroy, + NULL, + multifont_draw_text, + NULL, + NULL, + NULL, + "client", +}; + +unifont *multifont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + int i; + unifont *font, *fallback; + struct multifont *mfont; + + font = unifont_create(widget, name, wide, bold, + shadowoffset, shadowalways); + if (!font) + return NULL; + + fallback = NULL; + if (font->want_fallback) { + for (i = 0; i < lenof(unifont_types); i++) { + if (unifont_types[i]->create_fallback) { + fallback = unifont_types[i]->create_fallback + (widget, font->height, wide, bold, + shadowoffset, shadowalways); + if (fallback) + break; + } + } + } + + /* + * Construct our multifont. Public members are all copied from the + * primary font we're wrapping. + */ + mfont = snew(struct multifont); + mfont->u.vt = &multifont_vtable; + mfont->u.width = font->width; + mfont->u.ascent = font->ascent; + mfont->u.descent = font->descent; + mfont->u.height = font->height; + mfont->u.public_charset = font->public_charset; + mfont->u.want_fallback = FALSE; /* shouldn't be needed, but just in case */ + mfont->main = font; + mfont->fallback = fallback; + + return (unifont *)mfont; +} + +static void multifont_destroy(unifont *font) +{ + struct multifont *mfont = (struct multifont *)font; + unifont_destroy(mfont->main); + if (mfont->fallback) + unifont_destroy(mfont->fallback); + sfree(font); +} + +static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth) +{ + struct multifont *mfont = (struct multifont *)font; + int ok, i; + + while (len > 0) { + /* + * Find a maximal sequence of characters which are, or are + * not, supported by our main font. + */ + ok = mfont->main->vt->has_glyph(mfont->main, string[0]); + for (i = 1; + i < len && + !mfont->main->vt->has_glyph(mfont->main, string[i]) == !ok; + i++); + + /* + * Now display it. + */ + unifont_draw_text(target, gc, ok ? mfont->main : mfont->fallback, + x, y, string, i, wide, bold, cellwidth); + string += i; + len -= i; + x += i * cellwidth; + } +} + #if GTK_CHECK_VERSION(2,0,0) /* ---------------------------------------------------------------------- @@ -1393,7 +1720,7 @@ static int strnullcasecmp(const char *a, const char *b) /* * Otherwise, ordinary strcasecmp. */ - return g_strcasecmp(a, b); + return g_ascii_strcasecmp(a, b); } static int fontinfo_realname_compare(void *av, void *bv) @@ -1692,11 +2019,11 @@ static void unifontsel_draw_preview_text(unifontsel_internal *fs) */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent, - "bankrupt jilted showmen quiz convex fogey", + L"bankrupt jilted showmen quiz convex fogey", 41, FALSE, FALSE, font->width); info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height, - "BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY", + L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY", 41, FALSE, FALSE, font->width); /* * The ordering of punctuation here is also selected @@ -1712,7 +2039,7 @@ static void unifontsel_draw_preview_text(unifontsel_internal *fs) */ info->fontclass->draw_text(fs->preview_pixmap, gc, font, 0, font->ascent + font->height * 2, - "0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", + L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", 42, FALSE, FALSE, font->width); } gdk_gc_unref(gc); @@ -1963,6 +2290,8 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs, */ below = findrelpos234(fs->fonts_by_selorder, &info2, NULL, REL234_LE, &pos); + if (!below) + pos = -1; above = index234(fs->fonts_by_selorder, pos+1); /* @@ -1970,7 +2299,7 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs, * case. If we have, it'll be in `below' and not `above', * because we did a REL234_LE rather than REL234_LT search. */ - if (!fontinfo_selorder_compare(&info2, below)) + if (below && !fontinfo_selorder_compare(&info2, below)) return below; /* diff --git a/putty/UNIX/GTKFONT.H b/putty/UNIX/GTKFONT.H index 41c0505..d702eb8 100644 --- a/putty/UNIX/GTKFONT.H +++ b/putty/UNIX/GTKFONT.H @@ -21,19 +21,21 @@ typedef struct unifont { /* * public_charset is the charset used when the user asks for * `Use font encoding'. - * - * real_charset is the charset used when translating text into - * a form suitable for sending to unifont_draw_text(). - * - * They can differ. For example, public_charset might be - * CS_ISO8859_1 while real_charset is CS_ISO8859_1_X11. */ - int public_charset, real_charset; + int public_charset; /* * Font dimensions needed by clients. */ int width, height, ascent, descent; + + /* + * Indicates whether this font is capable of handling all glyphs + * (Pango fonts can do this because Pango automatically supplies + * missing glyphs from other fonts), or whether it would like a + * fallback font to cope with missing glyphs. + */ + int want_fallback; } unifont; unifont *unifont_create(GtkWidget *widget, const char *name, @@ -41,10 +43,22 @@ unifont *unifont_create(GtkWidget *widget, const char *name, int shadowoffset, int shadowalways); void unifont_destroy(unifont *font); void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, - int x, int y, const char *string, int len, + int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); /* + * This function behaves exactly like the low-level unifont_create, + * except that as well as the requested font it also allocates (if + * necessary) a fallback font for filling in replacement glyphs. + * + * Return value is usable with unifont_destroy and unifont_draw_text + * as if it were an ordinary unifont. + */ +unifont *multifont_create(GtkWidget *widget, const char *name, + int wide, int bold, + int shadowoffset, int shadowalways); + +/* * Unified font selector dialog. I can't be bothered to do a * proper GTK subclassing today, so this will just be an ordinary * data structure with some useful members. diff --git a/putty/UNIX/GTKWIN.C b/putty/UNIX/GTKWIN.C index f2a13ae..bc1bedf 100644 --- a/putty/UNIX/GTKWIN.C +++ b/putty/UNIX/GTKWIN.C @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,14 @@ #include #include +#if GTK_CHECK_VERSION(2,0,0) +#include +#endif + #define PUTTY_DO_GLOBALS /* actually _define_ globals */ +#define MAY_REFER_TO_GTK_IN_HEADERS + #include "putty.h" #include "terminal.h" #include "gtkfont.h" @@ -45,7 +52,6 @@ ASSERT(sizeof(long) <= sizeof(gpointer)); #endif /* Colours come in two flavours: configurable, and xterm-extended. */ -#define NCFGCOLOURS (lenof(((Config *)0)->colours)) #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) @@ -69,6 +75,9 @@ struct gui_data { *restartitem; GtkWidget *sessionsmenu; GdkPixmap *pixmap; +#if GTK_CHECK_VERSION(2,0,0) + GtkIMContext *imc; +#endif unifont *fonts[4]; /* normal, bold, wide, widebold */ int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; @@ -88,8 +97,8 @@ struct gui_data { guint term_exit_idle_id; int alt_keycode; int alt_digits; - char wintitle[sizeof(((Config *)0)->wintitle)]; - char icontitle[sizeof(((Config *)0)->wintitle)]; + char *wintitle; + char *icontitle; int master_fd, master_func_id; void *ldisc; Backend *back; @@ -98,14 +107,25 @@ struct gui_data { void *logctx; int exited; struct unicode_data ucsdata; - Config cfg; + Conf *conf; void *eventlogstuff; char *progname, **gtkargvstart; int ngtkargs; guint32 input_event_time; /* Timestamp of the most recent input event. */ int reconfiguring; + /* Cached things out of conf that we refer to a lot */ + int bold_style; + int window_border; + int cursor_type; }; +static void cache_conf_values(struct gui_data *inst) +{ + inst->bold_style = conf_get_int(inst->conf, CONF_bold_style); + inst->window_border = conf_get_int(inst->conf, CONF_window_border); + inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type); +} + struct draw_ctx { GdkGC *gc; struct gui_data *inst; @@ -134,31 +154,27 @@ void connection_fatal(void *frontend, char *p, ...) inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); - if (inst->cfg.close_on_exit == FORCE_ON) + if (conf_get_int(inst->conf, CONF_close_on_exit) == FORCE_ON) cleanup_exit(1); } /* * Default settings that are specific to pterm. */ -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; if (!strcmp(name, "Font")) - strcpy(ret.name, "server:fixed"); + return fontspec_new("server:fixed"); else - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *platform_default_s(const char *name) @@ -200,6 +216,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len) return term_data_untrusted(inst->term, data, len); } +int from_backend_eof(void *frontend) +{ + return TRUE; /* do respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { struct gui_data *inst = (struct gui_data *)p->frontend; @@ -394,7 +415,7 @@ char *get_window_title(void *frontend, int icon) gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; - if (!inst->exited && inst->cfg.warn_on_close) { + if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) { if (!reallyclose(inst)) return TRUE; } @@ -425,7 +446,7 @@ static void update_mouseptr(struct gui_data *inst) static void show_mouseptr(struct gui_data *inst, int show) { - if (!inst->cfg.hide_mouseptr) + if (!conf_get_int(inst->conf, CONF_hide_mouseptr)) show = 1; inst->mouseptr_visible = show; update_mouseptr(inst); @@ -436,8 +457,8 @@ void draw_backing_rect(struct gui_data *inst) GdkGC *gc = gdk_gc_new(inst->area->window); gdk_gc_set_foreground(gc, &inst->cols[258]); /* default background */ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0, - inst->cfg.width * inst->font_width + 2*inst->cfg.window_border, - inst->cfg.height * inst->font_height + 2*inst->cfg.window_border); + inst->width * inst->font_width + 2*inst->window_border, + inst->height * inst->font_height + 2*inst->window_border); gdk_gc_unref(gc); } @@ -450,11 +471,13 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) * See if the terminal size has changed, in which case we must * let the terminal know. */ - w = (event->width - 2*inst->cfg.window_border) / inst->font_width; - h = (event->height - 2*inst->cfg.window_border) / inst->font_height; + w = (event->width - 2*inst->window_border) / inst->font_width; + h = (event->height - 2*inst->window_border) / inst->font_height; if (w != inst->width || h != inst->height) { - inst->cfg.width = inst->width = w; - inst->cfg.height = inst->height = h; + inst->width = w; + inst->height = h; + conf_set_int(inst->conf, CONF_width, inst->width); + conf_set_int(inst->conf, CONF_height, inst->height); need_size = 1; } @@ -464,20 +487,22 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) } inst->pixmap = gdk_pixmap_new(widget->window, - (inst->cfg.width * inst->font_width + - 2*inst->cfg.window_border), - (inst->cfg.height * inst->font_height + - 2*inst->cfg.window_border), -1); + (w * inst->font_width + 2*inst->window_border), + (h * inst->font_height + 2*inst->window_border), -1); draw_backing_rect(inst); if (need_size && inst->term) { - term_size(inst->term, h, w, inst->cfg.savelines); + term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines)); } if (inst->term) term_invalidate(inst->term); +#if GTK_CHECK_VERSION(2,0,0) + gtk_im_context_set_client_window(inst->imc, widget->window); +#endif + return TRUE; } @@ -509,6 +534,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) char output[256]; wchar_t ucsoutput[2]; int ucsval, start, end, special, output_charset, use_ucsoutput; + int nethack_mode, app_keypad_mode; /* Remember the timestamp. */ inst->input_event_time = event->time; @@ -528,21 +554,26 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * inconvenience in having to type a zero before a single-digit * character code. */ - if (event->type == GDK_KEY_RELEASE && - (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L || - event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) && - inst->alt_keycode >= 0 && inst->alt_digits > 1) { + if (event->type == GDK_KEY_RELEASE) { + if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L || + event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) && + inst->alt_keycode >= 0 && inst->alt_digits > 1) { #ifdef KEY_DEBUGGING - printf("Alt key up, keycode = %d\n", inst->alt_keycode); + printf("Alt key up, keycode = %d\n", inst->alt_keycode); +#endif + /* + * FIXME: we might usefully try to do something clever here + * about interpreting the generated key code in a way that's + * appropriate to the line code page. + */ + output[0] = inst->alt_keycode; + end = 1; + goto done; + } +#if GTK_CHECK_VERSION(2,0,0) + if (gtk_im_context_filter_keypress(inst->imc, event)) + return TRUE; #endif - /* - * FIXME: we might usefully try to do something clever here - * about interpreting the generated key code in a way that's - * appropriate to the line code page. - */ - output[0] = inst->alt_keycode; - end = 1; - goto done; } if (event->type == GDK_KEY_PRESS) { @@ -618,7 +649,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * at all. */ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) { - term_scroll(inst->term, 0, -inst->cfg.height/2); + term_scroll(inst->term, 0, -inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) { @@ -626,7 +657,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) { - term_scroll(inst->term, 0, +inst->cfg.height/2); + term_scroll(inst->term, 0, +inst->height/2); return TRUE; } if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) { @@ -645,6 +676,10 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) special = FALSE; use_ucsoutput = FALSE; + nethack_mode = conf_get_int(inst->conf, CONF_nethack_keypad); + app_keypad_mode = (inst->term->app_keypad_keys && + !conf_get_int(inst->conf, CONF_no_applic_k)); + /* ALT+things gives leading Escape. */ output[0] = '\033'; #if !GTK_CHECK_VERSION(2,0,0) @@ -658,13 +693,73 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) output_charset = CS_ISO8859_1; strncpy(output+1, event->string, lenof(output)-1); #else + /* + * Most things can now be passed to + * gtk_im_context_filter_keypress without breaking anything + * below this point. An exception is the numeric keypad if + * we're in Nethack or application mode: the IM will eat + * numeric keypad presses if Num Lock is on, but we don't want + * it to. + */ + if (app_keypad_mode && + (event->keyval == GDK_Num_Lock || + event->keyval == GDK_KP_Divide || + event->keyval == GDK_KP_Multiply || + event->keyval == GDK_KP_Subtract || + event->keyval == GDK_KP_Add || + event->keyval == GDK_KP_Enter || + event->keyval == GDK_KP_0 || + event->keyval == GDK_KP_Insert || + event->keyval == GDK_KP_1 || + event->keyval == GDK_KP_End || + event->keyval == GDK_KP_2 || + event->keyval == GDK_KP_Down || + event->keyval == GDK_KP_3 || + event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_KP_4 || + event->keyval == GDK_KP_Left || + event->keyval == GDK_KP_5 || + event->keyval == GDK_KP_Begin || + event->keyval == GDK_KP_6 || + event->keyval == GDK_KP_Right || + event->keyval == GDK_KP_7 || + event->keyval == GDK_KP_Home || + event->keyval == GDK_KP_8 || + event->keyval == GDK_KP_Up || + event->keyval == GDK_KP_9 || + event->keyval == GDK_KP_Page_Up || + event->keyval == GDK_KP_Decimal || + event->keyval == GDK_KP_Delete)) { + /* app keypad; do nothing */ + } else if (nethack_mode && + (event->keyval == GDK_KP_1 || + event->keyval == GDK_KP_End || + event->keyval == GDK_KP_2 || + event->keyval == GDK_KP_Down || + event->keyval == GDK_KP_3 || + event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_KP_4 || + event->keyval == GDK_KP_Left || + event->keyval == GDK_KP_5 || + event->keyval == GDK_KP_Begin || + event->keyval == GDK_KP_6 || + event->keyval == GDK_KP_Right || + event->keyval == GDK_KP_7 || + event->keyval == GDK_KP_Home || + event->keyval == GDK_KP_8 || + event->keyval == GDK_KP_Up || + event->keyval == GDK_KP_9 || + event->keyval == GDK_KP_Page_Up)) { + /* nethack mode; do nothing */ + } else { + if (gtk_im_context_filter_keypress(inst->imc, event)) + return TRUE; + } + /* * GDK 2.0 arranges to have done some translation for us: in * GDK 2.0, event->string is encoded in the current locale. * - * (However, it's also deprecated; we really ought to be - * using a GTKIMContext.) - * * So we use the standard C library function mbstowcs() to * convert from the current locale into Unicode; from there * we can convert to whatever PuTTY is currently working in. @@ -675,7 +770,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) */ output_charset = CS_UTF8; { - wchar_t widedata[32], *wp; + wchar_t widedata[32]; + const wchar_t *wp; int wlen; int ulen; @@ -755,7 +851,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* We don't let GTK tell us what Backspace is! We know better. */ if (event->keyval == GDK_BackSpace && !(event->state & GDK_SHIFT_MASK)) { - output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08'; + output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? + '\x7F' : '\x08'; use_ucsoutput = FALSE; end = 2; special = TRUE; @@ -763,7 +860,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* For Shift Backspace, do opposite of what is configured. */ if (event->keyval == GDK_BackSpace && (event->state & GDK_SHIFT_MASK)) { - output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F'; + output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ? + '\x08' : '\x7F'; use_ucsoutput = FALSE; end = 2; special = TRUE; @@ -786,7 +884,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* * NetHack keypad mode. */ - if (inst->cfg.nethack_keypad) { + if (nethack_mode) { char *keys = NULL; switch (event->keyval) { case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break; @@ -815,7 +913,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) /* * Application keypad mode. */ - if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) { + if (app_keypad_mode) { int xkey = 0; switch (event->keyval) { case GDK_Num_Lock: xkey = 'P'; break; @@ -829,7 +927,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * in xterm function key mode we change which two... */ case GDK_KP_Add: - if (inst->cfg.funky_type == FUNKY_XTERM) { + if (conf_get_int(inst->conf, CONF_funky_type) == FUNKY_XTERM) { if (event->state & GDK_SHIFT_MASK) xkey = 'l'; else @@ -876,6 +974,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) */ { int code = 0; + int funky_type = conf_get_int(inst->conf, CONF_funky_type); switch (event->keyval) { case GDK_F1: code = (event->state & GDK_SHIFT_MASK ? 23 : 11); @@ -959,7 +1058,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) break; } /* Reorder edit keys to physical order */ - if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6) + if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (inst->term->vt52_mode && code > 0 && code <= 6) { @@ -968,7 +1067,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) goto done; } - if (inst->cfg.funky_type == FUNKY_SCO && /* SCO function keys */ + if (funky_type == FUNKY_SCO && /* SCO function keys */ code >= 11 && code <= 34) { char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; @@ -992,7 +1091,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { @@ -1004,7 +1103,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) && + if ((inst->term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) @@ -1020,12 +1119,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11); use_ucsoutput = FALSE; goto done; } - if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (inst->term->vt52_mode) end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11); else @@ -1033,7 +1132,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) use_ucsoutput = FALSE; goto done; } - if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) { + if ((code == 1 || code == 4) && + conf_get_int(inst->conf, CONF_rxvt_homeend)) { end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw"); use_ucsoutput = FALSE; goto done; @@ -1119,6 +1219,17 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) return TRUE; } +#if GTK_CHECK_VERSION(2,0,0) +void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) +{ + struct gui_data *inst = (struct gui_data *)data; + if (inst->ldisc) + lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), 1); + show_mouseptr(inst, 0); + term_seen_key_event(inst->term); +} +#endif + gboolean button_internal(struct gui_data *inst, guint32 timestamp, GdkEventType type, guint ebutton, guint state, gdouble ex, gdouble ey) @@ -1166,12 +1277,13 @@ gboolean button_internal(struct gui_data *inst, guint32 timestamp, default: return FALSE; /* don't know this event type */ } - if (send_raw_mouse && !(inst->cfg.mouse_override && shift) && + if (send_raw_mouse && !(shift && conf_get_int(inst->conf, + CONF_mouse_override)) && act != MA_CLICK && act != MA_RELEASE) return TRUE; /* we ignore these in raw mouse mode */ - x = (ex - inst->cfg.window_border) / inst->font_width; - y = (ey - inst->cfg.window_border) / inst->font_height; + x = (ex - inst->window_border) / inst->font_width; + y = (ey - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), act, x, y, shift, ctrl, alt); @@ -1231,8 +1343,8 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) else return FALSE; /* don't even know what button! */ - x = (event->x - inst->cfg.window_border) / inst->font_width; - y = (event->y - inst->cfg.window_border) / inst->font_height; + x = (event->x - inst->window_border) / inst->font_width; + y = (event->y - inst->window_border) / inst->font_height; term_mouse(inst->term, button, translate_button(button), MA_DRAG, x, y, shift, ctrl, alt); @@ -1249,31 +1361,30 @@ void frontend_keypress(void *handle) * any keypress. */ if (inst->exited) - exit(0); + cleanup_exit(0); } static gint idle_exit_func(gpointer data) { struct gui_data *inst = (struct gui_data *)data; - int exitcode; + int exitcode, close_on_exit; if (!inst->exited && (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) { inst->exited = TRUE; - if (inst->cfg.close_on_exit == FORCE_ON || - (inst->cfg.close_on_exit == AUTO && exitcode == 0)) + close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit); + if (close_on_exit == FORCE_ON || + (close_on_exit == AUTO && exitcode == 0)) gtk_main_quit(); /* just go */ if (inst->ldisc) { ldisc_free(inst->ldisc); inst->ldisc = NULL; } - if (inst->back) { - inst->back->free(inst->backhandle); - inst->backhandle = NULL; - inst->back = NULL; - term_provide_resize_fn(inst->term, NULL, NULL); - update_specials_menu(inst); - } + inst->back->free(inst->backhandle); + inst->backhandle = NULL; + inst->back = NULL; + term_provide_resize_fn(inst->term, NULL, NULL); + update_specials_menu(inst); gtk_widget_set_sensitive(inst->restartitem, TRUE); } @@ -1290,13 +1401,18 @@ void notify_remote_exit(void *frontend) static gint timer_trigger(gpointer data) { - long now = GPOINTER_TO_LONG(data); - long next; + unsigned long now = GPOINTER_TO_LONG(data); + unsigned long next, then; long ticks; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger, + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + timer_id = gtk_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next)); } @@ -1307,7 +1423,7 @@ static gint timer_trigger(gpointer data) return FALSE; } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { long ticks; @@ -1364,7 +1480,7 @@ void set_busy_status(void *frontend, int status) void set_raw_mouse_mode(void *frontend, int activate) { struct gui_data *inst = (struct gui_data *)frontend; - activate = activate && !inst->cfg.no_mouse_rep; + activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep); send_raw_mouse = activate; update_mouseptr(inst); } @@ -1412,8 +1528,8 @@ void request_resize(void *frontend, int w, int h) offset_x = outer.width - inner.width; offset_y = outer.height - inner.height; - area_x = inst->font_width * w + 2*inst->cfg.window_border; - area_y = inst->font_height * h + 2*inst->cfg.window_border; + area_x = inst->font_width * w + 2*inst->window_border; + area_y = inst->font_height * h + 2*inst->window_border; /* * Now we must set the size request on the drawing area back to @@ -1480,7 +1596,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) struct gui_data *inst = (struct gui_data *)frontend; if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; real_palette_set(inst, n, r, g, b); if (n == 258) { @@ -1495,7 +1611,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) void palette_reset(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; - /* This maps colour indices in inst->cfg to those used in inst->cols. */ + /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, @@ -1513,9 +1629,12 @@ void palette_reset(void *frontend) } for (i = 0; i < NCFGCOLOURS; i++) { - inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101; - inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101; - inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101; + inst->cols[ww[i]].red = + conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101; + inst->cols[ww[i]].green = + conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101; + inst->cols[ww[i]].blue = + conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101; } for (i = 0; i < NEXTCOLOURS; i++) { @@ -1537,8 +1656,10 @@ void palette_reset(void *frontend) for (i = 0; i < NALLCOLOURS; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, i, inst->cfg.colours[i][0], - inst->cfg.colours[i][1], inst->cfg.colours[i][2]); + appname, i, + conf_get_int_int(inst->conf, CONF_colours, i*3+0), + conf_get_int_int(inst->conf, CONF_colours, i*3+1), + conf_get_int_int(inst->conf, CONF_colours, i*3+2)); } /* Since Default Background may have changed, ensure that space @@ -1611,7 +1732,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des * if we aren't in direct-to-font mode using the D800 hack. */ if (!inst->direct_to_font) { - wchar_t *tmp = data; + const wchar_t *tmp = data; int tmplen = len; XTextProperty tp; char *list[1]; @@ -1898,30 +2019,40 @@ static void set_window_titles(struct gui_data *inst) * is life. */ gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle); - if (!inst->cfg.win_name_always) + if (!conf_get_int(inst->conf, CONF_win_name_always)) gdk_window_set_icon_name(inst->window->window, inst->icontitle); } void set_title(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; - strncpy(inst->wintitle, title, lenof(inst->wintitle)); - inst->wintitle[lenof(inst->wintitle)-1] = '\0'; + sfree(inst->wintitle); + inst->wintitle = dupstr(title); set_window_titles(inst); } void set_icon(void *frontend, char *title) { struct gui_data *inst = (struct gui_data *)frontend; - strncpy(inst->icontitle, title, lenof(inst->icontitle)); - inst->icontitle[lenof(inst->icontitle)-1] = '\0'; + sfree(inst->icontitle); + inst->icontitle = dupstr(title); + set_window_titles(inst); +} + +void set_title_and_icon(void *frontend, char *title, char *icon) +{ + struct gui_data *inst = (struct gui_data *)frontend; + sfree(inst->wintitle); + inst->wintitle = dupstr(title); + sfree(inst->icontitle); + inst->icontitle = dupstr(icon); set_window_titles(inst); } void set_sbar(void *frontend, int total, int start, int page) { struct gui_data *inst = (struct gui_data *)frontend; - if (!inst->cfg.scrollbar) + if (!conf_get_int(inst->conf, CONF_scrollbar)) return; inst->sbar_adjust->lower = 0; inst->sbar_adjust->upper = total; @@ -1938,7 +2069,7 @@ void scrollbar_moved(GtkAdjustment *adj, gpointer data) { struct gui_data *inst = (struct gui_data *)data; - if (!inst->cfg.scrollbar) + if (!conf_get_int(inst->conf, CONF_scrollbar)) return; if (!inst->ignore_sbar) term_scroll(inst->term, 1, (int)adj->value); @@ -2027,11 +2158,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = nbg; nbg = t; } - if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) { + if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) { + if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -2049,7 +2180,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, widefactor = 1; } - if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) { + if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) { bold = 1; fontid |= 1; } else { @@ -2086,8 +2217,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, { GdkRectangle r; - r.x = x*inst->font_width+inst->cfg.window_border; - r.y = y*inst->font_height+inst->cfg.window_border; + r.x = x*inst->font_width+inst->window_border; + r.y = y*inst->font_height+inst->window_border; r.width = rlen*widefactor*inst->font_width; r.height = inst->font_height; gdk_gc_set_clip_rectangle(gc, &r); @@ -2095,43 +2226,27 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, gdk_gc_set_foreground(gc, &inst->cols[nbg]); gdk_draw_rectangle(inst->pixmap, gc, 1, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, inst->font_height); gdk_gc_set_foreground(gc, &inst->cols[nfg]); - { - gchar *gcs; - - /* - * FIXME: this length is hardwired on the assumption that - * conversions from wide to multibyte characters will - * never generate more than 10 bytes for a single wide - * character. - */ - gcs = snewn(len*10+1, gchar); - - for (combining = 0; combining < ncombining; combining++) { - int mblen = wc_to_mb(inst->fonts[fontid]->real_charset, 0, - text + combining, len, gcs, len*10+1, ".", - NULL, NULL); - unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent, - gcs, mblen, widefactor > 1, bold, inst->font_width); - } - - sfree(gcs); + for (combining = 0; combining < ncombining; combining++) { + unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+inst->fonts[0]->ascent, + text + combining, len, widefactor > 1, + bold, inst->font_width); } if (attr & ATTR_UNDER) { int uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; - gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border, - y*inst->font_height + uheight + inst->cfg.window_border, - (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border, - y*inst->font_height + uheight + inst->cfg.window_border); + gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->window_border, + y*inst->font_height + uheight + inst->window_border, + (x+len)*widefactor*inst->font_width-1+inst->window_border, + y*inst->font_height + uheight + inst->window_border); } if ((lattr & LATTR_MODE) != LATTR_NORM) { @@ -2146,10 +2261,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, int i; for (i = 0; i < len * widefactor * inst->font_width; i++) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border + 2*i, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border + 2*i+1, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border + 2*i, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border + 2*i+1, + y*inst->font_height+inst->window_border, len * widefactor * inst->font_width - i, inst->font_height); } len *= 2; @@ -2162,10 +2277,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, dt = 1, db = 0; for (i = 0; i < inst->font_height; i+=2) { gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+dt*i+db, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border+dt*(i+1), + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+dt*i+db, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border+dt*(i+1), len * widefactor * inst->font_width, inst->font_height-i-1); } } @@ -2198,10 +2313,10 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len, } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); } @@ -2219,7 +2334,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, passive = 1; } else passive = 0; - if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) { + if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) { attr &= ~TATTR_ACTCURS; active = 1; } else @@ -2244,7 +2359,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, len *= 2; } - if (inst->cfg.cursor_type == 0) { + if (inst->cursor_type == 0) { /* * An active block cursor will already have been done by * the above do_text call, so we only need to do anything @@ -2253,8 +2368,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, if (passive) { gdk_gc_set_foreground(gc, &inst->cols[261]); gdk_draw_rectangle(inst->pixmap, gc, 0, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width-1, inst->font_height-1); } } else { @@ -2268,13 +2383,13 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, else char_width = inst->font_width; - if (inst->cfg.cursor_type == 1) { + if (inst->cursor_type == 1) { uheight = inst->fonts[0]->ascent + 1; if (uheight >= inst->font_height) uheight = inst->font_height - 1; - startx = x * inst->font_width + inst->cfg.window_border; - starty = y * inst->font_height + inst->cfg.window_border + uheight; + startx = x * inst->font_width + inst->window_border; + starty = y * inst->font_height + inst->window_border + uheight; dx = 1; dy = 0; length = len * widefactor * char_width; @@ -2282,8 +2397,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, int xadjust = 0; if (attr & TATTR_RIGHTCURS) xadjust = char_width - 1; - startx = x * inst->font_width + inst->cfg.window_border + xadjust; - starty = y * inst->font_height + inst->cfg.window_border; + startx = x * inst->font_width + inst->window_border + xadjust; + starty = y * inst->font_height + inst->window_border; dx = 0; dy = 1; length = inst->font_height; @@ -2305,11 +2420,22 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, } gdk_draw_pixmap(inst->area->window, gc, inst->pixmap, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, - x*inst->font_width+inst->cfg.window_border, - y*inst->font_height+inst->cfg.window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, + x*inst->font_width+inst->window_border, + y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); + +#if GTK_CHECK_VERSION(2,0,0) + { + GdkRectangle cursorrect; + cursorrect.x = x*inst->font_width+inst->window_border; + cursorrect.y = y*inst->font_height+inst->window_border; + cursorrect.width = len*widefactor*inst->font_width; + cursorrect.height = inst->font_height; + gtk_im_context_set_cursor_location(inst->imc, &cursorrect); + } +#endif } GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val) @@ -2467,8 +2593,15 @@ static void help(FILE *fp) { } } +static void version(FILE *fp) { + if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) { + perror("output error"); + exit(1); + } +} + int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, - struct gui_data *inst, Config *cfg) + struct gui_data *inst, Conf *conf) { int err = 0; char *val; @@ -2507,7 +2640,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, p = "-title"; ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - do_everything ? 1 : -1, cfg); + do_everything ? 1 : -1, conf); if (ret == -2) { cmdline_error("option \"%s\" requires an argument", p); @@ -2519,34 +2652,41 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } if (!strcmp(p, "-fn") || !strcmp(p, "-font")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->font.name, val, sizeof(cfg->font.name)); - cfg->font.name[sizeof(cfg->font.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_font, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fb")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name)); - cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_boldfont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fw")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name)); - cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_widefont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-fwb")) { + FontSpec *fs; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name)); - cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0'; + fs = fontspec_new(val); + conf_set_fontspec(conf, CONF_wideboldfont, fs); + fontspec_free(fs); } else if (!strcmp(p, "-cs")) { EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage)); - cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0'; + conf_set_str(conf, CONF_line_codepage, val); } else if (!strcmp(p, "-geometry")) { int flags, x, y; @@ -2556,9 +2696,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, flags = XParseGeometry(val, &x, &y, &w, &h); if (flags & WidthValue) - cfg->width = (int)w; + conf_set_int(conf, CONF_width, w); if (flags & HeightValue) - cfg->height = (int)h; + conf_set_int(conf, CONF_height, h); if (flags & (XValue | YValue)) { inst->xpos = x; @@ -2571,7 +2711,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } else if (!strcmp(p, "-sl")) { EXPECTS_ARG; SECOND_PASS_ONLY; - cfg->savelines = atoi(val); + conf_set_int(conf, CONF_savelines, atoi(val)); } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") || !strcmp(p, "-bfg") || !strcmp(p, "-bbg") || @@ -2593,9 +2733,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, !strcmp(p, "-cfg") ? 4 : !strcmp(p, "-cbg") ? 5 : -1); assert(index != -1); - cfg->colours[index][0] = col.red / 256; - cfg->colours[index][1] = col.green / 256; - cfg->colours[index][2] = col.blue / 256; + conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256); + conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256); + conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256); } } else if (use_pty_argv && !strcmp(p, "-e")) { @@ -2618,43 +2758,44 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, } else if (!strcmp(p, "-title")) { EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->wintitle, val, sizeof(cfg->wintitle)); - cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0'; + conf_set_str(conf, CONF_wintitle, val); } else if (!strcmp(p, "-log")) { + Filename *fn; EXPECTS_ARG; SECOND_PASS_ONLY; - strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path)); - cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0'; - cfg->logtype = LGTYP_DEBUG; + fn = filename_from_str(val); + conf_set_filename(conf, CONF_logfilename, fn); + conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); + filename_free(fn); } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) { SECOND_PASS_ONLY; - cfg->stamp_utmp = 0; + conf_set_int(conf, CONF_stamp_utmp, 0); } else if (!strcmp(p, "-ut")) { SECOND_PASS_ONLY; - cfg->stamp_utmp = 1; + conf_set_int(conf, CONF_stamp_utmp, 1); } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) { SECOND_PASS_ONLY; - cfg->login_shell = 0; + conf_set_int(conf, CONF_login_shell, 0); } else if (!strcmp(p, "-ls")) { SECOND_PASS_ONLY; - cfg->login_shell = 1; + conf_set_int(conf, CONF_login_shell, 1); } else if (!strcmp(p, "-nethack")) { SECOND_PASS_ONLY; - cfg->nethack_keypad = 1; + conf_set_int(conf, CONF_nethack_keypad, 1); } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) { SECOND_PASS_ONLY; - cfg->scrollbar = 0; + conf_set_int(conf, CONF_scrollbar, 0); } else if (!strcmp(p, "-sb")) { SECOND_PASS_ONLY; - cfg->scrollbar = 0; + conf_set_int(conf, CONF_scrollbar, 1); } else if (!strcmp(p, "-name")) { EXPECTS_ARG; @@ -2668,12 +2809,16 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, help(stdout); exit(0); + } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) { + version(stdout); + exit(0); + } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); } else if(p[0] != '-' && (!do_everything || - process_nonoption_arg(p, cfg, + process_nonoption_arg(p, conf, allow_launch))) { /* do nothing */ @@ -2699,86 +2844,112 @@ void uxsel_input_remove(int id) { gdk_input_remove(id); } -void setup_fonts_ucs(struct gui_data *inst) -{ - if (inst->fonts[0]) - unifont_destroy(inst->fonts[0]); - if (inst->fonts[1]) - unifont_destroy(inst->fonts[1]); - if (inst->fonts[2]) - unifont_destroy(inst->fonts[2]); - if (inst->fonts[3]) - unifont_destroy(inst->fonts[3]); - - inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name, - FALSE, FALSE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[0]) { - fprintf(stderr, "%s: unable to load font \"%s\"\n", appname, - inst->cfg.font.name); - exit(1); +int frontend_net_pending_error_idle_id; +int frontend_got_net_pending_errors = FALSE; +gboolean frontend_net_pending_errors(gpointer data) +{ + net_pending_errors(); + gtk_idle_remove(frontend_net_pending_error_idle_id); + frontend_got_net_pending_errors = FALSE; + return FALSE; +} +void frontend_net_error_pending(void) +{ + if (!frontend_got_net_pending_errors) { + frontend_got_net_pending_errors = TRUE; + frontend_net_pending_error_idle_id = + gtk_idle_add(frontend_net_pending_errors, NULL); } +} + +char *setup_fonts_ucs(struct gui_data *inst) +{ + int shadowbold = conf_get_int(inst->conf, CONF_shadowbold); + int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset); + FontSpec *fs; + unifont *fonts[4]; + int i; - if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) { - inst->fonts[1] = NULL; + fs = conf_get_fontspec(inst->conf, CONF_font); + fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE, + shadowboldoffset, shadowbold); + if (!fonts[0]) { + return dupprintf("unable to load font \"%s\"", fs->name); + } + + fs = conf_get_fontspec(inst->conf, CONF_boldfont); + if (shadowbold || !fs->name[0]) { + fonts[1] = NULL; } else { - inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name, - FALSE, TRUE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[1]) { - fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname, - inst->cfg.boldfont.name); - exit(1); + fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE, + shadowboldoffset, shadowbold); + if (!fonts[1]) { + if (fonts[0]) + unifont_destroy(fonts[0]); + return dupprintf("unable to load bold font \"%s\"", fs->name); } } - if (inst->cfg.widefont.name[0]) { - inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name, - TRUE, FALSE, - inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[2]) { - fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname, - inst->cfg.widefont.name); - exit(1); + fs = conf_get_fontspec(inst->conf, CONF_widefont); + if (fs->name[0]) { + fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE, + shadowboldoffset, shadowbold); + if (!fonts[2]) { + for (i = 0; i < 2; i++) + if (fonts[i]) + unifont_destroy(fonts[i]); + return dupprintf("%s: unable to load wide font \"%s\"", fs->name); } } else { - inst->fonts[2] = NULL; + fonts[2] = NULL; } - if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) { - inst->fonts[3] = NULL; + fs = conf_get_fontspec(inst->conf, CONF_wideboldfont); + if (shadowbold || !fs->name[0]) { + fonts[3] = NULL; } else { - inst->fonts[3] = unifont_create(inst->area, - inst->cfg.wideboldfont.name, TRUE, - TRUE, inst->cfg.shadowboldoffset, - inst->cfg.shadowbold); - if (!inst->fonts[3]) { - fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname, - inst->cfg.boldfont.name); - exit(1); + fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE, + shadowboldoffset, shadowbold); + if (!fonts[3]) { + for (i = 0; i < 3; i++) + if (fonts[i]) + unifont_destroy(fonts[i]); + return dupprintf("%s: unable to load wide bold font \"%s\"", + fs->name); } } + /* + * Now we've got past all the possible error conditions, we can + * actually update our state. + */ + + for (i = 0; i < 4; i++) { + if (inst->fonts[i]) + unifont_destroy(inst->fonts[i]); + inst->fonts[i] = fonts[i]; + } + inst->font_width = inst->fonts[0]->width; inst->font_height = inst->fonts[0]->height; - inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage, - inst->cfg.utf8_override, + inst->direct_to_font = init_ucs(&inst->ucsdata, + conf_get_str(inst->conf, CONF_line_codepage), + conf_get_int(inst->conf, CONF_utf8_override), inst->fonts[0]->public_charset, - inst->cfg.vtmode); + conf_get_int(inst->conf, CONF_vtmode)); + + return NULL; } void set_geom_hints(struct gui_data *inst) { GdkGeometry geom; - geom.min_width = inst->font_width + 2*inst->cfg.window_border; - geom.min_height = inst->font_height + 2*inst->cfg.window_border; + geom.min_width = inst->font_width + 2*inst->window_border; + geom.min_height = inst->font_height + 2*inst->window_border; geom.max_width = geom.max_height = -1; - geom.base_width = 2*inst->cfg.window_border; - geom.base_height = 2*inst->cfg.window_border; + geom.base_width = 2*inst->window_border; + geom.base_height = 2*inst->window_border; geom.width_inc = inst->font_width; geom.height_inc = inst->font_height; geom.min_aspect = geom.max_aspect = 0; @@ -2831,16 +3002,16 @@ void event_log_menuitem(GtkMenuItem *item, gpointer data) void change_settings_menuitem(GtkMenuItem *item, gpointer data) { - /* This maps colour indices in inst->cfg to those used in inst->cols. */ + /* This maps colour indices in inst->conf to those used in inst->cols. */ static const int ww[] = { 256, 257, 258, 259, 260, 261, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 }; struct gui_data *inst = (struct gui_data *)data; - char *title = dupcat(appname, " Reconfiguration", NULL); - Config cfg2, oldcfg; - int i, need_size; + char *title; + Conf *oldconf, *newconf; + int i, j, need_size; assert(lenof(ww) == NCFGCOLOURS); @@ -2849,50 +3020,58 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) else inst->reconfiguring = TRUE; - cfg2 = inst->cfg; /* structure copy */ + title = dupcat(appname, " Reconfiguration", NULL); - if (do_config_box(title, &cfg2, 1, - inst->back?inst->back->cfg_info(inst->backhandle):0)) { + oldconf = inst->conf; + newconf = conf_copy(inst->conf); - oldcfg = inst->cfg; /* structure copy */ - inst->cfg = cfg2; /* structure copy */ + if (do_config_box(title, newconf, 1, + inst->back?inst->back->cfg_info(inst->backhandle):0)) { + inst->conf = newconf; /* Pass new config data to the logging module */ - log_reconfig(inst->logctx, &cfg2); + log_reconfig(inst->logctx, inst->conf); /* * Flush the line discipline's edit buffer in the case * where local editing has just been disabled. */ - if (inst->ldisc) + if (inst->ldisc) { + ldisc_configure(inst->ldisc, inst->conf); ldisc_send(inst->ldisc, NULL, 0, 0); + } /* Pass new config data to the terminal */ - term_reconfig(inst->term, &cfg2); + term_reconfig(inst->term, inst->conf); /* Pass new config data to the back end */ if (inst->back) - inst->back->reconfig(inst->backhandle, &cfg2); + inst->back->reconfig(inst->backhandle, inst->conf); + + cache_conf_values(inst); /* - * Just setting inst->cfg is sufficient to cause colour + * Just setting inst->conf is sufficient to cause colour * setting changes to appear on the next ESC]R palette * reset. But we should also check whether any colour - * settings have been changed, and revert the ones that - * have to the new default, on the assumption that the user - * is most likely to want an immediate update. + * settings have been changed, and revert the ones that have + * to the new default, on the assumption that the user is + * most likely to want an immediate update. */ for (i = 0; i < NCFGCOLOURS; i++) { - if (oldcfg.colours[i][0] != cfg2.colours[i][0] || - oldcfg.colours[i][1] != cfg2.colours[i][1] || - oldcfg.colours[i][2] != cfg2.colours[i][2]) { - real_palette_set(inst, ww[i], cfg2.colours[i][0], - cfg2.colours[i][1], - cfg2.colours[i][2]); + for (j = 0; j < 3; j++) + if (conf_get_int_int(oldconf, CONF_colours, i*3+j) != + conf_get_int_int(newconf, CONF_colours, i*3+j)) + break; + if (j < 3) { + real_palette_set(inst, ww[i], + conf_get_int_int(newconf,CONF_colours,i*3+0), + conf_get_int_int(newconf,CONF_colours,i*3+1), + conf_get_int_int(newconf,CONF_colours,i*3+2)); /* * If the default background has changed, we must * repaint the space in between the window border * and the text area. */ - if (i == 258) { + if (ww[i] == 258) { set_window_background(inst); draw_backing_rect(inst); } @@ -2903,47 +3082,80 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * If the scrollbar needs to be shown, hidden, or moved * from one end to the other of the window, do so now. */ - if (oldcfg.scrollbar != cfg2.scrollbar) { - if (cfg2.scrollbar) + if (conf_get_int(oldconf, CONF_scrollbar) != + conf_get_int(newconf, CONF_scrollbar)) { + if (conf_get_int(newconf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); } - if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) { + if (conf_get_int(oldconf, CONF_scrollbar_on_left) != + conf_get_int(newconf, CONF_scrollbar_on_left)) { gtk_box_reorder_child(inst->hbox, inst->sbar, - cfg2.scrollbar_on_left ? 0 : 1); + conf_get_int(newconf, CONF_scrollbar_on_left) + ? 0 : 1); } /* * Change the window title, if required. */ - if (strcmp(oldcfg.wintitle, cfg2.wintitle)) - set_title(inst, cfg2.wintitle); + if (strcmp(conf_get_str(oldconf, CONF_wintitle), + conf_get_str(newconf, CONF_wintitle))) + set_title(inst, conf_get_str(newconf, CONF_wintitle)); set_window_titles(inst); /* * Redo the whole tangled fonts and Unicode mess if * necessary. */ - if (strcmp(oldcfg.font.name, cfg2.font.name) || - strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) || - strcmp(oldcfg.widefont.name, cfg2.widefont.name) || - strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) || - strcmp(oldcfg.line_codepage, cfg2.line_codepage) || - oldcfg.vtmode != cfg2.vtmode || - oldcfg.shadowbold != cfg2.shadowbold) { - setup_fonts_ucs(inst); - need_size = 1; - } else - need_size = 0; + need_size = FALSE; + if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name, + conf_get_fontspec(newconf, CONF_font)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name, + conf_get_fontspec(newconf, CONF_boldfont)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name, + conf_get_fontspec(newconf, CONF_widefont)->name) || + strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name, + conf_get_fontspec(newconf, CONF_wideboldfont)->name) || + strcmp(conf_get_str(oldconf, CONF_line_codepage), + conf_get_str(newconf, CONF_line_codepage)) || + conf_get_int(oldconf, CONF_utf8_override) != + conf_get_int(newconf, CONF_utf8_override) || + conf_get_int(oldconf, CONF_vtmode) != + conf_get_int(newconf, CONF_vtmode) || + conf_get_int(oldconf, CONF_shadowbold) != + conf_get_int(newconf, CONF_shadowbold) || + conf_get_int(oldconf, CONF_shadowboldoffset) != + conf_get_int(newconf, CONF_shadowboldoffset)) { + char *errmsg = setup_fonts_ucs(inst); + if (errmsg) { + char *msgboxtext = + dupprintf("Could not change fonts in terminal window: %s\n", + errmsg); + messagebox(inst->window, "Font setup error", msgboxtext, + string_width("Could not change fonts in terminal window:"), + "OK", 'o', +1, 1, + NULL); + sfree(msgboxtext); + sfree(errmsg); + } else { + need_size = TRUE; + } + } /* * Resize the window. */ - if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height || - oldcfg.window_border != cfg2.window_border || need_size) { + if (conf_get_int(oldconf, CONF_width) != + conf_get_int(newconf, CONF_width) || + conf_get_int(oldconf, CONF_height) != + conf_get_int(newconf, CONF_height) || + conf_get_int(oldconf, CONF_window_border) != + conf_get_int(newconf, CONF_window_border) || + need_size) { set_geom_hints(inst); - request_resize(inst, cfg2.width, cfg2.height); + request_resize(inst, conf_get_int(newconf, CONF_width), + conf_get_int(newconf, CONF_height)); } else { /* * The above will have caused a call to term_size() for @@ -2952,9 +3164,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * happened and we will need an explicit term_size() * here. */ - if (oldcfg.savelines != cfg2.savelines) + if (conf_get_int(oldconf, CONF_savelines) != + conf_get_int(newconf, CONF_savelines)) term_size(inst->term, inst->term->rows, inst->term->cols, - cfg2.savelines); + conf_get_int(newconf, CONF_savelines)); } term_invalidate(inst->term); @@ -2964,6 +3177,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * border has been redrawn as well as the text area. */ gtk_widget_queue_draw(inst->area); + + conf_free(oldconf); + } else { + conf_free(newconf); } sfree(title); inst->reconfiguring = FALSE; @@ -3014,6 +3231,7 @@ void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...) pid = fork(); if (pid < 0) { perror("fork"); + sfree(args); return; } @@ -3047,6 +3265,7 @@ void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...) } else { int status; + sfree(args); waitpid(pid, &status, 0); } @@ -3056,11 +3275,11 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { struct gui_data *inst = (struct gui_data *)gdata; /* - * For this feature we must marshal cfg and (possibly) pty_argv + * For this feature we must marshal conf and (possibly) pty_argv * into a byte stream, create a pipe, and send this byte stream * to the child through the pipe. */ - int i, ret, size; + int i, ret, sersize, size; char *data; char option[80]; int pipefd[2]; @@ -3070,16 +3289,16 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) return; } - size = sizeof(inst->cfg); + size = sersize = conf_serialised_size(inst->conf); if (use_pty_argv && pty_argv) { for (i = 0; pty_argv[i]; i++) size += strlen(pty_argv[i]) + 1; } data = snewn(size, char); - memcpy(data, &inst->cfg, sizeof(inst->cfg)); + conf_serialise(inst->conf, data); if (use_pty_argv && pty_argv) { - int p = sizeof(inst->cfg); + int p = sersize; for (i = 0; pty_argv[i]; i++) { strcpy(data + p, pty_argv[i]); p += strlen(pty_argv[i]) + 1; @@ -3088,7 +3307,7 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) } sprintf(option, "---[%d,%d]", pipefd[0], size); - fcntl(pipefd[0], F_SETFD, 0); + noncloexec(pipefd[0]); fork_and_exec_self(inst, pipefd[1], option, NULL); close(pipefd[0]); @@ -3101,9 +3320,9 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) sfree(data); } -int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) +int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg) { - int fd, i, ret, size; + int fd, i, ret, size, size_used; char *data; if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) { @@ -3124,10 +3343,10 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) exit(1); } - memcpy(cfg, data, sizeof(Config)); - if (use_pty_argv && size > sizeof(Config)) { + size_used = conf_deserialise(conf, data, size); + if (use_pty_argv && size > size_used) { int n = 0; - i = sizeof(Config); + i = size_used; while (i < size) { while (i < size && data[i]) i++; if (i >= size) { @@ -3141,7 +3360,7 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) pty_argv = snewn(n+1, char *); pty_argv[n] = NULL; n = 0; - i = sizeof(Config); + i = size_used; while (i < size) { char *p = data + i; while (i < size && data[i]) i++; @@ -3151,6 +3370,8 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg) } } + sfree(data); + return 0; } @@ -3320,33 +3541,36 @@ void update_specials_menu(void *frontend) static void start_backend(struct gui_data *inst) { - extern Backend *select_backend(Config *cfg); + extern Backend *select_backend(Conf *conf); char *realhost; const char *error; + char *s; - inst->back = select_backend(&inst->cfg); + inst->back = select_backend(inst->conf); error = inst->back->init((void *)inst, &inst->backhandle, - &inst->cfg, inst->cfg.host, inst->cfg.port, - &realhost, inst->cfg.tcp_nodelay, - inst->cfg.tcp_keepalives); + inst->conf, + conf_get_str(inst->conf, CONF_host), + conf_get_int(inst->conf, CONF_port), + &realhost, + conf_get_int(inst->conf, CONF_tcp_nodelay), + conf_get_int(inst->conf, CONF_tcp_keepalives)); if (error) { char *msg = dupprintf("Unable to open connection to %s:\n%s", - inst->cfg.host, error); + conf_get_str(inst->conf, CONF_host), error); inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); exit(0); } - if (inst->cfg.wintitle[0]) { - set_title(inst, inst->cfg.wintitle); - set_icon(inst, inst->cfg.wintitle); + s = conf_get_str(inst->conf, CONF_wintitle); + if (s[0]) { + set_title_and_icon(inst, s, s); } else { char *title = make_default_wintitle(realhost); - set_title(inst, title); - set_icon(inst, title); + set_title_and_icon(inst, title, title); sfree(title); } sfree(realhost); @@ -3356,7 +3580,7 @@ static void start_backend(struct gui_data *inst) term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle); inst->ldisc = - ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle, + ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle, inst); gtk_widget_set_sensitive(inst->restartitem, FALSE); @@ -3364,9 +3588,11 @@ static void start_backend(struct gui_data *inst) int pt_main(int argc, char **argv) { - extern int cfgbox(Config *cfg); + extern int cfgbox(Conf *conf); struct gui_data *inst; + setlocale(LC_CTYPE, ""); + /* * Create an instance structure and initialise to zeroes */ @@ -3374,6 +3600,8 @@ int pt_main(int argc, char **argv) memset(inst, 0, sizeof(*inst)); inst->alt_keycode = -1; /* this one needs _not_ to be zero */ inst->busy_status = BUSY_NOT; + inst->conf = conf_new(); + inst->wintitle = inst->icontitle = NULL; /* defer any child exit handling until we're ready to deal with * it */ @@ -3395,27 +3623,27 @@ int pt_main(int argc, char **argv) } if (argc > 1 && !strncmp(argv[1], "---", 3)) { - read_dupsession_data(inst, &inst->cfg, argv[1]); + read_dupsession_data(inst, inst->conf, argv[1]); /* Splatter this argument so it doesn't clutter a ps listing */ - memset(argv[1], 0, strlen(argv[1])); + smemclr(argv[1], strlen(argv[1])); } else { /* 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)) + if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf)) exit(1); /* pre-defaults pass to get -class */ - do_defaults(NULL, &inst->cfg); - if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg)) + do_defaults(NULL, inst->conf); + if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf)) exit(1); /* post-defaults, do everything */ - cmdline_run_saved(&inst->cfg); + cmdline_run_saved(inst->conf); if (loaded_session) allow_launch = TRUE; - if ((!allow_launch || !cfg_launchable(&inst->cfg)) && - !cfgbox(&inst->cfg)) + if ((!allow_launch || !conf_launchable(inst->conf)) && + !cfgbox(inst->conf)) exit(0); /* config box hit Cancel */ } @@ -3426,25 +3654,39 @@ int pt_main(int argc, char **argv) inst->area = gtk_drawing_area_new(); - setup_fonts_ucs(inst); +#if GTK_CHECK_VERSION(2,0,0) + inst->imc = gtk_im_multicontext_new(); +#endif + + { + char *errmsg = setup_fonts_ucs(inst); + if (errmsg) { + fprintf(stderr, "%s: %s\n", appname, errmsg); + exit(1); + } + } init_cutbuffers(); inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - if (inst->cfg.winclass[0]) - gtk_window_set_wmclass(GTK_WINDOW(inst->window), - inst->cfg.winclass, inst->cfg.winclass); + { + const char *winclass = conf_get_str(inst->conf, CONF_winclass); + if (*winclass) + gtk_window_set_wmclass(GTK_WINDOW(inst->window), + winclass, winclass); + } /* * Set up the colour map. */ palette_reset(inst); - inst->width = inst->cfg.width; - inst->height = inst->cfg.height; + inst->width = conf_get_int(inst->conf, CONF_width); + inst->height = conf_get_int(inst->conf, CONF_height); + cache_conf_values(inst); gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), - inst->font_width * inst->cfg.width + 2*inst->cfg.window_border, - inst->font_height * inst->cfg.height + 2*inst->cfg.window_border); + inst->font_width * inst->width + 2*inst->window_border, + inst->font_height * inst->height + 2*inst->window_border); inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0)); inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust); inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); @@ -3453,10 +3695,10 @@ int pt_main(int argc, char **argv) * unwanted, so we can pop it up quickly if it suddenly becomes * desirable. */ - if (inst->cfg.scrollbar_on_left) + if (conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0); - if (!inst->cfg.scrollbar_on_left) + if (!conf_get_int(inst->conf, CONF_scrollbar_on_left)) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox)); @@ -3464,7 +3706,7 @@ int pt_main(int argc, char **argv) set_geom_hints(inst); gtk_widget_show(inst->area); - if (inst->cfg.scrollbar) + if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); @@ -3512,7 +3754,11 @@ int pt_main(int argc, char **argv) GTK_SIGNAL_FUNC(selection_get), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event", GTK_SIGNAL_FUNC(selection_clear), inst); - if (inst->cfg.scrollbar) +#if GTK_CHECK_VERSION(2,0,0) + g_signal_connect(G_OBJECT(inst->imc), "commit", + G_CALLBACK(input_method_commit_event), inst); +#endif + if (conf_get_int(inst->conf, CONF_scrollbar)) gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed", GTK_SIGNAL_FUNC(scrollbar_moved), inst); gtk_widget_add_events(GTK_WIDGET(inst->area), @@ -3612,13 +3858,14 @@ int pt_main(int argc, char **argv) inst->eventlogstuff = eventlogstuff_new(); - inst->term = term_init(&inst->cfg, &inst->ucsdata, inst); - inst->logctx = log_init(inst, &inst->cfg); + inst->term = term_init(inst->conf, &inst->ucsdata, inst); + inst->logctx = log_init(inst, inst->conf); term_provide_logctx(inst->term, inst->logctx); uxsel_init(); - term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines); + term_size(inst->term, inst->height, inst->width, + conf_get_int(inst->conf, CONF_savelines)); start_backend(inst); diff --git a/putty/UNIX/MAKEFILE.AM b/putty/UNIX/MAKEFILE.AM new file mode 100644 index 0000000..99ef1ce --- /dev/null +++ b/putty/UNIX/MAKEFILE.AM @@ -0,0 +1,338 @@ +# Makefile.am for putty under Unix with Autoconf/Automake. +# +# This file was created by `mkfiles.pl' from the `Recipe' file. +# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. + +allsources = $(srcdir)/../be_all_s.c $(srcdir)/../be_none.c \ + $(srcdir)/../be_nos_s.c $(srcdir)/../be_ssh.c \ + $(srcdir)/../charset/charset.h $(srcdir)/../charset/enum.c \ + $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/internal.h \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdgen.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../config.c \ + $(srcdir)/../cproxy.c $(srcdir)/../dialog.c \ + $(srcdir)/../dialog.h $(srcdir)/../import.c \ + $(srcdir)/../int64.c $(srcdir)/../int64.h \ + $(srcdir)/../ldisc.c $(srcdir)/../ldisc.h \ + $(srcdir)/../ldiscucs.c $(srcdir)/../logging.c \ + $(srcdir)/../macosx/osx.h $(srcdir)/../macosx/osxclass.h \ + $(srcdir)/../macosx/osxctrls.m $(srcdir)/../macosx/osxdlg.m \ + $(srcdir)/../macosx/osxmain.m $(srcdir)/../macosx/osxsel.m \ + $(srcdir)/../macosx/osxwin.m $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../misc.h \ + $(srcdir)/../network.h $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../notiming.c \ + $(srcdir)/../pgssapi.c $(srcdir)/../pgssapi.h \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../proxy.h \ + $(srcdir)/../pscp.c $(srcdir)/../psftp.c \ + $(srcdir)/../psftp.h $(srcdir)/../putty.h \ + $(srcdir)/../puttymem.h $(srcdir)/../puttyps.h \ + $(srcdir)/../raw.c $(srcdir)/../rlogin.c \ + $(srcdir)/../sercfg.c $(srcdir)/../settings.c \ + $(srcdir)/../sftp.c $(srcdir)/../sftp.h $(srcdir)/../ssh.c \ + $(srcdir)/../ssh.h $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshdssg.c $(srcdir)/../sshgss.h \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshgssc.h \ + $(srcdir)/../sshmd5.c $(srcdir)/../sshprime.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshrsag.c \ + $(srcdir)/../sshsh256.c $(srcdir)/../sshsh512.c \ + $(srcdir)/../sshsha.c $(srcdir)/../sshzlib.c \ + $(srcdir)/../storage.h $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../terminal.h \ + $(srcdir)/../testback.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../tree234.h $(srcdir)/../unix/gtkcfg.c \ + $(srcdir)/../unix/gtkcols.c $(srcdir)/../unix/gtkcols.h \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkfont.h $(srcdir)/../unix/gtkwin.c \ + $(srcdir)/../unix/unix.h $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxcons.c $(srcdir)/../unix/uxgen.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxplink.c $(srcdir)/../unix/uxprint.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxpterm.c \ + $(srcdir)/../unix/uxpty.c $(srcdir)/../unix/uxputty.c \ + $(srcdir)/../unix/uxsel.c $(srcdir)/../unix/uxser.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmptcfg.c \ + $(srcdir)/../unix/xpmpterm.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../version.c \ + $(srcdir)/../wcwidth.c $(srcdir)/../wildcard.c \ + $(srcdir)/../windows/pageant.rc \ + $(srcdir)/../windows/plink.rc $(srcdir)/../windows/pscp.rc \ + $(srcdir)/../windows/psftp.rc $(srcdir)/../windows/putty.rc \ + $(srcdir)/../windows/puttygen.rc \ + $(srcdir)/../windows/puttytel.rc \ + $(srcdir)/../windows/rcstuff.h \ + $(srcdir)/../windows/sizetip.c \ + $(srcdir)/../windows/version.rc2 \ + $(srcdir)/../windows/win_res.h \ + $(srcdir)/../windows/win_res.rc2 \ + $(srcdir)/../windows/wincfg.c $(srcdir)/../windows/wincons.c \ + $(srcdir)/../windows/winctrls.c \ + $(srcdir)/../windows/windefs.c $(srcdir)/../windows/windlg.c \ + $(srcdir)/../windows/window.c $(srcdir)/../windows/wingss.c \ + $(srcdir)/../windows/winhandl.c \ + $(srcdir)/../windows/winhelp.c \ + $(srcdir)/../windows/winhelp.h \ + $(srcdir)/../windows/winjump.c \ + $(srcdir)/../windows/winmisc.c $(srcdir)/../windows/winnet.c \ + $(srcdir)/../windows/winnoise.c \ + $(srcdir)/../windows/winnojmp.c \ + $(srcdir)/../windows/winpgen.c \ + $(srcdir)/../windows/winpgnt.c \ + $(srcdir)/../windows/winpgntc.c \ + $(srcdir)/../windows/winplink.c \ + $(srcdir)/../windows/winprint.c \ + $(srcdir)/../windows/winproxy.c \ + $(srcdir)/../windows/winser.c $(srcdir)/../windows/winsftp.c \ + $(srcdir)/../windows/winstore.c \ + $(srcdir)/../windows/winstuff.h \ + $(srcdir)/../windows/wintime.c $(srcdir)/../windows/winucs.c \ + $(srcdir)/../windows/winutils.c \ + $(srcdir)/../windows/winx11.c $(srcdir)/../x11fwd.c + +if HAVE_GTK +bin_PROGRAMS = plink pscp psftp puttygen pterm putty puttytel +else +bin_PROGRAMS = plink pscp psftp puttygen +endif + +AM_CPPFLAGS = -I$(srcdir)/.././ -I$(srcdir)/../charset/ \ + -I$(srcdir)/../windows/ -I$(srcdir)/../unix/ \ + -I$(srcdir)/../macosx/ +if HAVE_GTK +AM_CFLAGS = $(GTK_CFLAGS) $(COMPAT) $(XFLAGS) $(WARNINGOPTS) +else +AM_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) +endif + +libversion_a_SOURCES = $(srcdir)/../version.c +libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) $(VER) \ + -DINCLUDE_EMPTY_H `if test -z "$(VER)" && (cd $(srcdir)/..; \ + md5sum -c manifest >/dev/null 2>&1); then cat \ + $(srcdir)/../version.def; else echo "$(VER)"; fi` +noinst_LIBRARIES = libversion.a + +plink_SOURCES = $(srcdir)/../be_all_s.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../ldisc.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../raw.c \ + $(srcdir)/../rlogin.c $(srcdir)/../settings.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../telnet.c \ + $(srcdir)/../time.c $(srcdir)/../timing.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxplink.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxsel.c $(srcdir)/../unix/uxser.c \ + $(srcdir)/../unix/uxsignal.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +plink_LDADD = libversion.a + +pscp_SOURCES = $(srcdir)/../be_ssh.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../int64.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../pscp.c \ + $(srcdir)/../settings.c $(srcdir)/../sftp.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +pscp_LDADD = libversion.a + +psftp_SOURCES = $(srcdir)/../be_ssh.c $(srcdir)/../cmdline.c \ + $(srcdir)/../conf.c $(srcdir)/../cproxy.c \ + $(srcdir)/../int64.c $(srcdir)/../logging.c \ + $(srcdir)/../misc.c $(srcdir)/../pgssapi.c \ + $(srcdir)/../pinger.c $(srcdir)/../portfwd.c \ + $(srcdir)/../proxy.c $(srcdir)/../psftp.c \ + $(srcdir)/../settings.c $(srcdir)/../sftp.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxproxy.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsftp.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +psftp_LDADD = libversion.a + +if HAVE_GTK +pterm_SOURCES = $(srcdir)/../be_none.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../dialog.c \ + $(srcdir)/../ldisc.c $(srcdir)/../ldiscucs.c \ + $(srcdir)/../logging.c $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../sercfg.c \ + $(srcdir)/../settings.c $(srcdir)/../terminal.c \ + $(srcdir)/../time.c $(srcdir)/../timing.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/gtkcfg.c \ + $(srcdir)/../unix/gtkcols.c $(srcdir)/../unix/gtkdlg.c \ + $(srcdir)/../unix/gtkfont.c $(srcdir)/../unix/gtkwin.c \ + $(srcdir)/../unix/uxcfg.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxpterm.c \ + $(srcdir)/../unix/uxpty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxsignal.c $(srcdir)/../unix/uxstore.c \ + $(srcdir)/../unix/uxucs.c $(srcdir)/../unix/xkeysym.c \ + $(srcdir)/../unix/xpmptcfg.c $(srcdir)/../unix/xpmpterm.c \ + $(srcdir)/../wcwidth.c +pterm_LDADD = libversion.a $(GTK_LIBS) +endif + +if HAVE_GTK +putty_SOURCES = $(srcdir)/../be_all_s.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../cproxy.c \ + $(srcdir)/../dialog.c $(srcdir)/../ldisc.c \ + $(srcdir)/../ldiscucs.c $(srcdir)/../logging.c \ + $(srcdir)/../minibidi.c $(srcdir)/../misc.c \ + $(srcdir)/../pgssapi.c $(srcdir)/../pinger.c \ + $(srcdir)/../portfwd.c $(srcdir)/../proxy.c \ + $(srcdir)/../raw.c $(srcdir)/../rlogin.c \ + $(srcdir)/../sercfg.c $(srcdir)/../settings.c \ + $(srcdir)/../ssh.c $(srcdir)/../sshaes.c \ + $(srcdir)/../ssharcf.c $(srcdir)/../sshblowf.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshcrc.c \ + $(srcdir)/../sshcrcda.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdh.c $(srcdir)/../sshdss.c \ + $(srcdir)/../sshgssc.c $(srcdir)/../sshmd5.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshsh256.c \ + $(srcdir)/../sshsh512.c $(srcdir)/../sshsha.c \ + $(srcdir)/../sshzlib.c $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/gtkcfg.c $(srcdir)/../unix/gtkcols.c \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkwin.c $(srcdir)/../unix/ux_x11.c \ + $(srcdir)/../unix/uxagentc.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxgss.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnet.c $(srcdir)/../unix/uxnoise.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxputty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxser.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../wcwidth.c \ + $(srcdir)/../wildcard.c $(srcdir)/../x11fwd.c +putty_LDADD = libversion.a $(GTK_LIBS) +endif + +puttygen_SOURCES = $(srcdir)/../cmdgen.c $(srcdir)/../conf.c \ + $(srcdir)/../import.c $(srcdir)/../misc.c \ + $(srcdir)/../notiming.c $(srcdir)/../sshaes.c \ + $(srcdir)/../sshbn.c $(srcdir)/../sshdes.c \ + $(srcdir)/../sshdss.c $(srcdir)/../sshdssg.c \ + $(srcdir)/../sshmd5.c $(srcdir)/../sshprime.c \ + $(srcdir)/../sshpubk.c $(srcdir)/../sshrand.c \ + $(srcdir)/../sshrsa.c $(srcdir)/../sshrsag.c \ + $(srcdir)/../sshsh256.c $(srcdir)/../sshsh512.c \ + $(srcdir)/../sshsha.c $(srcdir)/../time.c \ + $(srcdir)/../tree234.c $(srcdir)/../unix/uxcons.c \ + $(srcdir)/../unix/uxgen.c $(srcdir)/../unix/uxmisc.c \ + $(srcdir)/../unix/uxnoise.c $(srcdir)/../unix/uxstore.c +puttygen_LDADD = libversion.a + +if HAVE_GTK +puttytel_SOURCES = $(srcdir)/../be_nos_s.c $(srcdir)/../charset/fromucs.c \ + $(srcdir)/../charset/localenc.c \ + $(srcdir)/../charset/macenc.c $(srcdir)/../charset/mimeenc.c \ + $(srcdir)/../charset/sbcs.c $(srcdir)/../charset/sbcsdat.c \ + $(srcdir)/../charset/slookup.c $(srcdir)/../charset/toucs.c \ + $(srcdir)/../charset/utf8.c $(srcdir)/../charset/xenc.c \ + $(srcdir)/../cmdline.c $(srcdir)/../conf.c \ + $(srcdir)/../config.c $(srcdir)/../dialog.c \ + $(srcdir)/../ldisc.c $(srcdir)/../ldiscucs.c \ + $(srcdir)/../logging.c $(srcdir)/../minibidi.c \ + $(srcdir)/../misc.c $(srcdir)/../nocproxy.c \ + $(srcdir)/../nogss.c $(srcdir)/../pinger.c \ + $(srcdir)/../proxy.c $(srcdir)/../raw.c \ + $(srcdir)/../rlogin.c $(srcdir)/../sercfg.c \ + $(srcdir)/../settings.c $(srcdir)/../telnet.c \ + $(srcdir)/../terminal.c $(srcdir)/../time.c \ + $(srcdir)/../timing.c $(srcdir)/../tree234.c \ + $(srcdir)/../unix/gtkcfg.c $(srcdir)/../unix/gtkcols.c \ + $(srcdir)/../unix/gtkdlg.c $(srcdir)/../unix/gtkfont.c \ + $(srcdir)/../unix/gtkwin.c $(srcdir)/../unix/uxcfg.c \ + $(srcdir)/../unix/uxmisc.c $(srcdir)/../unix/uxnet.c \ + $(srcdir)/../unix/uxprint.c $(srcdir)/../unix/uxproxy.c \ + $(srcdir)/../unix/uxputty.c $(srcdir)/../unix/uxsel.c \ + $(srcdir)/../unix/uxser.c $(srcdir)/../unix/uxsignal.c \ + $(srcdir)/../unix/uxstore.c $(srcdir)/../unix/uxucs.c \ + $(srcdir)/../unix/xkeysym.c $(srcdir)/../unix/xpmpucfg.c \ + $(srcdir)/../unix/xpmputty.c $(srcdir)/../wcwidth.c +puttytel_LDADD = libversion.a $(GTK_LIBS) +endif + +BUILT_SOURCES = empty.h +empty.h: $(allsources) + echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ + +man1_MANS = ../doc/plink.1 ../doc/pscp.1 ../doc/psftp.1 ../doc/pterm.1 \ + ../doc/putty.1 ../doc/puttygen.1 ../doc/puttytel.1 +if HAVE_SETID_CMD +install-exec-local: + @SETID_CMD@ $(bindir)/pterm + chmod @SETID_MODE@ $(bindir)/pterm +endif diff --git a/putty/UNIX/MAKEFILE.GTK b/putty/UNIX/MAKEFILE.GTK index 480e052..c49e8d2 100644 --- a/putty/UNIX/MAKEFILE.GTK +++ b/putty/UNIX/MAKEFILE.GTK @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -148,16 +153,7 @@ man1dir=$(mandir)/man1 all: plink pscp psftp pterm putty puttygen puttytel -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ @@ -165,9 +161,18 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -175,16 +180,17 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -192,46 +198,49 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o psftp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \ - gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \ - nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \ - xpmptcfg.o xpmpterm.o - $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ +pterm: be_none.o cmdline.o conf.o config.o dialog.o fromucs.o gtkcfg.o \ gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \ slookup.o terminal.o time.o timing.o toucs.o tree234.o \ utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \ uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) + xkeysym.o xpmptcfg.o xpmpterm.o + $(CC) -o $@ be_none.o cmdline.o conf.o config.o dialog.o fromucs.o \ + gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ + ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ + minibidi.o misc.o nocproxy.o nogss.o sbcs.o sbcsdat.o \ + sercfg.o settings.o slookup.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o uxcfg.o uxmisc.o uxprint.o \ + uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ + version.o wcwidth.o xenc.o xkeysym.o xpmptcfg.o xpmpterm.o \ + $(XLDFLAGS) -putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ - uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ - uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \ +putty: be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o fromucs.o \ + gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ + ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ + minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ + rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ + ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \ + uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \ + uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ + wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ + xpmputty.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o \ fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \ ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ @@ -246,18 +255,18 @@ putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ xpmputty.o $(XLDFLAGS) -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) + uxstore.o version.o + $(CC) -o $@ cmdgen.o conf.o import.o misc.o notiming.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ + uxnoise.o uxstore.o version.o $(ULDFLAGS) -puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ +puttytel: be_nos_s.o cmdline.o conf.o config.o dialog.o fromucs.o gtkcfg.o \ gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \ @@ -266,7 +275,7 @@ puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \ + $(CC) -o $@ be_nos_s.o cmdline.o conf.o config.o dialog.o fromucs.o \ gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \ @@ -306,6 +315,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -584,7 +598,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c diff --git a/putty/UNIX/MAKEFILE.IN b/putty/UNIX/MAKEFILE.IN deleted file mode 100644 index fc5d577..0000000 --- a/putty/UNIX/MAKEFILE.IN +++ /dev/null @@ -1,877 +0,0 @@ -# Makefile.in for putty under Unix with Autoconf. -# -# This file was created by `mkfiles.pl' from the `Recipe' file. -# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. -# -# Extra options you can set: -# -# - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234" -# Generates executables whose About box report them as being a -# development snapshot. SVN_REV is a Subversion revision number. -# -# - VER=-DRELEASE=0.43 -# Generates executables whose About box report them as being a -# release version. -# -# - COMPAT=-DAUTO_WINSOCK (Windows only) -# Causes PuTTY to assume that includes its own WinSock -# header file, so that it won't try to include . -# -# - COMPAT=-DWINSOCK_TWO (Windows only) -# Causes the PuTTY utilities to include instead of -# , except Plink which _needs_ WinSock 2 so it already -# does this. -# -# - COMPAT=-DNO_SECURITY (Windows only) -# Disables Pageant's use of , which is not available -# with some development environments (such as older versions of -# the Cygwin/mingw GNU toolchain). This means that Pageant -# won't care about the local user ID of processes accessing it; a -# version of Pageant built with this option will therefore refuse -# to run under NT-series OSes on security grounds (although it -# will run fine on Win95-series OSes where there is no access -# control anyway). -# -# - COMPAT=-DNO_MULTIMON (Windows only) -# Disables PuTTY's use of , which is not available -# with some development environments. This means that PuTTY's -# full-screen mode (configurable to work on Alt-Enter) will -# not behave usefully in a multi-monitor environment. -# -# Note that this definition is always enabled in the Cygwin -# build, since at the time of writing this is -# known not to be available in Cygwin. -# -# - COMPAT=-DNO_HTMLHELP (Windows only) -# Disables PuTTY's use of , which is not available -# with some development environments. The resulting binary -# will only look for an old-style WinHelp file (.HLP/.CNT), and -# will ignore any .CHM file. -# -# Note that this definition is always enabled in the Cygwin -# build, since at the time of writing this is -# 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 -# binaries. This may be necessary to build for 64-bit Windows; -# the manifests are only included to use the XP GUI style on -# Windows XP, and the architecture tags are a lie on 64-bit. -# -# - COMPAT=-DNO_IPV6 -# Disables PuTTY's ability to make IPv6 connections, enabling -# it to compile under development environments which do not -# support IPv6 in their header files. -# -# - COMPAT=-DNO_GSSAPI -# Disables PuTTY's ability to use GSSAPI functions for -# authentication and key exchange. -# -# - COMPAT=-DSTATIC_GSSAPI -# Causes PuTTY to try to link statically against the GSSAPI -# library instead of the default of doing it at run time. -# -# - COMPAT=-DMSVC4 (Windows only) -# - RCFL=-DMSVC4 -# Makes a couple of minor changes so that PuTTY compiles using -# MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. -# -# - RCFL=-DASCIICTLS (Windows only) -# Uses ASCII rather than Unicode to specify the tab control in -# the resource file. Probably most useful when compiling with -# Cygnus/mingw32, whose resource compiler may have less of a -# problem with it. -# -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# -# - XFLAGS=-DDEBUG -# Causes PuTTY to enable internal debugging. -# -# - XFLAGS=-DMALLOC_LOG -# Causes PuTTY to emit a file called putty_mem.log, logging every -# memory allocation and free, so you can track memory leaks. -# -# - XFLAGS=-DMINEFIELD (Windows only) -# Causes PuTTY to use a custom memory allocator, similar in -# concept to Electric Fence, in place of regular malloc(). Wastes -# huge amounts of RAM, but should cause heap-corruption bugs to -# show up as GPFs at the point of failure rather than appearing -# later on as second-level damage. -# - -CC = @CC@ - -CFLAGS = @CFLAGS@ @PUTTYCFLAGS@ @CPPFLAGS@ @DEFS@ @GTK_CFLAGS@ -I.././ \ - -I../charset/ -I../windows/ -I../unix/ -I../macosx/ -XLDFLAGS = @LDFLAGS@ @LIBS@ @GTK_LIBS@ -ULDFLAGS = @LDFLAGS@ @LIBS@ -INSTALL=@INSTALL@ -INSTALL_PROGRAM=$(INSTALL) -INSTALL_DATA=$(INSTALL) -prefix=@prefix@ -exec_prefix=@exec_prefix@ -bindir=@bindir@ -datarootdir=@datarootdir@ -mandir=@mandir@ -man1dir=$(mandir)/man1 - - -.SUFFIXES: - - -all: @all_targets@ -all-cli: plink pscp psftp puttygen -all-gtk: pterm putty puttytel - -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ - settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ - ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ - uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ - uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ - uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) - -pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \ - gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \ - logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \ - nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \ - xpmptcfg.o xpmpterm.o - $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \ - slookup.o terminal.o time.o timing.o toucs.o tree234.o \ - utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \ - uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) - -putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ - uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ - uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \ - fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \ - ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ - minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ - rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \ - tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \ - uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ - wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \ - xpmputty.o $(XLDFLAGS) - -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ - sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) - -puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \ - gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \ - localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \ - nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \ - sbcsdat.o sercfg.o settings.o slookup.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o uxcfg.o uxmisc.o \ - uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ - uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ - xkeysym.o xpmpucfg.o xpmputty.o - $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \ - gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \ - ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \ - minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \ - rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \ - telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \ - uxcfg.o uxmisc.o uxnet.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \ - wcwidth.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) - -be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c -be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c -be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c -be_ssh.o: ../be_ssh.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_ssh.c -cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c -cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c -config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c -cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ - ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c -dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c -fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c -gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c -gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c -gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \ - ../storage.h ../dialog.h ../tree234.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c -gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c -gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \ - ../puttyps.h ../network.h ../misc.h ../tree234.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c -import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c -int64.o: ../int64.c ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c -ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ - ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c -ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ - ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c -localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c -logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c -macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c -mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c -minibidi.o: ../minibidi.c ../misc.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c -misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c -nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c -nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c -notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c -osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \ - ../tree234.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m -osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \ - ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m -osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m -osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m -osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \ - ../puttyps.h ../network.h ../misc.h ../tree234.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m -pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c -pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c -portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c -proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c -pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ - ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c -psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ - ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c -raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c -rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c -sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c -sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c -sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c -settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c -sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c -sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c -slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ - ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c -ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \ - ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c -sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c -ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c -sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c -sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c -sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c -sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c -sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c -sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c -sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c -sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c -sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \ - ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../tree234.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c -sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c -sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c -sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c -sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c -sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \ - ../network.h ../int64.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c -sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c -sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c -sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c -sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c -sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ - ../int64.h ../misc.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c -telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c -terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \ - ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c -testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c -time.o: ../time.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c -timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c -toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c -utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c -ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c -uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ - ../puttymem.h ../puttyps.h ../network.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c -uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c -uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c -uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c -uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c -uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c -uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c -uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c -uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c -uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c -uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ - ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c -uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c -uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c -uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c -uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c -uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c -uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c -uxsignal.o: ../unix/uxsignal.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c -uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c -uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ - ../misc.h ../puttyps.h ../network.h ../tree234.h \ - ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c -wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c -wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c -wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c -wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c -winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \ - ../puttyps.h ../network.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../tree234.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c -windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c -windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ - ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \ - ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c -window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ - ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c -wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ - ../sshgssc.h ../misc.h ../puttyps.h ../network.h \ - ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c -winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c -winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c -winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c -winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c -winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \ - ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c -winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../int64.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c -winnojmp.o: ../windows/winnojmp.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c -winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c -winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ - ../puttyps.h ../network.h ../puttymem.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c -winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c -winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ - ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c -winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c -winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \ - ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c -winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c -winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \ - ../puttyps.h ../network.h ../misc.h ../puttymem.h \ - ../tree234.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c -winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \ - ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c -wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \ - ../misc.h ../windows/winstuff.h ../macosx/osx.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c -winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \ - ../puttyps.h ../network.h ../tree234.h ../puttymem.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c -winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \ - ../network.h ../puttymem.h ../windows/winstuff.h \ - ../macosx/osx.h ../unix/unix.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c -winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ - ../misc.h ../puttymem.h ../tree234.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c -x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \ - ../network.h ../misc.h ../puttymem.h ../int64.h \ - ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c -xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c -xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c -xpmptcfg.o: ../unix/xpmptcfg.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c -xpmpterm.o: ../unix/xpmpterm.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c -xpmpucfg.o: ../unix/xpmpucfg.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c -xpmputty.o: ../unix/xpmputty.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c - -version.o: FORCE - if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \ - $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \ - else \ - $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \ - fi -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 - $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm - if test -n "$(UTMP_GROUP)"; then \ - chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \ - chmod 2755 $(DESTDIR)$(bindir)/pterm; \ - elif test -n "$(UTMP_USER)"; then \ - chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \ - chmod 4755 $(DESTDIR)$(bindir)/pterm; \ - fi - $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty - $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen - $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel - $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1 - $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1 - $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1 - $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1 - $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1 - $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1 - $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1 - -install-strip: - $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" - -clean: - rm -f *.o plink pscp psftp pterm putty puttygen puttytel - -distclean: clean - rm -f config.status config.cache config.log configure.lineno \ - config.status.lineno Makefile - -FORCE: diff --git a/putty/UNIX/MAKEFILE.UX b/putty/UNIX/MAKEFILE.UX index 5b03b95..f4c584b 100644 --- a/putty/UNIX/MAKEFILE.UX +++ b/putty/UNIX/MAKEFILE.UX @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -129,16 +134,7 @@ man1dir=$(mandir)/man1 all: plink pscp psftp puttygen -plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \ - sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ - telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \ - uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \ - uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \ - wildcard.o x11fwd.o - $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \ +plink: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ @@ -146,9 +142,18 @@ plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ + uxstore.o version.o wildcard.o x11fwd.o + $(CC) -o $@ be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ + sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \ + uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \ uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) -pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +pscp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -156,16 +161,17 @@ pscp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ +psftp: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o pgssapi.o \ pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \ sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ @@ -173,25 +179,26 @@ psftp: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \ uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \ uxstore.o version.o wildcard.o x11fwd.o - $(CC) -o $@ be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o \ - pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \ - uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ - uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) + $(CC) -o $@ be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o \ + misc.o pgssapi.o pinger.o portfwd.o proxy.o psftp.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \ + uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \ + uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \ + x11fwd.o $(ULDFLAGS) -puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \ - tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \ - version.o - $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \ +puttygen: cmdgen.o conf.o import.o misc.o notiming.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ - uxstore.o version.o $(ULDFLAGS) + uxstore.o version.o + $(CC) -o $@ cmdgen.o conf.o import.o misc.o notiming.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \ + uxnoise.o uxstore.o version.o $(ULDFLAGS) be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -223,6 +230,11 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -501,7 +513,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c diff --git a/putty/UNIX/UNIX.H b/putty/UNIX/UNIX.H index 198085f..89ef1e0 100644 --- a/putty/UNIX/UNIX.H +++ b/putty/UNIX/UNIX.H @@ -13,13 +13,14 @@ #include "charset.h" struct Filename { - char path[FILENAME_MAX]; + char *path; }; -FILE *f_open(struct Filename, char const *, int); +FILE *f_open(const struct Filename *, char const *, int); struct FontSpec { - char name[256]; + char *name; /* may be "" to indicate no selected font at all */ }; +struct FontSpec *fontspec_new(const char *name); typedef void *Context; /* FIXME: probably needs changing */ @@ -59,11 +60,6 @@ unsigned long getticks(void); /* based on gettimeofday(2) */ #define GETTICKCOUNT getticks #define TICKSPERSEC 1000 /* we choose to use milliseconds */ #define CURSORBLINK 450 /* no standard way to set this */ -/* getticks() works using gettimeofday(), so it's vulnerable to system clock - * changes causing chaos. Therefore, we provide a compensation mechanism. */ -#define TIMING_SYNC -#define TIMING_SYNC_ANOW -extern long tickcount_offset; #define WCHAR wchar_t #define BYTE unsigned char @@ -89,18 +85,23 @@ long get_windowid(void *frontend); void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ /* Things pterm.c needs from gtkdlg.c */ -int do_config_box(const char *title, Config *cfg, +int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo); void fatal_message_box(void *window, char *msg); +void nonfatal_message_box(void *window, char *msg); void about_box(void *window); void *eventlogstuff_new(void); void showeventlog(void *estuff, void *parentwin); void logevent_dlg(void *estuff, const char *string); int reallyclose(void *frontend); +#ifdef MAY_REFER_TO_GTK_IN_HEADERS +int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...); +int string_width(char *text); +#endif /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); -int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch); +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); @@ -155,7 +156,10 @@ void (*putty_signal(int sig, void (*func)(int)))(int); void block_signal(int sig, int block_it); /* uxmisc.c */ -int cloexec(int); +void cloexec(int); +void noncloexec(int); +int nonblock(int); +int no_nonblock(int); /* * Exports from unicode.c. @@ -170,6 +174,13 @@ int init_ucs(struct unicode_data *ucsdata, char *line_codepage, void *sk_getxdmdata(void *sock, int *lenp); /* + * Function provided by front ends, and called by uxnet.c to indicate + * that net_pending_errors() would like to be called back when the + * front end has a spare moment and isn't deep in any other recursion. + */ +void frontend_net_error_pending(void); + +/* * General helpful Unix stuff: more helpful version of the FD_SET * macro, which also handles maxfd. */ diff --git a/putty/UNIX/UXAGENTC.C b/putty/UNIX/UXAGENTC.C index 74cd9cd..1642a69 100644 --- a/putty/UNIX/UXAGENTC.C +++ b/putty/UNIX/UXAGENTC.C @@ -17,7 +17,8 @@ int agent_exists(void) { - if (getenv("SSH_AUTH_SOCK") != NULL) + const char *p = getenv("SSH_AUTH_SOCK"); + if (p && *p) return TRUE; return FALSE; } @@ -74,13 +75,12 @@ static int agent_select_result(int fd, int event) } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { - conn->retsize = GET_32BIT(conn->retbuf); + conn->retsize = toint(GET_32BIT(conn->retbuf) + 4); if (conn->retsize <= 0) { conn->retbuf = NULL; conn->retlen = 0; goto done; } - conn->retsize += 4; assert(conn->retbuf == conn->sizebuf); conn->retbuf = snewn(conn->retsize, char); memcpy(conn->retbuf, conn->sizebuf, 4); diff --git a/putty/UNIX/UXCFG.C b/putty/UNIX/UXCFG.C index 2e872cc..0e0bab2 100644 --- a/putty/UNIX/UXCFG.C +++ b/putty/UNIX/UXCFG.C @@ -16,11 +16,11 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) union control *c; /* - * The Config structure contains two Unix-specific elements - * which are not configured in here: stamp_utmp and - * login_shell. This is because pterm does not put up a - * configuration box right at the start, which is the only time - * when these elements would be useful to configure. + * The Conf structure contains two Unix-specific elements which + * are not configured in here: stamp_utmp and login_shell. This + * is because pterm does not put up a configuration box right at + * the start, which is the only time when these elements would + * be useful to configure. */ /* @@ -41,8 +41,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, proxy_type)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_proxy_type) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -58,9 +58,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol) for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && - c->generic.context.i == - offsetof(Config, proxy_telnet_command)) { - assert(c->generic.handler == dlg_stdeditbox_handler); + c->generic.context.i == CONF_proxy_telnet_command) { + assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); diff --git a/putty/UNIX/UXCONS.C b/putty/UNIX/UXCONS.C index 2e71df4..ee78a6d 100644 --- a/putty/UNIX/UXCONS.C +++ b/putty/UNIX/UXCONS.C @@ -7,8 +7,10 @@ #include #include #include + #include #include +#include #include "putty.h" #include "storage.h" @@ -68,7 +70,7 @@ void notify_remote_exit(void *frontend) { } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { } @@ -233,7 +235,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -254,11 +256,11 @@ int askappend(void *frontend, Filename filename, premsg(&cf); if (console_batch_mode) { - fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); fflush(stderr); return 0; } - fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); { @@ -319,38 +321,54 @@ void console_provide_logctx(void *logctx) void logevent(void *frontend, const char *string) { struct termios cf; - premsg(&cf); + if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) + premsg(&cf); if (console_logctx) log_eventlog(console_logctx, string); - postmsg(&cf); + if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) + postmsg(&cf); } /* - * Special function to print text to the console for password - * prompts and the like. Uses /dev/tty or stderr, in that order of - * preference; also sanitises escape sequences out of the text, on + * Special functions to read and print to the console for password + * prompts and the like. Uses /dev/tty or stdin/stderr, in that order + * of preference; also sanitises escape sequences out of the text, on * the basis that it might have been sent by a hostile SSH server * doing malicious keyboard-interactive. */ -static void console_prompt_text(FILE **confp, const char *data, int len) +static void console_open(FILE **outfp, int *infd) { - int i; + int fd; - if (!*confp) { - if ((*confp = fopen("/dev/tty", "w")) == NULL) - *confp = stderr; + if ((fd = open("/dev/tty", O_RDWR)) >= 0) { + *infd = fd; + *outfp = fdopen(*infd, "w"); + } else { + *infd = 0; + *outfp = stderr; } +} +static void console_close(FILE *outfp, int infd) +{ + if (outfp != stderr) + fclose(outfp); /* will automatically close infd too */ +} + +static void console_prompt_text(FILE *outfp, const char *data, int len) +{ + int i; for (i = 0; i < len; i++) if ((data[i] & 0x60) || (data[i] == '\n')) - fputc(data[i], *confp); - fflush(*confp); + fputc(data[i], outfp); + fflush(outfp); } int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { size_t curr_prompt; - FILE *confp = NULL; + FILE *outfp = NULL; + int infd; /* * Zero all the results, in case we abort half-way through. @@ -358,62 +376,80 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } if (p->n_prompts && console_batch_mode) return 0; + console_open(&outfp, &infd); + /* * Preamble. */ /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { size_t l = strlen(p->name); - console_prompt_text(&confp, p->name, l); + console_prompt_text(outfp, p->name, l); if (p->name[l-1] != '\n') - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); } /* ...but we always print any `instruction'. */ if (p->instruction) { size_t l = strlen(p->instruction); - console_prompt_text(&confp, p->instruction, l); + console_prompt_text(outfp, p->instruction, l); if (p->instruction[l-1] != '\n') - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; - int i; + int len; prompt_t *pr = p->prompts[curr_prompt]; - tcgetattr(0, &oldmode); + tcgetattr(infd, &oldmode); newmode = oldmode; newmode.c_lflag |= ISIG | ICANON; if (!pr->echo) newmode.c_lflag &= ~ECHO; else newmode.c_lflag |= ECHO; - tcsetattr(0, TCSANOW, &newmode); + tcsetattr(infd, TCSANOW, &newmode); - console_prompt_text(&confp, pr->prompt, strlen(pr->prompt)); + console_prompt_text(outfp, pr->prompt, strlen(pr->prompt)); - i = read(0, pr->result, pr->result_len - 1); + len = 0; + while (1) { + int ret; - tcsetattr(0, TCSANOW, &oldmode); + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + ret = read(infd, pr->result + len, pr->resultsize - len - 1); + if (ret <= 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + break; + } + } - if (i > 0 && pr->result[i-1] == '\n') - i--; - pr->result[i] = '\0'; + tcsetattr(infd, TCSANOW, &oldmode); if (!pr->echo) - console_prompt_text(&confp, "\n", 1); + console_prompt_text(outfp, "\n", 1); + + if (len < 0) { + console_close(outfp, infd); + return 0; /* failure due to read error */ + } + pr->result[len] = '\0'; } - if (confp && confp != stderr) - fclose(confp); + console_close(outfp, infd); return 1; /* success */ } diff --git a/putty/UNIX/UXGEN.C b/putty/UNIX/UXGEN.C index b1b4d19..636d1cc 100644 --- a/putty/UNIX/UXGEN.C +++ b/putty/UNIX/UXGEN.C @@ -26,6 +26,7 @@ char *get_random_data(int len) ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); + sfree(buf); perror("puttygen: unable to read /dev/random"); return NULL; } diff --git a/putty/UNIX/UXGSS.C b/putty/UNIX/UXGSS.C index 1bb803c..61747ef 100644 --- a/putty/UNIX/UXGSS.C +++ b/putty/UNIX/UXGSS.C @@ -53,9 +53,10 @@ static void gss_init(struct ssh_gss_library *lib, void *dlhandle, } /* Dynamically load gssapi libs. */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { void *gsslib; + char *gsspath; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); list->libraries = snewn(4, struct ssh_gss_library); @@ -77,11 +78,11 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) 2, "Using GSSAPI from libgss.so.1"); /* User-specified GSSAPI library */ - if (cfg->ssh_gss_custom.path[0] && - (gsslib = dlopen(cfg->ssh_gss_custom.path, RTLD_LAZY)) != NULL) + gsspath = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*gsspath && (gsslib = dlopen(gsspath, RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 3, dupprintf("Using GSSAPI from user-specified" - " library '%s'", cfg->ssh_gss_custom.path)); + " library '%s'", gsspath)); return list; } @@ -129,7 +130,7 @@ const struct keyvalwhere gsslibkeywords[] = { #include /* Dynamically load gssapi libs. */ -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); diff --git a/putty/UNIX/UXMISC.C b/putty/UNIX/UXMISC.C index 4bda059..5e4075c 100644 --- a/putty/UNIX/UXMISC.C +++ b/putty/UNIX/UXMISC.C @@ -6,48 +6,92 @@ #include #include #include +#include #include +#include #include #include #include #include "putty.h" -long tickcount_offset = 0; - unsigned long getticks(void) { - struct timeval tv; - gettimeofday(&tv, NULL); /* - * We want to use milliseconds rather than microseconds, - * because we need a decent number of them to fit into a 32-bit - * word so it can be used for keepalives. + * We want to use milliseconds rather than the microseconds or + * nanoseconds given by the underlying clock functions, because we + * need a decent number of them to fit into a 32-bit word so it + * can be used for keepalives. */ - return tv.tv_sec * 1000 + tv.tv_usec / 1000 + tickcount_offset; +#if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC + { + /* Use CLOCK_MONOTONIC if available, so as to be unconfused if + * the system clock changes. */ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec * TICKSPERSEC + + ts.tv_nsec / (1000000000 / TICKSPERSEC); + } +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); + } } -Filename filename_from_str(const char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } +Filename *filename_copy(const Filename *fn) +{ + return filename_from_str(fn->path); +} + const char *filename_to_str(const Filename *fn) { return fn->path; } -int filename_equal(Filename f1, Filename f2) +int filename_equal(const Filename *f1, const Filename *f2) +{ + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) +{ + return !fn->path[0]; +} + +void filename_free(Filename *fn) { - return !strcmp(f1.path, f2.path); + sfree(fn->path); + sfree(fn); } -int filename_is_null(Filename fn) +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) { - return !*fn.path; + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); } #ifdef DEBUG @@ -125,28 +169,115 @@ void pgp_fingerprints(void) } /* - * Set FD_CLOEXEC on a file descriptor + * Set and clear fcntl options on a file descriptor. We don't + * realistically expect any of these operations to fail (the most + * plausible error condition is EBADF, but we always believe ourselves + * to be passing a valid fd so even that's an assertion-fail sort of + * response), so we don't make any effort to return sensible error + * codes to the caller - we just log to standard error and die + * unceremoniously. However, nonblock and no_nonblock do return the + * previous state of O_NONBLOCK. */ -int cloexec(int fd) { +void cloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); - if (fdflags == -1) return -1; - return fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } } +void noncloexec(int fd) { + int fdflags; -FILE *f_open(struct Filename filename, char const *mode, int is_private) + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +int nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} +int no_nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} + +FILE *f_open(const Filename *filename, char const *mode, int is_private) { if (!is_private) { - return fopen(filename.path, mode); + return fopen(filename->path, mode); } else { int fd; assert(mode[0] == 'w'); /* is_private is meaningless for read, and tricky for append */ - fd = open(filename.path, O_WRONLY | O_CREAT | O_TRUNC, - 0700); + fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) return NULL; return fdopen(fd, mode); } } + +FontSpec *fontspec_new(const char *name) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *data) +{ + int len = strlen(f->name); + if (data) + strcpy(data, f->name); + return len + 1; /* include trailing NUL */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + *used = end - data + 1; + return fontspec_new(data); +} diff --git a/putty/UNIX/UXNET.C b/putty/UNIX/UXNET.C index 0882638..76f87e0 100644 --- a/putty/UNIX/UXNET.C +++ b/putty/UNIX/UXNET.C @@ -80,13 +80,13 @@ struct Socket_tag { int connected; /* irrelevant for listening sockets */ int writable; int frozen; /* this causes readability notifications to be ignored */ - int frozen_readable; /* this means we missed at least one readability - * notification while we were frozen */ int localhost_only; /* for listening sockets */ char oobdata[1]; int sending_oob; int oobpending; /* is there OOB data available to read? */ int oobinline; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; + int incomingeof; int pending_error; /* in case send() returns error */ int listener; int nodelay, keepalive; /* for connect()-type sockets */ @@ -341,7 +341,7 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } } -int sk_hostname_is_local(char *name) +int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || @@ -388,6 +388,11 @@ int sk_address_is_local(SockAddr addr) } } +int sk_address_is_special_local(SockAddr addr) +{ + return addr->superfamily == UNIX; +} + int sk_addrtype(SockAddr addr) { SockAddrStep step; @@ -466,6 +471,7 @@ static void sk_tcp_flush(Socket s) static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); +static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_private_ptr(Socket s, void *ptr); static void *sk_tcp_get_private_ptr(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); @@ -476,6 +482,7 @@ static struct socket_function_table tcp_fn_table = { sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -498,10 +505,11 @@ Socket sk_register(OSSocket sockfd, Plug plug) ret->writable = 1; /* to start with */ ret->sending_oob = 0; ret->frozen = 1; - ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 0; ret->parent = ret->child = NULL; ret->addr = NULL; @@ -529,7 +537,7 @@ static int try_connect(Actual_Socket sock) const union sockaddr_union *sa; int err = 0; short localport; - int fl, salen, family; + int salen, family; /* * Remove the socket from the tree before we overwrite its @@ -561,17 +569,32 @@ static int try_connect(Actual_Socket sock) if (sock->oobinline) { int b = TRUE; - setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } if (sock->nodelay) { int b = TRUE; - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)); + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } if (sock->keepalive) { int b = TRUE; - setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)); + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (void *) &b, sizeof(b)) < 0) { + err = errno; + close(s); + goto ret; + } } /* @@ -669,9 +692,7 @@ static int try_connect(Actual_Socket sock) exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ } - fl = fcntl(s, F_GETFL); - if (fl != -1) - fcntl(s, F_SETFL, fl | O_NONBLOCK); + nonblock(s); if ((connect(s, &(sa->sa), salen)) < 0) { if ( errno != EINPROGRESS ) { @@ -719,11 +740,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; - ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 0; ret->addr = addr; START_STEP(ret->addr, ret->step); @@ -771,11 +793,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; - ret->frozen_readable = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobpending = FALSE; + ret->outgoingeof = EOF_NO; + ret->incomingeof = FALSE; ret->listener = 1; ret->addr = NULL; @@ -820,7 +843,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->oobinline = 0; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const char *)&on, sizeof(on)) < 0) { + ret->error = strerror(errno); + close(s); + return (Socket) ret; + } retcode = -1; addr = NULL; addrlen = -1; /* placate optimiser */ @@ -1038,6 +1066,16 @@ void try_send(Actual_Socket s) * plug_closing()) at some suitable future moment. */ s->pending_error = err; + /* + * Immediately cease selecting on this socket, so that + * we don't tight-loop repeatedly trying to do + * whatever it was that went wrong. + */ + uxsel_tell(s); + /* + * Notify the front end that it might want to call us. + */ + frontend_net_error_pending(); return; } } else { @@ -1053,6 +1091,20 @@ void try_send(Actual_Socket s) } } } + + /* + * If we reach here, we've finished sending everything we might + * have needed to send. Send EOF, if we need to. + */ + if (s->outgoingeof == EOF_PENDING) { + shutdown(s->s, SHUT_WR); + s->outgoingeof = EOF_SENT; + } + + /* + * Also update the select status, because we don't need to select + * for writing any more. + */ uxsel_tell(s); } @@ -1060,6 +1112,8 @@ static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Add the data to the buffer list on the socket. */ @@ -1084,6 +1138,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Replace the buffer list on the socket with the data. */ @@ -1107,6 +1163,30 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) return s->sending_oob; } +static void sk_tcp_write_eof(Socket sock) +{ + Actual_Socket s = (Actual_Socket) sock; + + assert(s->outgoingeof == EOF_NO); + + /* + * Mark the socket as pending outgoing EOF. + */ + s->outgoingeof = EOF_PENDING; + + /* + * Now try sending from the start of the buffer list. + */ + if (s->writable) + try_send(s); + + /* + * Update the select() status to correctly reflect whether or + * not we should be selecting for write. + */ + uxsel_tell(s); +} + static int net_select_result(int fd, int event) { int ret; @@ -1168,7 +1248,6 @@ static int net_select_result(int fd, int event) union sockaddr_union su; socklen_t addrlen = sizeof(su); int t; /* socket of connection */ - int fl; memset(&su, 0, addrlen); t = accept(s->s, &su.sa, &addrlen); @@ -1176,9 +1255,7 @@ 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); + nonblock(t); if (s->localhost_only && !sockaddr_is_loopback(&su.sa)) { @@ -1195,10 +1272,8 @@ static int net_select_result(int fd, int event) */ /* In the case the socket is still frozen, we don't even bother */ - if (s->frozen) { - s->frozen_readable = 1; + if (s->frozen) break; - } /* * We have received data on the socket. For an oobinline @@ -1237,6 +1312,8 @@ static int net_select_result(int fd, int event) if (err != 0) return plug_closing(s->plug, strerror(err), err, 0); } else if (0 == ret) { + s->incomingeof = TRUE; /* stop trying to read now */ + uxsel_tell(s); return plug_closing(s->plug, NULL, 0, 0); } else { /* @@ -1349,26 +1426,23 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen) if (s->frozen == is_frozen) return; s->frozen = is_frozen; - if (!is_frozen && s->frozen_readable) { - char c; - recv(s->s, &c, 1, MSG_PEEK); - } - s->frozen_readable = 0; uxsel_tell(s); } static void uxsel_tell(Actual_Socket s) { int rwx = 0; - if (s->listener) { - rwx |= 1; /* read == accept */ - } else { - if (!s->connected) - rwx |= 2; /* write == connect */ - if (s->connected && !s->frozen) - rwx |= 1 | 4; /* read, except */ - if (bufchain_size(&s->output_data)) - rwx |= 2; /* write */ + if (!s->pending_error) { + if (s->listener) { + rwx |= 1; /* read == accept */ + } else { + if (!s->connected) + rwx |= 2; /* write == connect */ + if (s->connected && !s->frozen && !s->incomingeof) + rwx |= 1 | 4; /* read, except */ + if (bufchain_size(&s->output_data)) + rwx |= 2; /* write */ + } } uxsel_set(s->s, rwx, net_select_result); } diff --git a/putty/UNIX/UXPLINK.C b/putty/UNIX/UXPLINK.C index 90baa68..3addc3e 100644 --- a/putty/UNIX/UXPLINK.C +++ b/putty/UNIX/UXPLINK.C @@ -63,6 +63,22 @@ void modalfatalbox(char *p, ...) } cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + struct termios cf; + va_list ap; + premsg(&cf); + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + postmsg(&cf); + if (logctx) { + log_free(logctx); + logctx = NULL; + } +} void connection_fatal(void *frontend, char *p, ...) { struct termios cf; @@ -98,7 +114,7 @@ static int local_tty = FALSE; /* do we have a local tty? */ static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; /* * Default settings that are specific to pterm. @@ -116,30 +132,20 @@ char *platform_default_s(const char *name) int platform_default_i(const char *name, int def) { - if (!strcmp(name, "TermWidth") || - !strcmp(name, "TermHeight")) { - struct winsize size; - if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) - return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row); - } return def; } -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *x_get_default(const char *key) @@ -383,31 +389,33 @@ void cleanup_termios(void) } bufchain stdout_data, stderr_data; +enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int try_output(int is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); void *senddata; - int sendlen, ret, fl; - - if (bufchain_size(chain) == 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); + int sendlen, ret; + + if (bufchain_size(chain) > 0) { + int prev_nonblock = nonblock(fd); + 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 (!prev_nonblock) + no_nonblock(fd); + if (ret < 0 && errno != EAGAIN) { + perror(is_stderr ? "stderr: write" : "stdout: write"); + exit(1); + } + } + if (outgoingeof == EOF_PENDING && bufchain_size(&stdout_data) == 0) { + close(STDOUT_FILENO); + outgoingeof = EOF_SENT; } return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); } @@ -419,6 +427,7 @@ int from_backend(void *frontend_handle, int is_stderr, bufchain_add(&stderr_data, data, len); return try_output(TRUE); } else { + assert(outgoingeof == EOF_NO); bufchain_add(&stdout_data, data, len); return try_output(FALSE); } @@ -434,6 +443,14 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) return 0; /* not reached */ } +int from_backend_eof(void *frontend_handle) +{ + assert(outgoingeof == EOF_NO); + outgoingeof = EOF_PENDING; + try_output(FALSE); + return FALSE; /* do not respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; @@ -576,6 +593,8 @@ static void version(void) exit(1); } +void frontend_net_error_pending(void) {} + int main(int argc, char **argv) { int sending; @@ -588,7 +607,8 @@ int main(int argc, char **argv) int errors; int use_subsystem = 0; int got_host = FALSE; - long now; + unsigned long now; + struct winsize size; fdlist = NULL; fdcount = fdsize = 0; @@ -599,16 +619,21 @@ int main(int argc, char **argv) default_protocol = PROT_SSH; default_port = 22; + bufchain_init(&stdout_data); + bufchain_init(&stderr_data); + outgoingeof = EOF_NO; + flags = FLAG_STDERR | FLAG_STDERR_TTY; stderr_tty_init(); /* * Process the command line. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; - default_protocol = cfg.protocol; - default_port = cfg.port; + default_protocol = conf_get_int(conf, CONF_protocol); + default_port = conf_get_int(conf, CONF_port); errors = 0; { /* @@ -618,8 +643,10 @@ int main(int argc, char **argv) if (p) { const Backend *b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; - default_port = cfg.port = b->default_port; + default_protocol = b->protocol; + default_port = b->default_port; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); } } } @@ -627,7 +654,7 @@ int main(int argc, char **argv) char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, &cfg); + 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); @@ -639,10 +666,13 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { - /* Save status to write to cfg later. */ + /* Save status to write to conf later. */ use_subsystem = 1; - } else if (!strcmp(p, "-V")) { + } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); + } else if (!strcmp(p, "--help")) { + usage(); + exit(0); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); @@ -660,7 +690,7 @@ int main(int argc, char **argv) errors = 1; } } else if (*p) { - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* @@ -674,7 +704,7 @@ int main(int argc, char **argv) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg.protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -682,11 +712,10 @@ int main(int argc, char **argv) if (*p) *p++ = '\0'; if (c == ':') - cfg.port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; @@ -701,7 +730,9 @@ int main(int argc, char **argv) *r = '\0'; b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); portnumber = b->default_port; } p = r + 1; @@ -728,26 +759,24 @@ int main(int argc, char **argv) * same name as the hostname. */ { - Config cfg2; - do_defaults(host, &cfg2); - if (loaded_session || !cfg_launchable(&cfg2)) { + Conf *conf2 = conf_new(); + do_defaults(host, conf2); + if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = default_port; + conf_set_str(conf, CONF_host, host); + conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { - cfg = cfg2; + conf_copy_into(conf, conf2); loaded_session = TRUE; } + conf_free(conf2); } if (user) { /* Patch in specified username. */ - strncpy(cfg.username, user, - sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } } @@ -774,9 +803,9 @@ int main(int argc, char **argv) } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ - cfg.remote_cmd_ptr = command; - cfg.remote_cmd_ptr2 = NULL; - cfg.nopty = TRUE; /* command => no terminal */ + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } @@ -786,70 +815,78 @@ int main(int argc, char **argv) if (errors) return 1; - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; + + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } + + /* + * Trim off a colon suffix if it's there. + */ + host[strcspn(host, ":")] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) - cfg.ssh_subsys = TRUE; - - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; - - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; - } - cfg.host[p1] = '\0'; - } + conf_set_int(conf, CONF_ssh_subsys, TRUE); - if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host) + if (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); @@ -860,7 +897,13 @@ int main(int argc, char **argv) * Select port. */ if (portnumber != -1) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); + + /* + * Block SIGPIPE, so that we'll get EPIPE individually on + * particular network connections that go wrong. + */ + putty_signal(SIGPIPE, SIG_IGN); /* * Set up the pipe we'll use to tell us about SIGWINCH. @@ -871,6 +914,15 @@ int main(int argc, char **argv) } putty_signal(SIGWINCH, sigwinch); + /* + * Now that we've got the SIGWINCH handler installed, try to find + * out the initial terminal size. + */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) >= 0) { + conf_set_int(conf, CONF_width, size.ws_col); + conf_set_int(conf, CONF_height, size.ws_row); + } + sk_init(); uxsel_init(); @@ -879,28 +931,34 @@ int main(int argc, char **argv) * 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; + if (conf_get_int(conf, CONF_protocol) == PROT_SSH && + !conf_get_int(conf, CONF_x11_forward) && + !conf_get_int(conf, CONF_agentfwd) && + !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) + conf_set_int(conf, CONF_ssh_simple, TRUE); + /* * Start up the connection. */ - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); console_provide_logctx(logctx); { const char *error; char *realhost; /* nodelay is only useful if stdin is a terminal device */ - int nodelay = cfg.tcp_nodelay && isatty(0); + int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && isatty(0); - error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, - &realhost, nodelay, cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, nodelay, + conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s\n", error); return 1; } back->provide_logctx(backhandle, logctx); - ldisc_create(&cfg, NULL, back, backhandle, NULL); + ldisc_create(conf, NULL, back, backhandle, NULL); sfree(realhost); } connopen = 1; @@ -975,12 +1033,17 @@ int main(int argc, char **argv) } do { - long next, ticks; + unsigned long next, then; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; @@ -990,27 +1053,8 @@ int main(int argc, char **argv) ret = select(maxfd, &rset, &wset, &xset, ptv); if (ret == 0) now = next; - else { - long newnow = GETTICKCOUNT(); - /* - * Check to see whether the system clock has - * changed massively during the select. - */ - if (newnow - now < 0 || newnow - now > next - now) { - /* - * If so, look at the elapsed time in the - * select and use it to compute a new - * tickcount_offset. - */ - long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000; - /* So we'd like GETTICKCOUNT to have returned othernow, - * but instead it return newnow. Hence ... */ - tickcount_offset += othernow - newnow; - now = othernow; - } else { - now = newnow; - } - } + else + now = GETTICKCOUNT(); } while (ret < 0 && errno == EINTR); if (ret < 0) { @@ -1039,7 +1083,7 @@ int main(int argc, char **argv) if (read(signalpipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ - if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0) + if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) back->size(backhandle, size.ws_col, size.ws_row); } @@ -1072,6 +1116,8 @@ int main(int argc, char **argv) back->unthrottle(backhandle, try_output(TRUE)); } + net_pending_errors(); + if ((!connopen || !back->connected(backhandle)) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) diff --git a/putty/UNIX/UXPROXY.C b/putty/UNIX/UXPROXY.C index 147df6e..dc52607 100644 --- a/putty/UNIX/UXPROXY.C +++ b/putty/UNIX/UXPROXY.C @@ -29,6 +29,7 @@ struct Socket_localproxy_tag { bufchain pending_output_data; bufchain pending_input_data; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; void *privptr; }; @@ -95,12 +96,14 @@ static void sk_localproxy_close (Socket s) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; - del234(localproxy_by_fromfd, ps); - del234(localproxy_by_tofd, ps); + if (ps->to_cmd >= 0) { + del234(localproxy_by_tofd, ps); + uxsel_del(ps->to_cmd); + close(ps->to_cmd); + } - uxsel_del(ps->to_cmd); + del234(localproxy_by_fromfd, ps); uxsel_del(ps->from_cmd); - close(ps->to_cmd); close(ps->from_cmd); sfree(ps); @@ -129,6 +132,14 @@ static int localproxy_try_send(Local_Proxy_Socket ps) } } + if (ps->outgoingeof == EOF_PENDING) { + del234(localproxy_by_tofd, ps); + close(ps->to_cmd); + uxsel_del(ps->to_cmd); + ps->to_cmd = -1; + ps->outgoingeof = EOF_SENT; + } + if (bufchain_size(&ps->pending_output_data) == 0) uxsel_del(ps->to_cmd); else @@ -141,6 +152,8 @@ static int sk_localproxy_write (Socket s, const char *data, int len) { Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + assert(ps->outgoingeof == EOF_NO); + bufchain_add(&ps->pending_output_data, data, len); localproxy_try_send(ps); @@ -157,6 +170,16 @@ static int sk_localproxy_write_oob (Socket s, const char *data, int len) return sk_localproxy_write(s, data, len); } +static void sk_localproxy_write_eof (Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + assert(ps->outgoingeof == EOF_NO); + ps->outgoingeof = EOF_PENDING; + + localproxy_try_send(ps); +} + static void sk_localproxy_flush (Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ @@ -224,7 +247,7 @@ static int localproxy_select_result(int fd, int event) Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { char *cmd; @@ -233,6 +256,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, + sk_localproxy_write_eof, sk_localproxy_flush, sk_localproxy_set_private_ptr, sk_localproxy_get_private_ptr, @@ -243,15 +267,16 @@ Socket platform_new_connection(SockAddr addr, char *hostname, Local_Proxy_Socket ret; int to_cmd_pipe[2], from_cmd_pipe[2], pid; - if (cfg->proxy_type != PROXY_CMD) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; - cmd = format_telnet_command(addr, port, cfg); + cmd = format_telnet_command(addr, port, conf); ret = snew(struct Socket_localproxy_tag); ret->fn = &socket_fn_table; ret->plug = plug; ret->error = NULL; + ret->outgoingeof = EOF_NO; bufchain_init(&ret->pending_input_data); bufchain_init(&ret->pending_output_data); @@ -263,6 +288,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, if (pipe(to_cmd_pipe) < 0 || pipe(from_cmd_pipe) < 0) { ret->error = dupprintf("pipe: %s", strerror(errno)); + sfree(cmd); return (Socket)ret; } cloexec(to_cmd_pipe[1]); @@ -272,6 +298,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, if (pid < 0) { ret->error = dupprintf("fork: %s", strerror(errno)); + sfree(cmd); return (Socket)ret; } else if (pid == 0) { close(0); @@ -280,8 +307,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname, dup2(from_cmd_pipe[1], 1); close(to_cmd_pipe[0]); close(from_cmd_pipe[1]); - fcntl(0, F_SETFD, 0); - fcntl(1, F_SETFD, 0); + noncloexec(0); + noncloexec(1); execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); _exit(255); } diff --git a/putty/UNIX/UXPTERM.C b/putty/UNIX/UXPTERM.C index 73670e8..ef5254a 100644 --- a/putty/UNIX/UXPTERM.C +++ b/putty/UNIX/UXPTERM.C @@ -12,19 +12,29 @@ const int use_event_log = 0; /* pterm doesn't need it */ const int new_session = 0, saved_sessions = 0; /* or these */ const int use_pty_argv = TRUE; -Backend *select_backend(Config *cfg) +Backend *select_backend(Conf *conf) { return &pty_backend; } -int cfgbox(Config *cfg) +void net_pending_errors(void) +{ + /* + * Stub version of net_pending_errors(), because gtkwin.c has to + * be prepared to call it when linked into PuTTY and therefore we + * have to avoid a link failure when linking gtkwin.c in turn into + * a non-networked application. + */ +} + +int cfgbox(Conf *conf) { /* * This is a no-op in pterm, except that we'll ensure the * protocol is set to -1 to inhibit the useless Connection * panel in the config box. */ - cfg->protocol = -1; + conf_set_int(conf, CONF_protocol, -1); return 1; } @@ -33,7 +43,7 @@ void cleanup_exit(int code) exit(code); } -int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { return 0; /* pterm doesn't have any. */ } @@ -47,11 +57,14 @@ int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); extern void pty_pre_init(void); /* declared in pty.c */ + int ret; cmdline_tooltype = TOOLTYPE_NONNETWORK; default_protocol = -1; pty_pre_init(); - return pt_main(argc, argv); + ret = pt_main(argc, argv); + cleanup_exit(ret); + return ret; /* not reached, but placates optimisers */ } diff --git a/putty/UNIX/UXPTY.C b/putty/UNIX/UXPTY.C index b1e240e..8f0d9e3 100644 --- a/putty/UNIX/UXPTY.C +++ b/putty/UNIX/UXPTY.C @@ -76,7 +76,7 @@ typedef struct pty_tag *Pty; static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */ struct pty_tag { - Config cfg; + Conf *conf; int master_fd, slave_fd; void *frontend; char name[FILENAME_MAX]; @@ -167,8 +167,8 @@ static tree234 *ptys_by_pid = NULL; static Pty single_pty = NULL; #ifndef OMIT_UTMP -static pid_t pty_utmp_helper_pid; -static int pty_utmp_helper_pipe; +static pid_t pty_utmp_helper_pid = -1; +static int pty_utmp_helper_pipe = -1; static int pty_stamped_utmp; static struct utmpx utmp_entry; #endif @@ -270,7 +270,6 @@ static void fatal_sig_handler(int signum) { putty_signal(signum, SIG_DFL); cleanup_utmp(); - setuid(getuid()); raise(signum); } #endif @@ -335,12 +334,28 @@ static void pty_open_master(Pty pty) chown(pty->name, getuid(), gp ? gp->gr_gid : -1); chmod(pty->name, 0600); #else - pty->master_fd = open("/dev/ptmx", O_RDWR); + + const int flags = O_RDWR +#ifdef O_NOCTTY + | O_NOCTTY +#endif + ; + +#ifdef HAVE_POSIX_OPENPT + pty->master_fd = posix_openpt(flags); + + if (pty->master_fd < 0) { + perror("posix_openpt"); + exit(1); + } +#else + pty->master_fd = open("/dev/ptmx", flags); if (pty->master_fd < 0) { perror("/dev/ptmx: open"); exit(1); } +#endif if (grantpt(pty->master_fd) < 0) { perror("grantpt"); @@ -358,15 +373,7 @@ static void pty_open_master(Pty pty) strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1); #endif - { - /* - * Set the pty master into non-blocking mode. - */ - int fl; - fl = fcntl(pty->master_fd, F_GETFL); - if (fl != -1 && !(fl & O_NONBLOCK)) - fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK); - } + nonblock(pty->master_fd); if (!ptys_by_fd) ptys_by_fd = newtree234(pty_compare_by_fd); @@ -396,6 +403,7 @@ void pty_pre_init(void) #endif pty = single_pty = snew(struct pty_tag); + pty->conf = NULL; bufchain_init(&pty->output_data); /* set the child signal handler straight away; it needs to be set @@ -408,106 +416,106 @@ void pty_pre_init(void) if (geteuid() != getuid() || getegid() != getgid()) { pty_open_master(pty); - } #ifndef OMIT_UTMP - /* - * Fork off the utmp helper. - */ - if (pipe(pipefd) < 0) { - perror("pterm: pipe"); - exit(1); - } - cloexec(pipefd[0]); - cloexec(pipefd[1]); - pid = fork(); - if (pid < 0) { - perror("pterm: fork"); - exit(1); - } else if (pid == 0) { - char display[128], buffer[128]; - int dlen, ret; - - close(pipefd[1]); - /* - * Now sit here until we receive a display name from the - * other end of the pipe, and then stamp utmp. Unstamp utmp - * again, and exit, when the pipe closes. - */ + /* + * Fork off the utmp helper. + */ + if (pipe(pipefd) < 0) { + perror("pterm: pipe"); + exit(1); + } + cloexec(pipefd[0]); + cloexec(pipefd[1]); + pid = fork(); + if (pid < 0) { + perror("pterm: fork"); + exit(1); + } else if (pid == 0) { + char display[128], buffer[128]; + int dlen, ret; + + close(pipefd[1]); + /* + * Now sit here until we receive a display name from the + * other end of the pipe, and then stamp utmp. Unstamp utmp + * again, and exit, when the pipe closes. + */ - dlen = 0; - while (1) { + dlen = 0; + while (1) { - ret = read(pipefd[0], buffer, lenof(buffer)); - if (ret <= 0) { - cleanup_utmp(); - _exit(0); - } else if (!pty_stamped_utmp) { - if (dlen < lenof(display)) - memcpy(display+dlen, buffer, - min(ret, lenof(display)-dlen)); - if (buffer[ret-1] == '\0') { - /* - * Now we have a display name. NUL-terminate - * it, and stamp utmp. - */ - display[lenof(display)-1] = '\0'; - /* - * Trap as many fatal signals as we can in the - * hope of having the best possible chance to - * clean up utmp before termination. We are - * unfortunately unprotected against SIGKILL, - * but that's life. - */ - putty_signal(SIGHUP, fatal_sig_handler); - putty_signal(SIGINT, fatal_sig_handler); - putty_signal(SIGQUIT, fatal_sig_handler); - putty_signal(SIGILL, fatal_sig_handler); - putty_signal(SIGABRT, fatal_sig_handler); - putty_signal(SIGFPE, fatal_sig_handler); - putty_signal(SIGPIPE, fatal_sig_handler); - putty_signal(SIGALRM, fatal_sig_handler); - putty_signal(SIGTERM, fatal_sig_handler); - putty_signal(SIGSEGV, fatal_sig_handler); - putty_signal(SIGUSR1, fatal_sig_handler); - putty_signal(SIGUSR2, fatal_sig_handler); + ret = read(pipefd[0], buffer, lenof(buffer)); + if (ret <= 0) { + cleanup_utmp(); + _exit(0); + } else if (!pty_stamped_utmp) { + if (dlen < lenof(display)) + memcpy(display+dlen, buffer, + min(ret, lenof(display)-dlen)); + if (buffer[ret-1] == '\0') { + /* + * Now we have a display name. NUL-terminate + * it, and stamp utmp. + */ + display[lenof(display)-1] = '\0'; + /* + * Trap as many fatal signals as we can in the + * hope of having the best possible chance to + * clean up utmp before termination. We are + * unfortunately unprotected against SIGKILL, + * but that's life. + */ + putty_signal(SIGHUP, fatal_sig_handler); + putty_signal(SIGINT, fatal_sig_handler); + putty_signal(SIGQUIT, fatal_sig_handler); + putty_signal(SIGILL, fatal_sig_handler); + putty_signal(SIGABRT, fatal_sig_handler); + putty_signal(SIGFPE, fatal_sig_handler); + putty_signal(SIGPIPE, fatal_sig_handler); + putty_signal(SIGALRM, fatal_sig_handler); + putty_signal(SIGTERM, fatal_sig_handler); + putty_signal(SIGSEGV, fatal_sig_handler); + putty_signal(SIGUSR1, fatal_sig_handler); + putty_signal(SIGUSR2, fatal_sig_handler); #ifdef SIGBUS - putty_signal(SIGBUS, fatal_sig_handler); + putty_signal(SIGBUS, fatal_sig_handler); #endif #ifdef SIGPOLL - putty_signal(SIGPOLL, fatal_sig_handler); + putty_signal(SIGPOLL, fatal_sig_handler); #endif #ifdef SIGPROF - putty_signal(SIGPROF, fatal_sig_handler); + putty_signal(SIGPROF, fatal_sig_handler); #endif #ifdef SIGSYS - putty_signal(SIGSYS, fatal_sig_handler); + putty_signal(SIGSYS, fatal_sig_handler); #endif #ifdef SIGTRAP - putty_signal(SIGTRAP, fatal_sig_handler); + putty_signal(SIGTRAP, fatal_sig_handler); #endif #ifdef SIGVTALRM - putty_signal(SIGVTALRM, fatal_sig_handler); + putty_signal(SIGVTALRM, fatal_sig_handler); #endif #ifdef SIGXCPU - putty_signal(SIGXCPU, fatal_sig_handler); + putty_signal(SIGXCPU, fatal_sig_handler); #endif #ifdef SIGXFSZ - putty_signal(SIGXFSZ, fatal_sig_handler); + putty_signal(SIGXFSZ, fatal_sig_handler); #endif #ifdef SIGIO - putty_signal(SIGIO, fatal_sig_handler); + putty_signal(SIGIO, fatal_sig_handler); #endif - setup_utmp(pty->name, display); - } - } - } - } else { - close(pipefd[0]); - pty_utmp_helper_pid = pid; - pty_utmp_helper_pipe = pipefd[1]; - } + setup_utmp(pty->name, display); + } + } + } + } else { + close(pipefd[0]); + pty_utmp_helper_pid = pid; + pty_utmp_helper_pipe = pipefd[1]; + } #endif + } /* Drop privs. */ { @@ -515,11 +523,23 @@ void pty_pre_init(void) int gid = getgid(), uid = getuid(); int setresgid(gid_t, gid_t, gid_t); int setresuid(uid_t, uid_t, uid_t); - setresgid(gid, gid, gid); - setresuid(uid, uid, uid); + if (setresgid(gid, gid, gid) < 0) { + perror("setresgid"); + exit(1); + } + if (setresuid(uid, uid, uid) < 0) { + perror("setresuid"); + exit(1); + } #else - setgid(getgid()); - setuid(getuid()); + if (setgid(getgid()) < 0) { + perror("setgid"); + exit(1); + } + if (setuid(getuid()) < 0) { + perror("setuid"); + exit(1); + } #endif } } @@ -588,6 +608,8 @@ int pty_real_select_result(Pty pty, int event, int status) } if (finished && !pty->finished) { + int close_on_exit; + uxsel_del(pty->master_fd); pty_close(pty); pty->master_fd = -1; @@ -600,9 +622,11 @@ int pty_real_select_result(Pty pty, int event, int status) * Only On Clean and it wasn't a clean exit) do we output a * `terminated' message. */ - if (pty->cfg.close_on_exit == FORCE_OFF || - (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) { + close_on_exit = conf_get_int(pty->conf, CONF_close_on_exit); + if (close_on_exit == FORCE_OFF || + (close_on_exit == AUTO && pty->exit_code != 0)) { char message[512]; + message[0] = '\0'; if (WIFEXITED(pty->exit_code)) sprintf(message, "\r\n[pterm: process terminated with exit" " code %d]\r\n", WEXITSTATUS(pty->exit_code)); @@ -681,7 +705,7 @@ static void pty_uxsel_setup(Pty pty) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, +static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { @@ -694,6 +718,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, if (single_pty) { pty = single_pty; + assert(pty->conf == NULL); } else { pty = snew(struct pty_tag); pty->master_fd = pty->slave_fd = -1; @@ -705,9 +730,9 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, pty->frontend = frontend; *backend_handle = NULL; /* we can't sensibly use this, sadly */ - pty->cfg = *cfg; /* structure copy */ - pty->term_width = cfg->width; - pty->term_height = cfg->height; + pty->conf = conf_copy(conf); + pty->term_width = conf_get_int(conf, CONF_width); + pty->term_height = conf_get_int(conf, CONF_height); if (pty->master_fd < 0) pty_open_master(pty); @@ -719,7 +744,8 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, { struct termios attrs; tcgetattr(pty->master_fd, &attrs); - attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010'; + attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete) + ? '\177' : '\010'; tcsetattr(pty->master_fd, TCSANOW, &attrs); } @@ -728,21 +754,23 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, * Stamp utmp (that is, tell the utmp helper process to do so), * or not. */ - if (!cfg->stamp_utmp) { - close(pty_utmp_helper_pipe); /* just let the child process die */ - pty_utmp_helper_pipe = -1; - } else { - char *location = get_x_display(pty->frontend); - int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ - while (pos < len) { - int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); - if (ret < 0) { - perror("pterm: writing to utmp helper process"); - close(pty_utmp_helper_pipe); /* arrgh, just give up */ - pty_utmp_helper_pipe = -1; - break; - } - pos += ret; + if (pty_utmp_helper_pipe >= 0) { /* if it's < 0, we can't anyway */ + if (!conf_get_int(conf, CONF_stamp_utmp)) { + close(pty_utmp_helper_pipe); /* just let the child process die */ + pty_utmp_helper_pipe = -1; + } else { + char *location = get_x_display(pty->frontend); + int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ + while (pos < len) { + int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); + if (ret < 0) { + perror("pterm: writing to utmp helper process"); + close(pty_utmp_helper_pipe); /* arrgh, just give up */ + pty_utmp_helper_pipe = -1; + break; + } + pos += ret; + } } } #endif @@ -772,7 +800,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, } close(pty->master_fd); - fcntl(slavefd, F_SETFD, 0); /* don't close on exec */ + noncloexec(slavefd); dup2(slavefd, 0); dup2(slavefd, 1); dup2(slavefd, 2); @@ -784,10 +812,15 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, pgrp = getpid(); tcsetpgrp(0, pgrp); setpgid(pgrp, pgrp); - close(open(pty->name, O_WRONLY, 0)); + { + int ptyfd = open(pty->name, O_WRONLY, 0); + if (ptyfd >= 0) + close(ptyfd); + } setpgid(pgrp, pgrp); { - char *term_env_var = dupprintf("TERM=%s", cfg->termtype); + char *term_env_var = dupprintf("TERM=%s", + conf_get_str(conf, CONF_termtype)); putenv(term_env_var); /* We mustn't free term_env_var, as putenv links it into the * environment in place. @@ -803,18 +836,12 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, } #endif { - char *e = cfg->environmt; - char *var, *varend, *val, *varval; - while (*e) { - var = e; - while (*e && *e != '\t') e++; - varend = e; - if (*e == '\t') e++; - val = e; - while (*e) e++; - e++; - - varval = dupprintf("%.*s=%s", varend-var, var, val); + char *key, *val; + + for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { + char *varval = dupcat(key, "=", val, NULL); putenv(varval); /* * We must not free varval, since putenv links it @@ -836,12 +863,44 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, putty_signal(SIGQUIT, SIG_DFL); putty_signal(SIGPIPE, SIG_DFL); block_signal(SIGCHLD, 0); - if (pty_argv) + if (pty_argv) { + /* + * Exec the exact argument list we were given. + */ execvp(pty_argv[0], pty_argv); - else { + /* + * If that fails, and if we had exactly one argument, pass + * that argument to $SHELL -c. + * + * This arranges that we can _either_ follow 'pterm -e' + * with a list of argv elements to be fed directly to + * exec, _or_ with a single argument containing a command + * to be parsed by a shell (but, in cases of doubt, the + * former is more reliable). + * + * A quick survey of other terminal emulators' -e options + * (as of Debian squeeze) suggests that: + * + * - xterm supports both modes, more or less like this + * - gnome-terminal will only accept a one-string shell command + * - Eterm, kterm and rxvt will only accept a list of + * argv elements (as did older versions of pterm). + * + * It therefore seems important to support both usage + * modes in order to be a drop-in replacement for either + * xterm or gnome-terminal, and hence for anyone's + * plausible uses of the Debian-style alias + * 'x-terminal-emulator'... + */ + if (pty_argv[1] == NULL) { + char *shell = getenv("SHELL"); + if (shell) + execl(shell, shell, "-c", pty_argv[0], (void *)NULL); + } + } else { char *shell = getenv("SHELL"); char *shellname; - if (cfg->login_shell) { + if (conf_get_int(conf, CONF_login_shell)) { char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; @@ -884,7 +943,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg, return NULL; } -static void pty_reconfig(void *handle, Config *cfg) +static void pty_reconfig(void *handle, Conf *conf) { Pty pty = (Pty)handle; /* @@ -892,7 +951,7 @@ static void pty_reconfig(void *handle, Config *cfg) * unfortunately we do need to pick up the setting of Close On * Exit so we know whether to give a `terminated' message. */ - pty->cfg = *cfg; /* structure copy */ + conf_copy_into(pty->conf, conf); } /* @@ -906,7 +965,17 @@ static void pty_free(void *handle) del234(ptys_by_pid, pty); del234(ptys_by_fd, pty); - sfree(pty); + conf_free(pty->conf); + pty->conf = NULL; + + if (pty == single_pty) { + /* + * Leave this structure around in case we need to Restart + * Session. + */ + } else { + sfree(pty); + } } static void pty_try_write(Pty pty) diff --git a/putty/UNIX/UXPUTTY.C b/putty/UNIX/UXPUTTY.C index 83eae33..457a4a0 100644 --- a/putty/UNIX/UXPUTTY.C +++ b/putty/UNIX/UXPUTTY.C @@ -31,17 +31,17 @@ void cleanup_exit(int code) exit(code); } -Backend *select_backend(Config *cfg) +Backend *select_backend(Conf *conf) { - Backend *back = backend_from_proto(cfg->protocol); + Backend *back = backend_from_proto(conf_get_int(conf, CONF_protocol)); assert(back != NULL); return back; } -int cfgbox(Config *cfg) +int cfgbox(Conf *conf) { char *title = dupcat(appname, " Configuration", NULL); - int ret = do_config_box(title, cfg, 0, 0); + int ret = do_config_box(title, conf, 0, 0); sfree(title); return ret; } @@ -50,7 +50,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 *allow_launch) +int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch) { char *p, *q = arg; @@ -61,7 +61,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) * argument, so that it will be deferred until it's a good * moment to run it. */ - int ret = cmdline_process_param("-P", arg, 1, cfg); + int ret = cmdline_process_param("-P", arg, 1, conf); assert(ret == 2); } else if (!strncmp(q, "telnet:", 7)) { /* @@ -74,7 +74,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg->protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -82,11 +82,10 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) if (*p) *p++ = '\0'; if (c == ':') - cfg->port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg->port = -1; - strncpy(cfg->host, q, sizeof(cfg->host) - 1); - cfg->host[sizeof(cfg->host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = 1; } else { /* @@ -97,8 +96,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch) p++; if (*p) *p++ = '\0'; - strncpy(cfg->host, q, sizeof(cfg->host) - 1); - cfg->host[sizeof(cfg->host) - 1] = '\0'; + conf_set_str(conf, CONF_host, q); got_host = 1; } if (got_host) @@ -127,6 +125,8 @@ char *platform_get_x_display(void) { int main(int argc, char **argv) { extern int pt_main(int argc, char **argv); + int ret; + sk_init(); flags = FLAG_VERBOSE | FLAG_INTERACTIVE; default_protocol = be_default_protocol; @@ -137,5 +137,7 @@ int main(int argc, char **argv) if (b) default_port = b->default_port; } - return pt_main(argc, argv); + ret = pt_main(argc, argv); + cleanup_exit(ret); + return ret; /* not reached, but placates optimisers */ } diff --git a/putty/UNIX/UXSER.C b/putty/UNIX/UXSER.C index de47877..6425f96 100644 --- a/putty/UNIX/UXSER.C +++ b/putty/UNIX/UXSER.C @@ -60,10 +60,10 @@ static int serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial serial); static void serial_try_write(Serial serial); -static const char *serial_configure(Serial serial, Config *cfg) +static const char *serial_configure(Serial serial, Conf *conf) { struct termios options; - int bflag, bval; + int bflag, bval, speed, flow, parity; const char *str; char *msg; @@ -75,8 +75,9 @@ static const char *serial_configure(Serial serial, Config *cfg) /* * Find the appropriate baud rate flag. */ + speed = conf_get_int(conf, CONF_serspeed); #define SETBAUD(x) (bflag = B ## x, bval = x) -#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0) +#define CHECKBAUD(x) do { if (speed >= x) SETBAUD(x); } while (0) SETBAUD(50); #ifdef B75 CHECKBAUD(75); @@ -183,18 +184,19 @@ static const char *serial_configure(Serial serial, Config *cfg) sfree(msg); options.c_cflag &= ~CSIZE; - switch (cfg->serdatabits) { + switch (conf_get_int(conf, CONF_serdatabits)) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: return "Invalid number of data bits (need 5, 6, 7 or 8)"; } - msg = dupprintf("Configuring %d data bits", cfg->serdatabits); + msg = dupprintf("Configuring %d data bits", + conf_get_int(conf, CONF_serdatabits)); logevent(serial->frontend, msg); sfree(msg); - if (cfg->serstopbits >= 4) { + if (conf_get_int(conf, CONF_serstopbits) >= 4) { options.c_cflag |= CSTOPB; } else { options.c_cflag &= ~CSTOPB; @@ -211,10 +213,11 @@ static const char *serial_configure(Serial serial, Config *cfg) #ifdef CNEW_RTSCTS options.c_cflag &= ~CNEW_RTSCTS; #endif - if (cfg->serflow == SER_FLOW_XONXOFF) { + flow = conf_get_int(conf, CONF_serflow); + if (flow == SER_FLOW_XONXOFF) { options.c_iflag |= IXON | IXOFF; str = "XON/XOFF"; - } else if (cfg->serflow == SER_FLOW_RTSCTS) { + } else if (flow == SER_FLOW_RTSCTS) { #ifdef CRTSCTS options.c_cflag |= CRTSCTS; #endif @@ -229,11 +232,12 @@ static const char *serial_configure(Serial serial, Config *cfg) sfree(msg); /* Parity */ - if (cfg->serparity == SER_PAR_ODD) { + parity = conf_get_int(conf, CONF_serparity); + if (parity == SER_PAR_ODD) { options.c_cflag |= PARENB; options.c_cflag |= PARODD; str = "odd"; - } else if (cfg->serparity == SER_PAR_EVEN) { + } else if (parity == SER_PAR_EVEN) { options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; str = "even"; @@ -284,12 +288,13 @@ static const char *serial_configure(Serial serial, Config *cfg) * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, - Config *cfg, + Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { Serial serial; const char *err; + char *line; serial = snew(struct serial_backend_data); *backend_handle = serial; @@ -299,22 +304,24 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, serial->inbufsize = 0; bufchain_init(&serial->output_data); + line = conf_get_str(conf, CONF_serline); { - char *msg = dupprintf("Opening serial device %s", cfg->serline); + char *msg = dupprintf("Opening serial device %s", line); logevent(serial->frontend, msg); + sfree(msg); } - serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (serial->fd < 0) return "Unable to open serial port"; cloexec(serial->fd); - err = serial_configure(serial, cfg); + err = serial_configure(serial, conf); if (err) return err; - *realhost = dupstr(cfg->serline); + *realhost = dupstr(line); if (!serial_by_fd) serial_by_fd = newtree234(serial_compare_by_fd); @@ -349,14 +356,14 @@ static void serial_free(void *handle) sfree(serial); } -static void serial_reconfig(void *handle, Config *cfg) +static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; /* * FIXME: what should we do if this returns an error? */ - serial_configure(serial, cfg); + serial_configure(serial, conf); } static int serial_select_result(int fd, int event) diff --git a/putty/UNIX/UXSFTP.C b/putty/UNIX/UXSFTP.C index a0ae9fb..33e7648 100644 --- a/putty/UNIX/UXSFTP.C +++ b/putty/UNIX/UXSFTP.C @@ -34,7 +34,7 @@ char *x_get_default(const char *key) return NULL; /* this is a stub */ } -void platform_get_x11_auth(struct X11Display *display, const Config *cfg) +void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } @@ -53,21 +53,17 @@ int platform_default_i(const char *name, int def) return def; } -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - *ret.name = '\0'; - return ret; + return fontspec_new(""); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *get_ttymode(void *frontend, const char *mode) { return NULL; } @@ -125,7 +121,8 @@ struct RFile { }; RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime) + unsigned long *mtime, unsigned long *atime, + long *perms) { int fd; RFile *ret; @@ -137,7 +134,7 @@ RFile *open_existing_file(char *name, uint64 *size, ret = snew(RFile); ret->fd = fd; - if (size || mtime || atime) { + if (size || mtime || atime || perms) { struct stat statbuf; if (fstat(fd, &statbuf) < 0) { fprintf(stderr, "%s: stat: %s\n", name, strerror(errno)); @@ -153,6 +150,9 @@ RFile *open_existing_file(char *name, uint64 *size, if (atime) *atime = statbuf.st_atime; + + if (perms) + *perms = statbuf.st_mode; } return ret; @@ -174,12 +174,13 @@ struct WFile { char *name; }; -WFile *open_new_file(char *name) +WFile *open_new_file(char *name, long perms) { int fd; WFile *ret; - fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666); + fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, + (mode_t)(perms ? perms : 0666)); if (fd < 0) return NULL; @@ -442,7 +443,7 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) fd_set rset, wset, xset; int i, fdcount, fdsize, *fdlist; int fd, fdstate, rwx, ret, maxfd; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(); fdlist = NULL; fdcount = fdsize = 0; @@ -488,13 +489,17 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) FD_SET_MAX(0, maxfd, rset); do { - long next, ticks; + unsigned long next, then; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks <= 0) - ticks = 1; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; @@ -504,27 +509,8 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) ret = select(maxfd, &rset, &wset, &xset, ptv); if (ret == 0) now = next; - else { - long newnow = GETTICKCOUNT(); - /* - * Check to see whether the system clock has - * changed massively during the select. - */ - if (newnow - now < 0 || newnow - now > next - now) { - /* - * If so, look at the elapsed time in the - * select and use it to compute a new - * tickcount_offset. - */ - long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000; - /* So we'd like GETTICKCOUNT to have returned othernow, - * but instead it return newnow. Hence ... */ - tickcount_offset += othernow - newnow; - now = othernow; - } else { - now = newnow; - } - } + else + now = GETTICKCOUNT(); } while (ret < 0 && errno != EINTR); } while (ret == 0); @@ -579,6 +565,7 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) ret = ssh_sftp_do_select(TRUE, no_fds_ok); if (ret < 0) { printf("connection died\n"); + sfree(buf); return NULL; /* woop woop */ } if (ret > 0) { @@ -589,10 +576,12 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) ret = read(0, buf+buflen, 1); if (ret < 0) { perror("read"); + sfree(buf); return NULL; } if (ret == 0) { /* eof on stdin; no error, but no answer either */ + sfree(buf); return NULL; } @@ -604,6 +593,8 @@ char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok) } } +void frontend_net_error_pending(void) {} + /* * Main program: do platform-specific initialisation and then call * psftp_main(). diff --git a/putty/UNIX/UXSTORE.C b/putty/UNIX/UXSTORE.C index 29cfa34..b666afe 100644 --- a/putty/UNIX/UXSTORE.C +++ b/putty/UNIX/UXSTORE.C @@ -166,25 +166,31 @@ void *open_settings_w(const char *sessionname, char **errmsg) /* * Start by making sure the .putty directory and its sessions - * subdir actually exist. Ignore error returns from mkdir since - * they're perfectly likely to be `already exists', and any - * other error will trip us up later on so there's no real need - * to catch it now. + * subdir actually exist. */ + filename = make_filename(INDEX_DIR, NULL); + if (mkdir(filename, 0700) < 0 && errno != EEXIST) { + *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " + "returned '%s'", filename, strerror(errno)); + sfree(filename); + return NULL; + } + sfree(filename); + filename = make_filename(INDEX_SESSIONDIR, NULL); - if (mkdir(filename, 0700) != 0) { - char *filename2 = make_filename(INDEX_DIR, NULL); - mkdir(filename2, 0700); - sfree(filename2); - mkdir(filename, 0700); + if (mkdir(filename, 0700) < 0 && errno != EEXIST) { + *errmsg = dupprintf("Unable to save session: mkdir(\"%s\") " + "returned '%s'", filename, strerror(errno)); + sfree(filename); + return NULL; } sfree(filename); filename = make_filename(INDEX_SESSION, sessionname); fp = fopen(filename, "w"); if (!fp) { - *errmsg = dupprintf("Unable to create %s: %s", - filename, strerror(errno)); + *errmsg = dupprintf("Unable to save session: open(\"%s\") " + "returned '%s'", filename, strerror(errno)); sfree(filename); return NULL; /* can't open */ } @@ -299,8 +305,10 @@ void *open_settings_r(const char *sessionname) char *value = strchr(line, '='); struct skeyval *kv; - if (!value) + if (!value) { + sfree(line); continue; + } *value++ = '\0'; value[strcspn(value, "\r\n")] = '\0'; /* trim trailing NL */ @@ -317,7 +325,7 @@ void *open_settings_r(const char *sessionname) return ret; } -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) +char *read_setting_s(void *handle, const char *key) { tree234 *tree = (tree234 *)handle; const char *val; @@ -333,11 +341,8 @@ char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) if (!val) return NULL; - else { - strncpy(buffer, val, buflen); - buffer[buflen-1] = '\0'; - return buffer; - } + else + return dupstr(val); } int read_setting_i(void *handle, const char *key, int defvalue) @@ -360,7 +365,7 @@ int read_setting_i(void *handle, const char *key, int defvalue) return atoi(val); } -int read_setting_fontspec(void *handle, const char *name, FontSpec *result) +FontSpec *read_setting_fontspec(void *handle, const char *name) { /* * In GTK1-only PuTTY, we used to store font names simply as a @@ -375,29 +380,41 @@ int read_setting_fontspec(void *handle, const char *name, FontSpec *result) * ("FontName"). */ char *suffname = dupcat(name, "Name", NULL); - if (read_setting_s(handle, suffname, result->name, sizeof(result->name))) { + char *tmp; + + if ((tmp = read_setting_s(handle, suffname)) != NULL) { + FontSpec *fs = fontspec_new(tmp); sfree(suffname); - return TRUE; /* got new-style name */ + sfree(tmp); + return fs; /* got new-style name */ } sfree(suffname); /* Fall back to old-style name. */ - memcpy(result->name, "server:", 7); - if (!read_setting_s(handle, name, - result->name + 7, sizeof(result->name) - 7) || - !result->name[7]) { - result->name[0] = '\0'; - return FALSE; + tmp = read_setting_s(handle, name); + if (tmp && *tmp) { + char *tmp2 = dupcat("server:", tmp, NULL); + FontSpec *fs = fontspec_new(tmp2); + sfree(tmp2); + sfree(tmp); + return fs; } else { - return TRUE; + sfree(tmp); + return NULL; } } -int read_setting_filename(void *handle, const char *name, Filename *result) +Filename *read_setting_filename(void *handle, const char *name) { - return !!read_setting_s(handle, name, result->path, sizeof(result->path)); + char *tmp = read_setting_s(handle, name); + if (tmp) { + Filename *ret = filename_from_str(tmp); + sfree(tmp); + return ret; + } else + return NULL; } -void write_setting_fontspec(void *handle, const char *name, FontSpec result) +void write_setting_fontspec(void *handle, const char *name, FontSpec *fs) { /* * read_setting_fontspec had to handle two cases, but when @@ -405,12 +422,12 @@ void write_setting_fontspec(void *handle, const char *name, FontSpec result) * new-style name. */ char *suffname = dupcat(name, "Name", NULL); - write_setting_s(handle, suffname, result.name); + write_setting_s(handle, suffname, fs->name); sfree(suffname); } -void write_setting_filename(void *handle, const char *name, Filename result) +void write_setting_filename(void *handle, const char *name, Filename *result) { - write_setting_s(handle, name, result.path); + write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) @@ -580,30 +597,40 @@ void store_host_key(const char *hostname, int port, int headerlen; char *filename, *tmpfilename; - newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key); - headerlen = 1 + strcspn(newtext, " "); /* count the space too */ - /* * Open both the old file and a new file. */ tmpfilename = make_filename(INDEX_HOSTKEYS_TMP, NULL); wfp = fopen(tmpfilename, "w"); - if (!wfp) { + if (!wfp && errno == ENOENT) { char *dir; dir = make_filename(INDEX_DIR, NULL); - mkdir(dir, 0700); + if (mkdir(dir, 0700) < 0) { + char *msg = dupprintf("Unable to store host key: mkdir(\"%s\") " + "returned '%s'", dir, strerror(errno)); + nonfatal(msg); + sfree(dir); + sfree(tmpfilename); + return; + } sfree(dir); wfp = fopen(tmpfilename, "w"); } if (!wfp) { - sfree(tmpfilename); - return; + char *msg = dupprintf("Unable to store host key: open(\"%s\") " + "returned '%s'", tmpfilename, strerror(errno)); + nonfatal(msg); + sfree(tmpfilename); + return; } filename = make_filename(INDEX_HOSTKEYS, NULL); rfp = fopen(filename, "r"); + newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key); + headerlen = 1 + strcspn(newtext, " "); /* count the space too */ + /* * Copy all lines from the old file to the new one that _don't_ * involve the same host key identifier as the one we're adding. @@ -612,6 +639,7 @@ void store_host_key(const char *hostname, int port, while ( (line = fgetline(rfp)) ) { if (strncmp(line, newtext, headerlen)) fputs(line, wfp); + sfree(line); } fclose(rfp); } @@ -623,7 +651,12 @@ void store_host_key(const char *hostname, int port, fclose(wfp); - rename(tmpfilename, filename); + if (rename(tmpfilename, filename) < 0) { + char *msg = dupprintf("Unable to store host key: rename(\"%s\",\"%s\")" + " returned '%s'", tmpfilename, filename, + strerror(errno)); + nonfatal(msg); + } sfree(tmpfilename); sfree(filename); @@ -660,18 +693,48 @@ void write_random_seed(void *data, int len) */ fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { + if (errno != ENOENT) { + char *msg = dupprintf("Unable to write random seed: open(\"%s\") " + "returned '%s'", fname, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + return; + } char *dir; dir = make_filename(INDEX_DIR, NULL); - mkdir(dir, 0700); + if (mkdir(dir, 0700) < 0) { + char *msg = dupprintf("Unable to write random seed: mkdir(\"%s\") " + "returned '%s'", dir, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + sfree(dir); + return; + } sfree(dir); fd = open(fname, O_CREAT | O_WRONLY, 0600); + if (fd < 0) { + char *msg = dupprintf("Unable to write random seed: open(\"%s\") " + "returned '%s'", fname, strerror(errno)); + nonfatal(msg); + sfree(msg); + sfree(fname); + return; + } } while (len > 0) { int ret = write(fd, data, len); - if (ret <= 0) break; + if (ret < 0) { + char *msg = dupprintf("Unable to write random seed: write " + "returned '%s'", strerror(errno)); + nonfatal(msg); + sfree(msg); + break; + } len -= ret; data = (char *)data + len; } diff --git a/putty/UNIX/UXUCS.C b/putty/UNIX/UXUCS.C index d2d6522..20023b9 100644 --- a/putty/UNIX/UXUCS.C +++ b/putty/UNIX/UXUCS.C @@ -21,7 +21,7 @@ int is_dbcs_leadbyte(int codepage, char byte) return 0; /* we don't do DBCS */ } -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { if (codepage == DEFAULT_CODEPAGE) { @@ -29,7 +29,6 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, mbstate_t state; memset(&state, 0, sizeof state); - setlocale(LC_CTYPE, ""); while (mblen > 0) { size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state); @@ -40,8 +39,6 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, mblen -= i; } - setlocale(LC_CTYPE, "C"); - return n; } else if (codepage == CS_NONE) { int n = 0; @@ -59,7 +56,7 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, NULL, NULL, 0); } -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { @@ -73,7 +70,6 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, int n = 0; memset(&state, 0, sizeof state); - setlocale(LC_CTYPE, ""); while (wclen > 0) { int i = wcrtomb(output, wcstr[0], &state); @@ -85,8 +81,6 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, wclen--; } - setlocale(LC_CTYPE, "C"); - return n; } else if (codepage == CS_NONE) { int n = 0; @@ -139,7 +133,7 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, /* * Failing that, line_codepage should be decoded from the - * specification in cfg. + * specification in conf. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = decode_codepage(linecharset); @@ -162,7 +156,8 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, * in the line codepage into Unicode. */ for (i = 0; i < 256; i++) { - char c[1], *p; + char c[1]; + const char *p; wchar_t wc[1]; int len; c[0] = i; @@ -216,7 +211,8 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset, * simply CP437. */ for (i = 0; i < 256; i++) { - char c[1], *p; + char c[1]; + const char *p; wchar_t wc[1]; int len; c[0] = i; @@ -257,17 +253,19 @@ const char *cp_name(int codepage) const char *cp_enumerate(int index) { int charset; - if (index == 0) - return "Use font encoding"; - charset = charset_localenc_nth(index-1); - if (charset == CS_NONE) + charset = charset_localenc_nth(index); + if (charset == CS_NONE) { + /* "Use font encoding" comes after all the named charsets */ + if (charset_localenc_nth(index-1) != CS_NONE) + return "Use font encoding"; return NULL; + } return charset_to_localenc(charset); } int decode_codepage(char *cp_name) { - if (!*cp_name) - return CS_NONE; /* use font encoding */ + if (!cp_name || !*cp_name) + return CS_UTF8; return charset_from_localenc(cp_name); } diff --git a/putty/UNIX/UX_X11.C b/putty/UNIX/UX_X11.C index 7dd17df..46f9957 100644 --- a/putty/UNIX/UX_X11.C +++ b/putty/UNIX/UX_X11.C @@ -12,7 +12,7 @@ #include "ssh.h" #include "network.h" -void platform_get_x11_auth(struct X11Display *disp, const Config *cfg) +void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { char *xauthfile; int needs_free; diff --git a/putty/VERSION.C b/putty/VERSION.C index 918a920..16347ca 100644 --- a/putty/VERSION.C +++ b/putty/VERSION.C @@ -5,6 +5,22 @@ #define STR1(x) #x #define STR(x) STR1(x) +#ifdef INCLUDE_EMPTY_H +/* + * Horrible hack to force version.o to be rebuilt unconditionally in + * the automake world: empty.h is an empty header file, created by the + * makefile and forcibly updated every time make is run. Including it + * here causes automake to track it as a dependency, which will cause + * version.o to be rebuilt too. + * + * The space between # and include causes mkfiles.pl's dependency + * scanner (for all other makefile types) to ignore this include, + * which is correct because only the automake makefile passes + * -DINCLUDE_EMPTY_H to enable it. + */ +# include "empty.h" +#endif + #if defined SNAPSHOT #if defined SVN_REV diff --git a/putty/WCWIDTH.C b/putty/WCWIDTH.C index bcd153d..0a3e2d0 100644 --- a/putty/WCWIDTH.C +++ b/putty/WCWIDTH.C @@ -50,7 +50,7 @@ * * http://www.unicode.org/unicode/reports/tr11/ * - * Markus Kuhn -- 2003-05-20 (Unicode 4.0) + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author @@ -64,12 +64,12 @@ #include "putty.h" /* for prototypes */ struct interval { - int first; - int last; + unsigned int first; + unsigned int last; }; /* auxiliary function for binary search in interval table */ -static int bisearch(wchar_t ucs, const struct interval *table, int max) { +static int bisearch(unsigned int ucs, const struct interval *table, int max) { int min = 0; int mid; @@ -121,32 +121,32 @@ static int bisearch(wchar_t ucs, const struct interval *table, int max) { * in ISO 10646. */ -int mk_wcwidth(wchar_t ucs) +int mk_wcwidth(unsigned int ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { - { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 }, - { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, - { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, - { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, - { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, - { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, - { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, - { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, - { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, - { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, - { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, - { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, - { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, - { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, - { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, - { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, - { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, - { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, - { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, - { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, @@ -155,18 +155,25 @@ int mk_wcwidth(wchar_t ucs) { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 }, - { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, - { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, - { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, - { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, - { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F }, - { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, - { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, - { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, - { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ @@ -190,6 +197,7 @@ int mk_wcwidth(wchar_t ucs) ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || @@ -198,7 +206,7 @@ int mk_wcwidth(wchar_t ucs) } -int mk_wcswidth(const wchar_t *pwcs, size_t n) +int mk_wcswidth(const unsigned int *pwcs, size_t n) { int w, width = 0; @@ -214,14 +222,14 @@ int mk_wcswidth(const wchar_t *pwcs, size_t n) /* * The following functions are the same as mk_wcwidth() and - * mk_wcwidth_cjk(), except that spacing characters in the East Asian + * mk_wcswidth(), except that spacing characters in the East Asian * Ambiguous (A) category as defined in Unicode Technical Report #11 * have a column width of 2. This variant might be useful for users of * CJK legacy encodings who want to migrate to UCS without changing * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ -int mk_wcwidth_cjk(wchar_t ucs) +int mk_wcwidth_cjk(unsigned int ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ @@ -289,7 +297,7 @@ int mk_wcwidth_cjk(wchar_t ucs) } -int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n) { int w, width = 0; diff --git a/putty/WILDCARD.C b/putty/WILDCARD.C index 75a7573..de5c3bc 100644 --- a/putty/WILDCARD.C +++ b/putty/WILDCARD.C @@ -326,7 +326,8 @@ int wc_unescape(char *output, const char *wildcard) wildcard++; } } - *output = '\0'; + if (output) + *output = '\0'; return 1; /* it's clean */ } diff --git a/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV b/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV index 215755a..f5b9647 100644 --- a/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV +++ b/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=35 +UnitCount=36 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pageant_private.rc @@ -33,7 +33,7 @@ CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] -FileName=..\..\..\misc.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -43,7 +43,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit2] -FileName=..\..\..\sshaes.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\windows\winpgnt.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winpgnt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,6 +213,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit20] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -222,7 +232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit20] +[Unit21] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -232,7 +242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit21] +[Unit22] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -242,7 +252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit22] +[Unit23] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -252,7 +262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit23] +[Unit24] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -262,7 +272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit24] +[Unit25] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -272,7 +282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit25] +[Unit26] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -282,7 +292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit26] +[Unit27] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -292,7 +302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit27] +[Unit28] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -302,7 +312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit28] +[Unit29] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -312,7 +322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit29] +[Unit30] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -322,7 +332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit30] +[Unit31] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -332,7 +342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit31] +[Unit32] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -342,7 +352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit32] +[Unit33] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -352,7 +362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit33] +[Unit34] FileName=..\..\..\windows\pageant.ico Folder=Resource Files Compile=0 @@ -362,7 +372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit35] FileName=..\..\..\windows\pageant.rc Folder=Resource Files Compile=1 @@ -372,7 +382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit36] FileName=..\..\..\windows\pageants.ico Folder=Resource Files Compile=0 diff --git a/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV b/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV index a7f5a5b..40d4018 100644 --- a/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV +++ b/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=76 +UnitCount=77 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=plink_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\ldisc.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\settings.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\timing.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winplink.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winplink.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,6 +563,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -762,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit75] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -772,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit75] +[Unit76] FileName=..\..\..\windows\plink.rc Folder=Resource Files Compile=1 @@ -782,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit76] +[Unit77] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 diff --git a/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV b/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV index 785f535..99e1327 100644 --- a/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV +++ b/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=73 +UnitCount=74 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pscp_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\int64.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\int64.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\pscp.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\settings.c +FileName=..\..\..\pscp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sftp.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\timing.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,6 +533,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\pscp.rc Folder=Resource Files Compile=1 diff --git a/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV b/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV index d9608f6..44e02d0 100644 --- a/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV +++ b/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=73 +UnitCount=74 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=psftp_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\int64.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\logging.c +FileName=..\..\..\int64.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\psftp.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\settings.c +FileName=..\..\..\psftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sftp.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\timing.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\wildcard.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,6 +533,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -672,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit66] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -682,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit67] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -692,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit68] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -702,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit69] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\windows\psftp.rc Folder=Resource Files Compile=1 diff --git a/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV b/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV index a8cad26..146044b 100644 --- a/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV +++ b/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=93 +UnitCount=94 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=putty_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\config.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\cproxy.c +FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\dialog.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\ldisc.c +FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\ldiscucs.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\logging.c +FileName=..\..\..\ldiscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\minibidi.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\misc.c +FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\pinger.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\proxy.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sercfg.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\settings.c +FileName=..\..\..\sercfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\ssh.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\terminal.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\timing.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\wildcard.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,6 +703,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit69] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -712,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit70] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -722,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit71] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -732,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit72] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -742,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit73] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -752,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit74] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -762,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit75] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -772,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit75] +[Unit76] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -782,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit76] +[Unit77] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -792,7 +802,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit77] +[Unit78] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -802,7 +812,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit78] +[Unit79] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -812,7 +822,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit79] +[Unit80] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -822,7 +832,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit80] +[Unit81] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -832,7 +842,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit81] +[Unit82] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -842,7 +852,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit82] +[Unit83] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -852,7 +862,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit83] +[Unit84] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -862,7 +872,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit84] +[Unit85] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -872,7 +882,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit85] +[Unit86] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -882,7 +892,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit86] +[Unit87] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -892,7 +902,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit87] +[Unit88] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -902,7 +912,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit88] +[Unit89] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -912,7 +922,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit89] +[Unit90] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -922,7 +932,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit90] +[Unit91] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -932,7 +942,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit91] +[Unit92] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -942,7 +952,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit92] +[Unit93] FileName=..\..\..\windows\putty.rc Folder=Resource Files Compile=1 @@ -952,7 +962,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit93] +[Unit94] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 diff --git a/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV b/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV index 7d1ec3c..39ce582 100644 --- a/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV +++ b/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=46 +UnitCount=47 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttygen_private.rc @@ -33,7 +33,7 @@ CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] -FileName=..\..\..\import.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -43,7 +43,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit2] -FileName=..\..\..\misc.c +FileName=..\..\..\import.c Folder=Source Files Compile=1 CompileCpp=0 @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\notiming.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\sshaes.c +FileName=..\..\..\notiming.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\sshbn.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshbn.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\sshdssg.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshprime.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\sshrsag.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsag.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\windows\winpgen.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winpgen.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,6 +313,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit30] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -322,7 +332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit30] +[Unit31] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -332,7 +342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit31] +[Unit32] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -342,7 +352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit32] +[Unit33] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -352,7 +362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit33] +[Unit34] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -362,7 +372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit35] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -372,7 +382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit36] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -382,7 +392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit36] +[Unit37] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -392,7 +402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit37] +[Unit38] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -402,7 +412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit38] +[Unit39] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -412,7 +422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit39] +[Unit40] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -422,7 +432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit40] +[Unit41] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -432,7 +442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit41] +[Unit42] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -442,7 +452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit42] +[Unit43] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -452,7 +462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit44] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -462,7 +472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit45] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -472,7 +482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit46] FileName=..\..\..\windows\puttygen.ico Folder=Resource Files Compile=0 @@ -482,7 +492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit47] FileName=..\..\..\windows\puttygen.rc Folder=Resource Files Compile=1 diff --git a/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV b/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV index 6da9f45..538cf8e 100644 --- a/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV +++ b/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=64 +UnitCount=65 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttytel_private.rc @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\config.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\dialog.c +FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\ldisc.c +FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\ldiscucs.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\logging.c +FileName=..\..\..\ldiscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\minibidi.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\misc.c +FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\nocproxy.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\nogss.c +FileName=..\..\..\nocproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\pinger.c +FileName=..\..\..\nogss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sercfg.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\settings.c +FileName=..\..\..\sercfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\telnet.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\terminal.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\timing.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\version.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,6 +443,16 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -452,7 +462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit44] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -462,7 +472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit45] FileName=..\..\..\int64.h Folder=Header Files Compile=1 @@ -472,7 +482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit46] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -482,7 +492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit47] FileName=..\..\..\macosx\osx.h Folder=Header Files Compile=1 @@ -492,7 +502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit47] +[Unit48] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -502,7 +512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit48] +[Unit49] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -512,7 +522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit49] +[Unit50] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -522,7 +532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit50] +[Unit51] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -532,7 +542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit51] +[Unit52] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -542,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit53] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -552,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit54] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -562,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit55] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -572,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit56] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -582,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit57] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -592,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit58] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -602,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit59] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -612,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit60] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -622,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit61] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -632,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit62] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -642,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit63] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -652,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit64] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 @@ -662,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit65] FileName=..\..\..\windows\puttytel.rc Folder=Resource Files Compile=1 diff --git a/putty/WINDOWS/MAKEFILE.BOR b/putty/WINDOWS/MAKEFILE.BOR index edb5ee5..2166f77 100644 --- a/putty/WINDOWS/MAKEFILE.BOR +++ b/putty/WINDOWS/MAKEFILE.BOR @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -130,29 +135,30 @@ RCFLAGS = $(RCFLAGS) $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj pageant.rsp +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj \ + pageant.rsp ilink32 -aa -Gn -L$(BCB)\lib @pageant.rsp -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \ - plink.rsp +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj plink.rsp ilink32 -ap -Gn -L$(BCB)\lib @plink.rsp -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -163,9 +169,9 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj pscp.rsp ilink32 -ap -Gn -L$(BCB)\lib @pscp.rsp -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -176,46 +182,46 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj psftp.rsp ilink32 -ap -Gn -L$(BCB)\lib @psftp.rsp -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \ putty.rsp ilink32 -aa -Gn -L$(BCB)\lib @putty.rsp -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj puttygen.rsp +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ + winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ + wintime.obj winutils.obj puttygen.rsp ilink32 -aa -Gn -L$(BCB)\lib @puttygen.rsp -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj puttytel.rsp ilink32 -aa -Gn -L$(BCB)\lib @puttytel.rsp pageant.rsp: $(MAKEFILE) echo c0w32 + > pageant.rsp - echo misc.obj sshaes.obj sshbn.obj sshdes.obj + >> pageant.rsp + echo conf.obj misc.obj sshaes.obj sshbn.obj sshdes.obj + >> pageant.rsp echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj + >> pageant.rsp echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj + >> pageant.rsp echo version.obj winhelp.obj winmisc.obj winpgnt.obj + >> pageant.rsp @@ -226,79 +232,79 @@ pageant.rsp: $(MAKEFILE) plink.rsp: $(MAKEFILE) echo c0x32 + > plink.rsp - echo be_all_s.obj cmdline.obj cproxy.obj ldisc.obj + >> plink.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> plink.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> plink.rsp - echo settings.obj ssh.obj sshaes.obj ssharcf.obj + >> plink.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> plink.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> plink.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> plink.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> plink.rsp - echo telnet.obj timing.obj tree234.obj version.obj + >> plink.rsp - echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> plink.rsp - echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> plink.rsp - echo winnojmp.obj winpgntc.obj winplink.obj + >> plink.rsp - echo winproxy.obj winser.obj winstore.obj wintime.obj + >> plink.rsp - echo winx11.obj x11fwd.obj >> plink.rsp + echo be_all_s.obj cmdline.obj conf.obj cproxy.obj + >> plink.rsp + echo ldisc.obj logging.obj misc.obj pgssapi.obj + >> plink.rsp + echo pinger.obj portfwd.obj proxy.obj raw.obj + >> plink.rsp + echo rlogin.obj settings.obj ssh.obj sshaes.obj + >> plink.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> plink.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> plink.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> plink.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> plink.rsp + echo sshzlib.obj telnet.obj timing.obj tree234.obj + >> plink.rsp + echo version.obj wildcard.obj wincons.obj windefs.obj + >> plink.rsp + echo wingss.obj winhandl.obj winmisc.obj winnet.obj + >> plink.rsp + echo winnoise.obj winnojmp.obj winpgntc.obj + >> plink.rsp + echo winplink.obj winproxy.obj winser.obj winstore.obj + >> plink.rsp + echo wintime.obj winx11.obj x11fwd.obj >> plink.rsp echo plink.exe >> plink.rsp echo nul,cw32 import32 ole32, >> plink.rsp echo plink.res >> plink.rsp pscp.rsp: $(MAKEFILE) echo c0x32 + > pscp.rsp - echo be_ssh.obj cmdline.obj cproxy.obj int64.obj + >> pscp.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> pscp.rsp - echo portfwd.obj proxy.obj pscp.obj settings.obj + >> pscp.rsp - echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> pscp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> pscp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> pscp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> pscp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> pscp.rsp - echo timing.obj tree234.obj version.obj wildcard.obj + >> pscp.rsp - echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> pscp.rsp - echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> pscp.rsp - echo winpgntc.obj winproxy.obj winsftp.obj + >> pscp.rsp - echo winstore.obj wintime.obj x11fwd.obj >> pscp.rsp + echo be_ssh.obj cmdline.obj conf.obj cproxy.obj + >> pscp.rsp + echo int64.obj logging.obj misc.obj pgssapi.obj + >> pscp.rsp + echo pinger.obj portfwd.obj proxy.obj pscp.obj + >> pscp.rsp + echo settings.obj sftp.obj ssh.obj sshaes.obj + >> pscp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> pscp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> pscp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> pscp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> pscp.rsp + echo sshzlib.obj timing.obj tree234.obj version.obj + >> pscp.rsp + echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> pscp.rsp + echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> pscp.rsp + echo winnojmp.obj winpgntc.obj winproxy.obj + >> pscp.rsp + echo winsftp.obj winstore.obj wintime.obj x11fwd.obj >> pscp.rsp echo pscp.exe >> pscp.rsp echo nul,cw32 import32 ole32, >> pscp.rsp echo pscp.res >> pscp.rsp psftp.rsp: $(MAKEFILE) echo c0x32 + > psftp.rsp - echo be_ssh.obj cmdline.obj cproxy.obj int64.obj + >> psftp.rsp - echo logging.obj misc.obj pgssapi.obj pinger.obj + >> psftp.rsp - echo portfwd.obj proxy.obj psftp.obj settings.obj + >> psftp.rsp - echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> psftp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> psftp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> psftp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> psftp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> psftp.rsp - echo timing.obj tree234.obj version.obj wildcard.obj + >> psftp.rsp - echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> psftp.rsp - echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> psftp.rsp - echo winpgntc.obj winproxy.obj winsftp.obj + >> psftp.rsp - echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp + echo be_ssh.obj cmdline.obj conf.obj cproxy.obj + >> psftp.rsp + echo int64.obj logging.obj misc.obj pgssapi.obj + >> psftp.rsp + echo pinger.obj portfwd.obj proxy.obj psftp.obj + >> psftp.rsp + echo settings.obj sftp.obj ssh.obj sshaes.obj + >> psftp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj + >> psftp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj + >> psftp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj + >> psftp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj + >> psftp.rsp + echo sshzlib.obj timing.obj tree234.obj version.obj + >> psftp.rsp + echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> psftp.rsp + echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> psftp.rsp + echo winnojmp.obj winpgntc.obj winproxy.obj + >> psftp.rsp + echo winsftp.obj winstore.obj wintime.obj x11fwd.obj >> psftp.rsp echo psftp.exe >> psftp.rsp echo nul,cw32 import32 ole32, >> psftp.rsp echo psftp.res >> psftp.rsp putty.rsp: $(MAKEFILE) echo c0w32 + > putty.rsp - echo be_all_s.obj cmdline.obj config.obj cproxy.obj + >> putty.rsp - echo dialog.obj ldisc.obj ldiscucs.obj logging.obj + >> putty.rsp - echo minibidi.obj misc.obj pgssapi.obj pinger.obj + >> putty.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> putty.rsp - echo sercfg.obj settings.obj sizetip.obj ssh.obj + >> putty.rsp - echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj + >> putty.rsp - echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj + >> putty.rsp - echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj + >> putty.rsp - echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj + >> putty.rsp - echo sshsha.obj sshzlib.obj telnet.obj terminal.obj + >> putty.rsp - echo timing.obj tree234.obj version.obj wcwidth.obj + >> putty.rsp - echo wildcard.obj wincfg.obj winctrls.obj windefs.obj + >> putty.rsp - echo windlg.obj window.obj wingss.obj winhandl.obj + >> putty.rsp - echo winhelp.obj winjump.obj winmisc.obj winnet.obj + >> putty.rsp - echo winnoise.obj winpgntc.obj winprint.obj + >> putty.rsp + echo be_all_s.obj cmdline.obj conf.obj config.obj + >> putty.rsp + echo cproxy.obj dialog.obj ldisc.obj ldiscucs.obj + >> putty.rsp + echo logging.obj minibidi.obj misc.obj pgssapi.obj + >> putty.rsp + echo pinger.obj portfwd.obj proxy.obj raw.obj + >> putty.rsp + echo rlogin.obj sercfg.obj settings.obj sizetip.obj + >> putty.rsp + echo ssh.obj sshaes.obj ssharcf.obj sshblowf.obj + >> putty.rsp + echo sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj + >> putty.rsp + echo sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj + >> putty.rsp + echo sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj + >> putty.rsp + echo sshsh512.obj sshsha.obj sshzlib.obj telnet.obj + >> putty.rsp + echo terminal.obj timing.obj tree234.obj version.obj + >> putty.rsp + echo wcwidth.obj wildcard.obj wincfg.obj winctrls.obj + >> putty.rsp + echo windefs.obj windlg.obj window.obj wingss.obj + >> putty.rsp + echo winhandl.obj winhelp.obj winjump.obj winmisc.obj + >> putty.rsp + echo winnet.obj winnoise.obj winpgntc.obj winprint.obj + >> putty.rsp echo winproxy.obj winser.obj winstore.obj wintime.obj + >> putty.rsp echo winucs.obj winutils.obj winx11.obj x11fwd.obj >> putty.rsp echo putty.exe >> putty.rsp @@ -307,30 +313,31 @@ putty.rsp: $(MAKEFILE) puttygen.rsp: $(MAKEFILE) echo c0w32 + > puttygen.rsp - echo import.obj misc.obj notiming.obj sshaes.obj + >> puttygen.rsp - echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj + >> puttygen.rsp - echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj + >> puttygen.rsp - echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj + >> puttygen.rsp - echo sshsha.obj tree234.obj version.obj winctrls.obj + >> puttygen.rsp - echo winhelp.obj winmisc.obj winnoise.obj winnojmp.obj + >> puttygen.rsp - echo winpgen.obj winstore.obj wintime.obj winutils.obj >> puttygen.rsp + echo conf.obj import.obj misc.obj notiming.obj + >> puttygen.rsp + echo sshaes.obj sshbn.obj sshdes.obj sshdss.obj + >> puttygen.rsp + echo sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj + >> puttygen.rsp + echo sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj + >> puttygen.rsp + echo sshsh512.obj sshsha.obj tree234.obj version.obj + >> puttygen.rsp + echo winctrls.obj winhelp.obj winmisc.obj winnoise.obj + >> puttygen.rsp + echo winnojmp.obj winpgen.obj winstore.obj wintime.obj + >> puttygen.rsp + echo winutils.obj >> puttygen.rsp echo puttygen.exe >> puttygen.rsp echo nul,cw32 import32 ole32, >> puttygen.rsp echo puttygen.res >> puttygen.rsp puttytel.rsp: $(MAKEFILE) echo c0w32 + > puttytel.rsp - echo be_nos_s.obj cmdline.obj config.obj dialog.obj + >> puttytel.rsp - echo ldisc.obj ldiscucs.obj logging.obj minibidi.obj + >> puttytel.rsp - echo misc.obj nocproxy.obj nogss.obj pinger.obj + >> puttytel.rsp - echo proxy.obj raw.obj rlogin.obj sercfg.obj + >> puttytel.rsp - echo settings.obj sizetip.obj telnet.obj terminal.obj + >> puttytel.rsp - echo timing.obj tree234.obj version.obj wcwidth.obj + >> puttytel.rsp - echo wincfg.obj winctrls.obj windefs.obj windlg.obj + >> puttytel.rsp - echo window.obj winhandl.obj winhelp.obj winjump.obj + >> puttytel.rsp - echo winmisc.obj winnet.obj winprint.obj winproxy.obj + >> puttytel.rsp - echo winser.obj winstore.obj wintime.obj winucs.obj + >> puttytel.rsp - echo winutils.obj >> puttytel.rsp + echo be_nos_s.obj cmdline.obj conf.obj config.obj + >> puttytel.rsp + echo dialog.obj ldisc.obj ldiscucs.obj logging.obj + >> puttytel.rsp + echo minibidi.obj misc.obj nocproxy.obj nogss.obj + >> puttytel.rsp + echo pinger.obj proxy.obj raw.obj rlogin.obj + >> puttytel.rsp + echo sercfg.obj settings.obj sizetip.obj telnet.obj + >> puttytel.rsp + echo terminal.obj timing.obj tree234.obj version.obj + >> puttytel.rsp + echo wcwidth.obj wincfg.obj winctrls.obj windefs.obj + >> puttytel.rsp + echo windlg.obj window.obj winhandl.obj winhelp.obj + >> puttytel.rsp + echo winjump.obj winmisc.obj winnet.obj winprint.obj + >> puttytel.rsp + echo winproxy.obj winser.obj winstore.obj wintime.obj + >> puttytel.rsp + echo winucs.obj winutils.obj >> puttytel.rsp echo puttytel.exe >> puttytel.rsp echo nul,cw32 import32 ole32, >> puttytel.rsp echo puttytel.res >> puttytel.rsp @@ -359,6 +366,10 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -577,7 +588,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\puttyps.h \ ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \ diff --git a/putty/WINDOWS/MAKEFILE.CYG b/putty/WINDOWS/MAKEFILE.CYG index 18caabc..162f7ff 100644 --- a/putty/WINDOWS/MAKEFILE.CYG +++ b/putty/WINDOWS/MAKEFILE.CYG @@ -1,4 +1,4 @@ -# Makefile for putty under cygwin. +# Makefile for putty under Cygwin, MinGW, or Winelib. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -115,11 +120,13 @@ RC = $(TOOLPATH)windres # RCINC = --include-dir c:\cygwin\include\ CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT \ - -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -I.././ \ - -I../charset/ -I../windows/ -I../unix/ -I../macosx/ + -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP \ + -DNO_SECUREZEROMEMORY -I.././ -I../charset/ -I../windows/ \ + -I../unix/ -I../macosx/ LDFLAGS = -mno-cygwin -s RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400 +CFLAGS += -DSECURITY_WIN32 # XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile. RCFLAGS += $(patsubst -D%,--define %,$(VER)) # _WIN32_IE is required to expose identifiers that only make sense on @@ -133,28 +140,29 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.o pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o \ +pageant.exe: conf.o misc.o pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o \ sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ tree234.o version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \ winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map misc.o \ + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map conf.o misc.o \ pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o sshmd5.o \ sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o tree234.o \ version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \ winutils.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 -lwinmm -lwinspool -plink.exe: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ - pinger.o plink.res.o portfwd.o proxy.o raw.o rlogin.o \ - settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o timing.o tree234.o version.o \ - wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ - winnet.o winnoise.o winnojmp.o winpgntc.o winplink.o \ - winproxy.o winser.o winstore.o wintime.o winx11.o x11fwd.o +plink.exe: be_all_s.o cmdline.o conf.o cproxy.o ldisc.o logging.o misc.o \ + pgssapi.o pinger.o plink.res.o portfwd.o proxy.o raw.o \ + rlogin.o settings.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o telnet.o timing.o tree234.o \ + version.o wildcard.o wincons.o windefs.o wingss.o winhandl.o \ + winmisc.o winnet.o winnoise.o winnojmp.o winpgntc.o \ + winplink.o winproxy.o winser.o winstore.o wintime.o winx11.o \ + x11fwd.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map be_all_s.o cmdline.o \ - cproxy.o ldisc.o logging.o misc.o pgssapi.o pinger.o \ + conf.o cproxy.o ldisc.o logging.o misc.o pgssapi.o pinger.o \ plink.res.o portfwd.o proxy.o raw.o rlogin.o settings.o \ ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ @@ -166,37 +174,37 @@ plink.exe: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \ -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ -luser32 -lwinmm -lwinspool -pscp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o pscp.o pscp.res.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \ - windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \ - winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \ - wintime.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map be_ssh.o cmdline.o cproxy.o \ - int64.o logging.o misc.o pgssapi.o pinger.o portfwd.o \ - proxy.o pscp.o pscp.res.o settings.o sftp.o ssh.o sshaes.o \ - ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \ - sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o timing.o \ - tree234.o version.o wildcard.o wincons.o windefs.o wingss.o \ - winhandl.o winmisc.o winnet.o winnoise.o winnojmp.o \ +pscp.exe: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o pscp.o pscp.res.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o timing.o tree234.o version.o \ + wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ + winnet.o winnoise.o winnojmp.o winpgntc.o winproxy.o \ + winsftp.o winstore.o wintime.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map be_ssh.o cmdline.o conf.o \ + cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \ + portfwd.o proxy.o pscp.o pscp.res.o settings.o sftp.o ssh.o \ + sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ + sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \ + sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \ + timing.o tree234.o version.o wildcard.o wincons.o windefs.o \ + wingss.o winhandl.o winmisc.o winnet.o winnoise.o winnojmp.o \ winpgntc.o winproxy.o winsftp.o winstore.o wintime.o \ x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 -lwinmm -lwinspool -psftp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ - pinger.o portfwd.o proxy.o psftp.o psftp.res.o settings.o \ - sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \ - sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \ - windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \ - winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \ - wintime.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map be_ssh.o cmdline.o \ +psftp.exe: be_ssh.o cmdline.o conf.o cproxy.o int64.o logging.o misc.o \ + pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftp.res.o \ + settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o timing.o tree234.o version.o \ + wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \ + winnet.o winnoise.o winnojmp.o winpgntc.o winproxy.o \ + winsftp.o winstore.o wintime.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map be_ssh.o cmdline.o conf.o \ cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \ portfwd.o proxy.o psftp.o psftp.res.o settings.o sftp.o \ ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \ @@ -208,7 +216,7 @@ psftp.exe: be_ssh.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \ wintime.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \ -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool -putty.exe: be_all_s.o cmdline.o config.o cproxy.o dialog.o ldisc.o \ +putty.exe: be_all_s.o cmdline.o conf.o config.o cproxy.o dialog.o ldisc.o \ ldiscucs.o logging.o minibidi.o misc.o pgssapi.o pinger.o \ portfwd.o proxy.o putty.res.o raw.o rlogin.o sercfg.o \ settings.o sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o \ @@ -221,52 +229,54 @@ putty.exe: be_all_s.o cmdline.o config.o cproxy.o dialog.o ldisc.o \ winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ winutils.o winx11.o x11fwd.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,putty.map be_all_s.o \ - cmdline.o config.o cproxy.o dialog.o ldisc.o ldiscucs.o \ - logging.o minibidi.o misc.o pgssapi.o pinger.o portfwd.o \ - proxy.o putty.res.o raw.o rlogin.o sercfg.o settings.o \ - sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \ - sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshzlib.o telnet.o terminal.o timing.o tree234.o \ - version.o wcwidth.o wildcard.o wincfg.o winctrls.o windefs.o \ - windlg.o window.o wingss.o winhandl.o winhelp.o winjump.o \ - winmisc.o winnet.o winnoise.o winpgntc.o winprint.o \ - winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o \ - winx11.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \ - -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool - -puttygen.exe: import.o misc.o notiming.o puttygen.res.o sshaes.o sshbn.o \ + cmdline.o conf.o config.o cproxy.o dialog.o ldisc.o \ + ldiscucs.o logging.o minibidi.o misc.o pgssapi.o pinger.o \ + portfwd.o proxy.o putty.res.o raw.o rlogin.o sercfg.o \ + settings.o sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o \ + sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o timing.o \ + tree234.o version.o wcwidth.o wildcard.o wincfg.o winctrls.o \ + windefs.o windlg.o window.o wingss.o winhandl.o winhelp.o \ + winjump.o winmisc.o winnet.o winnoise.o winpgntc.o \ + winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ + winutils.o winx11.o x11fwd.o -ladvapi32 -lcomctl32 \ + -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 \ + -lwinmm -lwinspool + +puttygen.exe: conf.o import.o misc.o notiming.o puttygen.res.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o tree234.o version.o winctrls.o winhelp.o winmisc.o \ + winnoise.o winnojmp.o winpgen.o winstore.o wintime.o \ + winutils.o + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map conf.o \ + import.o misc.o notiming.o puttygen.res.o sshaes.o sshbn.o \ sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \ sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ tree234.o version.o winctrls.o winhelp.o winmisc.o \ winnoise.o winnojmp.o winpgen.o winstore.o wintime.o \ - winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map import.o \ - misc.o notiming.o puttygen.res.o sshaes.o sshbn.o sshdes.o \ - sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o tree234.o \ - version.o winctrls.o winhelp.o winmisc.o winnoise.o \ - winnojmp.o winpgen.o winstore.o wintime.o winutils.o \ - -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 \ - -lshell32 -luser32 -lwinmm -lwinspool + winutils.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \ + -lole32 -lshell32 -luser32 -lwinmm -lwinspool -puttytel.exe: be_nos_s.o cmdline.o config.o dialog.o ldisc.o ldiscucs.o \ +puttytel.exe: be_nos_s.o cmdline.o conf.o config.o dialog.o ldisc.o \ + ldiscucs.o logging.o minibidi.o misc.o nocproxy.o nogss.o \ + pinger.o proxy.o puttytel.res.o raw.o rlogin.o sercfg.o \ + settings.o sizetip.o telnet.o terminal.o timing.o tree234.o \ + version.o wcwidth.o wincfg.o winctrls.o windefs.o windlg.o \ + window.o winhandl.o winhelp.o winjump.o winmisc.o winnet.o \ + winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \ + winutils.o + $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_nos_s.o \ + cmdline.o conf.o config.o dialog.o ldisc.o ldiscucs.o \ logging.o minibidi.o misc.o nocproxy.o nogss.o pinger.o \ proxy.o puttytel.res.o raw.o rlogin.o sercfg.o settings.o \ sizetip.o telnet.o terminal.o timing.o tree234.o version.o \ wcwidth.o wincfg.o winctrls.o windefs.o windlg.o window.o \ winhandl.o winhelp.o winjump.o winmisc.o winnet.o winprint.o \ - winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o - $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_nos_s.o \ - cmdline.o config.o dialog.o ldisc.o ldiscucs.o logging.o \ - minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o \ - puttytel.res.o raw.o rlogin.o sercfg.o settings.o sizetip.o \ - telnet.o terminal.o timing.o tree234.o version.o wcwidth.o \ - wincfg.o winctrls.o windefs.o windlg.o window.o winhandl.o \ - winhelp.o winjump.o winmisc.o winnet.o winprint.o winproxy.o \ - winser.o winstore.o wintime.o winucs.o winutils.o -ladvapi32 \ - -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ - -luser32 -lwinmm -lwinspool + winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o \ + -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 \ + -lshell32 -luser32 -lwinmm -lwinspool be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -304,6 +314,12 @@ cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c +conf.o: ../conf.c ../tree234.h ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c + config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -448,7 +464,7 @@ osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m pageant.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc pageant.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc -o pageant.res.o pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \ ../misc.h ../windows/winstuff.h ../macosx/osx.h \ @@ -463,7 +479,7 @@ pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c plink.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc plink.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc -o plink.res.o portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ ../misc.h ../puttymem.h ../tree234.h ../int64.h \ @@ -484,7 +500,7 @@ pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c pscp.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc pscp.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc -o pscp.res.o psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ @@ -493,16 +509,16 @@ psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c psftp.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc psftp.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc -o psftp.res.o putty.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc putty.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc -o putty.res.o puttygen.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc puttygen.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc -o puttygen.res.o puttytel.res.o: FORCE - $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc puttytel.res.o + $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc -o puttytel.res.o raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \ ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \ @@ -673,7 +689,7 @@ timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c -tree234.o: ../tree234.c ../puttymem.h ../tree234.h +tree234.o: ../tree234.c ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h @@ -1007,6 +1023,6 @@ version.o: FORCE $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c clean: - rm -f *.o *.exe *.res.o *.map + rm -f *.o *.exe *.res.o *.so *.map FORCE: diff --git a/putty/WINDOWS/MAKEFILE.LCC b/putty/WINDOWS/MAKEFILE.LCC index 2f30ede..5137b04 100644 --- a/putty/WINDOWS/MAKEFILE.LCC +++ b/putty/WINDOWS/MAKEFILE.LCC @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=-DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -119,29 +124,30 @@ RCFLAGS += $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj - lcclnk -subsystem windows -o pageant.exe misc.obj pageant.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshmd5.obj sshpubk.obj \ - sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ - version.obj winhelp.obj winmisc.obj winpgnt.obj winpgntc.obj \ - winutils.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ - winmm.lib imm32.lib +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj + lcclnk -subsystem windows -o pageant.exe conf.obj misc.obj pageant.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshmd5.obj \ + sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ + tree234.obj version.obj winhelp.obj winmisc.obj winpgnt.obj \ + winpgntc.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ + winspool.lib winmm.lib imm32.lib -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj - lcclnk -o plink.exe be_all_s.obj cmdline.obj cproxy.obj ldisc.obj \ +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj + lcclnk -o plink.exe be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ @@ -155,18 +161,7 @@ plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ winmm.lib imm32.lib -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \ - wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \ - winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ - winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ - wintime.obj x11fwd.obj - lcclnk -o pscp.exe be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ @@ -176,12 +171,23 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \ winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ - wintime.obj x11fwd.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + wintime.obj x11fwd.obj + lcclnk -o pscp.exe be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ + proxy.obj pscp.obj pscp.res settings.obj sftp.obj ssh.obj \ + sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \ + sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshgssc.obj \ + sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj sshzlib.obj timing.obj tree234.obj \ + version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ + winhandl.obj winmisc.obj winnet.obj winnoise.obj \ + winnojmp.obj winpgntc.obj winproxy.obj winsftp.obj \ + winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \ + ws2_32.lib winspool.lib winmm.lib imm32.lib -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -190,7 +196,7 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ winmisc.obj winnet.obj winnoise.obj winnojmp.obj \ winpgntc.obj winproxy.obj winsftp.obj winstore.obj \ wintime.obj x11fwd.obj - lcclnk -o psftp.exe be_ssh.obj cmdline.obj cproxy.obj int64.obj \ + lcclnk -o psftp.exe be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj \ logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ proxy.obj psftp.obj psftp.res settings.obj sftp.obj ssh.obj \ sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \ @@ -203,73 +209,73 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \ ws2_32.lib winspool.lib winmm.lib imm32.lib -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj - lcclnk -subsystem windows -o putty.exe be_all_s.obj cmdline.obj config.obj \ - cproxy.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \ - minibidi.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \ - proxy.obj putty.res raw.obj rlogin.obj sercfg.obj \ - settings.obj sizetip.obj ssh.obj sshaes.obj ssharcf.obj \ - sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj \ - sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj \ - sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ - sshzlib.obj telnet.obj terminal.obj timing.obj tree234.obj \ - version.obj wcwidth.obj wildcard.obj wincfg.obj winctrls.obj \ - windefs.obj windlg.obj window.obj wingss.obj winhandl.obj \ - winhelp.obj winjump.obj winmisc.obj winnet.obj winnoise.obj \ - winpgntc.obj winprint.obj winproxy.obj winser.obj \ - winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ - x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ - winmm.lib imm32.lib + lcclnk -subsystem windows -o putty.exe be_all_s.obj cmdline.obj conf.obj \ + config.obj cproxy.obj dialog.obj ldisc.obj ldiscucs.obj \ + logging.obj minibidi.obj misc.obj pgssapi.obj pinger.obj \ + portfwd.obj proxy.obj putty.res raw.obj rlogin.obj \ + sercfg.obj settings.obj sizetip.obj ssh.obj sshaes.obj \ + ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ + sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ + sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ + sshsha.obj sshzlib.obj telnet.obj terminal.obj timing.obj \ + tree234.obj version.obj wcwidth.obj wildcard.obj wincfg.obj \ + winctrls.obj windefs.obj windlg.obj window.obj wingss.obj \ + winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ + winnoise.obj winpgntc.obj winprint.obj winproxy.obj \ + winser.obj winstore.obj wintime.obj winucs.obj winutils.obj \ + winx11.obj x11fwd.obj shell32.lib wsock32.lib ws2_32.lib \ + winspool.lib winmm.lib imm32.lib -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj - lcclnk -subsystem windows -o puttygen.exe import.obj misc.obj notiming.obj \ - puttygen.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj \ - sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj \ - tree234.obj version.obj winctrls.obj winhelp.obj winmisc.obj \ +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ - wintime.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + wintime.obj winutils.obj + lcclnk -subsystem windows -o puttygen.exe conf.obj import.obj misc.obj \ + notiming.obj puttygen.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj \ + sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj \ + sshsha.obj tree234.obj version.obj winctrls.obj winhelp.obj \ + winmisc.obj winnoise.obj winnojmp.obj winpgen.obj \ + winstore.obj wintime.obj winutils.obj shell32.lib \ + wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj lcclnk -subsystem windows -o puttytel.exe be_nos_s.obj cmdline.obj \ - config.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \ - minibidi.obj misc.obj nocproxy.obj nogss.obj pinger.obj \ - proxy.obj puttytel.res raw.obj rlogin.obj sercfg.obj \ - settings.obj sizetip.obj telnet.obj terminal.obj timing.obj \ - tree234.obj version.obj wcwidth.obj wincfg.obj winctrls.obj \ - windefs.obj windlg.obj window.obj winhandl.obj winhelp.obj \ - winjump.obj winmisc.obj winnet.obj winprint.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winucs.obj winutils.obj \ - shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ - imm32.lib + conf.obj config.obj dialog.obj ldisc.obj ldiscucs.obj \ + logging.obj minibidi.obj misc.obj nocproxy.obj nogss.obj \ + pinger.obj proxy.obj puttytel.res raw.obj rlogin.obj \ + sercfg.obj settings.obj sizetip.obj telnet.obj terminal.obj \ + timing.obj tree234.obj version.obj wcwidth.obj wincfg.obj \ + winctrls.obj windefs.obj windlg.obj window.obj winhandl.obj \ + winhelp.obj winjump.obj winmisc.obj winnet.obj winprint.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winucs.obj \ + winutils.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ + winmm.lib imm32.lib be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ @@ -301,6 +307,11 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdline.c +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\conf.c config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -596,7 +607,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\timing.c toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\toucs.c -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\utf8.c diff --git a/putty/WINDOWS/MAKEFILE.VC b/putty/WINDOWS/MAKEFILE.VC index 59c3c43..0ad1f17 100644 --- a/putty/WINDOWS/MAKEFILE.VC +++ b/putty/WINDOWS/MAKEFILE.VC @@ -83,6 +83,11 @@ # Cygnus/mingw32, whose resource compiler may have less of a # problem with it. # +# - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) +# Disables PuTTY's use of SecureZeroMemory(), which is missing +# from some environments' header files. This is enabled by +# default in the Cygwin Makefile. +# # - XFLAGS=/DTELNET_DEFAULT # Causes PuTTY to default to the Telnet protocol (in the absence # of Default Settings and so on to the contrary). Normally PuTTY @@ -119,29 +124,30 @@ RCFLAGS = $(RCFLAGS) $(VER) all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ puttytel.exe -pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \ - sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \ - winpgnt.obj winpgntc.obj winutils.obj pageant.rsp +pageant.exe: conf.obj misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj \ + sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj \ + sshsh512.obj sshsha.obj tree234.obj version.obj winhelp.obj \ + winmisc.obj winpgnt.obj winpgntc.obj winutils.obj \ + pageant.rsp link $(LFLAGS) $(XLFLAGS) -out:pageant.exe -map:pageant.map @pageant.rsp -plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \ - misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \ - ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ - sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \ - version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \ - winhandl.obj winmisc.obj winnet.obj winnoise.obj \ - winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \ - winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \ - plink.rsp +plink.exe: be_all_s.obj cmdline.obj conf.obj cproxy.obj ldisc.obj \ + logging.obj misc.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \ + ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ + sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ + sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ + timing.obj tree234.obj version.obj wildcard.obj wincons.obj \ + windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \ + winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \ + winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \ + x11fwd.obj plink.rsp link $(LFLAGS) $(XLFLAGS) -out:plink.exe -map:plink.map @plink.rsp -pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ - pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +pscp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + pscp.obj pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -152,9 +158,9 @@ pscp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj pscp.rsp link $(LFLAGS) $(XLFLAGS) -out:pscp.exe -map:pscp.map @pscp.rsp -psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ - psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ +psftp.exe: be_ssh.obj cmdline.obj conf.obj cproxy.obj int64.obj logging.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \ ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \ sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \ sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ @@ -165,81 +171,81 @@ psftp.exe: be_ssh.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \ wintime.obj x11fwd.obj psftp.rsp link $(LFLAGS) $(XLFLAGS) -out:psftp.exe -map:psftp.map @psftp.rsp -putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \ - ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ - pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \ - raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ - ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ - window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \ - winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ +putty.exe: be_all_s.obj cmdline.obj conf.obj config.obj cproxy.obj \ + dialog.obj ldisc.obj ldiscucs.obj logging.obj minibidi.obj \ + misc.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + putty.res raw.obj rlogin.obj sercfg.obj settings.obj \ + sizetip.obj ssh.obj sshaes.obj ssharcf.obj sshblowf.obj \ + sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wildcard.obj wincfg.obj winctrls.obj windefs.obj \ + windlg.obj window.obj wingss.obj winhandl.obj winhelp.obj \ + winjump.obj winmisc.obj winnet.obj winnoise.obj winpgntc.obj \ winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \ putty.rsp link $(LFLAGS) $(XLFLAGS) -out:putty.exe -map:putty.map @putty.rsp -puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \ - sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \ - sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \ - winctrls.obj winhelp.obj winmisc.obj winnoise.obj \ - winnojmp.obj winpgen.obj winstore.obj wintime.obj \ - winutils.obj puttygen.rsp +puttygen.exe: conf.obj import.obj misc.obj notiming.obj puttygen.res \ + sshaes.obj sshbn.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \ + version.obj winctrls.obj winhelp.obj winmisc.obj \ + winnoise.obj winnojmp.obj winpgen.obj winstore.obj \ + wintime.obj winutils.obj puttygen.rsp link $(LFLAGS) $(XLFLAGS) -out:puttygen.exe -map:puttygen.map @puttygen.rsp -puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \ - ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \ - nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \ - rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \ - terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \ - winprint.obj winproxy.obj winser.obj winstore.obj \ +puttytel.exe: be_nos_s.obj cmdline.obj conf.obj config.obj dialog.obj \ + ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \ + nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ + raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \ + telnet.obj terminal.obj timing.obj tree234.obj version.obj \ + wcwidth.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj winhandl.obj winhelp.obj winjump.obj winmisc.obj \ + winnet.obj winprint.obj winproxy.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj puttytel.rsp link $(LFLAGS) $(XLFLAGS) -out:puttytel.exe -map:puttytel.map @puttytel.rsp pageant.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > pageant.rsp - echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> pageant.rsp - echo imm32.lib misc.obj ole32.lib pageant.res >> pageant.rsp - echo shell32.lib sshaes.obj sshbn.obj sshdes.obj >> pageant.rsp - echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj >> pageant.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj >> pageant.rsp - echo user32.lib version.obj winhelp.obj winmisc.obj >> pageant.rsp - echo winmm.lib winpgnt.obj winpgntc.obj winspool.lib >> pageant.rsp - echo winutils.obj >> pageant.rsp + echo advapi32.lib comctl32.lib comdlg32.lib conf.obj >> pageant.rsp + echo gdi32.lib imm32.lib misc.obj ole32.lib >> pageant.rsp + echo pageant.res shell32.lib sshaes.obj sshbn.obj >> pageant.rsp + echo sshdes.obj sshdss.obj sshmd5.obj sshpubk.obj >> pageant.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> pageant.rsp + echo tree234.obj user32.lib version.obj winhelp.obj >> pageant.rsp + echo winmisc.obj winmm.lib winpgnt.obj winpgntc.obj >> pageant.rsp + echo winspool.lib winutils.obj >> pageant.rsp plink.rsp: $(MAKEFILE) echo /nologo /subsystem:console > plink.rsp echo advapi32.lib be_all_s.obj cmdline.obj >> plink.rsp - echo comctl32.lib comdlg32.lib cproxy.obj gdi32.lib >> plink.rsp - echo imm32.lib ldisc.obj logging.obj misc.obj >> plink.rsp - echo ole32.lib pgssapi.obj pinger.obj plink.res >> plink.rsp - echo portfwd.obj proxy.obj raw.obj rlogin.obj >> plink.rsp - echo settings.obj shell32.lib ssh.obj sshaes.obj >> plink.rsp - echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj >> plink.rsp - echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj >> plink.rsp - echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj >> plink.rsp - echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> plink.rsp - echo sshzlib.obj telnet.obj timing.obj tree234.obj >> plink.rsp - echo user32.lib version.obj wildcard.obj wincons.obj >> plink.rsp - echo windefs.obj wingss.obj winhandl.obj winmisc.obj >> plink.rsp - echo winmm.lib winnet.obj winnoise.obj winnojmp.obj >> plink.rsp - echo winpgntc.obj winplink.obj winproxy.obj winser.obj >> plink.rsp - echo winspool.lib winstore.obj wintime.obj winx11.obj >> plink.rsp - echo x11fwd.obj >> plink.rsp + echo comctl32.lib comdlg32.lib conf.obj cproxy.obj >> plink.rsp + echo gdi32.lib imm32.lib ldisc.obj logging.obj >> plink.rsp + echo misc.obj ole32.lib pgssapi.obj pinger.obj >> plink.rsp + echo plink.res portfwd.obj proxy.obj raw.obj >> plink.rsp + echo rlogin.obj settings.obj shell32.lib ssh.obj >> plink.rsp + echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj >> plink.rsp + echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj >> plink.rsp + echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj >> plink.rsp + echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj >> plink.rsp + echo sshsha.obj sshzlib.obj telnet.obj timing.obj >> plink.rsp + echo tree234.obj user32.lib version.obj wildcard.obj >> plink.rsp + echo wincons.obj windefs.obj wingss.obj winhandl.obj >> plink.rsp + echo winmisc.obj winmm.lib winnet.obj winnoise.obj >> plink.rsp + echo winnojmp.obj winpgntc.obj winplink.obj >> plink.rsp + echo winproxy.obj winser.obj winspool.lib winstore.obj >> plink.rsp + echo wintime.obj winx11.obj x11fwd.obj >> plink.rsp pscp.rsp: $(MAKEFILE) echo /nologo /subsystem:console > pscp.rsp echo advapi32.lib be_ssh.obj cmdline.obj comctl32.lib >> pscp.rsp - echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> pscp.rsp - echo int64.obj logging.obj misc.obj ole32.lib >> pscp.rsp - echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> pscp.rsp - echo pscp.obj pscp.res settings.obj sftp.obj >> pscp.rsp + echo comdlg32.lib conf.obj cproxy.obj gdi32.lib >> pscp.rsp + echo imm32.lib int64.obj logging.obj misc.obj >> pscp.rsp + echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> pscp.rsp + echo proxy.obj pscp.obj pscp.res settings.obj sftp.obj >> pscp.rsp echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> pscp.rsp echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> pscp.rsp echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> pscp.rsp @@ -255,75 +261,76 @@ pscp.rsp: $(MAKEFILE) psftp.rsp: $(MAKEFILE) echo /nologo /subsystem:console > psftp.rsp echo advapi32.lib be_ssh.obj cmdline.obj comctl32.lib >> psftp.rsp - echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> psftp.rsp - echo int64.obj logging.obj misc.obj ole32.lib >> psftp.rsp - echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> psftp.rsp - echo psftp.obj psftp.res settings.obj sftp.obj >> psftp.rsp - echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> psftp.rsp - echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> psftp.rsp - echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> psftp.rsp - echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> psftp.rsp - echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> psftp.rsp - echo timing.obj tree234.obj user32.lib version.obj >> psftp.rsp - echo wildcard.obj wincons.obj windefs.obj wingss.obj >> psftp.rsp - echo winhandl.obj winmisc.obj winmm.lib winnet.obj >> psftp.rsp - echo winnoise.obj winnojmp.obj winpgntc.obj >> psftp.rsp + echo comdlg32.lib conf.obj cproxy.obj gdi32.lib >> psftp.rsp + echo imm32.lib int64.obj logging.obj misc.obj >> psftp.rsp + echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> psftp.rsp + echo proxy.obj psftp.obj psftp.res settings.obj >> psftp.rsp + echo sftp.obj shell32.lib ssh.obj sshaes.obj >> psftp.rsp + echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj >> psftp.rsp + echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj >> psftp.rsp + echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj >> psftp.rsp + echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> psftp.rsp + echo sshzlib.obj timing.obj tree234.obj user32.lib >> psftp.rsp + echo version.obj wildcard.obj wincons.obj windefs.obj >> psftp.rsp + echo wingss.obj winhandl.obj winmisc.obj winmm.lib >> psftp.rsp + echo winnet.obj winnoise.obj winnojmp.obj winpgntc.obj >> psftp.rsp echo winproxy.obj winsftp.obj winspool.lib >> psftp.rsp echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp putty.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > putty.rsp echo advapi32.lib be_all_s.obj cmdline.obj >> putty.rsp - echo comctl32.lib comdlg32.lib config.obj cproxy.obj >> putty.rsp - echo dialog.obj gdi32.lib imm32.lib ldisc.obj >> putty.rsp - echo ldiscucs.obj logging.obj minibidi.obj misc.obj >> putty.rsp - echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> putty.rsp - echo proxy.obj putty.res raw.obj rlogin.obj sercfg.obj >> putty.rsp - echo settings.obj shell32.lib sizetip.obj ssh.obj >> putty.rsp - echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj >> putty.rsp - echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj >> putty.rsp - echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj >> putty.rsp - echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj >> putty.rsp - echo sshsha.obj sshzlib.obj telnet.obj terminal.obj >> putty.rsp - echo timing.obj tree234.obj user32.lib version.obj >> putty.rsp - echo wcwidth.obj wildcard.obj wincfg.obj winctrls.obj >> putty.rsp - echo windefs.obj windlg.obj window.obj wingss.obj >> putty.rsp - echo winhandl.obj winhelp.obj winjump.obj winmisc.obj >> putty.rsp - echo winmm.lib winnet.obj winnoise.obj winpgntc.obj >> putty.rsp - echo winprint.obj winproxy.obj winser.obj winspool.lib >> putty.rsp - echo winstore.obj wintime.obj winucs.obj winutils.obj >> putty.rsp - echo winx11.obj x11fwd.obj >> putty.rsp + echo comctl32.lib comdlg32.lib conf.obj config.obj >> putty.rsp + echo cproxy.obj dialog.obj gdi32.lib imm32.lib >> putty.rsp + echo ldisc.obj ldiscucs.obj logging.obj minibidi.obj >> putty.rsp + echo misc.obj ole32.lib pgssapi.obj pinger.obj >> putty.rsp + echo portfwd.obj proxy.obj putty.res raw.obj >> putty.rsp + echo rlogin.obj sercfg.obj settings.obj shell32.lib >> putty.rsp + echo sizetip.obj ssh.obj sshaes.obj ssharcf.obj >> putty.rsp + echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> putty.rsp + echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> putty.rsp + echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> putty.rsp + echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> putty.rsp + echo telnet.obj terminal.obj timing.obj tree234.obj >> putty.rsp + echo user32.lib version.obj wcwidth.obj wildcard.obj >> putty.rsp + echo wincfg.obj winctrls.obj windefs.obj windlg.obj >> putty.rsp + echo window.obj wingss.obj winhandl.obj winhelp.obj >> putty.rsp + echo winjump.obj winmisc.obj winmm.lib winnet.obj >> putty.rsp + echo winnoise.obj winpgntc.obj winprint.obj >> putty.rsp + echo winproxy.obj winser.obj winspool.lib winstore.obj >> putty.rsp + echo wintime.obj winucs.obj winutils.obj winx11.obj >> putty.rsp + echo x11fwd.obj >> putty.rsp puttygen.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > puttygen.rsp - echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> puttygen.rsp - echo imm32.lib import.obj misc.obj notiming.obj >> puttygen.rsp - echo ole32.lib puttygen.res shell32.lib sshaes.obj >> puttygen.rsp - echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj >> puttygen.rsp - echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj >> puttygen.rsp - echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj >> puttygen.rsp - echo sshsha.obj tree234.obj user32.lib version.obj >> puttygen.rsp - echo winctrls.obj winhelp.obj winmisc.obj winmm.lib >> puttygen.rsp - echo winnoise.obj winnojmp.obj winpgen.obj >> puttygen.rsp + echo advapi32.lib comctl32.lib comdlg32.lib conf.obj >> puttygen.rsp + echo gdi32.lib imm32.lib import.obj misc.obj >> puttygen.rsp + echo notiming.obj ole32.lib puttygen.res shell32.lib >> puttygen.rsp + echo sshaes.obj sshbn.obj sshdes.obj sshdss.obj >> puttygen.rsp + echo sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj >> puttygen.rsp + echo sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj >> puttygen.rsp + echo sshsh512.obj sshsha.obj tree234.obj user32.lib >> puttygen.rsp + echo version.obj winctrls.obj winhelp.obj winmisc.obj >> puttygen.rsp + echo winmm.lib winnoise.obj winnojmp.obj winpgen.obj >> puttygen.rsp echo winspool.lib winstore.obj wintime.obj >> puttygen.rsp echo winutils.obj >> puttygen.rsp puttytel.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > puttytel.rsp echo advapi32.lib be_nos_s.obj cmdline.obj >> puttytel.rsp - echo comctl32.lib comdlg32.lib config.obj dialog.obj >> puttytel.rsp - echo gdi32.lib imm32.lib ldisc.obj ldiscucs.obj >> puttytel.rsp - echo logging.obj minibidi.obj misc.obj nocproxy.obj >> puttytel.rsp - echo nogss.obj ole32.lib pinger.obj proxy.obj >> puttytel.rsp - echo puttytel.res raw.obj rlogin.obj sercfg.obj >> puttytel.rsp - echo settings.obj shell32.lib sizetip.obj telnet.obj >> puttytel.rsp - echo terminal.obj timing.obj tree234.obj user32.lib >> puttytel.rsp - echo version.obj wcwidth.obj wincfg.obj winctrls.obj >> puttytel.rsp - echo windefs.obj windlg.obj window.obj winhandl.obj >> puttytel.rsp - echo winhelp.obj winjump.obj winmisc.obj winmm.lib >> puttytel.rsp - echo winnet.obj winprint.obj winproxy.obj winser.obj >> puttytel.rsp - echo winspool.lib winstore.obj wintime.obj winucs.obj >> puttytel.rsp - echo winutils.obj >> puttytel.rsp + echo comctl32.lib comdlg32.lib conf.obj config.obj >> puttytel.rsp + echo dialog.obj gdi32.lib imm32.lib ldisc.obj >> puttytel.rsp + echo ldiscucs.obj logging.obj minibidi.obj misc.obj >> puttytel.rsp + echo nocproxy.obj nogss.obj ole32.lib pinger.obj >> puttytel.rsp + echo proxy.obj puttytel.res raw.obj rlogin.obj >> puttytel.rsp + echo sercfg.obj settings.obj shell32.lib sizetip.obj >> puttytel.rsp + echo telnet.obj terminal.obj timing.obj tree234.obj >> puttytel.rsp + echo user32.lib version.obj wcwidth.obj wincfg.obj >> puttytel.rsp + echo winctrls.obj windefs.obj windlg.obj window.obj >> puttytel.rsp + echo winhandl.obj winhelp.obj winjump.obj winmisc.obj >> puttytel.rsp + echo winmm.lib winnet.obj winprint.obj winproxy.obj >> puttytel.rsp + echo winser.obj winspool.lib winstore.obj wintime.obj >> puttytel.rsp + echo winucs.obj winutils.obj >> puttytel.rsp be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \ @@ -361,6 +368,12 @@ cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \ ..\charset\charset.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\cmdline.c +conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h + cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\conf.c + config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \ ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -746,7 +759,7 @@ timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \ toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\toucs.c -tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h +tree234.obj: ..\tree234.c ..\tree234.h ..\puttymem.h cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h diff --git a/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP b/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP index 935cedd..09ec866 100644 --- a/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP +++ b/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP @@ -94,6 +94,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\misc.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PLINK/PLINK.DSP b/putty/WINDOWS/MSVC/PLINK/PLINK.DSP index fc3d6eb..0cb6c6f 100644 --- a/putty/WINDOWS/MSVC/PLINK/PLINK.DSP +++ b/putty/WINDOWS/MSVC/PLINK/PLINK.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PSCP/PSCP.DSP b/putty/WINDOWS/MSVC/PSCP/PSCP.DSP index ae52b8c..26169e0 100644 --- a/putty/WINDOWS/MSVC/PSCP/PSCP.DSP +++ b/putty/WINDOWS/MSVC/PSCP/PSCP.DSP @@ -111,6 +111,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP b/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP index 65d5bb7..7385f35 100644 --- a/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP +++ b/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP @@ -111,6 +111,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP b/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP index 69a2480..a9b80ac 100644 --- a/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP +++ b/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\config.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP b/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP index 6ee999c..3dbf1fb 100644 --- a/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP +++ b/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP @@ -94,6 +94,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\import.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP b/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP index cd74ae6..3aa53d8 100644 --- a/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP +++ b/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP @@ -102,6 +102,10 @@ SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File +SOURCE=..\..\..\conf.c +# End Source File +# Begin Source File + SOURCE=..\..\..\config.c # End Source File # Begin Source File diff --git a/putty/WINDOWS/PAGEANT.RC b/putty/WINDOWS/PAGEANT.RC index f6422f3..5ff0cf2 100644 --- a/putty/WINDOWS/PAGEANT.RC +++ b/putty/WINDOWS/PAGEANT.RC @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/putty/WINDOWS/PUTTY.ISS b/putty/WINDOWS/PUTTY.ISS index 3299c1f..6aada2a 100644 --- a/putty/WINDOWS/PUTTY.ISS +++ b/putty/WINDOWS/PUTTY.ISS @@ -1,5 +1,5 @@ ; -*- no -*- -; $Id: putty.iss 9366 2011-12-10 12:08:09Z simon $ +; $Id: putty.iss 9998 2013-08-06 17:09:07Z simon $ ; ; -- Inno Setup installer script for PuTTY and its related tools. ; Last tested with Inno Setup 5.0.8. @@ -14,10 +14,10 @@ [Setup] AppName=PuTTY -AppVerName=PuTTY version 0.62 -VersionInfoTextVersion=Release 0.62 -AppVersion=0.62 -VersionInfoVersion=0.62.0.0 +AppVerName=PuTTY version 0.63 +VersionInfoTextVersion=Release 0.63 +AppVersion=0.63 +VersionInfoVersion=0.63.0.0 AppPublisher=Simon Tatham AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/ AppReadmeFile={app}\README.txt diff --git a/putty/WINDOWS/PUTTYGEN.RC b/putty/WINDOWS/PUTTYGEN.RC index 1cea261..53ec369 100644 --- a/putty/WINDOWS/PUTTYGEN.RC +++ b/putty/WINDOWS/PUTTYGEN.RC @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/putty/WINDOWS/VERSION.RC2 b/putty/WINDOWS/VERSION.RC2 index 1c76927..96e5e84 100644 --- a/putty/WINDOWS/VERSION.RC2 +++ b/putty/WINDOWS/VERSION.RC2 @@ -39,7 +39,7 @@ /* 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,62 +#define BASE_VERSION 0,63 #if defined SNAPSHOT @@ -120,7 +120,7 @@ BEGIN VALUE "OriginalFilename", APPNAME VALUE "FileVersion", VERSION_TEXT VALUE "ProductVersion", VERSION_TEXT - VALUE "LegalCopyright", "Copyright \251 1997-2011 Simon Tatham." + VALUE "LegalCopyright", "Copyright \251 1997-2013 Simon Tatham." #if (!defined SNAPSHOT) && (!defined RELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", VERSION_TEXT /* NBI */ diff --git a/putty/WINDOWS/WINCFG.C b/putty/WINDOWS/WINCFG.C index 60694fc..3a9d98d 100644 --- a/putty/WINDOWS/WINCFG.C +++ b/putty/WINDOWS/WINCFG.C @@ -70,8 +70,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Control the scrollback in the window"); ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', HELPCTX(window_scrollback), - dlg_stdcheckbox_handler, - I(offsetof(Config,scrollbar_in_fullscreen))); + conf_checkbox_handler, + I(CONF_scrollbar_in_fullscreen)); /* * Really this wants to go just after `Display scrollbar'. See * if we can find that control, and do some shuffling. @@ -81,7 +81,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_CHECKBOX && - c->generic.context.i == offsetof(Config,scrollbar)) { + c->generic.context.i == CONF_scrollbar) { /* * Control i is the scrollbar checkbox. * Control s->ncontrols-1 is the scrollbar-in-FS one. @@ -105,10 +105,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Enable extra keyboard features:"); ctrl_checkbox(s, "AltGr acts as Compose key", 't', HELPCTX(keyboard_compose), - dlg_stdcheckbox_handler, I(offsetof(Config,compose_key))); + conf_checkbox_handler, I(CONF_compose_key)); ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd', HELPCTX(keyboard_ctrlalt), - dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys))); + conf_checkbox_handler, I(CONF_ctrlaltkeys)); /* * Windows allows an arbitrary .WAV to be played as a bell, and @@ -133,8 +133,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, beep)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_beep) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 2; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -159,7 +159,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, FILTER_WAVE_FILES, FALSE, "Select bell sound file", HELPCTX(bell_style), - dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile))); + conf_filesel_handler, I(CONF_bell_wavefile)); /* * While we've got this box open, taskbar flashing on a bell is @@ -167,8 +167,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, */ ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3, HELPCTX(bell_taskbar), - dlg_stdradiobutton_handler, - I(offsetof(Config, beep_ind)), + conf_radiobutton_handler, + I(CONF_beep_ind), "Disabled", I(B_IND_DISABLED), "Flashing", I(B_IND_FLASH), "Steady", I(B_IND_STEADY), NULL); @@ -180,7 +180,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Adjust the window border"); ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's', HELPCTX(appearance_border), - dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge))); + conf_checkbox_handler, I(CONF_sunken_edge)); /* * Configurable font quality settings for Windows. @@ -191,8 +191,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, HELPCTX(appearance_font), variable_pitch_handler, I(0)); ctrl_radiobuttons(s, "Font quality:", 'q', 2, HELPCTX(appearance_font), - dlg_stdradiobutton_handler, - I(offsetof(Config, font_quality)), + conf_radiobutton_handler, + I(CONF_font_quality), "Antialiased", I(FQ_ANTIALIASED), "Non-Antialiased", I(FQ_NONANTIALIASED), "ClearType", I(FQ_CLEARTYPE), @@ -206,8 +206,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's', HELPCTX(translation_cyrillic), - dlg_stdcheckbox_handler, - I(offsetof(Config,xlat_capslockcyr))); + conf_checkbox_handler, + I(CONF_xlat_capslockcyr)); /* * On Windows we can use but not enumerate translation tables @@ -232,8 +232,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, vtmode)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_vtmode) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons += 3; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -272,7 +272,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Formatting of pasted characters"); ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f', HELPCTX(selection_rtf), - dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste))); + conf_checkbox_handler, I(CONF_rtf_paste)); /* * Windows often has no middle button, so we supply a selection @@ -283,8 +283,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "Control use of mouse"); ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1, HELPCTX(selection_buttons), - dlg_stdradiobutton_handler, - I(offsetof(Config, mouse_is_xterm)), + conf_radiobutton_handler, + I(CONF_mouse_is_xterm), "Windows (Middle extends, Right brings up menu)", I(2), "Compromise (Middle extends, Right pastes)", I(0), "xterm (Right extends, Middle pastes)", I(1), NULL); @@ -304,10 +304,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, "General options for colour usage"); ctrl_checkbox(s, "Attempt to use logical palettes", 'l', HELPCTX(colours_logpal), - dlg_stdcheckbox_handler, I(offsetof(Config,try_palette))); + conf_checkbox_handler, I(CONF_try_palette)); ctrl_checkbox(s, "Use system colours", 's', HELPCTX(colours_system), - dlg_stdcheckbox_handler, I(offsetof(Config,system_colour))); + conf_checkbox_handler, I(CONF_system_colour)); /* @@ -316,8 +316,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window", "size", "Set the size of the window"); ctrl_radiobuttons(s, "When window is resized:", 'z', 1, HELPCTX(window_resize), - dlg_stdradiobutton_handler, - I(offsetof(Config, resize_action)), + conf_radiobutton_handler, + I(CONF_resize_action), "Change the number of rows and columns", I(RESIZE_TERM), "Change the size of the font", I(RESIZE_FONT), "Change font size only when maximised", I(RESIZE_EITHER), @@ -331,20 +331,20 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, s = ctrl_getset(b, "Window/Behaviour", "main", NULL); ctrl_checkbox(s, "Window closes on ALT-F4", '4', HELPCTX(behaviour_altf4), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4))); + conf_checkbox_handler, I(CONF_alt_f4)); ctrl_checkbox(s, "System menu appears on ALT-Space", 'y', HELPCTX(behaviour_altspace), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_space))); + conf_checkbox_handler, I(CONF_alt_space)); ctrl_checkbox(s, "System menu appears on ALT alone", 'l', HELPCTX(behaviour_altonly), - dlg_stdcheckbox_handler, I(offsetof(Config,alt_only))); + conf_checkbox_handler, I(CONF_alt_only)); ctrl_checkbox(s, "Ensure window is always on top", 'e', HELPCTX(behaviour_alwaysontop), - dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop))); + conf_checkbox_handler, I(CONF_alwaysontop)); ctrl_checkbox(s, "Full screen on Alt-Enter", 'f', HELPCTX(behaviour_altenter), - dlg_stdcheckbox_handler, - I(offsetof(Config,fullscreenonaltenter))); + conf_checkbox_handler, + I(CONF_fullscreenonaltenter)); /* * Windows supports a local-command proxy. This also means we @@ -356,8 +356,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_RADIO && - c->generic.context.i == offsetof(Config, proxy_type)) { - assert(c->generic.handler == dlg_stdradiobutton_handler); + c->generic.context.i == CONF_proxy_type) { + assert(c->generic.handler == conf_radiobutton_handler); c->radio.nbuttons++; c->radio.buttons = sresize(c->radio.buttons, c->radio.nbuttons, char *); @@ -373,9 +373,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, for (i = 0; i < s->ncontrols; i++) { c = s->ctrls[i]; if (c->generic.type == CTRL_EDITBOX && - c->generic.context.i == - offsetof(Config, proxy_telnet_command)) { - assert(c->generic.handler == dlg_stdeditbox_handler); + c->generic.context.i == CONF_proxy_telnet_command) { + assert(c->generic.handler == conf_editbox_handler); sfree(c->generic.label); c->generic.label = dupstr("Telnet command, or local" " proxy command"); @@ -399,6 +398,6 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, ctrl_filesel(s, "X authority file for local display", 't', NULL, FALSE, "Select X authority file", HELPCTX(ssh_tunnels_xauthority), - dlg_stdfilesel_handler, I(offsetof(Config, xauthfile))); + conf_filesel_handler, I(CONF_xauthfile)); } } diff --git a/putty/WINDOWS/WINCONS.C b/putty/WINDOWS/WINCONS.C index 4f984d9..f1c8cbc 100644 --- a/putty/WINDOWS/WINCONS.C +++ b/putty/WINDOWS/WINCONS.C @@ -41,7 +41,7 @@ void notify_remote_exit(void *frontend) { } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { } @@ -201,7 +201,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; @@ -223,11 +223,11 @@ int askappend(void *frontend, Filename filename, char line[32]; if (console_batch_mode) { - fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); fflush(stderr); return 0; } - fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path); + fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); fflush(stderr); hin = GetStdHandle(STD_INPUT_HANDLE); @@ -315,7 +315,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } /* @@ -365,9 +365,9 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { - DWORD savemode, newmode, i = 0; + DWORD savemode, newmode; + int len; prompt_t *pr = p->prompts[curr_prompt]; - BOOL r; GetConsoleMode(hin, &savemode); newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; @@ -379,25 +379,44 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) console_data_untrusted(hout, pr->prompt, strlen(pr->prompt)); - r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL); + len = 0; + while (1) { + DWORD ret = 0; + BOOL r; + + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + + r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1, + &ret, NULL); + + if (!r || ret == 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + if (pr->result[len - 1] == '\r') + len--; + break; + } + } SetConsoleMode(hin, savemode); - if ((int) i > pr->result_len) - i = pr->result_len - 1; - else - i = i - 2; - pr->result[i] = '\0'; - if (!pr->echo) { DWORD dummy; WriteFile(hout, "\r\n", 2, &dummy, NULL); } + if (len < 0) { + return 0; /* failure due to read error */ + } + + pr->result[len] = '\0'; } return 1; /* success */ - } void frontend_keypress(void *handle) diff --git a/putty/WINDOWS/WINCTRLS.C b/putty/WINDOWS/WINCTRLS.C index 159c925..8673ef9 100644 --- a/putty/WINDOWS/WINCTRLS.C +++ b/putty/WINDOWS/WINCTRLS.C @@ -448,6 +448,8 @@ char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines) if (lines) *lines = nlines; + sfree(pwidths); + return ret; } @@ -1639,8 +1641,8 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, shortcuts[nshortcuts++] = ctrl->fontselect.shortcut; statictext(&pos, escaped, 1, base_id); staticbtn(&pos, "", base_id+1, "Change...", base_id+2); + data = fontspec_new("", 0, 0, 0); sfree(escaped); - data = snew(FontSpec); break; default: assert(!"Can't happen"); @@ -1665,7 +1667,9 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, winctrl_add_shortcuts(dp, c); if (actual_base_id == base_id) base_id += num_ids; - } + } else { + sfree(data); + } if (colstart >= 0) { /* @@ -1934,21 +1938,21 @@ int winctrl_handle_command(struct dlgparam *dp, UINT msg, CHOOSEFONT cf; LOGFONT lf; HDC hdc; - FontSpec fs = *(FontSpec *)c->data; - + FontSpec *fs = (FontSpec *)c->data; + hdc = GetDC(0); - lf.lfHeight = -MulDiv(fs.height, + lf.lfHeight = -MulDiv(fs->height, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(0, hdc); lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0; lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0; - lf.lfWeight = (fs.isbold ? FW_BOLD : 0); - lf.lfCharSet = fs.charset; + lf.lfWeight = (fs->isbold ? FW_BOLD : 0); + lf.lfCharSet = fs->charset; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; - strncpy(lf.lfFaceName, fs.name, + strncpy(lf.lfFaceName, fs->name, sizeof(lf.lfFaceName) - 1); lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0'; @@ -1959,13 +1963,11 @@ int winctrl_handle_command(struct dlgparam *dp, UINT msg, CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; if (ChooseFont(&cf)) { - strncpy(fs.name, lf.lfFaceName, - sizeof(fs.name) - 1); - fs.name[sizeof(fs.name) - 1] = '\0'; - fs.isbold = (lf.lfWeight == FW_BOLD); - fs.charset = lf.lfCharSet; - fs.height = cf.iPointSize / 10; + fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD), + cf.iPointSize / 10, lf.lfCharSet); dlg_fontsel_set(ctrl, dp, fs); + fontspec_free(fs); + ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } } @@ -2100,13 +2102,12 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) SetDlgItemText(dp->hwnd, c->base_id+1, text); } -void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) +char *dlg_editbox_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_EDITBOX); - GetDlgItemText(dp->hwnd, c->base_id+1, buffer, length); - buffer[length-1] = '\0'; + return GetDlgItemText_alloc(dp->hwnd, c->base_id+1); } /* The `listbox' functions can also apply to combo boxes. */ @@ -2286,51 +2287,56 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text) } } -void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - SetDlgItemText(dp->hwnd, c->base_id+1, fn.path); + SetDlgItemText(dp->hwnd, c->base_id+1, fn->path); } -void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn) +Filename *dlg_filesel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); + char *tmp; + Filename *ret; assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - GetDlgItemText(dp->hwnd, c->base_id+1, fn->path, lenof(fn->path)); - fn->path[lenof(fn->path)-1] = '\0'; + tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); + ret = filename_from_str(tmp); + sfree(tmp); + return ret; } -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs) +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs) { char *buf, *boldstr; struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - *(FontSpec *)c->data = fs; /* structure copy */ + fontspec_free((FontSpec *)c->data); + c->data = fontspec_copy(fs); - boldstr = (fs.isbold ? "bold, " : ""); - if (fs.height == 0) - buf = dupprintf("Font: %s, %sdefault height", fs.name, boldstr); + boldstr = (fs->isbold ? "bold, " : ""); + if (fs->height == 0) + buf = dupprintf("Font: %s, %sdefault height", fs->name, boldstr); else - buf = dupprintf("Font: %s, %s%d-%s", fs.name, boldstr, - (fs.height < 0 ? -fs.height : fs.height), - (fs.height < 0 ? "pixel" : "point")); + buf = dupprintf("Font: %s, %s%d-%s", fs->name, boldstr, + (fs->height < 0 ? -fs->height : fs->height), + (fs->height < 0 ? "pixel" : "point")); SetDlgItemText(dp->hwnd, c->base_id+1, buf); sfree(buf); dlg_auto_set_fixed_pitch_flag(dp); } -void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs) +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - *fs = *(FontSpec *)c->data; /* structure copy */ + return fontspec_copy((FontSpec *)c->data); } /* @@ -2364,6 +2370,8 @@ void dlg_set_focus(union control *ctrl, void *dlg) struct winctrl *c = dlg_findbyctrl(dp, ctrl); int id; HWND ctl; + if (!c) + return; switch (ctrl->generic.type) { case CTRL_EDITBOX: id = c->base_id + 1; break; case CTRL_RADIO: @@ -2471,8 +2479,10 @@ int dlg_coloursel_results(union control *ctrl, void *dlg, void dlg_auto_set_fixed_pitch_flag(void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; - Config *cfg = (Config *)dp->data; - HFONT font; + Conf *conf = (Conf *)dp->data; + FontSpec *fs; + int quality; + HFONT hfont; HDC hdc; TEXTMETRIC tm; int is_var; @@ -2483,16 +2493,19 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg) * dialog box as false. * * We assume here that any client of the dlg_* mechanism which is - * using font selectors at all is also using a normal 'Config *' + * using font selectors at all is also using a normal 'Conf *' * as dp->data. */ - font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg->font_quality), - FIXED_PITCH | FF_DONTCARE, cfg->font.name); + quality = conf_get_int(conf, CONF_font_quality); + fs = conf_get_fontspec(conf, CONF_font); + + hfont = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), + FIXED_PITCH | FF_DONTCARE, fs->name); hdc = GetDC(NULL); - if (font && hdc && SelectObject(hdc, font) && GetTextMetrics(hdc, &tm)) { + if (hdc && SelectObject(hdc, hfont) && GetTextMetrics(hdc, &tm)) { /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH); } else { @@ -2500,8 +2513,8 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg) } if (hdc) ReleaseDC(NULL, hdc); - if (font) - DeleteObject(font); + if (hfont) + DeleteObject(hfont); if (is_var) dp->fixed_pitch_fonts = FALSE; diff --git a/putty/WINDOWS/WINDEFS.C b/putty/WINDOWS/WINDEFS.C index de01daf..e2f04ac 100644 --- a/putty/WINDOWS/WINDEFS.C +++ b/putty/WINDOWS/WINDEFS.C @@ -6,28 +6,20 @@ #include -FontSpec platform_default_fontspec(const char *name) +FontSpec *platform_default_fontspec(const char *name) { - FontSpec ret; - if (!strcmp(name, "Font")) { - strcpy(ret.name, "Courier New"); - ret.isbold = 0; - ret.charset = ANSI_CHARSET; - ret.height = 10; - } else { - ret.name[0] = '\0'; - } - return ret; + if (!strcmp(name, "Font")) + return fontspec_new("Courier New", 0, 10, ANSI_CHARSET); + else + return fontspec_new("", 0, 0, 0); } -Filename platform_default_filename(const char *name) +Filename *platform_default_filename(const char *name) { - Filename ret; if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); + return filename_from_str("putty.log"); else - *ret.path = '\0'; - return ret; + return filename_from_str(""); } char *platform_default_s(const char *name) diff --git a/putty/WINDOWS/WINDLG.C b/putty/WINDOWS/WINDLG.C index debc510..d2b2a5f 100644 --- a/putty/WINDOWS/WINDLG.C +++ b/putty/WINDOWS/WINDLG.C @@ -44,7 +44,7 @@ static struct dlgparam dp; static char **events = NULL; static int nevents = 0, negsize = 0; -extern Config cfg; /* defined in window.c */ +extern Conf *conf; /* defined in window.c */ #define PRINTER_DISABLED_STRING "None (printing disabled)" @@ -648,7 +648,7 @@ int do_config(void) dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Configuration", appname); dp.errtitle = dupprintf("%s Error", appname); - dp.data = &cfg; + dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ @@ -666,15 +666,15 @@ int do_config(void) int do_reconfig(HWND hwnd, int protcfginfo) { - Config backup_cfg; - int ret; + Conf *backup_conf; + int ret, protocol; - backup_cfg = cfg; /* structure copy */ + backup_conf = conf_copy(conf); ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo); - win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, - cfg.protocol); + protocol = conf_get_int(conf, CONF_protocol); + setup_config_box(ctrlbox, TRUE, protocol, protcfginfo); + win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol); dp_init(&dp); winctrl_init(&ctrls_base); winctrl_init(&ctrls_panel); @@ -682,7 +682,7 @@ int do_reconfig(HWND hwnd, int protcfginfo) dp_add_tree(&dp, &ctrls_panel); dp.wintitle = dupprintf("%s Reconfiguration", appname); dp.errtitle = dupprintf("%s Error", appname); - dp.data = &cfg; + dp.data = conf; dlg_auto_set_fixed_pitch_flag(&dp); dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ @@ -695,7 +695,9 @@ int do_reconfig(HWND hwnd, int protcfginfo) dp_cleanup(&dp); if (!ret) - cfg = backup_cfg; /* structure copy */ + conf_copy_into(conf, backup_conf); + + conf_free(backup_conf); return ret; } @@ -856,7 +858,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, Filename filename, +int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -870,7 +872,7 @@ int askappend(void *frontend, Filename filename, char *mbtitle; int mbret; - message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); mbret = MessageBox(NULL, message, mbtitle, diff --git a/putty/WINDOWS/WINDOW.C b/putty/WINDOWS/WINDOW.C index c533542..e087a2b 100644 --- a/putty/WINDOWS/WINDOW.C +++ b/putty/WINDOWS/WINDOW.C @@ -80,7 +80,7 @@ static Mouse_Button translate_button(Mouse_Button button); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); -static void cfgtopalette(void); +static void conftopalette(void); static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); @@ -140,7 +140,11 @@ static struct { enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; -Config cfg; /* exported to windlg.c */ +Conf *conf; /* exported to windlg.c */ + +static void conf_cache_data(void); +int cursor_type; +int vtmode; static struct sesslist sesslist; /* for saved-session menu */ @@ -164,14 +168,15 @@ struct agent_callback { #define FONT_OEMUND 0x22 #define FONT_OEMBOLDUND 0x23 -#define FONT_MAXNO 0x2F +#define FONT_MAXNO 0x40 #define FONT_SHIFT 5 static HFONT fonts[FONT_MAXNO]; static LOGFONT lfont; static int fontflag[FONT_MAXNO]; static enum { - BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT -} bold_mode; + BOLD_NONE, BOLD_SHADOW, BOLD_FONT +} bold_font_mode; +static int bold_colours; static enum { UND_LINE, UND_FONT } und_mode; @@ -202,6 +207,12 @@ static int compose_state = 0; static UINT wm_mousewheel = WM_MOUSEWHEEL; +#define IS_HIGH_VARSEL(wch1, wch2) \ + ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF)) +#define IS_LOW_VARSEL(wch) \ + (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \ + ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */ + /* Dummy routine, only required in plink. */ void ldisc_update(void *frontend, int echo, int edit) { @@ -223,7 +234,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 = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", @@ -232,22 +243,24 @@ static void start_backend(void) cleanup_exit(1); } - error = back->init(NULL, &backhandle, &cfg, - cfg.host, cfg.port, &realhost, cfg.tcp_nodelay, - cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, + conf_get_int(conf, CONF_tcp_nodelay), + conf_get_int(conf, CONF_tcp_keepalives)); back->provide_logctx(backhandle, logctx); if (error) { char *str = dupprintf("%s Error", appname); sprintf(msg, "Unable to open connection to\n" - "%.800s\n" "%s", cfg_dest(&cfg), error); + "%.800s\n" "%s", conf_dest(conf), error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); exit(0); } window_name = icon_name = NULL; - if (*cfg.wintitle) { - title = cfg.wintitle; - } else { + title = conf_get_str(conf, CONF_wintitle); + if (!*title) { sprintf(msg, "%s - %s", realhost, appname); title = msg; } @@ -263,7 +276,7 @@ static void start_backend(void) /* * Set up a line discipline. */ - ldisc = ldisc_create(&cfg, term, back, backhandle, NULL); + ldisc = ldisc_create(conf, term, back, backhandle, NULL); /* * Destroy the Restart Session menu item. (This will return @@ -364,6 +377,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) init_flashwindow(); + conf = conf_new(); + /* * Initialize COM. */ @@ -395,9 +410,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (b) default_port = b->default_port; } - cfg.logtype = LGTYP_NONE; + conf_set_int(conf, CONF_logtype, LGTYP_NONE); - do_defaults(NULL, &cfg); + do_defaults(NULL, conf); p = cmdline; @@ -422,8 +437,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) while (i > 1 && isspace(p[i - 1])) i--; p[i] = '\0'; - do_defaults(p + 1, &cfg); - if (!cfg_launchable(&cfg) && !do_config()) { + do_defaults(p + 1, conf); + if (!conf_launchable(conf) && !do_config()) { cleanup_exit(0); } allow_launch = TRUE; /* allow it to be launched directly */ @@ -431,15 +446,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * An initial & means we've been given a command line * containing the hex value of a HANDLE for a file - * mapping object, which we must then extract as a - * config. + * mapping object, which we must then interpret as a + * serialised Conf. */ HANDLE filemap; - Config *cp; - if (sscanf(p + 1, "%p", &filemap) == 1 && + void *cp; + unsigned cpsize; + if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 && (cp = MapViewOfFile(filemap, FILE_MAP_READ, - 0, 0, sizeof(Config))) != NULL) { - cfg = *cp; + 0, 0, cpsize)) != NULL) { + conf_deserialise(conf, cp, cpsize); UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config()) { @@ -461,7 +477,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) int ret; ret = cmdline_process_param(p, i+1isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1409,7 +1440,7 @@ static void init_fonts(int pick_width, int pick_height) if (pick_height) font_height = pick_height; else { - font_height = cfg.font.height; + font_height = font->height; if (font_height > 0) { font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); @@ -1417,13 +1448,14 @@ static void init_fonts(int pick_width, int pick_height) } font_width = pick_width; + quality = conf_get_int(conf, CONF_font_quality); #define f(i,c,w,u) \ fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \ c, OUT_DEFAULT_PRECIS, \ - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \ - FIXED_PITCH | FF_DONTCARE, cfg.font.name) + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \ + FIXED_PITCH | FF_DONTCARE, font->name) - f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE); + f(FONT_NORMAL, font->charset, fw_dontcare, FALSE); SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); @@ -1466,7 +1498,7 @@ static void init_fonts(int pick_width, int pick_height) ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } - f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE); + f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE); /* * Some fonts, e.g. 9-pt Courier, draw their underlines @@ -1516,8 +1548,8 @@ static void init_fonts(int pick_width, int pick_height) } } - if (bold_mode == BOLD_FONT) { - f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE); + if (bold_font_mode == BOLD_FONT) { + f(FONT_BOLD, font->charset, fw_bold, FALSE); } #undef f @@ -1543,23 +1575,24 @@ static void init_fonts(int pick_width, int pick_height) fonts[FONT_UNDERLINE] = 0; } - if (bold_mode == BOLD_FONT && + if (bold_font_mode == BOLD_FONT && fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) { - bold_mode = BOLD_SHADOW; + bold_font_mode = BOLD_SHADOW; DeleteObject(fonts[FONT_BOLD]); fonts[FONT_BOLD] = 0; } fontflag[0] = fontflag[1] = fontflag[2] = 1; - init_ucs(&cfg, &ucsdata); + init_ucs(conf, &ucsdata); } static void another_font(int fontno) { int basefont; - int fw_dontcare, fw_bold; + int fw_dontcare, fw_bold, quality; int c, u, w, x; char *s; + FontSpec *font; if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno]) return; @@ -1568,7 +1601,9 @@ static void another_font(int fontno) if (basefont != fontno && !fontflag[basefont]) another_font(basefont); - if (cfg.font.isbold) { + font = conf_get_fontspec(conf, CONF_font); + + if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1576,10 +1611,10 @@ static void another_font(int fontno) fw_bold = FW_BOLD; } - c = cfg.font.charset; + c = font->charset; w = fw_dontcare; u = FALSE; - s = cfg.font.name; + s = font->name; x = font_width; if (fontno & FONT_WIDE) @@ -1593,10 +1628,12 @@ static void another_font(int fontno) if (fontno & FONT_UNDERLINE) u = TRUE; + quality = conf_get_int(conf, CONF_font_quality); + fonts[fontno] = CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w, FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), DEFAULT_PITCH | FF_DONTCARE, s); fontflag[fontno] = 1; @@ -1619,11 +1656,11 @@ void request_resize(void *frontend, int w, int h) /* If the window is maximized supress resizing attempts */ if (IsZoomed(hwnd)) { - if (cfg.resize_action == RESIZE_TERM) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } - if (cfg.resize_action == RESIZE_DISABLED) return; + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ @@ -1654,9 +1691,10 @@ void request_resize(void *frontend, int w, int h) } } - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); - if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) { + if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && + !IsZoomed(hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; @@ -1677,7 +1715,7 @@ static void reset_window(int reinit) { * This function doesn't like to change the terminal size but if the * font size is locked that may be it's only soluion. */ - int win_width, win_height; + int win_width, win_height, resize_action, window_border; RECT cr, wr; #ifdef RDB_DEBUG_PATCH @@ -1691,7 +1729,11 @@ static void reset_window(int reinit) { win_width = cr.right - cr.left; win_height = cr.bottom - cr.top; - if (cfg.resize_action == RESIZE_DISABLED) reinit = 2; + resize_action = conf_get_int(conf, CONF_resize_action); + window_border = conf_get_int(conf, CONF_window_border); + + if (resize_action == RESIZE_DISABLED) + reinit = 2; /* Are we being forced to reload the fonts ? */ if (reinit>1) { @@ -1726,9 +1768,9 @@ static void reset_window(int reinit) { extra_width = wr.right - wr.left - cr.right + cr.left; extra_height = wr.bottom - wr.top - cr.bottom + cr.top; - if (cfg.resize_action != RESIZE_TERM) { - if ( font_width != win_width/term->cols || - font_height != win_height/term->rows) { + if (resize_action != RESIZE_TERM) { + if (font_width != win_width/term->cols || + font_height != win_height/term->rows) { deinit_fonts(); init_fonts(win_width/term->cols, win_height/term->rows); offset_width = (win_width-font_width*term->cols)/2; @@ -1740,13 +1782,13 @@ static void reset_window(int reinit) { #endif } } else { - if ( font_width * term->cols != win_width || - font_height * term->rows != win_height) { + if (font_width * term->cols != win_width || + font_height * term->rows != win_height) { /* Our only choice at this point is to change the * size of the terminal; Oh well. */ term_size(term, win_height/font_height, win_width/font_width, - cfg.savelines); + conf_get_int(conf, CONF_savelines)); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; InvalidateRect(hwnd, NULL, TRUE); @@ -1766,7 +1808,7 @@ static void reset_window(int reinit) { debug((27, "reset_window() -> Forced re-init")); #endif - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1791,10 +1833,10 @@ static void reset_window(int reinit) { * window. But that may be too big for the screen which forces us * to change the terminal. */ - if ((cfg.resize_action == RESIZE_TERM && reinit<=0) || - (cfg.resize_action == RESIZE_EITHER && reinit<0) || + if ((resize_action == RESIZE_TERM && reinit<=0) || + (resize_action == RESIZE_EITHER && reinit<0) || reinit>0) { - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1811,7 +1853,7 @@ static void reset_window(int reinit) { /* Grrr too big */ if ( term->rows > height || term->cols > width ) { - if (cfg.resize_action == RESIZE_EITHER) { + if (resize_action == RESIZE_EITHER) { /* Make the font the biggest we can */ if (term->cols > width) font_width = (ss.right - ss.left - extra_width) @@ -1828,7 +1870,8 @@ static void reset_window(int reinit) { } else { if ( height > term->rows ) height = term->rows; if ( width > term->cols ) width = term->cols; - term_size(term, height, width, cfg.savelines); + term_size(term, height, width, + conf_get_int(conf, CONF_savelines)); #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> term resize to (%d,%d)", height, width)); @@ -1853,12 +1896,12 @@ static void reset_window(int reinit) { /* We're allowed to or must change the font but do we want to ? */ - if (font_width != (win_width-cfg.window_border*2)/term->cols || - font_height != (win_height-cfg.window_border*2)/term->rows) { + if (font_width != (win_width-window_border*2)/term->cols || + font_height != (win_height-window_border*2)/term->rows) { deinit_fonts(); - init_fonts((win_width-cfg.window_border*2)/term->cols, - (win_height-cfg.window_border*2)/term->rows); + init_fonts((win_width-window_border*2)/term->cols, + (win_height-window_border*2)/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; @@ -1887,7 +1930,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) { int thistime = GetMessageTime(); - if (send_raw_mouse && !(cfg.mouse_override && shift)) { + if (send_raw_mouse && + !(shift && conf_get_int(conf, CONF_mouse_override))) { lastbtn = MBT_NOTHING; term_mouse(term, b, translate_button(b), MA_CLICK, x, y, shift, ctrl, alt); @@ -1917,9 +1961,11 @@ static Mouse_Button translate_button(Mouse_Button button) if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) - return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) - return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } @@ -1928,8 +1974,8 @@ static void show_mouseptr(int show) /* NB that the counter in ShowCursor() is also frobbed by * update_mouse_pointer() */ static int cursor_visible = 1; - if (!cfg.hide_mouseptr) /* override if this feature disabled */ - show = 1; + if (!conf_get_int(conf, CONF_hide_mouseptr)) + show = 1; /* override if this feature disabled */ if (cursor_visible && !show) ShowCursor(FALSE); else if (!cursor_visible && show) @@ -1954,14 +2000,15 @@ static int resizing; void notify_remote_exit(void *fe) { - int exitcode; + int exitcode, close_on_exit; if (!session_closed && (exitcode = back->exitcode(backhandle)) >= 0) { + close_on_exit = conf_get_int(conf, CONF_close_on_exit); /* Abnormal exits will already have set session_closed and taken * appropriate action. */ - if (cfg.close_on_exit == FORCE_ON || - (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) { + if (close_on_exit == FORCE_ON || + (close_on_exit == AUTO && exitcode != INT_MAX)) { PostQuitMessage(0); } else { must_close_session = TRUE; @@ -1976,15 +2023,26 @@ void notify_remote_exit(void *fe) } } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { - long ticks = next - GETTICKCOUNT(); - if (ticks <= 0) ticks = 1; /* just in case */ + unsigned long now = GETTICKCOUNT(); + long ticks; + if (now - next < INT_MAX) + ticks = 0; + else + ticks = next - now; KillTimer(hwnd, TIMING_TIMER_ID); SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL); timing_next_time = next; } +static void conf_cache_data(void) +{ + /* Cache some items from conf to speed lookups in very hot code */ + cursor_type = conf_get_int(conf, CONF_cursor_type); + vtmode = conf_get_int(conf, CONF_vtmode); +} + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1994,11 +2052,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static int fullscr_on_max = FALSE; static int processed_resize = FALSE; static UINT last_mousemove = 0; + int resize_action; switch (message) { case WM_TIMER: if ((UINT_PTR)wParam == TIMING_TIMER_ID) { - long next; + unsigned long next; KillTimer(hwnd, TIMING_TIMER_ID); if (run_timers(timing_next_time, &next)) { @@ -2014,7 +2073,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, char *str; show_mouseptr(1); str = dupprintf("%s Exit Confirmation", appname); - if (!cfg.warn_on_close || session_closed || + if (session_closed || !conf_get_int(conf, CONF_warn_on_close) || MessageBox(hwnd, "Are you sure you want to close this session?", str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) @@ -2061,7 +2120,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * config structure. */ SECURITY_ATTRIBUTES sa; - Config *p; + void *p; + int size; + + size = conf_serialised_size(conf); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; @@ -2069,18 +2131,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, filemap = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, - 0, sizeof(Config), NULL); + 0, size, NULL); if (filemap && filemap != INVALID_HANDLE_VALUE) { - p = (Config *) MapViewOfFile(filemap, - FILE_MAP_WRITE, - 0, 0, sizeof(Config)); + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); if (p) { - *p = cfg; /* structure copy */ + conf_serialise(conf, p); UnmapViewOfFile(p); } } inherit_handles = TRUE; - sprintf(c, "putty &%p", filemap); + sprintf(c, "putty &%p:%u", filemap, (unsigned)size); cl = c; } else if (wParam == IDM_SAVEDSESS) { unsigned int sessno = ((lParam - IDM_SAVED_MIN) @@ -2107,6 +2167,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, si.lpReserved2 = NULL; CreateProcess(b, cl, NULL, NULL, inherit_handles, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); if (filemap) CloseHandle(filemap); @@ -2124,7 +2186,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case IDM_RECONF: { - Config prev_cfg; + Conf *prev_conf; int init_lvl = 1; int reconfig_result; @@ -2133,62 +2195,78 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else reconfiguring = TRUE; - GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle)); - prev_cfg = cfg; + /* + * Copy the current window title into the stored + * previous configuration, so that doing nothing to + * the window title field in the config box doesn't + * reset the title to its startup state. + */ + conf_set_str(conf, CONF_wintitle, window_name); + + prev_conf = conf_copy(conf); reconfig_result = do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0); reconfiguring = FALSE; - if (!reconfig_result) + if (!reconfig_result) { + conf_free(prev_conf); break; + } + conf_cache_data(); + + resize_action = conf_get_int(conf, CONF_resize_action); { /* Disable full-screen if resizing forbidden */ int i; for (i = 0; i < lenof(popup_menus); i++) EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_BYCOMMAND | - (cfg.resize_action == RESIZE_DISABLED) + (resize_action == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED); /* Gracefully unzoom if necessary */ - if (IsZoomed(hwnd) && - (cfg.resize_action == RESIZE_DISABLED)) { + if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) ShowWindow(hwnd, SW_RESTORE); - } } /* Pass new config data to the logging module */ - log_reconfig(logctx, &cfg); + log_reconfig(logctx, conf); sfree(logpal); /* * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ + ldisc_configure(ldisc, conf); if (ldisc) ldisc_send(ldisc, NULL, 0, 0); if (pal) DeleteObject(pal); logpal = NULL; pal = NULL; - cfgtopalette(); + conftopalette(); init_palette(); /* Pass new config data to the terminal */ - term_reconfig(term, &cfg); + term_reconfig(term, conf); /* Pass new config data to the back end */ if (back) - back->reconfig(backhandle, &cfg); + back->reconfig(backhandle, conf); /* Screen size changed ? */ - if (cfg.height != prev_cfg.height || - cfg.width != prev_cfg.width || - cfg.savelines != prev_cfg.savelines || - cfg.resize_action == RESIZE_FONT || - (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || - cfg.resize_action == RESIZE_DISABLED) - term_size(term, cfg.height, cfg.width, cfg.savelines); + if (conf_get_int(conf, CONF_height) != + conf_get_int(prev_conf, CONF_height) || + conf_get_int(conf, CONF_width) != + conf_get_int(prev_conf, CONF_width) || + conf_get_int(conf, CONF_savelines) != + conf_get_int(prev_conf, CONF_savelines) || + resize_action == RESIZE_FONT || + (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || + resize_action == RESIZE_DISABLED) + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); /* Enable or disable the scroll bar, etc */ { @@ -2197,8 +2275,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, GetWindowLongPtr(hwnd, GWL_EXSTYLE); nexflag = exflag; - if (cfg.alwaysontop != prev_cfg.alwaysontop) { - if (cfg.alwaysontop) { + if (conf_get_int(conf, CONF_alwaysontop) != + conf_get_int(prev_conf, CONF_alwaysontop)) { + if (conf_get_int(conf, CONF_alwaysontop)) { nexflag |= WS_EX_TOPMOST; SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -2208,25 +2287,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE); } } - if (cfg.sunken_edge) + if (conf_get_int(conf, CONF_sunken_edge)) nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; - if (is_full_screen() ? - cfg.scrollbar_in_fullscreen : cfg.scrollbar) + if (conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : + CONF_scrollbar)) nflg |= WS_VSCROLL; else nflg &= ~WS_VSCROLL; - if (cfg.resize_action == RESIZE_DISABLED || + if (resize_action == RESIZE_DISABLED || is_full_screen()) nflg &= ~WS_THICKFRAME; else nflg |= WS_THICKFRAME; - if (cfg.resize_action == RESIZE_DISABLED) + if (resize_action == RESIZE_DISABLED) nflg &= ~WS_MAXIMIZEBOX; else nflg |= WS_MAXIMIZEBOX; @@ -2247,34 +2327,47 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } /* Oops */ - if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { + if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { force_normal(hwnd); init_lvl = 2; } - set_title(NULL, cfg.wintitle); + set_title(NULL, conf_get_str(conf, CONF_wintitle)); if (IsIconic(hwnd)) { SetWindowText(hwnd, - cfg.win_name_always ? window_name : - icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); } - if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 || - strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 || - cfg.font.isbold != prev_cfg.font.isbold || - cfg.font.height != prev_cfg.font.height || - cfg.font.charset != prev_cfg.font.charset || - cfg.font_quality != prev_cfg.font_quality || - cfg.vtmode != prev_cfg.vtmode || - cfg.bold_colour != prev_cfg.bold_colour || - cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_EITHER || - (cfg.resize_action != prev_cfg.resize_action)) - init_lvl = 2; + { + FontSpec *font = conf_get_fontspec(conf, CONF_font); + FontSpec *prev_font = conf_get_fontspec(prev_conf, + CONF_font); + + if (!strcmp(font->name, prev_font->name) || + !strcmp(conf_get_str(conf, CONF_line_codepage), + conf_get_str(prev_conf, CONF_line_codepage)) || + font->isbold != prev_font->isbold || + font->height != prev_font->height || + font->charset != prev_font->charset || + conf_get_int(conf, CONF_font_quality) != + conf_get_int(prev_conf, CONF_font_quality) || + conf_get_int(conf, CONF_vtmode) != + conf_get_int(prev_conf, CONF_vtmode) || + conf_get_int(conf, CONF_bold_style) != + conf_get_int(prev_conf, CONF_bold_style) || + resize_action == RESIZE_DISABLED || + resize_action == RESIZE_EITHER || + resize_action != conf_get_int(prev_conf, + CONF_resize_action)) + init_lvl = 2; + } InvalidateRect(hwnd, NULL, TRUE); reset_window(init_lvl); net_pending_errors(); + + conf_free(prev_conf); } break; case IDM_COPYALL: @@ -2352,7 +2445,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_MBUTTONUP: case WM_RBUTTONUP: if (message == WM_RBUTTONDOWN && - ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) { + ((wParam & MK_CONTROL) || + (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) { POINT cursorpos; show_mouseptr(1); /* make sure pointer is visible */ @@ -2659,7 +2753,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, debug((27, "WM_EXITSIZEMOVE")); #endif if (need_backend_resize) { - term_size(term, cfg.height, cfg.width, cfg.savelines); + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); InvalidateRect(hwnd, NULL, TRUE); } break; @@ -2669,13 +2765,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * 1) Keep the sizetip uptodate * 2) Make sure the window size is _stepped_ in units of the font size. */ - if (cfg.resize_action == RESIZE_TERM || - (cfg.resize_action == RESIZE_EITHER && !is_alt_pressed())) { + resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && !is_alt_pressed())) { int width, height, w, h, ew, eh; LPRECT r = (LPRECT) lParam; - if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER && - (cfg.height != term->rows || cfg.width != term->cols )) { + if (!need_backend_resize && resize_action == RESIZE_EITHER && + (conf_get_int(conf, CONF_height) != term->rows || + conf_get_int(conf, CONF_width) != term->cols)) { /* * Great! It seems that both the terminal size and the * font size have been changed and the user is now dragging. @@ -2684,11 +2782,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * font size! * * This would be easier but it seems to be too confusing. - - term_size(term, cfg.height, cfg.width, cfg.savelines); - reset_window(2); */ - cfg.height=term->rows; cfg.width=term->cols; + conf_set_int(conf, CONF_height, term->rows); + conf_set_int(conf, CONF_width, term->cols); InvalidateRect(hwnd, NULL, TRUE); need_backend_resize = TRUE; @@ -2725,8 +2821,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return 0; } else { int width, height, w, h, rv = 0; - int ex_width = extra_width + (cfg.window_border - offset_width) * 2; - int ex_height = extra_height + (cfg.window_border - offset_height) * 2; + int window_border = conf_get_int(conf, CONF_window_border); + int ex_width = extra_width + (window_border - offset_width) * 2; + int ex_height = extra_height + (window_border - offset_height) * 2; LPRECT r = (LPRECT) lParam; width = r->right - r->left - ex_width; @@ -2762,6 +2859,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, sys_cursor_update(); break; case WM_SIZE: + resize_action = conf_get_int(conf, CONF_resize_action); #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED": @@ -2773,7 +2871,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #endif if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, - cfg.win_name_always ? window_name : icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) SetWindowText(hwnd, window_name); if (wParam == SIZE_RESTORED) { @@ -2806,12 +2905,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, processed_resize = TRUE; - if (cfg.resize_action == RESIZE_DISABLED) { + if (resize_action == RESIZE_DISABLED) { /* A resize, well it better be a minimize. */ reset_window(-1); } else { int width, height, w, h; + int window_border = conf_get_int(conf, CONF_window_border); width = LOWORD(lParam); height = HIWORD(lParam); @@ -2820,36 +2920,50 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, was_zoomed = 1; prev_rows = term->rows; prev_cols = term->cols; - if (cfg.resize_action == RESIZE_TERM) { + if (resize_action == RESIZE_TERM) { w = width / font_width; if (w < 1) w = 1; h = height / font_height; if (h < 1) h = 1; - term_size(term, h, w, cfg.savelines); + if (resizing) { + /* + * As below, if we're in the middle of an + * interactive resize we don't call + * back->size. In Windows 7, this case can + * arise in maximisation as well via the Aero + * snap UI. + */ + need_backend_resize = TRUE; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); + } else { + term_size(term, h, w, + conf_get_int(conf, CONF_savelines)); + } } reset_window(0); } else if (wParam == SIZE_RESTORED && was_zoomed) { was_zoomed = 0; - if (cfg.resize_action == RESIZE_TERM) { - w = (width-cfg.window_border*2) / font_width; + if (resize_action == RESIZE_TERM) { + w = (width-window_border*2) / font_width; if (w < 1) w = 1; - h = (height-cfg.window_border*2) / font_height; + h = (height-window_border*2) / font_height; if (h < 1) h = 1; - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); reset_window(2); - } else if (cfg.resize_action != RESIZE_FONT) + } else if (resize_action != RESIZE_FONT) reset_window(2); else reset_window(0); } else if (wParam == SIZE_MINIMIZED) { /* do nothing */ - } else if (cfg.resize_action == RESIZE_TERM || - (cfg.resize_action == RESIZE_EITHER && + } else if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && !is_alt_pressed())) { - w = (width-cfg.window_border*2) / font_width; + w = (width-window_border*2) / font_width; if (w < 1) w = 1; - h = (height-cfg.window_border*2) / font_height; + h = (height-window_border*2) / font_height; if (h < 1) h = 1; if (resizing) { @@ -2860,10 +2974,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * opaque drag.) */ need_backend_resize = TRUE; - cfg.height = h; - cfg.width = w; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); } else { - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); } } else { reset_window(0); @@ -2893,7 +3007,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case SB_THUMBPOSITION: case SB_THUMBTRACK: - term_scroll(term, 1, HIWORD(wParam)); + /* + * Use GetScrollInfo instead of HIWORD(wParam) to get + * 32-bit scroll position. + */ + { + SCROLLINFO si; + + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) + si.nTrackPos = HIWORD(wParam); + term_scroll(term, 1, si.nTrackPos); + } break; } break; @@ -3018,9 +3144,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * instead we luni_send the characters one by one. */ term_seen_key_event(term); - for (i = 0; i < n; i += 2) { - if (ldisc) + /* don't divide SURROGATE PAIR */ + if (ldisc) { + for (i = 0; i < n; i += 2) { + WCHAR hs = *(unsigned short *)(buff+i); + if (IS_HIGH_SURROGATE(hs) && i+2 < n) { + WCHAR ls = *(unsigned short *)(buff+i+2); + if (IS_LOW_SURROGATE(ls)) { + luni_send(ldisc, (unsigned short *)(buff+i), 2, 1); + i += 2; + continue; + } + } luni_send(ldisc, (unsigned short *)(buff+i), 1, 1); + } } free(buff); } @@ -3060,7 +3197,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } return 0; case WM_SYSCOLORCHANGE: - if (cfg.system_colour) { + if (conf_get_int(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ /* XXX actually this zaps the entire palette. */ systopalette(); @@ -3112,7 +3249,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; if (send_raw_mouse && - !(cfg.mouse_override && shift_pressed)) { + !(conf_get_int(conf, CONF_mouse_override) && + shift_pressed)) { /* Mouse wheel position is in screen coordinates for * some reason */ POINT p; @@ -3219,9 +3357,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, int text_adjust = 0; int xoffset = 0; int maxlen, remaining, opaque; + int is_cursor = FALSE; static int *lpDx = NULL; static int lpDx_len = 0; int *lpDx_maybe; + int len2; /* for SURROGATE PAIR */ lattr &= LATTR_MODE; @@ -3239,17 +3379,15 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, x += offset_width; y += offset_height; - if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) { + if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) { attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS); - if (bold_mode == BOLD_COLOURS) - attr &= ~ATTR_BOLD; - /* cursor fg and bg */ attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT); + is_cursor = TRUE; } nfont = 0; - if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) { + if (vtmode == VT_POORMAN && lattr != LATTR_NORM) { /* Assume a poorman font is borken in other ways too. */ lattr = LATTR_WIDE; } else @@ -3266,6 +3404,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, if (attr & ATTR_NARROW) nfont |= FONT_NARROW; +#ifdef USES_VTLINE_HACK /* Special hack for the VT100 linedraw glyphs. */ if (text[0] >= 0x23BA && text[0] <= 0x23BD) { switch ((unsigned char) (text[0])) { @@ -3290,9 +3429,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, force_manual_underline = 1; } } +#endif /* Anything left as an original character set is unprintable. */ - if (DIRECT_CHAR(text[0])) { + if (DIRECT_CHAR(text[0]) && + (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) { int i; for (i = 0; i < len; i++) text[i] = 0xFFFD; @@ -3304,7 +3445,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); - if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD)) + if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD)) nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) nfont |= FONT_UNDERLINE; @@ -3324,11 +3465,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = nbg; nbg = t; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) { + if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) { + if (bold_colours && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -3345,6 +3486,24 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, line_box.top = y; line_box.right = x + char_width * len; line_box.bottom = y + font_height; + /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */ + { + int i; + int rc_width = 0; + for (i = 0; i < len ; i++) { + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + i++; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) { + rc_width += char_width; + i++; + } else if (IS_LOW_VARSEL(text[i])) { + /* do nothing */ + } else { + rc_width += char_width; + } + } + line_box.right = line_box.left + rc_width; + } /* Only want the left half of double width lines */ if (line_box.right > font_width*term->cols+offset_width) @@ -3375,19 +3534,47 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, opaque = TRUE; /* start by erasing the rectangle */ for (remaining = len; remaining > 0; - text += len, remaining -= len, x += char_width * len) { + text += len, remaining -= len, x += char_width * len2) { len = (maxlen < remaining ? maxlen : remaining); - - if (len > lpDx_len) { - if (len > lpDx_len) { - lpDx_len = len * 9 / 8 + 16; - lpDx = sresize(lpDx, lpDx_len, int); - } + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + len2 = len; + if (maxlen == 1) { + if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) + len++; + if (remaining-len >= 1 && IS_LOW_VARSEL(text[len])) + len++; + else if (remaining-len >= 2 && + IS_HIGH_VARSEL(text[len], text[len+1])) + len += 2; } + + if (len > lpDx_len) { + lpDx_len = len * 9 / 8 + 16; + lpDx = sresize(lpDx, lpDx_len, int); + + if (lpDx_maybe) lpDx_maybe = lpDx; + } + { int i; - for (i = 0; i < len; i++) + /* only last char has dx width in SURROGATE PAIR and + * VARIATION sequence */ + for (i = 0; i < len; i++) { lpDx[i] = char_width; + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) { + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (IS_LOW_VARSEL(text[i])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = char_width; + } + } } /* We're using a private area for direct to font. (512 chars.) */ @@ -3432,7 +3619,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, uni_buf, nlen, lpDx_maybe); - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == @@ -3457,7 +3644,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, y - font_height * (lattr == LATTR_BOT) + text_adjust, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), &line_box, directbuf, len, lpDx_maybe); - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); /* GRR: This draws the character outside its box and @@ -3496,7 +3683,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, opaque && !(attr & TATTR_COMBINING)); /* And the shadow bold hack. */ - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode(hdc, TRANSPARENT); ExtTextOutW(hdc, x + xoffset - 1, y - font_height * (lattr == @@ -3536,9 +3723,35 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len, { if (attr & TATTR_COMBINING) { unsigned long a = 0; - attr &= ~TATTR_COMBINING; + int len0 = 1; + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1])) + len0 = 2; + if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+1, attr, lattr); + text += len0+1; + len -= len0+1; + a = TATTR_COMBINING; + } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+2, attr, lattr); + text += len0+2; + len -= len0+2; + a = TATTR_COMBINING; + } else { + attr &= ~TATTR_COMBINING; + } + while (len--) { - do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) { + do_text_internal(ctx, x, y, text, 2, attr | a, lattr); + len--; + text++; + } else { + do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + } + text++; a = TATTR_COMBINING; } @@ -3553,7 +3766,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, int fnt_width; int char_width; HDC hdc = ctx; - int ctype = cfg.cursor_type; + int ctype = cursor_type; lattr &= LATTR_MODE; @@ -3698,13 +3911,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, int r, i, code; unsigned char *p = output; static int alt_sum = 0; + int funky_type = conf_get_int(conf, CONF_funky_type); + int no_applic_k = conf_get_int(conf, CONF_no_applic_k); + int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys); + int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad); HKL kbd_layout = GetKeyboardLayout(0); - /* keys is for ToAsciiEx. There's some ick here, see below. */ - static WORD keys[3]; + static wchar_t keys_unicode[3]; static int compose_char = 0; - static WPARAM compose_key = 0; + static WPARAM compose_keycode = 0; r = GetKeyboardState(keystate); if (!r) @@ -3754,12 +3970,12 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else if (ch) debug((", $%02x", ch)); - if (keys[0]) - debug((", KB0=%02x", keys[0])); - if (keys[1]) - debug((", KB1=%02x", keys[1])); - if (keys[2]) - debug((", KB2=%02x", keys[2])); + if (keys_unicode[0]) + debug((", KB0=%04x", keys_unicode[0])); + if (keys_unicode[1]) + debug((", KB1=%04x", keys_unicode[1])); + if (keys_unicode[2]) + debug((", KB2=%04x", keys_unicode[2])); if ((keystate[VK_SHIFT] & 0x80) != 0) debug((", S")); @@ -3791,9 +4007,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ - if ((cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys && - !cfg.no_applic_k)) + if ((funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && term->app_keypad_keys && + !no_applic_k)) && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) { wParam = VK_EXECUTE; @@ -3819,7 +4035,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */ if (left_alt && (keystate[VK_CONTROL] & 0x80)) { - if (cfg.ctrlaltkeys) + if (ctrlaltkeys) keystate[VK_MENU] = 0; else { keystate[VK_RMENU] = 0x80; @@ -3833,16 +4049,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Note if AltGr was pressed and if it was used as a compose key */ if (!compose_state) { - compose_key = 0x100; - if (cfg.compose_key) { + compose_keycode = 0x100; + if (conf_get_int(conf, CONF_compose_key)) { if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) - compose_key = wParam; + compose_keycode = wParam; } if (wParam == VK_APPS) - compose_key = wParam; + compose_keycode = wParam; } - if (wParam == compose_key) { + if (wParam == compose_keycode) { if (compose_state == 0 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state = 1; @@ -3857,9 +4073,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, compose_state = 0; /* Sanitize the number pad if not using a PC NumPad */ - if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k - && cfg.funky_type != FUNKY_XTERM) - || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) { + if (left_alt || (term->app_keypad_keys && !no_applic_k + && funky_type != FUNKY_XTERM) + || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) { if ((HIWORD(lParam) & KF_EXTENDED) == 0) { int nParam = 0; switch (wParam) { @@ -3936,15 +4152,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, request_paste(NULL); return 0; } - if (left_alt && wParam == VK_F4 && cfg.alt_f4) { + if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) { return -1; } - if (left_alt && wParam == VK_SPACE && cfg.alt_space) { + if (left_alt && wParam == VK_SPACE && conf_get_int(conf, + CONF_alt_space)) { SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } - if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter && - (cfg.resize_action != RESIZE_DISABLED)) { + if (left_alt && wParam == VK_RETURN && + conf_get_int(conf, CONF_fullscreenonaltenter) && + (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) { if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) flip_full_screen(); return -1; @@ -3956,7 +4174,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } /* Nethack keypad */ - if (cfg.nethack_keypad && !left_alt) { + if (nethack_keypad && !left_alt) { switch (wParam) { case VK_NUMPAD1: *p++ = "bB\002\002"[shift_state & 3]; @@ -3992,9 +4210,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (!left_alt) { int xkey = 0; - if (cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && - term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) { + if (funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && + term->app_keypad_keys && !no_applic_k)) switch (wParam) { case VK_EXECUTE: xkey = 'P'; break; @@ -4008,7 +4226,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'S'; break; } - if (term->app_keypad_keys && !cfg.no_applic_k) + if (term->app_keypad_keys && !no_applic_k) switch (wParam) { case VK_NUMPAD0: xkey = 'p'; @@ -4045,7 +4263,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'n'; break; case VK_ADD: - if (cfg.funky_type == FUNKY_XTERM) { + if (funky_type == FUNKY_XTERM) { if (shift_state) xkey = 'l'; else @@ -4057,15 +4275,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; case VK_DIVIDE: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'o'; break; case VK_MULTIPLY: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'j'; break; case VK_SUBTRACT: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'm'; break; @@ -4087,13 +4305,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ - *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08); *p++ = 0; return -2; } if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */ /* We do the opposite of what is configured */ - *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F); *p++ = 0; return -2; } @@ -4237,7 +4455,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; } /* Reorder edit keys to physical order */ - if (cfg.funky_type == FUNKY_VT400 && code <= 6) + if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (term->vt52_mode && code > 0 && code <= 6) { @@ -4245,8 +4463,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */ - code >= 11 && code <= 34) { + if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) { + /* SCO function keys */ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; switch (wParam) { @@ -4268,7 +4486,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p += sprintf((char *) p, "\x1B[%c", codes[index]); return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { @@ -4278,7 +4496,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } return p - output; } - if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { + if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) offt++; @@ -4291,18 +4509,19 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt); return p - output; } - if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11); return p - output; } - if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", code + 'P' - 11); else p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11); return p - output; } - if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + if ((code == 1 || code == 4) && + conf_get_int(conf, CONF_rxvt_homeend)) { p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw"); return p - output; } @@ -4362,7 +4581,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, BOOL capsOn=0; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ - if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) { + if(keystate[VK_CAPITAL] != 0 && + conf_get_int(conf, CONF_xlat_capslockcyr)) { capsOn= !left_alt; keystate[VK_CAPITAL] = 0; } @@ -4371,6 +4591,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * be is? There's indication on MS' website of an Inquire/InquireEx * functioning returning a KBINFO structure which tells us. */ if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) { + r = ToUnicodeEx(wParam, scan, keystate, keys_unicode, + lenof(keys_unicode), 0, kbd_layout); + } else { /* XXX 'keys' parameter is declared in MSDN documentation as * 'LPWORD lpChar'. * The experience of a French user indicates that on @@ -4381,12 +4604,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * Win9x/NT split, but I suspect it's worse than that. * See wishlist item `win-dead-keys' for more horrible detail * and speculations. */ - BYTE keybs[3]; int i; - r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout); - for (i=0; i<3; i++) keys[i] = keybs[i]; - } else { + static WORD keys[3]; + static BYTE keysb[3]; r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); + if (r > 0) { + for (i = 0; i < r; i++) { + keysb[i] = (BYTE)keys[i]; + } + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r, + keys_unicode, lenof(keys_unicode)); + } } #ifdef SHOW_TOASCII_RESULT if (r == 1 && !key_down) { @@ -4396,13 +4624,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else debug((", LCH(%d)", alt_sum)); } else { - debug((", ACH(%d)", keys[0])); + debug((", ACH(%d)", keys_unicode[0])); } } else if (r > 0) { int r1; debug((", ASC(")); for (r1 = 0; r1 < r; r1++) { - debug(("%s%d", r1 ? "," : "", keys[r1])); + debug(("%s%d", r1 ? "," : "", keys_unicode[r1])); } debug((")")); } @@ -4419,18 +4647,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p = output; for (i = 0; i < r; i++) { - unsigned char ch = (unsigned char) keys[i]; + wchar_t wch = keys_unicode[i]; - if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') { - compose_char = ch; + if (compose_state == 2 && wch >= ' ' && wch < 0x80) { + compose_char = wch; compose_state++; continue; } - if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') { + if (compose_state == 3 && wch >= ' ' && wch < 0x80) { int nc; compose_state = 0; - if ((nc = check_compose(compose_char, ch)) == -1) { + if ((nc = check_compose(compose_char, wch)) == -1) { MessageBeep(MB_ICONHAND); return 0; } @@ -4451,7 +4679,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (ldisc) luni_send(ldisc, &keybuf, 1, 1); } else { - ch = (char) alt_sum; + char ch = (char) alt_sum; /* * We need not bother about stdin * backlogs here, because in GUI PuTTY @@ -4469,40 +4697,39 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } else { term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, &ch, 1, 1); + luni_send(ldisc, &wch, 1, 1); } } else { - if(capsOn && ch < 0x80) { + if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; - cbuf[1] = xlat_uskbd2cyrllic(ch); + cbuf[1] = xlat_uskbd2cyrllic(wch); term_seen_key_event(term); if (ldisc) luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1); } else { - char cbuf[2]; + WCHAR cbuf[2]; cbuf[0] = '\033'; - cbuf[1] = ch; + cbuf[1] = wch; term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, - cbuf+!left_alt, 1+!!left_alt, 1); + luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1); } } show_mouseptr(0); } /* This is so the ALT-Numpad and dead keys work correctly. */ - keys[0] = 0; + keys_unicode[0] = 0; return p - output; } /* If we're definitly not building up an ALT-54321 then clear it */ if (!left_alt) - keys[0] = 0; + keys_unicode[0] = 0; /* If we will be using alt_sum fix the 256s */ - else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) - keys[0] = 10; + else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) + keys_unicode[0] = 10; } /* @@ -4512,7 +4739,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * we return -1, which means Windows will give the keystroke * its default handling (i.e. bring up the System menu). */ - if (wParam == VK_MENU && !cfg.alt_only) + if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only)) return 0; return -1; @@ -4523,7 +4750,7 @@ void set_title(void *frontend, char *title) sfree(window_name); window_name = snewn(1 + strlen(title), char); strcpy(window_name, title); - if (cfg.win_name_always || !IsIconic(hwnd)) + if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4532,7 +4759,7 @@ void set_icon(void *frontend, char *title) sfree(icon_name); icon_name = snewn(1 + strlen(title), char); strcpy(icon_name, title); - if (!cfg.win_name_always && IsIconic(hwnd)) + if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4540,7 +4767,8 @@ void set_sbar(void *frontend, int total, int start, int page) { SCROLLINFO si; - if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar) + if (!conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : CONF_scrollbar)) return; si.cbSize = sizeof(si); @@ -4588,7 +4816,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) { if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; real_palette_set(n, r, g, b); if (pal) { @@ -4688,15 +4916,22 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des GlobalFree(clipdata2); return; } - if (!(lock = GlobalLock(clipdata))) + if (!(lock = GlobalLock(clipdata))) { + GlobalFree(clipdata); + GlobalFree(clipdata2); return; - if (!(lock2 = GlobalLock(clipdata2))) + } + if (!(lock2 = GlobalLock(clipdata2))) { + GlobalUnlock(clipdata); + GlobalFree(clipdata); + GlobalFree(clipdata2); return; + } memcpy(lock, data, len * sizeof(wchar_t)); WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL); - if (cfg.rtf_paste) { + if (conf_get_int(conf, CONF_rtf_paste)) { wchar_t unitab[256]; char *rtf = NULL; unsigned char *tdata = (unsigned char *)lock2; @@ -4711,13 +4946,14 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des int attrUnder, lastAttrUnder = 0; int palette[NALLCOLOURS]; int numcolours; + FontSpec *font = conf_get_fontspec(conf, CONF_font); get_unitab(CP_ACP, unitab, 0); - rtfsize = 100 + strlen(cfg.font.name); + rtfsize = 100 + strlen(font->name); rtf = snewn(rtfsize, char); rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d", - cfg.font.name, cfg.font.height*2); + font->name, font->height*2); /* * Add colour palette @@ -4740,7 +4976,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) { + if (bold_colours && (attr[i] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4831,7 +5067,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) { + if (bold_colours && (attr[tindex] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4848,7 +5084,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des /* * Collect other attributes */ - if (bold_mode != BOLD_COLOURS) + if (bold_font_mode != BOLD_NONE) attrBold = attr[tindex] & ATTR_BOLD; else attrBold = 0; @@ -4867,7 +5103,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = -1; /* No coloring */ if (fgcolour >= 256) { /* Default colour */ - if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1) + if (bold_colours && (fgcolour & 1) && bgcolour == -1) attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */ fgcolour = -1; /* No coloring */ @@ -5132,6 +5368,22 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +/* + * Print a message box and don't close the connection. + */ +void nonfatal(char *fmt, ...) +{ + va_list ap; + char *stuff, morestuff[100]; + + va_start(ap, fmt); + stuff = dupvprintf(fmt, ap); + va_end(ap); + sprintf(morestuff, "%.70s Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); +} + DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO)); static void init_flashwindow(void) @@ -5163,9 +5415,9 @@ static int flashing = 0; * Timer for platforms where we must maintain window flashing manually * (e.g., Win95). */ -static void flash_window_timer(void *ctx, long now) +static void flash_window_timer(void *ctx, unsigned long now) { - if (flashing && now - next_flash >= 0) { + if (flashing && now == next_flash) { flash_window(1); } } @@ -5176,7 +5428,8 @@ static void flash_window_timer(void *ctx, long now) */ static void flash_window(int mode) { - if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) { + int beep_ind = conf_get_int(conf, CONF_beep_ind); + if ((mode == 0) || (beep_ind == B_IND_DISABLED)) { /* stop */ if (flashing) { flashing = 0; @@ -5198,7 +5451,7 @@ static void flash_window(int mode) * "flashing" mode, although I haven't seen this * documented. */ flash_window_ex(FLASHW_ALL | FLASHW_TIMER, - (cfg.beep_ind == B_IND_FLASH ? 0 : 2), + (beep_ind == B_IND_FLASH ? 0 : 2), 0 /* system cursor blink rate */); /* No need to schedule timer */ } else { @@ -5207,7 +5460,7 @@ static void flash_window(int mode) } } - } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) { + } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ if (flashing && !p_FlashWindowEx) { FlashWindow(hwnd, TRUE); /* toggle */ @@ -5241,16 +5494,17 @@ void do_beep(void *frontend, int mode) */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { - if (!PlaySound(cfg.bell_wavefile.path, NULL, + Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile); + if (!PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { - char buf[sizeof(cfg.bell_wavefile.path) + 80]; + char buf[sizeof(bell_wavefile->path) + 80]; char otherbuf[100]; sprintf(buf, "Unable to play sound file\n%s\n" - "Using default sound instead", cfg.bell_wavefile.path); + "Using default sound instead", bell_wavefile->path); sprintf(otherbuf, "%.70s Sound Error", appname); MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); - cfg.beep = BELL_DEFAULT; + conf_set_int(conf, CONF_beep, BELL_DEFAULT); } } else if (mode == BELL_PCSPEAKER) { static long lastbeep = 0; @@ -5296,8 +5550,9 @@ void set_iconic(void *frontend, int iconic) */ void move_window(void *frontend, int x, int y) { - if (cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_FONT || + int resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_DISABLED || + resize_action == RESIZE_FONT || IsZoomed(hwnd)) return; @@ -5310,7 +5565,7 @@ void move_window(void *frontend, int x, int y) */ void set_zorder(void *frontend, int top) { - if (cfg.alwaysontop) + if (conf_get_int(conf, CONF_alwaysontop)) return; /* ignore */ SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -5432,7 +5687,7 @@ static void make_full_screen() /* Remove the window furniture. */ style = GetWindowLongPtr(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); - if (cfg.scrollbar_in_fullscreen) + if (conf_get_int(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5467,11 +5722,11 @@ static void clear_full_screen() /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; - if (cfg.resize_action == RESIZE_DISABLED) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) style &= ~WS_THICKFRAME; else style |= WS_THICKFRAME; - if (cfg.scrollbar) + if (conf_get_int(conf, CONF_scrollbar)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5526,6 +5781,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len) return term_data_untrusted(term, data, len); } +int from_backend_eof(void *frontend) +{ + return TRUE; /* do respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; diff --git a/putty/WINDOWS/WINGSS.C b/putty/WINDOWS/WINGSS.C index a2076c5..91d2d45 100644 --- a/putty/WINDOWS/WINGSS.C +++ b/putty/WINDOWS/WINGSS.C @@ -65,11 +65,12 @@ const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); -struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { HMODULE module; HKEY regkey; struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); + char *path; list->libraries = snewn(3, struct ssh_gss_library); list->nlibraries = 0; @@ -148,8 +149,9 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) * Custom GSSAPI DLL. */ module = NULL; - if (cfg->ssh_gss_custom.path[0]) { - module = LoadLibrary(cfg->ssh_gss_custom.path); + path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*path) { + module = LoadLibrary(path); } if (module) { struct ssh_gss_library *lib = @@ -157,7 +159,7 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) lib->id = 2; lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" - " library '%s'", cfg->ssh_gss_custom.path); + " library '%s'", path); lib->handle = (void *)module; #define BIND_GSS_FN(name) \ diff --git a/putty/WINDOWS/WINHANDL.C b/putty/WINDOWS/WINHANDL.C index dbcab2b..06c2a6a 100644 --- a/putty/WINDOWS/WINHANDL.C +++ b/putty/WINDOWS/WINHANDL.C @@ -250,6 +250,7 @@ struct handle_output { * Data only ever read or written by the main thread. */ bufchain queued_data; /* data still waiting to be written */ + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; /* * Callback function called when the backlog in the bufchain @@ -320,6 +321,11 @@ static void handle_try_output(struct handle_output *ctx) ctx->len = sendlen; SetEvent(ctx->ev_from_main); ctx->busy = TRUE; + } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 && + ctx->outgoingeof == EOF_PENDING) { + CloseHandle(ctx->h); + ctx->h = INVALID_HANDLE_VALUE; + ctx->outgoingeof = EOF_SENT; } } @@ -408,6 +414,7 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, h->u.o.done = FALSE; h->u.o.privdata = privdata; bufchain_init(&h->u.o.queued_data); + h->u.o.outgoingeof = EOF_NO; h->u.o.sentdata = sentdata; h->u.o.flags = flags; @@ -424,11 +431,28 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, int handle_write(struct handle *h, const void *data, int len) { assert(h->output); + assert(h->u.o.outgoingeof == EOF_NO); bufchain_add(&h->u.o.queued_data, data, len); handle_try_output(&h->u.o); return bufchain_size(&h->u.o.queued_data); } +void handle_write_eof(struct handle *h) +{ + /* + * This function is called when we want to proactively send an + * end-of-file notification on the handle. We can only do this by + * actually closing the handle - so never call this on a + * bidirectional handle if we're still interested in its incoming + * direction! + */ + assert(h->output); + if (!h->u.o.outgoingeof == EOF_NO) { + h->u.o.outgoingeof = EOF_PENDING; + handle_try_output(&h->u.o); + } +} + HANDLE *handle_get_events(int *nevents) { HANDLE *ret; diff --git a/putty/WINDOWS/WINHELP.C b/putty/WINDOWS/WINHELP.C index c072e05..4924cf1 100644 --- a/putty/WINDOWS/WINHELP.C +++ b/putty/WINDOWS/WINHELP.C @@ -81,7 +81,7 @@ int has_help(void) * unrealistic, since even Vista will have it if the user * specifically downloads it. */ - return (help_path + return (help_path != NULL #ifndef NO_HTMLHELP || chm_path #endif /* NO_HTMLHELP */ diff --git a/putty/WINDOWS/WINHELP.H b/putty/WINDOWS/WINHELP.H index d670c0c..ba78d67 100644 --- a/putty/WINDOWS/WINHELP.H +++ b/putty/WINDOWS/WINHELP.H @@ -144,6 +144,7 @@ #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_ssh_bugs_winadj "ssh.bugs.winadj:config-ssh-bug-winadj" #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" diff --git a/putty/WINDOWS/WINJUMP.C b/putty/WINDOWS/WINJUMP.C index 3455a8a..9ae6dbd 100644 --- a/putty/WINDOWS/WINJUMP.C +++ b/putty/WINDOWS/WINJUMP.C @@ -353,12 +353,12 @@ static const PROPERTYKEY PKEY_Title = { 0x00000002 }; -/* Type-checking macro to provide arguments for CoCreateInstance() etc. - * The pointer arithmetic is a compile-time pointer type check that 'obj' - * really is a 'type **', but is intended to have no effect at runtime. */ +/* Type-checking macro to provide arguments for CoCreateInstance() + * etc, ensuring that 'obj' really is a 'type **'. */ +#define typecheck(checkexpr, result) \ + (sizeof(checkexpr) ? (result) : (result)) #define COMPTR(type, obj) &IID_##type, \ - (void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \ - - (sizeof((obj)-(type **)(obj)))) + typecheck((obj)-(type **)(obj), (void **)(void *)(obj)) static char putty_path[2048]; @@ -410,16 +410,20 @@ static IShellLink *make_shell_link(const char *appname, /* Check if this is a valid session, otherwise don't add. */ if (sessionname) { psettings_tmp = open_settings_r(sessionname); - if (!psettings_tmp) + if (!psettings_tmp) { + sfree(app_path); return NULL; + } close_settings_r(psettings_tmp); } /* Create the new item. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, - COMPTR(IShellLink, &ret)))) + COMPTR(IShellLink, &ret)))) { + sfree(app_path); return NULL; + } /* Set path, parameters, icon and description. */ ret->lpVtbl->SetPath(ret, app_path); diff --git a/putty/WINDOWS/WINMISC.C b/putty/WINDOWS/WINMISC.C index e70e77e..7e3b24d 100644 --- a/putty/WINDOWS/WINMISC.C +++ b/putty/WINDOWS/WINMISC.C @@ -14,28 +14,69 @@ char *platform_get_x_display(void) { return dupstr(getenv("DISPLAY")); } -Filename filename_from_str(const char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } +Filename *filename_copy(const Filename *fn) +{ + return filename_from_str(fn->path); +} + const char *filename_to_str(const Filename *fn) { return fn->path; } -int filename_equal(Filename f1, Filename f2) +int filename_equal(const Filename *f1, const Filename *f2) +{ + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) { - return !strcmp(f1.path, f2.path); + return !*fn->path; } -int filename_is_null(Filename fn) +void filename_free(Filename *fn) +{ + sfree(fn->path); + sfree(fn); +} + +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) { - return !*fn.path; + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); +} + +#ifndef NO_SECUREZEROMEMORY +/* + * Windows implementation of smemclr (see misc.c) using SecureZeroMemory. + */ +void smemclr(void *b, size_t n) { + if (b && n > 0) + SecureZeroMemory(b, n); } +#endif char *get_username(void) { @@ -132,6 +173,69 @@ HMODULE load_system32_dll(const char *libname) return ret; } +/* + * A tree234 containing mappings from system error codes to strings. + */ + +struct errstring { + int error; + char *text; +}; + +static int errstring_find(void *av, void *bv) +{ + int *a = (int *)av; + struct errstring *b = (struct errstring *)bv; + if (*a < b->error) + return -1; + if (*a > b->error) + return +1; + return 0; +} +static int errstring_compare(void *av, void *bv) +{ + struct errstring *a = (struct errstring *)av; + return errstring_find(&a->error, bv); +} + +static tree234 *errstrings = NULL; + +const char *win_strerror(int error) +{ + struct errstring *es; + + if (!errstrings) + errstrings = newtree234(errstring_compare); + + es = find234(errstrings, &error, errstring_find); + + if (!es) { + int bufsize; + + es = snew(struct errstring); + es->error = error; + /* maximum size for FormatMessage is 64K */ + bufsize = 65535; + es->text = snewn(bufsize, char); + if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + es->text, bufsize, NULL)) { + sprintf(es->text, + "Windows error code %d (and FormatMessage returned %d)", + error, GetLastError()); + } else { + int len = strlen(es->text); + if (len > 0 && es->text[len-1] == '\n') + es->text[len-1] = '\0'; + } + es->text = sresize(es->text, strlen(es->text) + 1, char); + add234(errstrings, es); + } + + return es->text; +} + #ifdef DEBUG static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; @@ -379,3 +483,51 @@ void *minefield_c_realloc(void *p, size_t size) } #endif /* MINEFIELD */ + +FontSpec *fontspec_new(const char *name, + int bold, int height, int charset) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + f->isbold = bold; + f->height = height; + f->charset = charset; + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name, f->isbold, f->height, f->charset); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->name) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->name); + PUT_32BIT_MSB_FIRST(data + len, f->isbold); + PUT_32BIT_MSB_FIRST(data + len + 4, f->height); + PUT_32BIT_MSB_FIRST(data + len + 8, f->charset); + } + return len + 12; /* also include three 4-byte ints */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end; + if (maxsize < 13) + return NULL; + end = memchr(data, '\0', maxsize-12); + if (!end) + return NULL; + end++; + *used = end - data + 12; + return fontspec_new(data, + GET_32BIT_MSB_FIRST(end), + GET_32BIT_MSB_FIRST(end + 4), + GET_32BIT_MSB_FIRST(end + 8)); +} diff --git a/putty/WINDOWS/WINNET.C b/putty/WINDOWS/WINNET.C index c5445c5..60e77ed 100644 --- a/putty/WINDOWS/WINNET.C +++ b/putty/WINDOWS/WINNET.C @@ -64,6 +64,7 @@ struct Socket_tag { char oobdata[1]; int sending_oob; int oobinline, nodelay, keepalive, privport; + enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; SockAddr addr; SockAddrStep step; int port; @@ -167,6 +168,7 @@ DECL_WINDOWS_FUNCTION(static, int, setsockopt, DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int)); DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); +DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int)); DECL_WINDOWS_FUNCTION(static, int, ioctlsocket, (SOCKET, long, u_long FAR *)); DECL_WINDOWS_FUNCTION(static, SOCKET, accept, @@ -291,6 +293,7 @@ void sk_init(void) GET_WINDOWS_FUNCTION(winsock_module, socket); GET_WINDOWS_FUNCTION(winsock_module, listen); GET_WINDOWS_FUNCTION(winsock_module, send); + GET_WINDOWS_FUNCTION(winsock_module, shutdown); GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket); GET_WINDOWS_FUNCTION(winsock_module, accept); GET_WINDOWS_FUNCTION(winsock_module, recv); @@ -329,8 +332,38 @@ void sk_cleanup(void) #endif } +struct errstring { + int error; + char *text; +}; + +static int errstring_find(void *av, void *bv) +{ + int *a = (int *)av; + struct errstring *b = (struct errstring *)bv; + if (*a < b->error) + return -1; + if (*a > b->error) + return +1; + return 0; +} +static int errstring_compare(void *av, void *bv) +{ + struct errstring *a = (struct errstring *)av; + return errstring_find(&a->error, bv); +} + +static tree234 *errstrings = NULL; + char *winsock_error_string(int error) { + const char prefix[] = "Network error: "; + struct errstring *es; + + /* + * Error codes we know about and have historically had reasonably + * sensible error messages for. + */ switch (error) { case WSAEACCES: return "Network error: Permission denied"; @@ -403,9 +436,53 @@ char *winsock_error_string(int error) return "Network error: Resource temporarily unavailable"; case WSAEDISCON: return "Network error: Graceful shutdown in progress"; - default: - return "Unknown network error"; } + + /* + * Generic code to handle any other error. + * + * Slightly nasty hack here: we want to return a static string + * which the caller will never have to worry about freeing, but on + * the other hand if we call FormatMessage to get it then it will + * want to either allocate a buffer or write into one we own. + * + * So what we do is to maintain a tree234 of error strings we've + * already used. New ones are allocated from the heap, but then + * put in this tree and kept forever. + */ + + if (!errstrings) + errstrings = newtree234(errstring_compare); + + es = find234(errstrings, &error, errstring_find); + + if (!es) { + int bufsize, bufused; + + es = snew(struct errstring); + es->error = error; + /* maximum size for FormatMessage is 64K */ + bufsize = 65535 + sizeof(prefix); + es->text = snewn(bufsize, char); + strcpy(es->text, prefix); + bufused = strlen(es->text); + if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + es->text + bufused, bufsize - bufused, NULL)) { + sprintf(es->text + bufused, + "Windows error code %d (and FormatMessage returned %d)", + error, GetLastError()); + } else { + int len = strlen(es->text); + if (len > 0 && es->text[len-1] == '\n') + es->text[len-1] = '\0'; + } + es->text = sresize(es->text, strlen(es->text) + 1, char); + add234(errstrings, es); + } + + return es->text; } SockAddr sk_namelookup(const char *host, char **canonicalname, @@ -595,7 +672,7 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } } -int sk_hostname_is_local(char *name) +int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || @@ -642,7 +719,7 @@ int sk_address_is_local(SockAddr addr) #ifndef NO_IPV6 if (family == AF_INET6) { - return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)step.ai->ai_addr); + return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr); } else #endif if (family == AF_INET) { @@ -664,6 +741,11 @@ int sk_address_is_local(SockAddr addr) } } +int sk_address_is_special_local(SockAddr addr) +{ + return 0; /* no Unix-domain socket analogue here */ +} + int sk_addrtype(SockAddr addr) { SockAddrStep step; @@ -745,6 +827,7 @@ static void sk_tcp_flush(Socket s) static void sk_tcp_close(Socket s); static int sk_tcp_write(Socket s, const char *data, int len); static int sk_tcp_write_oob(Socket s, const char *data, int len); +static void sk_tcp_write_eof(Socket s); static void sk_tcp_set_private_ptr(Socket s, void *ptr); static void *sk_tcp_get_private_ptr(Socket s); static void sk_tcp_set_frozen(Socket s, int is_frozen); @@ -759,6 +842,7 @@ Socket sk_register(void *sock, Plug plug) sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -780,6 +864,7 @@ Socket sk_register(void *sock, Plug plug) bufchain_init(&ret->output_data); ret->writable = 1; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 1; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ @@ -1007,6 +1092,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -1028,6 +1114,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->connected = 0; /* to start with */ ret->writable = 0; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = 0; /* unused, but best init anyway */ @@ -1058,6 +1145,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, + sk_tcp_write_eof, sk_tcp_flush, sk_tcp_set_private_ptr, sk_tcp_get_private_ptr, @@ -1089,6 +1177,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, bufchain_init(&ret->output_data); ret->writable = 0; /* to start with */ ret->sending_oob = 0; + ret->outgoingeof = EOF_NO; ret->frozen = 0; ret->frozen_readable = 0; ret->localhost_only = local_host_only; @@ -1200,7 +1289,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { p_closesocket(s); - ret->error = winsock_error_string(err); + ret->error = winsock_error_string(p_WSAGetLastError()); return (Socket) ret; } @@ -1325,12 +1414,23 @@ void try_send(Actual_Socket s) } } } + + /* + * If we reach here, we've finished sending everything we might + * have needed to send. Send EOF, if we need to. + */ + if (s->outgoingeof == EOF_PENDING) { + p_shutdown(s->s, SD_SEND); + s->outgoingeof = EOF_SENT; + } } static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Add the data to the buffer list on the socket. */ @@ -1349,6 +1449,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; + assert(s->outgoingeof == EOF_NO); + /* * Replace the buffer list on the socket with the data. */ @@ -1366,6 +1468,24 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len) return s->sending_oob; } +static void sk_tcp_write_eof(Socket sock) +{ + Actual_Socket s = (Actual_Socket) sock; + + assert(s->outgoingeof == EOF_NO); + + /* + * Mark the socket as pending outgoing EOF. + */ + s->outgoingeof = EOF_PENDING; + + /* + * Now try sending from the start of the buffer list. + */ + if (s->writable) + try_send(s); +} + int select_result(WPARAM wParam, LPARAM lParam) { int ret, open; diff --git a/putty/WINDOWS/WINNOISE.C b/putty/WINDOWS/WINNOISE.C index bdf8697..ce390db 100644 --- a/putty/WINDOWS/WINNOISE.C +++ b/putty/WINDOWS/WINNOISE.C @@ -9,10 +9,18 @@ #include "ssh.h" #include "storage.h" +#include + +DECL_WINDOWS_FUNCTION(static, BOOL, CryptAcquireContextA, + (HCRYPTPROV *, LPCTSTR, LPCTSTR, DWORD, DWORD)); +DECL_WINDOWS_FUNCTION(static, BOOL, CryptGenRandom, + (HCRYPTPROV, DWORD, BYTE *)); +DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext, + (HCRYPTPROV, DWORD)); +static HMODULE wincrypt_module = NULL; + /* - * This function is called once, at PuTTY startup, and will do some - * seriously silly things like listing directories and getting disk - * free space and a process snapshot. + * This function is called once, at PuTTY startup. */ void noise_get_heavy(void (*func) (void *, int)) @@ -20,6 +28,7 @@ void noise_get_heavy(void (*func) (void *, int)) HANDLE srch; WIN32_FIND_DATA finddata; DWORD pid; + HCRYPTPROV crypt_provider; char winpath[MAX_PATH + 3]; GetWindowsDirectory(winpath, sizeof(winpath)); @@ -35,6 +44,24 @@ void noise_get_heavy(void (*func) (void *, int)) pid = GetCurrentProcessId(); func(&pid, sizeof(pid)); + if (!wincrypt_module) { + wincrypt_module = load_system32_dll("advapi32.dll"); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom); + GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext); + } + + if (wincrypt_module && p_CryptAcquireContextA && + p_CryptGenRandom && p_CryptReleaseContext && + p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + BYTE buf[32]; + if (p_CryptGenRandom(crypt_provider, 32, buf)) { + func(buf, sizeof(buf)); + } + p_CryptReleaseContext(crypt_provider, 0); + } + read_random_seed(func); /* Update the seed immediately, in case another instance uses it. */ random_save_seed(); diff --git a/putty/WINDOWS/WINPGEN.C b/putty/WINDOWS/WINPGEN.C index 13dfac7..37d0ae0 100644 --- a/putty/WINDOWS/WINPGEN.C +++ b/putty/WINDOWS/WINPGEN.C @@ -5,6 +5,7 @@ #include #include #include +#include #define PUTTY_DO_GLOBALS @@ -19,7 +20,7 @@ #define WM_DONEKEY (WM_APP + 1) -#define DEFAULT_KEYSIZE 1024 +#define DEFAULT_KEYSIZE 2048 static char *cmdline_keyfile = NULL; @@ -40,6 +41,22 @@ void modalfatalbox(char *fmt, ...) exit(1); } +/* + * Print a non-fatal message box and do not exit. + */ +void nonfatal(char *fmt, ...) +{ + va_list ap; + char *stuff; + + va_start(ap, fmt); + stuff = dupvprintf(fmt, ap); + va_end(ap); + MessageBox(NULL, stuff, "PuTTYgen Error", + MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + sfree(stuff); +} + /* ---------------------------------------------------------------------- * Progress report code. This is really horrible :-) */ @@ -116,10 +133,8 @@ static void progress_update(void *param, int action, int phase, int iprogress) extern char ver[]; -#define PASSPHRASE_MAXLEN 512 - struct PassphraseProcStruct { - char *passphrase; + char **passphrase; char *comment; }; @@ -129,7 +144,7 @@ struct PassphraseProcStruct { static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static char *passphrase = NULL; + static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { @@ -157,8 +172,9 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); - *passphrase = 0; - SetDlgItemText(hwnd, 102, passphrase); + burnstr(*passphrase); + *passphrase = dupstr(""); + SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -173,9 +189,8 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - GetDlgItemText(hwnd, 102, passphrase, - PASSPHRASE_MAXLEN - 1); - passphrase[PASSPHRASE_MAXLEN - 1] = '\0'; + burnstr(*passphrase); + *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } @@ -405,11 +420,11 @@ static int save_ssh1_pubkey(char *filename, struct RSAKey *key) char *dec1, *dec2; FILE *fp; - dec1 = bignum_decimal(key->exponent); - dec2 = bignum_decimal(key->modulus); fp = fopen(filename, "wb"); if (!fp) return 0; + dec1 = bignum_decimal(key->exponent); + dec2 = bignum_decimal(key->modulus); fprintf(fp, "%d %s %s %s\n", bignum_bitcount(key->modulus), dec1, dec2, key->comment); fclose(fp); @@ -615,19 +630,18 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) } void load_key_file(HWND hwnd, struct MainDlgState *state, - Filename filename, int was_import_cmd) + Filename *filename, int was_import_cmd) { - char passphrase[PASSPHRASE_MAXLEN]; + char *passphrase; int needs_pass; int type, realtype; int ret; const char *errmsg = NULL; char *comment; - struct PassphraseProcStruct pps; struct RSAKey newkey1; struct ssh2_userkey *newkey2 = NULL; - type = realtype = key_type(&filename); + type = realtype = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2 && !import_possible(type)) { @@ -646,19 +660,22 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, } comment = NULL; + passphrase = NULL; if (realtype == SSH_KEYTYPE_SSH1) - needs_pass = rsakey_encrypted(&filename, &comment); + needs_pass = rsakey_encrypted(filename, &comment); else if (realtype == SSH_KEYTYPE_SSH2) - needs_pass = - ssh2_userkey_encrypted(&filename, &comment); + needs_pass = ssh2_userkey_encrypted(filename, &comment); else - needs_pass = import_encrypted(&filename, realtype, - &comment); - pps.passphrase = passphrase; - pps.comment = comment; + needs_pass = import_encrypted(filename, realtype, &comment); do { + burnstr(passphrase); + passphrase = NULL; + if (needs_pass) { int dlgret; + struct PassphraseProcStruct pps; + pps.passphrase = &passphrase; + pps.comment = comment; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, @@ -667,22 +684,20 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, ret = -2; break; } + assert(passphrase != NULL); } else - *passphrase = '\0'; + passphrase = dupstr(""); if (type == SSH_KEYTYPE_SSH1) { if (realtype == type) - ret = loadrsakey(&filename, &newkey1, - passphrase, &errmsg); + ret = loadrsakey(filename, &newkey1, passphrase, &errmsg); else - ret = import_ssh1(&filename, realtype, - &newkey1, passphrase, &errmsg); + ret = import_ssh1(filename, realtype, &newkey1, + passphrase, &errmsg); } else { if (realtype == type) - newkey2 = ssh2_load_userkey(&filename, - passphrase, &errmsg); + newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg); else - newkey2 = import_ssh2(&filename, realtype, - passphrase, &errmsg); + newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg); if (newkey2 == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!newkey2) @@ -784,6 +799,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, MB_OK | MB_ICONINFORMATION); } } + burnstr(passphrase); } /* @@ -938,8 +954,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, /* * Load a key file if one was provided on the command line. */ - if (cmdline_keyfile) - load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0); + if (cmdline_keyfile) { + Filename *fn = filename_from_str(cmdline_keyfile); + load_key_file(hwnd, state, fn, 0); + filename_free(fn); + } return 1; case WM_MOUSEMOVE: @@ -958,7 +977,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, * Seed the entropy pool */ random_add_heavynoise(state->entropy, state->entropy_size); - memset(state->entropy, 0, state->entropy_size); + smemclr(state->entropy, state->entropy_size); sfree(state->entropy); state->collecting_entropy = FALSE; @@ -1102,8 +1121,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; - char passphrase[PASSPHRASE_MAXLEN]; - char passphrase2[PASSPHRASE_MAXLEN]; + char *passphrase, *passphrase2; int type, realtype; if (state->ssh2) @@ -1129,16 +1147,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, break; } - GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, - passphrase, sizeof(passphrase)); - GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, - passphrase2, sizeof(passphrase2)); + passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT); + passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT); if (strcmp(passphrase, passphrase2)) { MessageBox(hwnd, "The two passphrases given do not match.", "PuTTYgen Error", MB_OK | MB_ICONERROR); + burnstr(passphrase); + burnstr(passphrase2); break; } + burnstr(passphrase2); if (!*passphrase) { int ret; ret = MessageBox(hwnd, @@ -1146,8 +1165,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, "without a passphrase to protect it?", "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); - if (ret != IDYES) - break; + if (ret != IDYES) { + burnstr(passphrase); + break; + } } if (prompt_keyfile(hwnd, "Save private key as:", filename, 1, (type == realtype))) { @@ -1161,33 +1182,38 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); sfree(buffer); - if (ret != IDYES) + if (ret != IDYES) { + burnstr(passphrase); break; + } } if (state->ssh2) { - Filename fn = filename_from_str(filename); + Filename *fn = filename_from_str(filename); if (type != realtype) - ret = export_ssh2(&fn, type, &state->ssh2key, + ret = export_ssh2(fn, type, &state->ssh2key, *passphrase ? passphrase : NULL); else - ret = ssh2_save_userkey(&fn, &state->ssh2key, + ret = ssh2_save_userkey(fn, &state->ssh2key, *passphrase ? passphrase : NULL); + filename_free(fn); } else { - Filename fn = filename_from_str(filename); + Filename *fn = filename_from_str(filename); if (type != realtype) - ret = export_ssh1(&fn, type, &state->key, + ret = export_ssh1(fn, type, &state->key, *passphrase ? passphrase : NULL); else - ret = saversakey(&fn, &state->key, + ret = saversakey(fn, &state->key, *passphrase ? passphrase : NULL); + filename_free(fn); } if (ret <= 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } + burnstr(passphrase); } break; case IDC_SAVEPUB: @@ -1233,9 +1259,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, if (!state->generation_thread_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Load private key:", - filename, 0, LOWORD(wParam)==IDC_LOAD)) - load_key_file(hwnd, state, filename_from_str(filename), - LOWORD(wParam) != IDC_LOAD); + filename, 0, LOWORD(wParam)==IDC_LOAD)) { + Filename *fn = filename_from_str(filename); + load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD); + filename_free(fn); + } } break; } diff --git a/putty/WINDOWS/WINPGNT.C b/putty/WINDOWS/WINPGNT.C index ba55162..3b1b43e 100644 --- a/putty/WINDOWS/WINPGNT.C +++ b/putty/WINDOWS/WINPGNT.C @@ -159,10 +159,8 @@ struct blob { }; static int cmpkeys_ssh2_asymm(void *av, void *bv); -#define PASSPHRASE_MAXLEN 512 - struct PassphraseProcStruct { - char *passphrase; + char **passphrase; char *comment; }; @@ -176,7 +174,7 @@ static void forget_passphrases(void) { while (count234(passphrases) > 0) { char *pp = index234(passphrases, 0); - memset(pp, 0, strlen(pp)); + smemclr(pp, strlen(pp)); delpos234(passphrases, 0); free(pp); } @@ -247,7 +245,7 @@ static HWND passphrase_box; static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static char *passphrase = NULL; + static char **passphrase = NULL; struct PassphraseProcStruct *p; switch (msg) { @@ -275,8 +273,9 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, passphrase = p->passphrase; if (p->comment) SetDlgItemText(hwnd, 101, p->comment); - *passphrase = 0; - SetDlgItemText(hwnd, 102, passphrase); + burnstr(*passphrase); + *passphrase = dupstr(""); + SetDlgItemText(hwnd, 102, *passphrase); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -291,9 +290,8 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, return 0; case 102: /* edit box */ if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - GetDlgItemText(hwnd, 102, passphrase, - PASSPHRASE_MAXLEN - 1); - passphrase[PASSPHRASE_MAXLEN - 1] = '\0'; + burnstr(*passphrase); + *passphrase = GetDlgItemText_alloc(hwnd, 102); } return 0; } @@ -355,28 +353,27 @@ static void keylist_update(void) 0, (LPARAM) listentry); } for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) { - char listentry[512], *p; - int len; + char *listentry, *p; + int fp_len; /* * Replace two spaces in the fingerprint with tabs, for * nice alignment in the box. */ p = skey->alg->fingerprint(skey->data); - strncpy(listentry, p, sizeof(listentry)); + listentry = dupprintf("%s\t%s", p, skey->comment); + fp_len = strlen(listentry); + sfree(p); + p = strchr(listentry, ' '); - if (p) + if (p && p < listentry + fp_len) *p = '\t'; p = strchr(listentry, ' '); - if (p) + if (p && p < listentry + fp_len) *p = '\t'; - len = strlen(listentry); - if (len < sizeof(listentry) - 2) { - listentry[len] = '\t'; - strncpy(listentry + len + 1, skey->comment, - sizeof(listentry) - len - 1); - } + SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); + sfree(listentry); } SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0); } @@ -385,9 +382,9 @@ static void keylist_update(void) /* * This function loads a key from a file and adds it. */ -static void add_keyfile(Filename filename) +static void add_keyfile(Filename *filename) { - char passphrase[PASSPHRASE_MAXLEN]; + char *passphrase; struct RSAKey *rkey = NULL; struct ssh2_userkey *skey = NULL; int needs_pass; @@ -395,11 +392,10 @@ static void add_keyfile(Filename filename) int attempts; char *comment; const char *error = NULL; - struct PassphraseProcStruct pps; int type; int original_pass; - type = key_type(&filename); + type = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { char *msg = dupprintf("Couldn't load this key (%s)", key_type_to_str(type)); @@ -419,7 +415,7 @@ static void add_keyfile(Filename filename) int i, nkeys, bloblen, keylistlen; if (type == SSH_KEYTYPE_SSH1) { - if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL, &error)) { + if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { char *msg = dupprintf("Couldn't load private key (%s)", error); message_box(msg, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); @@ -429,7 +425,7 @@ static void add_keyfile(Filename filename) keylist = get_keylist1(&keylistlen); } else { unsigned char *blob2; - blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, + blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, NULL, &error); if (!blob) { char *msg = dupprintf("Couldn't load private key (%s)", error); @@ -453,7 +449,12 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - nkeys = GET_32BIT(keylist); + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + MessageBox(NULL, "Received broken key list?!", APPNAME, + MB_OK | MB_ICONERROR); + return; + } p = keylist + 4; keylistlen -= 4; @@ -481,8 +482,8 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - n = 4 + GET_32BIT(p); - if (keylistlen < n) { + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; @@ -498,8 +499,8 @@ static void add_keyfile(Filename filename) MB_OK | MB_ICONERROR); return; } - n = 4 + GET_32BIT(p); - if (keylistlen < n) { + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { MessageBox(NULL, "Received broken key list?!", APPNAME, MB_OK | MB_ICONERROR); return; @@ -517,23 +518,30 @@ static void add_keyfile(Filename filename) error = NULL; if (type == SSH_KEYTYPE_SSH1) - needs_pass = rsakey_encrypted(&filename, &comment); + needs_pass = rsakey_encrypted(filename, &comment); else - needs_pass = ssh2_userkey_encrypted(&filename, &comment); + needs_pass = ssh2_userkey_encrypted(filename, &comment); attempts = 0; if (type == SSH_KEYTYPE_SSH1) rkey = snew(struct RSAKey); - pps.passphrase = passphrase; - pps.comment = comment; + passphrase = NULL; original_pass = 0; do { + burnstr(passphrase); + passphrase = NULL; + if (needs_pass) { /* try all the remembered passphrases first */ char *pp = index234(passphrases, attempts); if(pp) { - strcpy(passphrase, pp); + passphrase = dupstr(pp); } else { int dlgret; + struct PassphraseProcStruct pps; + + pps.passphrase = &passphrase; + pps.comment = comment; + original_pass = 1; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, (LPARAM) &pps); @@ -545,13 +553,16 @@ static void add_keyfile(Filename filename) sfree(rkey); return; /* operation cancelled */ } + + assert(passphrase != NULL); } } else - *passphrase = '\0'; + passphrase = dupstr(""); + if (type == SSH_KEYTYPE_SSH1) - ret = loadrsakey(&filename, rkey, passphrase, &error); + ret = loadrsakey(filename, rkey, passphrase, &error); else { - skey = ssh2_load_userkey(&filename, passphrase, &error); + skey = ssh2_load_userkey(filename, passphrase, &error); if (skey == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!skey) @@ -562,11 +573,14 @@ static void add_keyfile(Filename filename) attempts++; } while (ret == -1); - /* if they typed in an ok passphrase, remember it */ if(original_pass && ret) { - char *pp = dupstr(passphrase); - addpos234(passphrases, pp, 0); + /* If they typed in an ok passphrase, remember it */ + addpos234(passphrases, passphrase, 0); + } else { + /* Otherwise, destroy it */ + burnstr(passphrase); } + passphrase = NULL; if (comment) sfree(comment); @@ -797,8 +811,10 @@ static void *get_keylist1(int *length) retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; - if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) + if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { + sfree(response); return NULL; + } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); @@ -832,8 +848,10 @@ static void *get_keylist2(int *length) retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); assert(retval == 1); response = vresponse; - if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) + if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { + sfree(response); return NULL; + } ret = snewn(resplen-5, unsigned char); memcpy(ret, response+5, resplen-5); @@ -928,12 +946,17 @@ static void answer_msg(void *msg) goto failure; p += i; i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus); - if (i < 0) + if (i < 0) { + freebn(reqkey.exponent); goto failure; + } p += i; i = ssh1_read_bignum(p, msgend - p, &challenge); - if (i < 0) + if (i < 0) { + freebn(reqkey.exponent); + freebn(reqkey.modulus); goto failure; + } p += i; if (msgend < p+16) { freebn(reqkey.exponent); @@ -958,7 +981,7 @@ static void answer_msg(void *msg) MD5Init(&md5c); MD5Update(&md5c, response_source, 48); MD5Final(response_md5, &md5c); - memset(response_source, 0, 48); /* burn the evidence */ + smemclr(response_source, 48); /* burn the evidence */ freebn(response); /* and that evidence */ freebn(challenge); /* yes, and that evidence */ freebn(reqkey.exponent); /* and free some memory ... */ @@ -988,17 +1011,17 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - b.len = GET_32BIT(p); + b.len = toint(GET_32BIT(p)); + if (b.len < 0 || b.len > msgend - (p+4)) + goto failure; p += 4; - if (msgend < p+b.len) - goto failure; b.blob = p; p += b.len; if (msgend < p+4) goto failure; - datalen = GET_32BIT(p); + datalen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+datalen) + if (datalen < 0 || datalen > msgend - p) goto failure; data = p; key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm); @@ -1071,9 +1094,9 @@ static void answer_msg(void *msg) sfree(key); goto failure; } - commentlen = GET_32BIT(p); + commentlen = toint(GET_32BIT(p)); - if (msgend < p+commentlen) { + if (commentlen < 0 || commentlen > msgend - p) { freersakey(key); sfree(key); goto failure; @@ -1110,9 +1133,9 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - alglen = GET_32BIT(p); + alglen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+alglen) + if (alglen < 0 || alglen > msgend - p) goto failure; alg = p; p += alglen; @@ -1146,10 +1169,10 @@ static void answer_msg(void *msg) sfree(key); goto failure; } - commlen = GET_32BIT(p); + commlen = toint(GET_32BIT(p)); p += 4; - if (msgend < p+commlen) { + if (commlen < 0 || commlen > msgend - p) { key->alg->freekey(key->data); sfree(key); goto failure; @@ -1213,10 +1236,10 @@ static void answer_msg(void *msg) if (msgend < p+4) goto failure; - b.len = GET_32BIT(p); + b.len = toint(GET_32BIT(p)); p += 4; - if (msgend < p+b.len) + if (b.len < 0 || b.len > msgend - p) goto failure; b.blob = p; p += b.len; @@ -1423,10 +1446,12 @@ static void prompt_add_keyfile(void) of.lpstrTitle = "Select Private Key File"; of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; if (request_file(keypath, &of, TRUE, FALSE)) { - if(strlen(filelist) > of.nFileOffset) + if(strlen(filelist) > of.nFileOffset) { /* Only one filename returned? */ - add_keyfile(filename_from_str(filelist)); - else { + Filename *fn = filename_from_str(filelist); + add_keyfile(fn); + filename_free(fn); + } else { /* we are returned a bunch of strings, end to * end. first string is the directory, the * rest the filenames. terminated with an @@ -1436,7 +1461,9 @@ static void prompt_add_keyfile(void) char *filewalker = filelist + strlen(dir) + 1; while (*filewalker != '\0') { char *filename = dupcat(dir, "\\", filewalker, NULL); - add_keyfile(filename_from_str(filename)); + Filename *fn = filename_from_str(filename); + add_keyfile(fn); + filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; } @@ -1894,6 +1921,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #ifdef DEBUG_IPC debug(("couldn't get user SID\n")); #endif + CloseHandle(filemap); return 0; } @@ -1901,6 +1929,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #ifdef DEBUG_IPC debug(("couldn't get default SID\n")); #endif + CloseHandle(filemap); + sfree(ourself); return 0; } @@ -1912,6 +1942,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, debug(("couldn't get owner info for filemap: %d\n", rc)); #endif + CloseHandle(filemap); + sfree(ourself); + sfree(ourself2); return 0; } #ifdef DEBUG_IPC @@ -1930,6 +1963,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (!EqualSid(mapowner, ourself) && !EqualSid(mapowner, ourself2)) { CloseHandle(filemap); + LocalFree(psd); + sfree(ourself); + sfree(ourself2); return 0; /* security ID mismatch! */ } #ifdef DEBUG_IPC @@ -2003,7 +2039,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; - HMODULE advapi; char *command = NULL; int added_keys = 0; int argc, i; @@ -2044,8 +2079,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) "Pageant Fatal Error", MB_ICONERROR | MB_OK); return 1; #endif - } else - advapi = NULL; + } /* * See if we can find our Help file. @@ -2098,8 +2132,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-pgpfp")) { pgp_fingerprints(); - if (advapi) - FreeLibrary(advapi); return 1; } else if (!strcmp(argv[i], "-c")) { /* @@ -2113,7 +2145,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) command = ""; break; } else { - add_keyfile(filename_from_str(argv[i])); + Filename *fn = filename_from_str(argv[i]); + add_keyfile(fn); + filename_free(fn); added_keys = TRUE; } } @@ -2147,8 +2181,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); } - if (advapi) - FreeLibrary(advapi); return 0; } @@ -2228,9 +2260,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (keypath) filereq_free(keypath); - if (advapi) - FreeLibrary(advapi); - cleanup_exit(msg.wParam); return msg.wParam; /* just in case optimiser complains */ } diff --git a/putty/WINDOWS/WINPGNTC.C b/putty/WINDOWS/WINPGNTC.C index 0dabe71..c019d20 100644 --- a/putty/WINDOWS/WINPGNTC.C +++ b/putty/WINDOWS/WINPGNTC.C @@ -173,6 +173,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, return 1; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); + psa = NULL; #ifndef NO_SECURITY if (advapi_initialised || init_advapi()) { /* @@ -186,7 +187,6 @@ int agent_query(void *in, int inlen, void **out, int *outlen, */ usersid = get_user_sid(); - psa = NULL; if (usersid) { psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); @@ -209,8 +209,10 @@ int agent_query(void *in, int inlen, void **out, int *outlen, filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); - if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) + if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { + sfree(mapname); return 1; /* *out == NULL, so failure */ + } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); memcpy(p, in, inlen); cds.dwData = AGENT_COPYDATA_ID; @@ -237,6 +239,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, data->hwnd = hwnd; if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) return 0; + sfree(mapname); sfree(data); } #endif @@ -258,6 +261,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, } UnmapViewOfFile(p); CloseHandle(filemap); + sfree(mapname); if (psd) LocalFree(psd); sfree(usersid); diff --git a/putty/WINDOWS/WINPLINK.C b/putty/WINDOWS/WINPLINK.C index 7af9e1b..fc3e5b0 100644 --- a/putty/WINDOWS/WINPLINK.C +++ b/putty/WINDOWS/WINPLINK.C @@ -49,6 +49,19 @@ void modalfatalbox(char *p, ...) } cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + if (logctx) { + log_free(logctx); + logctx = NULL; + } +} void connection_fatal(void *frontend, char *p, ...) { va_list ap; @@ -83,7 +96,7 @@ WSAEVENT netevent; static Backend *back; static void *backhandle; -static Config cfg; +static Conf *conf; int term_ldisc(Terminal *term, int mode) { @@ -130,6 +143,12 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) return 0; /* not reached */ } +int from_backend_eof(void *frontend_handle) +{ + handle_write_eof(stdout_handle); + return FALSE; /* do not respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret; @@ -283,7 +302,7 @@ int main(int argc, char **argv) int errors; int got_host = FALSE; int use_subsystem = 0; - long now, next; + unsigned long now, next, then; sklist = NULL; skcount = sksize = 0; @@ -298,10 +317,11 @@ int main(int argc, char **argv) /* * Process the command line. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; - default_protocol = cfg.protocol; - default_port = cfg.port; + default_protocol = conf_get_int(conf, CONF_protocol); + default_port = conf_get_int(conf, CONF_port); errors = 0; { /* @@ -311,8 +331,10 @@ int main(int argc, char **argv) if (p) { const Backend *b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; - default_port = cfg.port = b->default_port; + default_protocol = b->protocol; + default_port = b->default_port; + conf_set_int(conf, CONF_protocol, default_protocol); + conf_set_int(conf, CONF_port, default_port); } } } @@ -320,7 +342,7 @@ int main(int argc, char **argv) char *p = *++argv; if (*p == '-') { int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, &cfg); + 1, conf); if (ret == -2) { fprintf(stderr, "plink: option \"%s\" requires an argument\n", p); @@ -332,10 +354,12 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-batch")) { console_batch_mode = 1; } else if (!strcmp(p, "-s")) { - /* Save status to write to cfg later. */ + /* Save status to write to conf later. */ use_subsystem = 1; - } else if (!strcmp(p, "-V")) { + } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); + } else if (!strcmp(p, "--help")) { + usage(); } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); @@ -344,7 +368,7 @@ int main(int argc, char **argv) errors = 1; } } else if (*p) { - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { char *q = p; /* * If the hostname starts with "telnet:", set the @@ -357,7 +381,7 @@ int main(int argc, char **argv) q += 7; if (q[0] == '/' && q[1] == '/') q += 2; - cfg.protocol = PROT_TELNET; + conf_set_int(conf, CONF_protocol, PROT_TELNET); p = q; while (*p && *p != ':' && *p != '/') p++; @@ -365,11 +389,10 @@ int main(int argc, char **argv) if (*p) *p++ = '\0'; if (c == ':') - cfg.port = atoi(p); + conf_set_int(conf, CONF_port, atoi(p)); else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_int(conf, CONF_port, -1); + conf_set_str(conf, CONF_host, q); got_host = TRUE; } else { char *r, *user, *host; @@ -384,7 +407,9 @@ int main(int argc, char **argv) *r = '\0'; b = backend_from_name(p); if (b) { - default_protocol = cfg.protocol = b->protocol; + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); portnumber = b->default_port; } p = r + 1; @@ -411,26 +436,24 @@ int main(int argc, char **argv) * same name as the hostname. */ { - Config cfg2; - do_defaults(host, &cfg2); - if (loaded_session || !cfg_launchable(&cfg2)) { + Conf *conf2 = conf_new(); + do_defaults(host, conf2); + if (loaded_session || !conf_launchable(conf2)) { /* No settings for this host; use defaults */ /* (or session was already loaded with -load) */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = default_port; + conf_set_str(conf, CONF_host, host); + conf_set_int(conf, CONF_port, default_port); got_host = TRUE; } else { - cfg = cfg2; + conf_copy_into(conf, conf2); loaded_session = TRUE; } + conf_free(conf2); } if (user) { /* Patch in specified username. */ - strncpy(cfg.username, user, - sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); } } @@ -457,9 +480,9 @@ int main(int argc, char **argv) } if (cmdlen) command[--cmdlen]='\0'; /* change trailing blank to NUL */ - cfg.remote_cmd_ptr = command; - cfg.remote_cmd_ptr2 = NULL; - cfg.nopty = TRUE; /* command => no terminal */ + conf_set_str(conf, CONF_remote_cmd, command); + conf_set_str(conf, CONF_remote_cmd2, ""); + conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */ break; /* done with cmdline */ } @@ -469,70 +492,78 @@ int main(int argc, char **argv) if (errors) return 1; - if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) { + if (!conf_launchable(conf) || !(got_host || loaded_session)) { usage(); } /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; - /* See if host is of the form user@host */ - if (cfg_launchable(&cfg)) { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); + + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } + + /* + * Trim off a colon suffix if it's there. + */ + host[strcspn(host, ":")] = '\0'; + + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; + } + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* * Perform command-line overrides on session configuration. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) - cfg.ssh_subsys = TRUE; + conf_set_int(conf, CONF_ssh_subsys, TRUE); - /* - * Trim a colon suffix off the hostname if it's there. - */ - cfg.host[strcspn(cfg.host, ":")] = '\0'; - - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; - } - cfg.host[p1] = '\0'; - } - - if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host) + if (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)) flags |= FLAG_INTERACTIVE; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = backend_from_proto(cfg.protocol); + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); @@ -543,7 +574,7 @@ int main(int argc, char **argv) * Select port. */ if (portnumber != -1) - cfg.port = portnumber; + conf_set_int(conf, CONF_port, portnumber); sk_init(); if (p_WSAEventSelect == NULL) { @@ -551,7 +582,7 @@ int main(int argc, char **argv) return 1; } - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); console_provide_logctx(logctx); /* @@ -562,11 +593,14 @@ int main(int argc, char **argv) const char *error; char *realhost; /* nodelay is only useful if stdin is a character device (console) */ - int nodelay = cfg.tcp_nodelay && + int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); - error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, - &realhost, nodelay, cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, nodelay, + conf_get_int(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); return 1; @@ -615,8 +649,12 @@ int main(int argc, char **argv) } if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; } else { ticks = INFINITE; } diff --git a/putty/WINDOWS/WINPRINT.C b/putty/WINDOWS/WINPRINT.C index 4098978..0bb7fc8 100644 --- a/putty/WINDOWS/WINPRINT.C +++ b/putty/WINDOWS/WINPRINT.C @@ -18,39 +18,39 @@ struct printer_job_tag { HANDLE hprinter; }; -static char *printer_add_enum(int param, DWORD level, char *buffer, - int offset, int *nprinters_ptr) +static int printer_add_enum(int param, DWORD level, char **buffer, + int offset, int *nprinters_ptr) { DWORD needed = 0, nprinters = 0; - buffer = sresize(buffer, offset+512, char); + *buffer = sresize(*buffer, offset+512, char); /* * Exploratory call to EnumPrinters to determine how much space * we'll need for the output. Discard the return value since it * will almost certainly be a failure due to lack of space. */ - EnumPrinters(param, NULL, level, buffer+offset, 512, + EnumPrinters(param, NULL, level, (*buffer)+offset, 512, &needed, &nprinters); if (needed < 512) needed = 512; - buffer = sresize(buffer, offset+needed, char); + *buffer = sresize(*buffer, offset+needed, char); - if (EnumPrinters(param, NULL, level, buffer+offset, + if (EnumPrinters(param, NULL, level, (*buffer)+offset, needed, &needed, &nprinters) == 0) - return NULL; + return FALSE; *nprinters_ptr += nprinters; - return buffer; + return TRUE; } printer_enum *printer_start_enum(int *nprinters_ptr) { printer_enum *ret = snew(printer_enum); - char *buffer = NULL, *retval; + char *buffer = NULL; *nprinters_ptr = 0; /* default return value */ buffer = snewn(512, char); @@ -71,12 +71,9 @@ printer_enum *printer_start_enum(int *nprinters_ptr) ret->enum_level = 4; } - retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, - ret->enum_level, buffer, 0, nprinters_ptr); - if (!retval) + if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, + ret->enum_level, &buffer, 0, nprinters_ptr)) goto error; - else - buffer = retval; switch (ret->enum_level) { case 4: diff --git a/putty/WINDOWS/WINPROXY.C b/putty/WINDOWS/WINPROXY.C index 4da4d2e..b1c5f0e 100644 --- a/putty/WINDOWS/WINPROXY.C +++ b/putty/WINDOWS/WINPROXY.C @@ -87,6 +87,13 @@ static int sk_localproxy_write_oob(Socket s, const char *data, int len) return sk_localproxy_write(s, data, len); } +static void sk_localproxy_write_eof(Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + handle_write_eof(ps->to_cmd_h); +} + static void sk_localproxy_flush(Socket s) { /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */ @@ -123,7 +130,7 @@ static const char *sk_localproxy_socket_error(Socket s) Socket platform_new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, - Plug plug, const Config *cfg) + Plug plug, Conf *conf) { char *cmd; @@ -132,6 +139,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sk_localproxy_close, sk_localproxy_write, sk_localproxy_write_oob, + sk_localproxy_write_eof, sk_localproxy_flush, sk_localproxy_set_private_ptr, sk_localproxy_get_private_ptr, @@ -145,10 +153,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname, STARTUPINFO si; PROCESS_INFORMATION pi; - if (cfg->proxy_type != PROXY_CMD) + if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) return NULL; - cmd = format_telnet_command(addr, port, cfg); + cmd = format_telnet_command(addr, port, conf); { char *msg = dupprintf("Starting local proxy command: %s", cmd); @@ -172,6 +180,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, sa.bInheritHandle = TRUE; if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) { ret->error = dupprintf("Unable to create pipes for proxy command"); + sfree(cmd); return (Socket)ret; } @@ -179,6 +188,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname, CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); ret->error = dupprintf("Unable to create pipes for proxy command"); + sfree(cmd); return (Socket)ret; } @@ -198,6 +208,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname, CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); sfree(cmd); diff --git a/putty/WINDOWS/WINSER.C b/putty/WINDOWS/WINSER.C index 1928324..2e3a907 100644 --- a/putty/WINDOWS/WINSER.C +++ b/putty/WINDOWS/WINSER.C @@ -87,7 +87,7 @@ static void serial_sentdata(struct handle *h, int new_backlog) } } -static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) +static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf) { DCB dcb; COMMTIMEOUTS timeouts; @@ -121,17 +121,17 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) /* * Configurable parameters. */ - dcb.BaudRate = cfg->serspeed; - msg = dupprintf("Configuring baud rate %d", cfg->serspeed); + dcb.BaudRate = conf_get_int(conf, CONF_serspeed); + msg = dupprintf("Configuring baud rate %d", dcb.BaudRate); logevent(serial->frontend, msg); sfree(msg); - dcb.ByteSize = cfg->serdatabits; - msg = dupprintf("Configuring %d data bits", cfg->serdatabits); + dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); + msg = dupprintf("Configuring %d data bits", dcb.ByteSize); logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serstopbits) { + switch (conf_get_int(conf, CONF_serstopbits)) { case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break; case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break; case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break; @@ -141,7 +141,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serparity) { + switch (conf_get_int(conf, CONF_serparity)) { case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break; case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break; @@ -152,7 +152,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) logevent(serial->frontend, msg); sfree(msg); - switch (cfg->serflow) { + switch (conf_get_int(conf, CONF_serflow)) { case SER_FLOW_NONE: str = "no"; break; @@ -199,13 +199,13 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) * freed by the caller. */ static const char *serial_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, int nodelay, - int keepalive) + Conf *conf, char *host, int port, + char **realhost, int nodelay, int keepalive) { Serial serial; HANDLE serport; const char *err; + char *serline; serial = snew(struct serial_backend_data); serial->port = INVALID_HANDLE_VALUE; @@ -216,8 +216,9 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, serial->frontend = frontend_handle; + serline = conf_get_str(conf, CONF_serline); { - char *msg = dupprintf("Opening serial device %s", cfg->serline); + char *msg = dupprintf("Opening serial device %s", serline); logevent(serial->frontend, msg); } @@ -246,9 +247,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, * existing configurations using \\.\ continue working.) */ char *serfilename = - dupprintf("%s%s", - strchr(cfg->serline, '\\') ? "" : "\\\\.\\", - cfg->serline); + dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); sfree(serfilename); @@ -257,7 +256,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; - err = serial_configure(serial, serport, cfg); + err = serial_configure(serial, serport, conf); if (err) return err; @@ -269,7 +268,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, HANDLE_FLAG_IGNOREEOF | HANDLE_FLAG_UNITBUFFER); - *realhost = dupstr(cfg->serline); + *realhost = dupstr(serline); /* * Specials are always available. @@ -288,12 +287,12 @@ static void serial_free(void *handle) sfree(serial); } -static void serial_reconfig(void *handle, Config *cfg) +static void serial_reconfig(void *handle, Conf *conf) { Serial serial = (Serial) handle; const char *err; - err = serial_configure(serial, serial->port, cfg); + err = serial_configure(serial, serial->port, conf); /* * FIXME: what should we do if err returns something? @@ -332,11 +331,11 @@ static void serial_size(void *handle, int width, int height) return; } -static void serbreak_timer(void *ctx, long now) +static void serbreak_timer(void *ctx, unsigned long now) { Serial serial = (Serial)ctx; - if (now >= serial->clearbreak_time && serial->port) { + if (now == serial->clearbreak_time && serial->port) { ClearCommBreak(serial->port); serial->break_in_progress = FALSE; logevent(serial->frontend, "Finished serial break"); diff --git a/putty/WINDOWS/WINSFTP.C b/putty/WINDOWS/WINSFTP.C index 5fd5b26..f3975f9 100644 --- a/putty/WINDOWS/WINSFTP.C +++ b/putty/WINDOWS/WINSFTP.C @@ -20,7 +20,7 @@ int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) return ret; } -void platform_get_x11_auth(struct X11Display *display, const Config *cfg) +void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } @@ -88,7 +88,8 @@ struct RFile { }; RFile *open_existing_file(char *name, uint64 *size, - unsigned long *mtime, unsigned long *atime) + unsigned long *mtime, unsigned long *atime, + long *perms) { HANDLE h; RFile *ret; @@ -116,6 +117,9 @@ RFile *open_existing_file(char *name, uint64 *size, TIME_WIN_TO_POSIX(wrtime, *mtime); } + if (perms) + *perms = -1; + return ret; } @@ -143,7 +147,7 @@ struct WFile { HANDLE h; }; -WFile *open_new_file(char *name) +WFile *open_new_file(char *name, long perms) { HANDLE h; WFile *ret; @@ -506,15 +510,20 @@ extern int select_result(WPARAM, LPARAM); int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; - long next, ticks; + unsigned long next, then; + long ticks; HANDLE *handles; SOCKET *sklist; int skcount; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(); if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks < 0) ticks = 0; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; } else { ticks = INFINITE; } @@ -626,7 +635,7 @@ int ssh_sftp_loop_iteration(void) if (p_WSAEventSelect == NULL) { fd_set readfds; int ret; - long now = GETTICKCOUNT(); + unsigned long now = GETTICKCOUNT(), then; if (sftp_ssh_socket == INVALID_SOCKET) return -1; /* doom */ @@ -635,13 +644,17 @@ int ssh_sftp_loop_iteration(void) select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE); do { - long next, ticks; + unsigned long next; + long ticks; struct timeval tv, *ptv; if (run_timers(now, &next)) { - ticks = next - GETTICKCOUNT(); - if (ticks <= 0) - ticks = 1; /* just in case */ + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; tv.tv_sec = ticks / 1000; tv.tv_usec = ticks % 1000 * 1000; ptv = &tv; diff --git a/putty/WINDOWS/WINSTORE.C b/putty/WINDOWS/WINSTORE.C index 13ee184..77437b4 100644 --- a/putty/WINDOWS/WINSTORE.C +++ b/putty/WINDOWS/WINSTORE.C @@ -150,17 +150,29 @@ void *open_settings_r(const char *sessionname) return (void *) sesskey; } -char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) +char *read_setting_s(void *handle, const char *key) { DWORD type, size; - size = buflen; + char *ret; - if (!handle || - RegQueryValueEx((HKEY) handle, key, 0, - &type, buffer, &size) != ERROR_SUCCESS || - type != REG_SZ) return NULL; - else - return buffer; + if (!handle) + return NULL; + + /* Find out the type and size of the data. */ + if (RegQueryValueEx((HKEY) handle, key, 0, + &type, NULL, &size) != ERROR_SUCCESS || + type != REG_SZ) + return NULL; + + ret = snewn(size+1, char); + if (RegQueryValueEx((HKEY) handle, key, 0, + &type, ret, &size) != ERROR_SUCCESS || + type != REG_SZ) { + sfree(ret); + return NULL; + } + + return ret; } int read_setting_i(void *handle, const char *key, int defvalue) @@ -177,53 +189,76 @@ int read_setting_i(void *handle, const char *key, int defvalue) return val; } -int read_setting_fontspec(void *handle, const char *name, FontSpec *result) +FontSpec *read_setting_fontspec(void *handle, const char *name) { char *settingname; - FontSpec ret; + char *fontname; + FontSpec *ret; + int isbold, height, charset; + + fontname = read_setting_s(handle, name); + if (!fontname) + return NULL; - if (!read_setting_s(handle, name, ret.name, sizeof(ret.name))) - return 0; settingname = dupcat(name, "IsBold", NULL); - ret.isbold = read_setting_i(handle, settingname, -1); + isbold = read_setting_i(handle, settingname, -1); sfree(settingname); - if (ret.isbold == -1) return 0; + if (isbold == -1) { + sfree(fontname); + return NULL; + } + settingname = dupcat(name, "CharSet", NULL); - ret.charset = read_setting_i(handle, settingname, -1); + charset = read_setting_i(handle, settingname, -1); sfree(settingname); - if (ret.charset == -1) return 0; + if (charset == -1) { + sfree(fontname); + return NULL; + } + settingname = dupcat(name, "Height", NULL); - ret.height = read_setting_i(handle, settingname, INT_MIN); + height = read_setting_i(handle, settingname, INT_MIN); sfree(settingname); - if (ret.height == INT_MIN) return 0; - *result = ret; - return 1; + if (height == INT_MIN) { + sfree(fontname); + return NULL; + } + + ret = fontspec_new(fontname, isbold, height, charset); + sfree(fontname); + return ret; } -void write_setting_fontspec(void *handle, const char *name, FontSpec font) +void write_setting_fontspec(void *handle, const char *name, FontSpec *font) { char *settingname; - write_setting_s(handle, name, font.name); + write_setting_s(handle, name, font->name); settingname = dupcat(name, "IsBold", NULL); - write_setting_i(handle, settingname, font.isbold); + write_setting_i(handle, settingname, font->isbold); sfree(settingname); settingname = dupcat(name, "CharSet", NULL); - write_setting_i(handle, settingname, font.charset); + write_setting_i(handle, settingname, font->charset); sfree(settingname); settingname = dupcat(name, "Height", NULL); - write_setting_i(handle, settingname, font.height); + write_setting_i(handle, settingname, font->height); sfree(settingname); } -int read_setting_filename(void *handle, const char *name, Filename *result) +Filename *read_setting_filename(void *handle, const char *name) { - return !!read_setting_s(handle, name, result->path, sizeof(result->path)); + char *tmp = read_setting_s(handle, name); + if (tmp) { + Filename *ret = filename_from_str(tmp); + sfree(tmp); + return ret; + } else + return NULL; } -void write_setting_filename(void *handle, const char *name, Filename result) +void write_setting_filename(void *handle, const char *name, Filename *result) { - write_setting_s(handle, name, result.path); + write_setting_s(handle, name, result->path); } void close_settings_r(void *handle) @@ -320,16 +355,18 @@ int verify_host_key(const char *hostname, int port, * Now read a saved key in from the registry and see what it * says. */ - otherstr = snewn(len, char); regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char); hostkey_regname(regname, hostname, port, keytype); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", - &rkey) != ERROR_SUCCESS) + &rkey) != ERROR_SUCCESS) { + sfree(regname); return 1; /* key does not exist in registry */ + } readlen = len; + otherstr = snewn(len, char); ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && @@ -392,6 +429,8 @@ int verify_host_key(const char *hostname, int port, RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr, strlen(otherstr) + 1); } + + sfree(oldstyle); } RegCloseKey(rkey); @@ -436,7 +475,10 @@ enum { DEL, OPEN_R, OPEN_W }; static int try_random_seed(char const *path, int action, HANDLE *ret) { if (action == DEL) { - remove(path); + if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) { + nonfatal("Unable to delete '%s': %s", path, + win_strerror(GetLastError())); + } *ret = INVALID_HANDLE_VALUE; return FALSE; /* so we'll do the next ones too */ } @@ -707,7 +749,7 @@ static int transform_jumplist_registry /* * Either return or free the result. */ - if (out) + if (out && ret == ERROR_SUCCESS) *out = old_value; else sfree(old_value); @@ -740,7 +782,7 @@ char *get_jumplist_registry_entries (void) { char *list_value; - if (transform_jumplist_registry(NULL,NULL,&list_value) != ERROR_SUCCESS) { + if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) { list_value = snewn(2, char); *list_value = '\0'; *(list_value + 1) = '\0'; diff --git a/putty/WINDOWS/WINSTUFF.H b/putty/WINDOWS/WINSTUFF.H index 81890a8..53906b3 100644 --- a/putty/WINDOWS/WINSTUFF.H +++ b/putty/WINDOWS/WINSTUFF.H @@ -16,16 +16,18 @@ #include "winhelp.h" struct Filename { - char path[FILENAME_MAX]; + char *path; }; -#define f_open(filename, mode, isprivate) ( fopen((filename).path, (mode)) ) +#define f_open(filename, mode, isprivate) ( fopen((filename)->path, (mode)) ) struct FontSpec { - char name[64]; + char *name; int isbold; int height; int charset; }; +struct FontSpec *fontspec_new(const char *name, + int bold, int height, int charset); #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 @@ -73,6 +75,10 @@ struct FontSpec { #define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) #define DF_END 0x0001 +#ifndef NO_SECUREZEROMEMORY +#define PLATFORM_HAS_SMEMCLR /* inhibit cross-platform one in misc.c */ +#endif + /* * Dynamically linked functions. These come in two flavours: * @@ -115,7 +121,7 @@ struct FontSpec { #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS -typedef struct config_tag Config; +typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif @@ -143,6 +149,7 @@ typedef struct terminal_tag Terminal; #define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ #define DEFAULT_CODEPAGE CP_ACP +#define USES_VTLINE_HACK typedef HDC Context; @@ -234,15 +241,6 @@ GLOBAL void *logctx; "All Files (*.*)\0*\0\0\0") /* - * On some versions of Windows, it has been known for WM_TIMER to - * occasionally get its callback time simply wrong, and call us - * back several minutes early. Defining these symbols enables - * compensation code in timing.c. - */ -#define TIMING_SYNC -#define TIMING_SYNC_TICKCOUNT - -/* * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on * what it can get, which means any WinSock routines used outside * that module must be exported from it as function pointers. So @@ -285,6 +283,7 @@ BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save); filereq *filereq_new(void); void filereq_free(filereq *state); int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); +char *GetDlgItemText_alloc(HWND hwnd, int id); void split_into_argv(char *, int *, char ***, char ***); /* @@ -462,6 +461,7 @@ void show_help(HWND hwnd); extern OSVERSIONINFO osVersion; BOOL init_winver(void); HMODULE load_system32_dll(const char *libname); +const char *win_strerror(int error); /* * Exports from sizetip.c. @@ -473,7 +473,7 @@ void EnableSizeTip(int bEnable); * Exports from unicode.c. */ struct unicode_data; -void init_ucs(Config *, struct unicode_data *); +void init_ucs(Conf *, struct unicode_data *); /* * Exports from winhandl.c. @@ -489,6 +489,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags); int handle_write(struct handle *h, const void *data, int len); +void handle_write_eof(struct handle *h); HANDLE *handle_get_events(int *nevents); void handle_free(struct handle *h); void handle_got_event(HANDLE event); diff --git a/putty/WINDOWS/WINUCS.C b/putty/WINDOWS/WINUCS.C index 1b72147..024e369 100644 --- a/putty/WINDOWS/WINUCS.C +++ b/putty/WINDOWS/WINUCS.C @@ -390,6 +390,8 @@ struct cp_list_item { }; static const struct cp_list_item cp_list[] = { + {"UTF-8", CP_UTF8}, + {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1}, {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2}, {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3}, @@ -406,8 +408,6 @@ static const struct cp_list_item cp_list[] = { {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15}, {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16}, - {"UTF-8", CP_UTF8}, - {"KOI8-U", 0, 128, koi8_u}, {"KOI8-R", 20866}, {"HP-ROMAN8", 0, 96, roman8}, @@ -427,6 +427,7 @@ static const struct cp_list_item cp_list[] = { {"CP437", 437}, {"CP620 (Mazovia)", 0, 128, mazovia}, {"CP819", 28591}, + {"CP852", 852}, {"CP878", 20866}, {"Use font encoding", -1}, @@ -436,24 +437,27 @@ static const struct cp_list_item cp_list[] = { static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr); -void init_ucs(Config *cfg, struct unicode_data *ucsdata) +void init_ucs(Conf *conf, struct unicode_data *ucsdata) { int i, j; int used_dtf = 0; char tbuf[256]; + int vtmode; for (i = 0; i < 256; i++) tbuf[i] = i; /* Decide on the Line and Font codepages */ - ucsdata->line_codepage = decode_codepage(cfg->line_codepage); + ucsdata->line_codepage = decode_codepage(conf_get_str(conf, + CONF_line_codepage)); if (ucsdata->font_codepage <= 0) { ucsdata->font_codepage=0; ucsdata->dbcs_screenfont=0; } - if (cfg->vtmode == VT_OEMONLY) { + vtmode = conf_get_int(conf, CONF_vtmode); + if (vtmode == VT_OEMONLY) { ucsdata->font_codepage = 437; ucsdata->dbcs_screenfont = 0; if (ucsdata->line_codepage <= 0) @@ -473,7 +477,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) if (ucsdata->font_codepage == 437) ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF; } - if (cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_font + 1, unitab_xterm_std, sizeof(unitab_xterm_std)); @@ -481,7 +485,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1); /* Collect CP437 ucs table for SCO acs */ - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, sizeof(ucsdata->unitab_scoacs)); else @@ -490,7 +494,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) /* Collect line set ucs table */ if (ucsdata->line_codepage == ucsdata->font_codepage && (ucsdata->dbcs_screenfont || - cfg->vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { + vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { /* For DBCS and POOR fonts force direct to font */ used_dtf = 1; @@ -560,14 +564,14 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) ucsdata->unitab_ctrl[i] = 0xFF; /* Generate line->screen direct conversion links. */ - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP); - if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) { + if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) { link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP); link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP); } @@ -581,7 +585,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata) } /* Last chance, if !unicode then try poorman links. */ - if (cfg->vtmode != VT_UNICODE) { + if (vtmode != VT_UNICODE) { static const char poorman_scoacs[] = "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* "; static const char poorman_latin1[] = @@ -1012,95 +1016,54 @@ int decode_codepage(char *cp_name) int codepage = -1; CPINFO cpinfo; - if (!*cp_name) { - /* - * Here we select a plausible default code page based on - * the locale the user is in. We wish to select an ISO code - * page or appropriate local default _rather_ than go with - * the Win125* series, because it's more important to have - * CSI and friends enabled by default than the ghastly - * Windows extra quote characters, and because it's more - * likely the user is connecting to a remote server that - * does something Unixy or VMSy and hence standards- - * compliant than that they're connecting back to a Windows - * box using horrible nonstandard charsets. - * - * Accordingly, Robert de Bath suggests a method for - * picking a default character set that runs as follows: - * first call GetACP to get the system's ANSI code page - * identifier, and translate as follows: - * - * 1250 -> ISO 8859-2 - * 1251 -> KOI8-U - * 1252 -> ISO 8859-1 - * 1253 -> ISO 8859-7 - * 1254 -> ISO 8859-9 - * 1255 -> ISO 8859-8 - * 1256 -> ISO 8859-6 - * 1257 -> ISO 8859-13 (changed from 8859-4 on advice of a Lithuanian) - * - * and for anything else, choose direct-to-font. - */ - int cp = GetACP(); - switch (cp) { - case 1250: cp_name = "ISO-8859-2"; break; - case 1251: cp_name = "KOI8-U"; break; - case 1252: cp_name = "ISO-8859-1"; break; - case 1253: cp_name = "ISO-8859-7"; break; - case 1254: cp_name = "ISO-8859-9"; break; - case 1255: cp_name = "ISO-8859-8"; break; - case 1256: cp_name = "ISO-8859-6"; break; - case 1257: cp_name = "ISO-8859-13"; break; - /* default: leave it blank, which will select -1, direct->font */ - } + if (!cp_name || !*cp_name) + return CP_UTF8; /* default */ + + for (cpi = cp_list; cpi->name; cpi++) { + s = cp_name; + d = cpi->name; + for (;;) { + while (*s && !isalnum(*s) && *s != ':') + s++; + while (*d && !isalnum(*d) && *d != ':') + d++; + if (*s == 0) { + codepage = cpi->codepage; + if (codepage == CP_UTF8) + goto break_break; + if (codepage == -1) + return codepage; + if (codepage == 0) { + codepage = 65536 + (cpi - cp_list); + goto break_break; + } + + if (GetCPInfo(codepage, &cpinfo) != 0) + goto break_break; + } + if (tolower((unsigned char)*s++) != tolower((unsigned char)*d++)) + break; + } } - if (cp_name && *cp_name) - for (cpi = cp_list; cpi->name; cpi++) { - s = cp_name; - d = cpi->name; - for (;;) { - while (*s && !isalnum(*s) && *s != ':') - s++; - while (*d && !isalnum(*d) && *d != ':') - d++; - if (*s == 0) { - codepage = cpi->codepage; - if (codepage == CP_UTF8) - goto break_break; - if (codepage == -1) - return codepage; - if (codepage == 0) { - codepage = 65536 + (cpi - cp_list); - goto break_break; - } - - if (GetCPInfo(codepage, &cpinfo) != 0) - goto break_break; - } - if (tolower(*s++) != tolower(*d++)) - break; - } - } + d = cp_name; + if (tolower((unsigned char)d[0]) == 'c' && + tolower((unsigned char)d[1]) == 'p') + d += 2; + if (tolower((unsigned char)d[0]) == 'i' && + tolower((unsigned char)d[1]) == 'b' && + tolower((unsigned char)d[2]) == 'm') + d += 3; + for (s = d; *s >= '0' && *s <= '9'; s++); + if (*s == 0 && s != d) + codepage = atoi(d); /* CP999 or IBM999 */ - if (cp_name && *cp_name) { - d = cp_name; - if (tolower(d[0]) == 'c' && tolower(d[1]) == 'p') - d += 2; - if (tolower(d[0]) == 'i' && tolower(d[1]) == 'b' - && tolower(d[2]) == 'm') - d += 3; - for (s = d; *s >= '0' && *s <= '9'; s++); - if (*s == 0 && s != d) - codepage = atoi(d); /* CP999 or IBM999 */ - - if (codepage == CP_ACP) - codepage = GetACP(); - if (codepage == CP_OEMCP) - codepage = GetOEMCP(); - if (codepage > 65535) - codepage = -2; - } + if (codepage == CP_ACP) + codepage = GetACP(); + if (codepage == CP_OEMCP) + codepage = GetOEMCP(); + if (codepage > 65535) + codepage = -2; break_break:; if (codepage != -1) { @@ -1201,7 +1164,7 @@ void get_unitab(int codepage, wchar_t * unitab, int ftype) } } -int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, +int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, char *defchr, int *defused, struct unicode_data *ucsdata) { @@ -1239,7 +1202,7 @@ int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, mbstr, mblen, defchr, defused); } -int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, +int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { return MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen); diff --git a/putty/WINDOWS/WINUTILS.C b/putty/WINDOWS/WINUTILS.C index 2daf1eb..29e65bc 100644 --- a/putty/WINDOWS/WINUTILS.C +++ b/putty/WINDOWS/WINUTILS.C @@ -151,12 +151,31 @@ void pgp_fingerprints(void) } /* - * Split a complete command line into argc/argv, attempting to do - * it exactly the same way Windows itself would do it (so that - * console utilities, which receive argc and argv from Windows, - * will have their command lines processed in the same way as GUI - * utilities which get a whole command line and must break it - * themselves). + * Handy wrapper around GetDlgItemText which doesn't make you invent + * an arbitrary length limit on the output string. Returned string is + * dynamically allocated; caller must free. + */ +char *GetDlgItemText_alloc(HWND hwnd, int id) +{ + char *ret = NULL; + int size = 0; + + do { + size = size * 4 / 3 + 512; + ret = sresize(ret, size, char); + GetDlgItemText(hwnd, id, ret, size); + } while (!memchr(ret, '\0', size-1)); + + return ret; +} + +/* + * Split a complete command line into argc/argv, attempting to do it + * exactly the same way the Visual Studio C library would do it (so + * that our console utilities, which receive argc and argv already + * broken apart by the C library, will have their command lines + * processed in the same way as the GUI utilities which get a whole + * command line and must call this function). * * Does not modify the input command line. * @@ -177,7 +196,17 @@ void split_into_argv(char *cmdline, int *argc, char ***argv, int outputargc; /* - * At first glance the rules appeared to be: + * These argument-breaking rules apply to Visual Studio 7, which + * is currently the compiler expected to be used for PuTTY. Visual + * Studio 10 has different rules, lacking the curious mod 3 + * behaviour of consecutive quotes described below; I presume they + * fixed a bug. As and when we migrate to a newer compiler, we'll + * have to adjust this to match; however, for the moment we + * faithfully imitate in our GUI utilities what our CLI utilities + * can't be prevented from doing. + * + * When I investigated this, at first glance the rules appeared to + * be: * * - Single quotes are not special characters. * diff --git a/putty/WINDOWS/WINX11.C b/putty/WINDOWS/WINX11.C index c8951b0..630fac7 100644 --- a/putty/WINDOWS/WINX11.C +++ b/putty/WINDOWS/WINX11.C @@ -9,10 +9,11 @@ #include "putty.h" #include "ssh.h" -void platform_get_x11_auth(struct X11Display *disp, const Config *cfg) +void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { - if (cfg->xauthfile.path[0]) - x11_get_auth_from_authfile(disp, cfg->xauthfile.path); + char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path; + if (xauthpath[0]) + x11_get_auth_from_authfile(disp, xauthpath); } const int platform_uses_x11_unix_by_default = FALSE; diff --git a/putty/WINDOWS/WIN_RES.RC2 b/putty/WINDOWS/WIN_RES.RC2 index e159c56..4cfaa92 100644 --- a/putty/WINDOWS/WIN_RES.RC2 +++ b/putty/WINDOWS/WIN_RES.RC2 @@ -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-2011 Simon Tatham. All rights reserved.", + CTEXT "\251 1997-2013 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-2011 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Copyright \251 1997-2013 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 diff --git a/putty/X11FWD.C b/putty/X11FWD.C index 9f22a23..70f426a 100644 --- a/putty/X11FWD.C +++ b/putty/X11FWD.C @@ -68,8 +68,7 @@ static const struct plug_function_table dummy_plug = { dummy_plug_sent, dummy_plug_accepting }; -struct X11Display *x11_setup_display(char *display, int authtype, - const Config *cfg) +struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf) { struct X11Display *disp = snew(struct X11Display); char *localcopy; @@ -166,12 +165,13 @@ struct X11Display *x11_setup_display(char *display, int authtype, disp->port = 6000 + disp->displaynum; disp->addr = name_lookup(disp->hostname, disp->port, - &disp->realhost, cfg, ADDRTYPE_UNSPEC); + &disp->realhost, conf, ADDRTYPE_UNSPEC); if ((err = sk_addr_error(disp->addr)) != NULL) { sk_addr_free(disp->addr); sfree(disp->hostname); sfree(disp->unixsocketpath); + sfree(disp); return NULL; /* FIXME: report an error */ } } @@ -249,7 +249,7 @@ struct X11Display *x11_setup_display(char *display, int authtype, disp->localauthproto = X11_NO_AUTH; disp->localauthdata = NULL; disp->localauthdatalen = 0; - platform_get_x11_auth(disp, cfg); + platform_get_x11_auth(disp, conf); return disp; } @@ -265,10 +265,10 @@ void x11_free_display(struct X11Display *disp) sfree(disp->hostname); sfree(disp->unixsocketpath); if (disp->localauthdata) - memset(disp->localauthdata, 0, disp->localauthdatalen); + smemclr(disp->localauthdata, disp->localauthdatalen); sfree(disp->localauthdata); if (disp->remoteauthdata) - memset(disp->remoteauthdata, 0, disp->remoteauthdatalen); + smemclr(disp->remoteauthdata, disp->remoteauthdatalen); sfree(disp->remoteauthdata); sfree(disp->remoteauthprotoname); sfree(disp->remoteauthdatastring); @@ -344,7 +344,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp, int len[4]; int family, protocol; int ideal_match = FALSE; - char *ourhostname = get_hostname(); + char *ourhostname; /* * Normally we should look for precisely the details specified in @@ -373,6 +373,8 @@ void x11_get_auth_from_authfile(struct X11Display *disp, if (!authfp) return; + ourhostname = get_hostname(); + /* Records in .Xauthority contain four strings of up to 64K each */ buf = snewn(65537 * 4, char); @@ -488,7 +490,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp, done: fclose(authfp); - memset(buf, 0, 65537 * 4); + smemclr(buf, 65537 * 4); sfree(buf); sfree(ourhostname); } @@ -504,13 +506,20 @@ static int x11_closing(Plug plug, const char *error_msg, int error_code, { struct X11Private *pr = (struct X11Private *) plug; - /* - * We have no way to communicate down the forwarded connection, - * so if an error occurred on the socket, we just ignore it - * and treat it like a proper close. - */ - sshfwd_close(pr->c); - x11_close(pr->s); + if (error_msg) { + /* + * Socket error. Slam the connection instantly shut. + */ + sshfwd_unclean_close(pr->c); + } else { + /* + * Ordinary EOF received on socket. Send an EOF on the SSH + * channel. + */ + if (pr->c) + sshfwd_write_eof(pr->c); + } + return 1; } @@ -558,8 +567,7 @@ int x11_get_screen_number(char *display) * also, fills the SocketsStructure */ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c, - const char *peeraddr, int peerport, - const Config *cfg) + const char *peeraddr, int peerport, Conf *conf) { static const struct plug_function_table fn_table = { x11_log, @@ -586,7 +594,7 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c, pr->s = *s = new_connection(sk_addr_dup(disp->addr), disp->realhost, disp->port, - 0, 1, 0, 0, (Plug) pr, cfg); + 0, 1, 0, 0, (Plug) pr, conf); if ((err = sk_socket_error(*s)) != NULL) { sfree(pr); return err; @@ -723,8 +731,7 @@ int x11_send(Socket s, char *data, int len) memset(reply + 8, 0, msgsize); memcpy(reply + 8, message, msglen); sshfwd_write(pr->c, (char *)reply, 8 + msgsize); - sshfwd_close(pr->c); - x11_close(s); + sshfwd_write_eof(pr->c); sfree(reply); sfree(message); return 0; @@ -789,3 +796,8 @@ int x11_send(Socket s, char *data, int len) return sk_write(s, data, len); } + +void x11_send_eof(Socket s) +{ + sk_write_eof(s); +} diff --git a/putty/x64/Release/PuTTY.dll b/putty/x64/Release/PuTTY.dll index fb6255ae4b4cbdc4b7f86bbd0ea4742c7e586ff1..f22a5b9b9abb806a68c0157e8533ba678df78625 100644 GIT binary patch literal 542208 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

    rk4R6TW1ik^BIE)w>^(R01^?^%nUr>rb% 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 Oz68E6f&X7i;Qs)y03A;N delta 266022 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

    >sN{wO(_aDo>?{7{#H- zU!{Mmd4}|VzBJN?Up2D+Qq{;fyOCLuMs#W9FMLyt+!bl$u`@JMYd4ZY4ULm&!4qvk zHE^xnz&UmUNs$I#F29Br3L`C~+bx_?<=9rJ5U}SSd#u#Lm8VZQdl`I$R~|EiVB21% zMsvZw_&lvH-u%bD_zV3zWDzB7V@+b;$8Zf%3;vOL#gW;6Bly0eSfL`{Ztk~g2j9=m z2hibsQ>aLhL4Fq9DBdplo|I%}{M{W9K>e}G(KZ=S$Qygvb=4T)hAWPqQ_%u}?0-4Y zzDoSC@>_AO;)fyO)jjK3`ps)%+PVTOoeWN^VTcTpL29;Jz-z5jLK}M*4+~e?Ud!wlu=HKH=2^; zq?9@-#a0he?13gLjPQO|B8=xhx#9P_)hJClQoytSR{Ld%yp-9`UU{C(v#8H{OeK#| ziBhxMC}scY(c~LKKErkVN;M^%Qxw6NsMMK73S)mH8yI`f?_lh<+rU^d--K)Y)+fT~ zXF=JGcF{+rC@AxmN7yEoYaXWpYYogoeL2<=HK@vb-q6z3AOmxad$pSVw-Un_2~_(B zmyC8Lv8<%~P|`t5hO`q5?1Jo`V2)M2Sk_F2yn2INrV&cf$zaWHPIj-6I$4zzVvcKf zUo^Yg8nfIf32|XDFI9*C0`m2mS9lJDd+0SfBP|dv)N6JDTi|E`u6pA;tJNG{o$Va# zay($- z*=LbP4I`ume<*FFQ~`Dm!pXMu(3fr|iv;q}Yv1BYf_Yp-jZJ~y0401sgQOWAVMc0e zvPoqvbl_QbEAaG-z;jdv$eD=DRk5y3cF~nm6gXx^;fSU3YHAR*`T4D4%x9-J;FYNX zn~tnIAE%1&U)9RjNGIK@FB4tVNadL|@oT(s(@eq<>#sa1qf4kVsk0|^XHsHu*$%72 zQghw@_di$iRlnkY;<(oGM zdyFn%dp)^e@vW)+YsIs91BKX`M*Gm0pN3YV$34m~Q#B!b>`l9&iRwbK zP9#{L7DS|kSSbAA-?M?}V=W+M8`8}g6?)AMmiI?T$cG;U~%K#0!#&7 zi5M_HtMQ!RoI$}kTqR>)7Vi!D zAMwFjIEaI;;GChsIXTv2vl8M8@?KOoS?e_t7|R#BS8{=>CocgV$n-srh_13!QmtW4 z@nQ|tYhMQfQQf5{zK~I;egdmi>WWv@k1Ao~WYjkjc%_(7Bz8TV=gD9-6AXySDg3nB zR<&b0(C%2fT`_}CRZT0fLCYjQNUG~z(|;ho+qfOprpHB!9xR_P$h`Uz1_389q{@q^ zlBk7g@hp|Nxjj;2omg-VJuLl+R65G3^zm9%segxcl~ZZ5RQlt^&6RFr6)!To4@|j> zRyZG0Z0xaSCyRY&D7Yg;WJjzg@@R{75l`XD7HqJmJ7ugT_lQVL`}aslyziiBcW+8v zzEGJ@B&qmI7)7!R=rvfzj8C<;%>)A2dLjkaBIuZF`L@G@VNux_-g-^ClkyT3B8{ze zH6krj9x0F+1M~7e>rDo{kye4h>hSu+@&&S{aJg$fs<ST%bm&ymL^FNe1!us=wNZ;>~Ze7o!JPK`FAQFN77rV_>cRJusy9sdrBaA5EZW$E= z?kUjDwM*7YNua&pjL_ak1tRLZ0!sKLI4TOj0~aUA+SE1HuI8c$0H4hQfQkqJUK;=c zh*0*_$!{8I)XlMUA_yW=cN7~?!1Hxg# z+xZy&JBA2V*(MUBl}$VBnnPV0`%uPl)^iO&J4*3)d%v{|?i>~6@IT)Swryie<^J$) zdSTr{2I^lyA@}MGh#O(@cs4u3uaJeKwu+k{6)X$^1VMMJjWV-hgtw>E#1VXJ&o^&q zMKV1_wYCL;Q_@zTkr7Yji;P%27M`iS5`@C#7l2@%pEfx3{{|@F?B?cI*C1w4OI7!g$>)_aj=5%XMs_lu?v$%U$PDu3J8Z z!}~^HisGRarRryg^~?&9;087%t$di>SpS&DUZFN&a=WcfvmtuBiqhDnf#z;8~GWa#}N?vuh97u_mF!Rp;No(ZelWegg&E*j32QDUo!`|W4Z{jB$S9`=HCMX!xVI5_I{ zA&0$c`eHUz+>%v3oEh_WtaRu|X)E$si##&t1n3)wwJH(Un^4+QB-&_~>CyWj=FQ;z zoG^YhL`_S~EGc6{fzX2XO?Uc|%`{qUOw3H@Y#{dy>MJYTsNR9~4DN=))4e5;c68adttw43t;+O%;3?6*v3gsj>H zb>NVRB*`l4Us(2>(IcxYZbw*){sLIrt+vn`?u!!?qt5n_QTT>r52g6er8veVV=qzo zt?EiVAu>o#kEB|+kVJw`g5>a1%$;|x?670hO(szh#$ZXYw-nGdtaWxe4y8CkMYs=S?y25?qr;4QzyYPnoAzmxT<018Gi!%pVB_V=kif<)o>G0lj7_my=D+_(bD4=DkQx|KdiSARDiRS zXZPw%K^PF-wM_A}e`w(_Hv071f1tJ6=;Q%1WFhwT8d-HybRl8&0Pqfen$nhEq1Z^T zd6SRB<_w}N4~8op8y9l87cB?XFqD-^RM)uhLM12~bCsa@^cF`@_yaSL6kYhJJb7pS@M} z?Fso|?hOvgjI0uy(w>`SkC+jWg`YQM9H2ZZ`C9BAEI$~!R@acwm@ozm*f2u z318<8zG+Ebu^&J@WYCCAm%ij@9;^I)5?Ne@dBi!u1*R{QP6mxb6wp_)@2IbgcboAC zi3H_ETtGNh>hjSO48$><BOxhQneXhSNxxjh|7Fr1HugCV|IZiWbW6S|_TMk#;A|7+0K4epQWXBb zl!0@)`tgL+U>G*Ti3e-{91@19RkB|5J>UExiw= z^^L%}&3`q3mhn*mDx+C3^pjFiG@7lwITIueouCU@#)K(HMN(iKBB4J0zjkuQSGTlJjvA(p^wtkN)@~*3n}4V-ADD^UIc%|RztIS zrB(oj(!CgiPjc0-XL3#Vi{f<5FJnFQH(JbtD&{Rr2f_3+5cIA*nA6XyySTH7&@v!Sl zx3rjVw|IC6L3dTyrU;4SsHnNUjO#ZGk- ztvM#2(A#3o!|daj!E7`MH_k=69d=Dp&^U{fAT>qcxQixiIQ$X|eA%=}$2GQ7co2Gsp-&18vf;8X?Qz%mu5^?k>kOwoRuUZz&r|1Ql$K__3_u7!ot(H^ zDL*fd+PJL{PZZlNY%X=4WH;O@bH^2=GSNim8L*_L8fEp{>;FRptR~4kQ1!@oRS?Iv zR!8fyfsn#bhFN=`Yq&~CSvE-ZxlYFHYtv3RAjl{PbAQg67Bg+oY))Uy__<^2Y^$05 zU_`BEViz-8NK}iNyynHs>!d-4-?9}<4?_nl1DGNLA(ZkANwS#fB4mx(GBV_Bgwz*Z z)C6axS|hI!0o!-b4a@_ayeAVQcE7Io&-jkWbqe4Kl+XJlMWUHMPQ&f@&vD!)~3IwkDfsG;?*(VIPe zsYVU37pS4QX`{fi^fon~Q@jHdoBnk$sGd7jsIkMUc|q+!=ru1xi{h>U{rn`-i%uiG+6g#+z6u+^B9%bz1tSPjSruHLndesPY&rQQIO9~7LKw5J z`kY}E{~qhL^i&T(n{3P&u=``Xse`jZryvW07{eMjS5Yd4=Lh zTEH=6tS!`rN{oY-P)8agLx$S0X}r)Ot3yyBrchM~Dw%Ck3Sn`Vix4!#rVM?)qrd8N z(IEQ#_?6KCVSUe%*0v#vcT%{Xy)y>I>Vb@@=^rQ`ZSj`j>h|jI4k*}Mf7*g_V>=?N zjv43SvmJPQF@#IhYkq+YfxFWEEbPBkj~!r(Co*M8qt`x3p1|AiQ(Sz-K#x(J>|D$# z;yEn1&mdcz^$D9O4MnoZiVM%?uW@A_0Z^CPJ7>Pk^Ky5)SO0;JL_o@kvj*>ysWa{S zY|*oQnFNsDVBO|tJ~dqP#WH1duk5E3=0PZ?S=R62GCB2#|6JPW>5jVmZ7J$;Vr9#Ylxr{LjIGv&34|YbQ}T!}Z8VH?hk;CT4Mp8l2VUUgnrr!E zZDu}{ppOPSsQaufFpoN6vBT>AoUC;k#4u%a-_@5`M0V$+OMa`b!0akfT})fbR)K=^ z0)cuP-_+Y52G~%K6sTjBd!-Y$Bz6b10sX9oNlsvu+(Cpx$^36Kp}t_{=(fbupJ z%_FUO(_?}S)d6?~Tpqo~f$HgTK;^$E0#-{KEWKv0R2W65y};Q(8qhsuSMiQi0d%*H zkD^sy`ZQStwc(IFK#Jz2k>Pq+%s@u>r!r3k&ySnO8<`4gv+c^KM&Q!~zQ_2cur`+= zCbU*qi=nk3Eypf7zya&y%cEe$QhT7(fDuesT#RFCP;}=o)9h~Y!MRkXdgB4UJI4>q zCBJb`>ml=Rzr}kpo?;IMvl@pJIL8&bEX&%itdeqcV#d^P!926g@%T(K=N^ym-Q&OW z1sfT0}dQf0i6vG2TOCDofy~Z?ZS|Q2nfP0j_Lx z;z{quXilWnaYQCqEoHoA_;9=4KxA{)I@uSjd0&+>oR6i6BxgpF8@qUe56qKN!3U&p zaT)$7sv*`$J}!PJHs0r)DCD${fc)GuARX#*$pUSillB;(Y&sJ!2K$&sH_vXpQMmK3jR{tq4j{ z|GW(aIf5Ief3;Pl{FX)liog|Z&>tR1l6@SdU;@YJoXeILqrZ6#*B9%f0uQqtoeOh1 zOf1awH?vA=4?R9bra#;``-U!04PE-9^&g>ODCOb>Xok7}@&z-vy)U^qSS!UEb4fym z8NXSd;Z5b`=wW%U=s6@$Fr(NANj?=xcb&>nS7CAGk&olay95H1f+~J%>TpYBYDW&1 zx(inMM!3t9M+_}b9C2Yq62a?AInP9?5k6AKlj^BBM_+lwPUIBdJW&d8#4R@b+oYyP zc$L$UBUWY8qeBQw({M0HGf5Cp+ldt!K)y zo=Sz=8f70|t+vHJWta3e)i17TeJicj)Kz@z?9pq)BvX~&qCz6nQOd3feM!mIh3{~M6#-~;`6Hf z;nXPUajFu3Y%M)!E{`evw2(Qr@K*v0iS!#!7WH+5^$G@X(HN!}7MqlJMn2{4W=fOo zC2YN=JrGfFX?uN@H@zt>*oob>RTc-2iyIUCZsR+JPN^=GwzRK}&MW4lie*d0JuY6@ zz;DHoz;A_s z`terQR~;(k63cEeWWC6NW9RLtIWw8efi9~MUepDbOqxCb6zE!xDD`qUv-);zwNh}{9S!~NH^h zs-xz>1g)EY3?~Ma-0=qPN{*{=6&o8HTf0@9-jA&Gg$Szr&E+KlB5t135Jw}l5{Z`k zS!a=`W1aKOyX?0Nd-c5De#^4oURQ5BtQYxh)Pfh=V|*~iUcb}22*&T!V6t8h$cc&W z^^Kwy91>~I(OM)Gw@-n>k!yO3c?r}7E=ISUDv8FJRAX!+vsiz7zk$VPUErus{B+aq zA$ud7mlooI^J-q*vzO|OF&th<`R_#K1)B0ZzLn^OTh&^q7JEiuQKAQQmZPqY<&L>A z=}7eoGOG-fuG=))s(Qx#8^vwteMhGw1SlizM9|XI)JRLrx;T*-NZ29QTuQauRWrs%t`q zfjy0Dc~#rl$0$`ASKpLgu((qD&5VB{{*pcd8fC0~OJ#aql&Y-D($1_X9OiAQTAKfP zG8nCboGi7^L#DHT7glaZY)59%vR)%bNhx}Dv?vW;EwElAf2PTw=>+tVn%V~^WB~Z? z3<2C;EN!(s0z_E^>g;0?P*Hfy#3u*{yz9~Uq!tndHN1kh%!`j|ea_n)YOZdSs!q1+ zq)#vMO)-T0Itw3;$$Tm(NpD>L3vkN ziBW!nPzHx9?p48z4KCnr%qE7#4q_TmuYIWsM%RC_#BR=kxtYkve#s3tmya(tmNlzb zpLGzM@lNX_ej6`>K?;OH*zCqfR3ePasEG|S@-q1Z^?y;nog~wEya?A-!Tf}EALTV9 zRzn5B;5Rn*H%1LzBXT!kR8E4~vxU~xpKmQ}DW}JGT7wjf5xF!nD z9J!RP-ga22{5EYzxJ>RYR^aXhUnkNLTOHeE>|w<{#Y$f!cxaYKwf;Mg3Bgq^CRr4L zxz;ZtFhw>Xul6RRvxP?1sw!Rg@Z${)UZs96X#F#B3i8>ytRj=wxI2ThbEx4d*NsEy z)nWimn<7sk*X8^m&US(aM9^saZ8uqpbu*`>*1-=8Wl0R348&hYRVJk^Z4?{35ojB% zKeOl$+@0vbNLSIa#Q05aI+)jZCC3`FMcKd;d?4E?LyTa>w1#bGxWzq+AlG7FxcdTeunk_EJsBN9QEk6 zm%%m0{5)e$vd?eaow}x!cPqD@u-K7^x{~~sv=yVSG_eV9-(z}|Cr4d5#AB?w^6efG z%dW3*C-nuNE1+Iq@J7}>uysvl6+GEcG>Al`^Nn0Uc4LOeAG|Tk7rZq!u)4G_>0A`Z zv|#LEY`FD@>XkXWp$<+77UAcLj6a0)s)Pwp?rua1fTi=>GKSiF4eU=GdfUN$U*4dc z?EfwXr#-uZ(=m+J5RHT$l2$eAElKt3=nE_8YsuK6odT zmPQ#Ht@hGt;fKWuqPcunTJl4SR4 zSpZmC>lE|46fMX9?d@PWe^dEz4jVC#v+@n4oeNGxUf(iPME7>IoRxPtpv(GOMK5Q@4 zHCe|O2VdW0uj@z2x;{PfPA(}CCQ2EMRZ6=3KE4+TQ4QnoVw;p{JVwJfbY>0XAvRjQ zWgR2a^A5Rjg*kbOOw3o(#1%@zn8Mptk~B5Wx{eYsY$z?9E)#W%bj{d7APguhbzPBR zV4@y#l7WvoU9X<+q48H{Ki=F>@9zi!t^fsVl|G64Afj?J3I4r_Fcc}UkL znSV&qlJleb$qw2%T|e0rXs*SrYOxiH9o7p0sb!*HPLMULNJjl)U*?>8D;@P=mQi1C zrSfo-Q4gYQonp3?NtIlrAiO4CMp4%JZn7{stRq$wSZBP=I(zW`J`3wy0uw01khe^D zM}qbo`Ibp5xJ0o~BTm67-;m%fQ@oTG`QY$QW4jneYILbFA+vr@ndC>eV(f-4cwUN! z1{bEu#5wXr2a!l5KVT=Hn??u)(!U`c4Pxi{`60im^uSVpiAUY%7q9Ct-je4FaKI2pi8G z3CY>(1L8FExVfaAmg}#a<=lKQO5J>Lm9MN$ZayeU*x(M1$|%3o*%)O#68K$mJN=>c zR;yi1p>M~br?cKT#NUx|RXi^JH;+z;A!4@3cbE;mZDYo?@Z{G8D=E9^mkP2kC$#gd zYfDe~nd}*BxzW9UYh|pqZ$ci^T2PzzvxoTx|2OesH!s9|d9F(5Eneg% z=F4HtmeuG*$eWx}=vB85JI!>7hTqECfPi!QNi%!?shY7S?GW~oFocnH%SA0DACRj~ zZ7?5guifNtB_<7{dxusEVB0w-7hZsjueO)jChgH>i7r1*rlZ`;^FpULKW zhh6eHDGA_{^W@^LXT`)GYjb^`8j?K2X3o-UUoopYXzBjYLc!sUV%jje&ubY2(z2Ec zq(OGY7eqk%bSNOr=bHj>j17`r^D5M9#-(j*eu6YQ^=&)Vsb5FgpuKQW6f}LQ|1R=~ znOd)v3!enHzof98C7}Y>il(i$l7#rB)(VX8_z%b_KunolqFyqiqY^)2iHr()=cy>a zM`Sxh)&^Fe-}L3|CDz)yGham_EMfhd_XMlMUi{0a5UNG?z{;7vjlV84#V# z);+{7nc;M97hHA*y!VnC81iqN!?}%)fa5W?vvd_q6?pU$JqYsHvMh-KI#Wnu%V6)!@PM$v$I7vKvz?*A^3OGWQePqsW}qMZ3^PpiCl0 zlC9$J9>bUEkuAX~h`N<2qD$_y{(>b|J`@mikb7i8qvJyp2o`oVLnHrAnaCm_0FrsA zi`L0sCd0dWqmWE|r>AqPTXESYEy zFgP~Z9h}y|T0a%LesEek|GM!n!yO#c+TAdQsYldghcmBqI8)rSek_xKCSzI`63={X z8oAWeCqN-A2%@r6WE+6TS)J~neZ%!*ni`46w3w0DnIkKV#LcbMczP$_8HqCoGZK$$ zj;Pp$AOcHz%|keYohrAhNTY@tyM|h+ff0E@PINBRmtJ$?T-M)8ZX=Z$eyyxUR2d$_ z%eI0^Yy?SEvI38x`O2=!VDy-;b<-|uIm3rWp&SFu#M^wN*ZkAz z`7GH&pC`g!#gdhPtQACNwV6y=eax}Fv;uQScdfO5LM-ve1ny2vtmwfAoR=#HCz6AA zcW4-`5Ffld!@A=N98|PiI*^X3M%Ec>LtQZ*8|pNeVg1A|f=u1md;|!l^;r@@k}#6E zH87nQz;st%vmp|Ut*!H*`YuXTfq7{UZA#V*`nK|l=Dz)&?g~}uRylRk6wz_sP^#HM3n6e*lay9QPuW3@s;L;X~ z^waYk(w{RB(m%;JHJi&yX z3WXjvDW*k9VdGzMpV#b`pLBeK%_q(S46UP(~ZZ45NWqv*sPnTWkH}9RXXa1ACV%MJ(F%a+9?HM4nfs3I8nGM zjc_RtJ`1Hy*ml0Hn||<)$T7#@zV?_*^jdN3W&Gg_Au)Ii*TuSU>d2zsVmNiC z@I=VkAxCeXSpexKt=DdRO;2@j33+(&c!OAIGA@QdLiO8l`A zRK5}MhiQrP{Bi7?5lBd{d5UL=D0FV%C`|Sj-q=J#7!hdkG4+Bg*Fy>>f~XNT#<0TG zq~ma{FI~WcF}4y_ZT?6Jj3AJ!9U!-H-tp{uw4d`9NQ{yEP5P)KC;-Oi))PILP!qoL zWd9Jqs8VzyDbvWE5`Qv3$<*?-q)}#iUoMHGSa!W#(3>!Jo?Tc!Q(=$smny;7u$h$= zzw;S)NKl)O>LfynE2Mwt!@b7#MprIXY71XEy=7RBOhuO= zM;Dg-s-P@Hfx+>W^5R70V2LvsJ^sTPu3IVvBg>v<6gPH^U`+CWHS5tvBI{X!MHz7w zy{eC!J^N{0wIP2nNA%;~f*Jj^=>;Q&vGHOI-YW+LtrM4u$3`6~oPO(N{Q-MrWLLNQ z@E;Q6ByQGosh?Jm`XpOEQL(h~XU!bfU+bqOtuXKGubr=b@SyoZe{E{^ z;n9GYtmYD)4CMy=sj>+bM67VzF#;7>-DjKUWoaEX*KD&mOB?DxRDHBF+jKtTOMTh> z$%ywRJ3+6>XA?{q<~7V8)!|Jz$m=yoMK(CI3CM{Bikr;!nuE9oy4Ah(ZOW$yj_BoO z*d%Um;+l*%SYV59HJ+UNEbjw?+?48H1SKC>e?>ZYjZi_nU&-JV8$<+eu-dN@5sc0S z^2xW=I*dDPa9uav!c#d(NBw&3e&J777E3GUjq|85>daP(iy$KNW?F|`4i9of(UUz8 zMML?f1XI!x8QGr@TuMB~{LMai5?UbFF1k^Q(tLR@POK;|yx6&Yvs^(PdRP==dS?yU zExKU$SqJ$EK3O`1!y@aBU~jZli z_R}vsQNxD{0@vZ+MAR_*Zns`2k`E7M`m1Ot!XZ2H{E+uLYaQl!<6!|w>U%}iVtXW= znz!D1K;>24B5&Y-$Qx(fKwf1#-EOVD7FWz#zpS1>BnS{H3oC{z=)B>&%%uoC@uwI9 z&;Lmg`1#|Cz@4)l0@rnez?1o=2t1IZe$50vnZCE!CBGSH)7R|jKNEq&upfHMRbK;P z;Vb||H>xdwu;CU+0>$=kvqFim{ZwK5>9BqEZ?OGFmgP~lpDg>wM#}OZZMI+C$66_8e2%e zWcGcvD;O!_n|kXa{fV;tNf`0FzW!Ae-8;ZW$$bo#(^>vwM$fQymHk8pB#8_9!*Tr; zH>*Jq3RUsNdT}4k1Zyqf8Zjz;2D=ekH9`)BUFCS7+zwoQNH!^Lav@Kx&+6Ay5 zH_L&(y+E(DDCx^W z4v(G<`kU;M!=)r*=3Dr8*qf2KXi;b15mH0Qwf!4m!|NS3oFQyj3>)$#fg{Ku{D^p9 z25S$=Psp{Dc;39Fl8?87@6}tn{WkuPddrcwU~QJ2IG8ggyk+?9*H<>UUJ3si?n$rti~1 zQ!U>Vk*CP8RYbl%>cOk*%p#7zJEDrFNm1~$39de!H#<>-npd|oulBG~m0S@j%9IsG zil7L(r<54Qr9$bPAzFri_l%P$-GH2KsLcbzg5-wU5zbS%H4hAT0$Manr^m@08To3$ zlaXHxpw<%3mqkZ@&-=ldkuv3=J@Tu1#*BP_+HCfSW#oVRiZkJD!%s=NxEovI-tNqWEX9R;IF`$H(lDQDhTH2W}x$t6T^Wlj%Wv)=$!}&i0iSGN%JdlH8`SCPn zwz5yMe=bS*s`jw|)$n@YjiH`c9lDLD84M>-ZpvX-#t9dc;~axfF7dyKxRn(Uwn@>= zjRF~^+mx;mx^+`@%j9E(ZrgxS=mx&KTB|S>a|)I9GGb}>w`#ok?JbfYVID@!+hhk+ zvyc2Bi7rlK!0q9idV5|*-dUt-ja~8vDG4*CF;Y(-Z#PmwL!qpl2po4ll^NarKHxxT z>?4hMn)X`WhH07&(`dWq#0X3;bO5GN5t!2c2bfyeC3p9>VY;W&8DR2TAHwEV(Fg@i zU04BgKmqe??3#Kvr}#n8=l@X%S|C*lf_e%tEBU70f>d=j1pV1AImQ9za7P;$wAfe$ z(Mt_7=P+`F2a{0#EHuQL1$gL<|qxKNrWVMpQbyy9xb&kMsR}GT^R-U$2?Q7fP zY}9|PCd{Obtr2oaxYpK>ycw>otinP4hZY>*j|A8H8AhaoIX0M&^>R8`(4G$N<(ul@ z-@dauc&1IV!@nrh+$bgCfKGJq3>K7G)DT#=L|CAq77fmvI8;j?d*o&5qgb2c%2U>& zu1jC6QGa(vY(uRCVUw@owXqdp$?%(aF@r5g&_8c7{3A?di`tnT%-`3>_b_)2)iUG9 zoBtWA4Pn82(FI!HPRD*uWF@@;#Z*jOYTs~r_{n^;ZSRC02(xx#P8C$iTKq@1vCm$P zS9-n1?@qx7C`B3L!VXcX{L@ zZJPE}y*c9|ZLRiYo!Ko$7}q=!H0^DYaiv4+RU^b!KliS2Fc_$LK*y&7=MOTL-oV<`{-oE;X3noZ`f*2`81!XBWI~b`CM1(8WYL zHh<=NXzCo_rI~FGw}Nd#Djl|&@$a*l#b0gU->iw$C zHqXfYJY3q&cGM0l_!)VEZwLjrb91bX9=1fffDVGCcF=2n&m%p4@gP0knoZdD1xm+I@P(Ibq-Jwhma+&Q6TKZOw)r%ciXWiUSBHPH2Lvb$guCfARq}AO zuUpnioG>DH6##h00GetC)3{;KNpi6F;n z0y+!Pd>K?M52d|$2zv(D+#Bm5n!M+2h;eLC{A>3BA8rWfd{&6hf{?taHNUyn1Nh5IT z9#+KODBZcAgAC!zDP=z3(Q>q#t~0CLT6gn+M{C)opbTn3DtvD2Fo#8w`7<1g%~uMv zL0yiqU@A5q7K+KWgE>B9RI1l#XWjmdIjvCZ-!V8&eYc>WZ`keW6?5exmqhdTh1#WE zhp~`iRLi##e6KPH6vFweN$FO`j7P>GRFY2q&TfkH{d-mo6qH7-FNa9bL0YyR{ZL?d zIMS3}E3xp^#hTVa^$9k!=JOQJ;zRY(S?NgaWtX>#KG(FsWI{AcW(d7ziR%X8?(uq! z0epd@+yXt6tPw8T#sRl*{!yFD{!4vIJn(~KJvt5d|A=?oVNRYki}^1}hc7;+C|JL0 zH@vo|yV=dFWnJ_uLLwYbb&99uG9ei~84}5DiL!@deaEhxyjM6YWnGxYpE%;sVDM^% zj@W!_Kav|iqL}3b&7^d}o^|sy^PVx#_`F2(b@^>dFuxw7U7{_RZ}uE$?Y;gp;6W0PMReBN$Qb=7V# zwZsMkv?p;~fQbHHN?E@j0GHYM`O@9~f=9}BtC}wipe@wIF!q+9$Z=_6+CX*IV7ql4 zpOHIHg9rNlJU?#Z#G;{`)BG&}P7vEOOH+*Z%hdtULZO2vS z)o!h~_B*eLuXFvyW#cm3MpAaY?3zvW8t)eyN_WJ9eWy*`XUN zbKGc$f&+J;D1&I{sunk#C2869xD2%kCE@}9adEqYZ7KtiDj?KoX7n`k{=;^%8=he@IsJL@)aGvnkXLTRim^F zZDNsGIZEqnUR#yW(|miB*3X%Z>w=#N9Wx8hhOz z91)3_=MEf*xAK0H!@T(?DmfiQn((Q|dX0@HZyCNlsym_SID{PXmTk3u#e8P$WCc=x z6i((+phJt3jhzLH7bMglOIz zE+knbahl?~ye}cbMXX^M*-z6cSh(s!Gu5X(e3A$lg%OnzxtttM#{WqKjH0Wy2xxh> za%U6)W1{7DH#V8MC0g4&C1+&VOVf3V#Q^K*e&No6W~WJg0+v5hM4Vk)4MaiRUo|>l*xXR_2D3yU}_)YyUZe$1oMw8&vuYsZ_5E$&{v1$ zMK1H{Se{NJG6RK&^TEwAHA=b`|}k3fSwTUyny-C4K3B zK!Is+y8S@VZRl1{Y7B4YrS_A%DgwWrsOTZ^%Yu&o6$Z*>Gq3!rLgZOFPwvBoa|`sE zd!#(a6+RaokKn7@t#f#p{3D#^(=GWb1 zzccg0QXUR*FQ@iQtKdqNA^8 z=0tlc!D#5=iBYPDMT1ojGt^J_>RG}~-Jcnag?+iVEGclrrQi1!oiUcw&MgJ63EP{# zkfx+M=~$lAF?m-fKnsWY-gwt7$lsdFX=Bx81{x(^2<4&;WFDHCYY~gQJGw-?7DYObHW--_aa$E zJK%qL6tN(WN@%|W{#5BIl9pT9m`Ea%fXGO%+93AF;cVcwUbzGzANV1@;^RmbuHO*m z-&|;X=H_lZsg~)^v#7i{^kSy4iPxw=QXMWI=QchY)?*R+&WS=SV;v$Dz{fv6g4+Ed z^>EIIX~X7y)D(`&wX@zLy=jAAZe-8z;eJ!Vu6&23iKnTKo7z!dnj5WrC{oxeQc#_M z_9>Ob`HNMCI363x#@!DytgFcyXo{a73(*XLXtcDfbfiF&U%P!vq?FYN7Mi;(6jWWV zkS+@YMY>#Sce(fsU5=9mz7lMn*5&az)_Z)AEuZEt`zzl*xzCYSD_2CpnsjEc?miW) zD{QdF0hYb(bSgcre;DA3P6BSK^hyWR$kj^uj-pfC>FqRk_9|D}`_#9DKD>G+Qu8r1Q5?eF4{>&_xsCB$%=2`2z zUg|44wZ3)CVs?yxXw6@Zw3QzjR*^(u$yOJe8zyRflYU!4MW2r}f0(Fs>a>~6u-me` zTyb(dAD&YICQN;j*0s}%l5?5lFu1eEW^l_t(sWPKy89Q%$EoV$nkK9B>dV9v%_%@@dSACp0Erja<*-a>i_Resewo(%r6d+8Ujqg|3J<_fOJV zYZs)Njgz#D!4udCh*kI|cx+|AcJ9BSkGNYMZDVv;uZ@JMRj?Ys>He|1th>QiI_>V6!iqc1;_G|o^s42aF7mprItN${&mHn8Y z9xMB~IeFzV{%MA<(0cf%s5^kZcMp5W7Bncw`ciS7E)FfRg$<`uOPwiA;?|+Nw-IBN z2%@-f6@Lu;2|p_~4wG?K?YoQS5KE1X3XGBJ!0*8dj?Tj6SttmF#s&=P1WUCZ{9G*P z6`A;Ua8SU*CPe|GQ;yFwKyi|;#N&*1VO|dr4;iOa=Dd52#6wumELv{lJUyu;!`_g$ zivBT`F~8L#$(nm5u85`jlHt%hlAkMZ&$G`DRI7D+%DwVaTj$jacUiY5v(Ng-SK^Y7 zPnZ`-rijM3^^U*PtiDG1C;oAY*0;;5ifE`+eAqp53pq=N;u+Rh^YbZMry=+9 z-CIVN66=Bd5U`3iD0@TYz0fvqb;*;`~e=gHq$+16XOt2kkILW3lz z+662TgGdy=v;;4_0<^*lN(zHRu%MWrl`w&9{>L?5Yh>9RP3}84*7+o1)zjR6jn<;mh+;(U zl;yF~%4?nb@gvi;4(8-*v;k)*)1ye0vFBih{mEq)nN8Pdh4H0IF8V7o<+Pgd9ufs?pfDp;=Ehx;^#vXLWu ztW|`WWzr~gB=R^+AbYH5RAPBY5nb~rT+z+lV4j$!75#sMr2x{O3d-zTP6MUkqcec= z50!W_C^cr&bdEdgHPxWZaQ%a2I;-_J-;7zU|MN|?TEFjI;l8x0UvP2}8Por3_~&iD zskaA&kz#Dev8Lk^yX06YDLiCFcqqz31d`FMaa2)gB$X&zU!8sDOQpxGpP>yLP?_!y z70a1He9wd9GV&WLlR}BQkm*d>JDxeax!_u@&@U=!a{26Hqa;Jj`K_2Ql##8eG!L7N zOw$FUKJ2u-nm@&vyfVx`5GLz)`BNE{NO+qfH^kj}{A{J_B5(h9g zla&`4Kg*#-dBir@#x@!53Jb`CAAIe&*wcl4g~R5h>a{De85T3S&N93R6S`=Wj>z}F zr;mHz9qf>aVNkM4d|bnwUVk7Vf7hZ!x%#o#cv3$2hRsj6Bhnf!>s>V_eq0$dCQ?_a zF>%8tH6~X6;EaiDzFR|yrrd!co%?y^d)>l3(-SMImN?#V2wz0ms1FQK2H2llbtvR~|6Gf@U zgh9M#SX1TQ7?-MK03Wifn?911Ej%Z(HU5b?LzSs;DN_&?t zVgzQ$GKz>c#8!mC+XBJDQk%Lc0^-JePQ7vvaj9Cq@cp32w^(i*=3i1z=R7PPo29MA| z?7XrOOPL)k1_}6mDK0#}U8KdW=3hsi_wbv;Z{&pNwr&1xL^1kwf_ow-&C{t0-=T*l zGJMAax&D*Y$p~;T4HyCfY=J3Yt z8u}{N4Lr>Zy1}OFY91H6+o)XDmF4CgH)(lV&;I7CH)$7ZpZ7Bl-K6zBcXdB>s-$31 zM&Ym*Gp$|-pd`XL$zY5uiFR5KNuEwV)N`E;Cx$EcGH zQkP{J|K-R{w!3xyk;*O-*(u%|9Gzutei^~Sl_?c1d;|?@Oo+d$^?><#*(Yk7{O=8z zQvhBH^!xM^RLV8G92d8ZPp@YdC{(?b>E)M2>x-G9){0SC5H2?5fqw-*)fx|Y2z2M|- z!O0oJR^$zmE>Q|eID>yv8j3i&i!nd009#UFeXyuBSmX;9dHE)JvVxOs_r~ZM>TMEA zp5RU8nHj24;-SZRXBYz{`2115_In7Vuf1Vz^8A*n&iq|^(II2=W7+H9Ka%hE>Pxnh zK`4@!f16LQ?IUl2b&Jmf25uCNhmjDbZ#AEgc_F19&W9gK{FuiP<*3+zeQSQki1f+yI6olgw zveo*YEfIz2?bhfa&UN+TCA#vz$D52D+2g$42HIh2HTE(8cmw_^#0o{oP9eOIQ8dFB zOb_^HX};j}8RhLkDm{s7Y#F?*w81?~imkbZKh|AY4jY)i+^P-HF7Ivjou}oebZBn- zrgP1k=V@Kd)C}z&?U-hExJ`Rt_`d{8T$sL6R!((nhxFsgrx(8OHkOIFv#vOfI|Ix7 z8RQ!OHo1&lCB}N|^B>I5Z_~QzZ70z;OxS4jTdlmmnk{c9xT6puSx89tk6L3HlXg%d zBGek*DWY4#@ofo>GzE|Ocw%vW{yZ+Xch8EiIIm%}Vj5wj0#4cIOy7V;uyGy4SR>3N z4yijaUu5tcXO**YACogOXt`HeJLkq|`#Tt(|4aMR|L6A0&(i)c6VUMH9?&`ubq2{l zrN5Wap4@1Td&0E9g7mnGDH0;YXz$K`Uv_g`cpk`&1eclDF4WF5FPpEWTv&)_jXib( z8#A0>=c+da6C^N6;JZ?0^fGYn_sasRB`)+CH7HC8uo#Q^KmF zGmQ1_?7DyiZ(3y@ov)qm*Ox5hqdQcPi1+X4jQlO|-k`U&DUc-iv z9Jn2Di+8{+rRtbMZj)?_t%NDmDcr~Hhw=exQ|v9_qsS@TSJX>9(?GGYM-Jc~2|qzn z?ZJ-v(iuER$eRKc2Ew+vn;({InXPBBz3EG6RL5-11DV=|+SfmtBPv*f{kXmPOU&6> zy;ioNob$8C*&T*O5A&1zi5#qWLPhEn4U9}pX~!E-Cm~6$oS>B?iP*fD-`GR63t&F- zbZ0p$S>#YWS!Ob!VFKj*^bV_ksSrS&HpbuS_@6i}Fs72MbWUHk7EV|3A$$fM%x!9f z|K%XCpuxKN_nfFb(%9WOoS(a!@EOxNMa^!r1j$-D+r07)ZBV+Ve6Fp(a!@IMk!p5 zda^U%**Wwt<;{+kSklPtGWnj6qQ~A@q%n}nL>4mH2*Ztgr5f1$p%Q);<<6bU z(sOr90HpXa`pT>0#h&hIwND}-#cS^Ruc9vr&WlJtci>l-A2~nCIzfh@qsq7(}Gj8J$R(k3!%->SkS{dm-bSKo2 zAQFKSS*}^^%(IyyHeY|nSR=6xU1QVha^{0`7Tc|3|G_T!zeZL`{{yUuFiPdOIkJk^ zcv3MOkyU1#&hT|Dqzo90aQzIwu>Id!O7c!?^HcoFe2X_lWFFnNO@+pq1d-S~I0lj4 zyk1!SYb@B@oFqF%c}hme4LTk}_p#9>v81ZVL|hQ#T|s0i9LT1M$M_neRgU)=(-m5~ z6zTqzo-Q$X?GlUEE>XPpa)&Mjul0q-0YYNo?3Q0wnL@z9crQV*aBfTZz*klz^iwlk z{K6Ihup9syiT+cR?!m&IH&iA(&c!^uo&p{&pOEjqeE&-#e)L)n+v1q>TPwZxN1l9y zEDEJfdIF;xm9~3~x?t^Gnz8!)a-!+YG8zsWwBj{d7W?z>&yqTN%rq`dp6SlN zKS$nD@4vLAd-(n5pIW(#dq@&Af6NQ$Rv+}5&H(EU9BpBIhJmT_HpzJoMJKu3 z^d|j+Yq~?8 zcEn*VPjctiyLHbeg`q24g|)jEEQ-`s@qehxuBtFJv0b8+{XfCMg>p7br^8XGdp00^ zTXIsfqGh4+Rb#R|C3uX_WP0~DD3Vrzcf1Aknk!M23b_grv&l~D2i6JbA-=zJh-w$H zA%4K{6*43*qzQ(E7u024MyJlgxL)J4W+iO_AANX%noE80+30G+gci2028-u^C?aKF zal*&$gpIQ7rpZn;RsSj)yRiX1#+pWBG$X-D?C>G*bz;S3Mnb;pS#rryUjg6|pa~!0 zPrmEvm}GGz4}YPOA9$6+>x7cw#V)c;zHR0YH4-xCA3V)O#|*hzKX$wc=3VZ!nRkNb z9=`n2Q<*o?u;^QA=b5rn$QM&+>?wu(`19Ls*HEf)>`bE-MHZLZMJ}VA#p6#Yf^(`} zLgwGbZV@Cjqr?7M#aAKK?W~PFMv_=32;k=-u1NM)5mL7c4Mv+uwc0>`rCaU5X4eVA zB^;l(>`-ySkM0DKC9oRXz@d~9b>L^S+t_Wb{TwAo24i!h4Al{2;0_Im|0>q&pFb3< zXxfFOqWyU>k?DnM%w||?f8ZX#s)ZO+ltIn^!=sovt1eNmJFPExbE1$jz?C=&*caWA zjI5Pwlcaa$S0UODq=~t0QnO{?q0AMk=gK(Xgr`^bfhCy_@WH@DV9Q2Ps+MFvNLqJM zte)4MxqG{Hyep4^jZ@s$$V6dYbFbE&4X3;B)p9t%SbwkP>GE9%$-I?iqIJ2QS@cW! z&+Kv^p21h;n8iyH7jP&StkH zM%YXPdz5R)i{>|rweD$!E`|#GA;yLh<7-DN8E1A5YB{5%I(K5BVP)@wM(+H5m92ec z2Yd-%%1lf`sId7;z&jZ*#bx^m-9txZkC>|%pIv7@9mH1Uc|oS-t;(;F0ELZQ(mCwT zSrx5)p~08)Da=U?%$uggfGQ5%w;B)$pgm8BpXDR0r?E--%`Xiyu_E4jK=9!da{>rX zraCI}N<}j64A-8&iZQ+WXOB3>bo>nmTE`!O;33zSe-I5Z?b)5eH|#K7T8}mT3%;qh z*HQLGS3J{^*XQk$w@FEOqSr<-rk{3N?sjTuC{+8sCC2yac-7rb(XNKi|a<6fpAj)GDB{~5ljRJhrO9)4|U1%?=U4}O_IoZ1T z-^cw3m~mXJQpA=`+S_*Z!ehCr^1~uM(w3q3J51J%WSZOLH21z@4D&CFF`i!GFoy8O zI}ioFm`hP=xM7>a7p;j`VLi(?#TR#&8kfeNg)z>zi{?sE7~`ut^!C%Q=w? zKwd#Y<0UXc$BOf5_d#@*=2A|Kj#tuU%$G{-4e??^@N5uz^ zAQ^0rAfZFBkn1xJ>NPil&NWw)%UYVk+HaEihJ?IW$=4iNpbwyMrPjYWG-w`H^D@ zQw1Y0wu)jTsm3Lpy2h+puH`0O>b$(kOMI&N!*c9y;Y1~rioPULS9&}jKtfi+j--ix{PKK@p(*+(wfUi07WAs#p<&y|`j@!u+M z5*uHJ*SJcZI`1mlW#Y81&SD51(bF}e&a0QGIzQN+PPcH>T-HcEd}Uiu=Hq+`TS?(K zPj&qk8pYSl3VkHgfHV@=m?Z$3Gx}?3TK_?IY_OlR%{dQhQwMh3Pb^QYk2|clK4mZK z?6JW{nui`FtYMPbZv_UFjOOd=WEH@n?E;}+iSdQoIN~$DWwFDd?bUn|0|-{*y)v)Y zN+u(e$zQMWhP>o{Qhl^O1Hs3#<562=OKU(HNNWlyR1ri4M2i(2bQoy`weYBEOXDMK zA`jFe<2E?XD2^j#6I-?drIbZk#0B>ot0U@&WzGM4&V5N!K!5Z5eLjCaH1EB;oV%TS z&bjBFdv3``sAY9#GD5HzDCc4Jl~Z5(%4rzEIx|TR3g0?9zA5ZH%Hoeayn;#p`Xe0xzG=F4dxuWUr(XP7K-{Dn}lD9a3QE|3d9 z<)VstKicD8d=O>e0{Tik;JJGbKRAaJs@|c(o0@&!!Wb9o;S*yhXsG!4VYu*7v z_NnDGhU6Zf28-O|)8NqrSx3*JorDyj$-#ZS8r6_}UC_KblX?if2W`u8==?T87%>SS zXb$@4!M{-dV3U-!TS8H&f-4!xKw92!jh&W~1Qn%!h*1qZ4uavEYV}PWLHZj$8e`;-of?8uN$20=8bJZlg9fDS z2P7o*NdpqgupNo?NPP3Hp^n4HDRdYf4b+R}i9jKH@kS|F%-KmSEqWFn!Puw%2JS?< z?&lDx**cfgC=X9~DuNS^dWtz=!$QgyORjO;eib6p(tm*!_C+MK!gGGSzzAp9Wrlv_ zhiRM4T0vPsIq$Ey7)*QN^5dJE0HAEman!eL%?bV`8^ztFC-H0ET}rbL+eX_?#~&#G2i$)n<8jwScaRqAxX4_l%(N?(&r~vG@;Y zFR)xrq+PbX;u7YE8B{Wn{kut3h;<52>XPbG-irjX|oK>RGK)gB2EZ<( zt49$o2ULc@yhV>;#?bBTu`^0Zo`G3St>}#gxqKT;NPzBZK)u~*jIQ<)K6GVU0asbT z#qxy_xVUv8m-gaQjG~f01>S^%4GJyUk3*Q) zEJ?*q3vHJ8veCryLFoNJELz4H*CiShcvybl>dYv_g~z1$W-!(2Ou`d9y0G8LfD^Da z+v!ypAUKVE7f~<_9sn<-5JP(c!$&R{HXu5`t!P(#=_PBTw`JX zdM8v^8!l`?L}90j!-YkQwkT}VKr}&^O~hp;!oWJrV&-3<$;Ufu7^XeXLUkkl*d*-x z=fO88MFwG^j~dH8Y&gcG53|^CHVjC7dye*fOA}=CV)(&Dh_&Y%Q|*+gsN>NIG$vFL z>VNS#Cu1h4MQJ6}L4KDx=qv4qH=G_^fE%eRFUU`AF+tEcip`CXGTK*4~F|Q;DM)Jr~ZwQr2eTIrtvdc@QC@xhNN8%jD;d}B*WI&pfl z=8wb6)nW2rMZgR&c8LrD{OA>cAF=+QanL9 z3|Yt@T@E*aNaBxOffr0mN@%c2_bRuUn`6|k3`UduT?Wl}=nc?~Feql1TUkuiE`U2nP_=j6VXAfs zRbi@@**i?tt~IDybu(l$1)ho}`jxhV`OHXw2l*Pb&YYht?kQqE^Xh7*%gwQ`$c*KU z*Fn)U5U28I9Egc#I|sD^5%$rza3Y=-5DF|U&sqy^gyfSa0o37INFwa&8)0tKo=-W% zkfG(FVgmCvz|=R>Y_F3R0_3z7HKh7Ta{BQqIy~z#88p?|R#YyCq;p5f1TBK7zKB?! z=a7+JMBXq_9ZcI$+RB4^t2Zni)nO+@ri9$@gxC2YP@45179YDUuB1piSVqPi^_zZnj0npxDqrg zMl3bpLw_&cM{eA24bjn9VTG`kcw@u$X4L;QN$LsG$99%>@uxRnH}LBrkKYLc%v!M` zk!}H~6>AIOfWYx0BxY|x3j~h?w1H43QXeM| z6mU90*pP&e@H)5~eMlf_OW>mcwCJCUnIvv`B0}{3@Chb~pQ3owb-}+3lGyiE zki>0>V~V&8%kJM2a$ljGL;SI0DJwee(z-A)j5zX@LlCr;R5h2B1w2}5l)kwFK8f7^ zk(kMmqaw#6s;LIxI*4Ji3j-VfLo+1$1?sbT@MRnd1Gv}giS-CYH{~KlW=1;d^*a#o9 zeA&1obPx8=F+2-KKp7?Q;3Y0loi~_ln5dyEz;7|UvDD6u;C6v+AW8@?l;Qm;d@Y5)%Ha~S zf90$Y$kZ@M?A1nRK8Cv$0j>jOdl1<|b@8tZS0X+z8KAO!&;$F{P-F2Rrh&&=JObA+ z=;#Y`xndd10V$p;&xC;%~4H-lV( z50d9hfp1zHLrp$JIQj7Wp40=fpzo=mjeo;rx<*#&TB_7ws#FMd8Nqyhf;PQie3xxEx&pd&NxumutOP6GSMXp zBD2K4r=j3`n*?>i*+wyrgSg|v9a7D#%&oxlp0AYJPYVO?v!rVGsw`gw5x^+8H;trW zo^zUt53g}IqBg{BAu{1CRcM$WK?AQc%#JgmVg56ibhsPgri@1?CmbT@xvK5)O^piX z#5aaVboiYuMe39l|yX>`P(NJ}ANmvk!sFrUb0 zcDc`BmkkE3M0Lbz5F7GrR&ux?+U*}+l>mRPn=$5?x6y+NNHJVZ79+k5a;4zhG@atG ze9(4_+t4IuaF@&Ru$pf8`GG`torh{(1!=Go-Yb)MK$fkbyVfEv+X6*0fT4mO6V?m z=HA5;bOFXKpdgZPXxf=cR9!zc%;9X!2}&rz7hlK(=^%ARJg-YjC?#_GCgC1b{=krn zPLZk732@XIz5jMgZ*|#0?CxmM`~Jl2WEA1dzyCsQodG!b4?D3qgTm-&AI!{Xzi}8= z&eU3e^fllEgb~9}VV@oM;Ngr=^gIB=)T8~l7Xb@T>dbHUpr-!VIP8Cf_Ty_s&sz~H z1yxB?u>N#GT$N_VW5d)$r8x&Gpq*MU%5{Tb~&fUz~^2;(lo$0M(S!lG_HqE+O?)Rc3S*7uLgLjH(^;tfiH z1dTHo3GR>y?xh6&t0hQ90=j^!_F=nnS{lShyF-Qmk=DObNGcS=__S8h|>G* z#Ffz9p`L^jO6!eDdkUJv-x#RFzf1OFK%Mp#yST=KARcA-GvY9uK4Uldj@?+YcR@I{ zM4ZGj@qeh_#`cl*vlriq^^d4{5X!4S4qh*(6U0OV-@>7W)Cg<=F>p842&6)75t``! z*e9PvzxQquQ~`y$xHdBADsqomsI#!K{hLH(rR*!I3Ez!vPPaA@m|gS9j0 z%-=W;eR1d?gdHpqIhnE$&5_?UEi5eOM0cLa6L4}FDep(D2~iuO8G&CuC41|pqis;1 z%LY9!HnLN3XyE~n9*=h3;LvGPqwK{AaDnE|efK!w7u*s;_%@xfEt+pL+43JH)dWdn zaANtZ7)>{GT*vx zT7G-+C=gC=b$BbMkv*XAIbdM0Itv~>4~2CSduAX|mLlRX6P+q}T$HzAkiCSg6*b#` z=Amb_;TIo_SnYQKXy~O{6k&?{W4FJAR?|M)1J7W!Zz4RWuOkoRPW8#cfXVq zi~aOfjf;J?(ZS1dK&Z|n%`zUz^azkOh_@jHtCgE<_L6)ML5Pk_6I1LZQAEjk1qNb< zZl@Jky+O;c0y88GGf5ccMhU~*C}5Z&V31idT?9?k?%O77v4(6VQohIr=72(!#6UpR znh~w|1aCO=V=uloie40Zv6Ww-uZ_Xt(%aA+RwPh`ljpzUw>WV#dojim-MoafJlgP8 z52z9STtVmq8(-PaQ%IdoJ!@Toetr&d{PpldzunJ=Wj`lTPW1E1m5u$}*vIC^E>X&R>R9);1_KgE9mu3`zq5sJ4OK1_ znv76#g-3nbk*Z)N^~#bD4NjAXwJ#>}d$}~9qi5WW1Ye-jL+4*=-TG}cmQHG`me5qs zMW)O73hht_9wBJHa<+`p7H>pl=rB`z(PBFFHCr*s(@hPz96*he0pB1XZR#L+j!D^z zYJ3E)gHFisJ1>bSZ{ty4Rh*RK!1S%?QXkGZON_#{fh-37OI6T(Sa4BJ z)N+!nr4XAg1vlM^wurH(Xz*iIjtH;0_k2X-{|psvpn-O4YccTTm7#9Yg+rMQZZ@R@ zWUIyi7{ok7MT@xrs6Dfk7?9+c;_D9ynB<QveC-` zthNJNkE4&v&;n_sHy2_DI+82g*a?<*f~wKZ8Em!``-i6O{QN2{YArlma?{qk#gN7d z%Xao|TERzQLMkgr>VQBOR8X^Bqmt%G8{TPp1Z_HAi%kURGH=H4z(^o0Qm?;*AfT%GZ7B@#Oqs0y} z5W9++%>}S%Z1%kojh%@ssA)%}Mq959wRO`Xv{lVPBsaDf>V^S@Lju(A^2VAG1`x;Q zGKk~P%Ep#0X>2UnSIWkcAs03FFerL}WHj$h@H%nE_F6u-ST;BokZ6=u8s^%2B3t~$ zvQUfTxW#WGHq_!z=Y(6_L5#$fhi1DCVA0|!_e8X~5Lr;&Lr9Gl_oV6)&hFLF;%SKE zuN2e|`#NZGZ&dQ~7L)oMAeKlFVzq4XD$D_C_GDlD17^2u@mF}G7JrV(5jc*IGq!EB zrOd!+{U934A_I@_sc8|dRmGX5q2~7B=59xHsJR1?(HP)(F=0M6_kI9Ib7xJ9XzmhZ zL0$XF(HTs2rRIM06q-95ar~7@Gxv%Kt6B@l((otQOvPyC+n5GdT6fua?ni%(wAlU2 z!XjFG92~(P{be}Bml-1-MW8RUU0V5csM_%}b1WSX7kK6R7k46krf!hv{!K zHy~-izQJtYa0`w(K4xWAoPC2+iph9UuJyfVns>1k;_n-M2CkUv8y}Wh6rw5pgg` zU?EEjGs$N@h~!|O&ISj2@&h$8%=#$8{uG%ka@|=b4MW9&!{I!_@;+$}uV#C-5iud& z`Oy(h6;JhSw7tTYSgvxiFxNZ+5v{rzs4IFu0W^jT(F3y&Nu}Gpls}El!R^Hp@R2sP zi@n5ym&T#uPJm$@0!SmiF-z7BvK?x@0VvVv7vFnP;@0Z`$-`gZQz zD)$4G6q}`BA6ITZh4!@@znX<%E<4-= z|3}iCDBxt#Z2%9+nxhZJK!9!BahZ#z^6m_lIZ}+I-VpB|#6Yf;ksR#ZZV~Gc7?v6% z@yM`bY^I9Rx=XMZjLcXj6Y?2HfR?6@Vk*)BrDOrdejS%1T!=AH%Gg5eKs2f=ghe^9 zWEmDnIRM&>;7m-4#$Ywj1>tE^zU8t@PA=$XSq&a8n=>OGtsGZNt-*zOejX)ulqeejYJx>F&H#gR%p@|-8do;J7566b3T)7|7 zM0NPbM(nzpR&YNrNus~Horh$@WE9^5az=GCLKrzPsniFXXt9WMF&HzL06AeDb{L)R zQT?{J?;@Kmn1KK%aLv&{d0YzzX3SU#&q6i{Wk0Qd^YMwT2s))r?15J<(-0Q1D`5rw z4pOMVUbCBud28C%Pqgg zzr$a1<=2F*IKc;?sq~q~>+bxA)5__vUw+=EdkNhYns#zm%IF2Su>$Q9=W3{5LU?q6 z=-&N6iEvOP(uDZ_?tPRX%(*(yTvTPb_F}q`4Wb!-?IlO~y|520{`cWsyV@=FtA~Se z!Noe_+3@u*^rA&SIe|O!^=YWltKf999m9w)T&*2yN|T06p@yv8{~HYnTaRDb`x_}j zP5qb?Jj0bUdP%!AM`QwWS~+dvh31UfxQTaJ>Pey_*Ms|u73s3A5}cOPPCVAMKlefI zl34v*Phk42J;7j6fL@Ev{)n-(FPwxBaHJP=KreEu>>G+Lg&S$bv*^ibI5p~4_60wS zm>LYEAF{AY%6l$1KgoM;I^8=G?R*5iGz^O5Ky@`hYSA&bai`xwI;laW(AITA;Q8?! zIGF;wBd|JyMi=P&3`(P^D()Bvb%lX>_F`Hfg)qRz@BLVvd6p3Ihih1MSo|G9(4yB$ z*k_3~>?{e5@|_2MW0Y@+9Ix1)Dc3gcyu~WZQREB&C0=$$pbKr|HR9_-;9atRH&g$% zMbjg?i^GDuuhv~JQ+akB-SsTuQTs1$<(}CO)}V^!s6O}C0U3IX7{*V<)L+xNzd|#F zWNpuEA&z9#HBv6I+Rs#$MV zXNehIUl@T&9RDWb!xPULnt0EXbo3s!Bm$!mkMZpccIn(057^ZFlGiTj<~rOTR^Jt> zP--Mhq6|!EiIkM)pD&|}LMZh;l7vv|GwKunyL1U#YIl3_VSM`5&=A=-G%vBMra-|bcGFV3tNGYWtxoM;aF$SvRBYn! zG~KR-WH0TC7_!*UNFa%c?A)s&*~2@-nZ%2Y05X!k!Wn(sg7|@$(ujt*dO&RB&+xD; z0XqnmQsbVYww3h3ZZ1S)3P7jcgGx|W`CbC9aG-C^Wa^I6N%*ZFjIol&4dDT5PXj~_ zYBH(**Qifpgq{aZ21_Yla53deV;^nyB|3UU<8Y%yxOpmw1y$HGmZ^e^KzzCYL}3E9 zqv2>N`b8OW7e(ww1gba(@6L_=jd3sfE96_KG-H<{7*+$ARJEe^Mf8zB_Ek;^4$n=R zgp}dYhfX9s`iH1xk4W;?y%!L)7pl(<^&uwIP5n%(1g#jxo!WX95ytex`YzrKPo_P`F5?mj8yj8yOggUUi{t_c+tv> zDHJ9=&4HT&mZpqR;vOPRS3{>sJFtnRp3KiOXbF1Y6YP_Tsz!{t>LfRd?%TsSMN5mN z$Pie73X(^f%tmO>6SUdC4^71ZvqM!)z>P$X`oWE0rV^O`zYhjbG=>t!+mAr1_CfRd_y`-60G2UA0$9}cOAShn3kq?HCEpy=U=lI8bhfeG#uzTTV4@qbE4lvZaCJ2 zD@a%x$BQU{pzDtJ)4}!Uus@^Oc99xF>w3V=iXnLs$oDv$g^BSjV4!GL1Kl(mN}&%p z9E#p`9wdM_l{C%EC#j5!9MppAkZq=s4X#u=K6+JGDi0z%-B*>4p+bfyI&c%k`IeXx zP&J|Xb>j03D#GZC59k99Fml}5OuD=W_l1#%KK4pL{xJBT+ow*Jo;%}R+O=?07w^=h z(^qpL{OqKgeIw&Nxp20TvR6hpwG(8hc_(>OOm=IJu>X!^^54-VIpiP_W{=4s*NIS$ zy5O-=`cWh=hj5;#opix{EP3!aQrP->51qR{;8b=Xglb97)4ShBLjZS5@WzZv$KM^; z_1vYP{=_LuFUzH+`r1>Lp_Z?<=qFBDZo1)>EkqPxBL)wMc-ig-$M0;nCW-3)`Cc7?cFX<%V1I66q@yDL7Z$8GF&@|9c=IRO=g_A>GobdsC+ z)m{RH_VVGg8gV`LwDqb-ES>ae&zd``z!!L?{BiR}RGD{y;ld>ge)nF-taol)kc9pO zA@G;U<&_XeU@VeyQaXZzECQXvX_}C}De?G&_pT{(vD>}?rQtz4%XqAkkEQalRz65| zia0p?;pck!sFaUA@9UsNd( zf5`x}o&z`X?^gajMVdCb^MZImA5$jz%B|=zUwIV&w&vd${%yyGOj~weC32u1O*ksDDyAfREvSfuXq`<7@0iJpaTcfd?hp+ z@HQ7-;$U{3h}T(e`1&fRxz`0?NZC&I=wkoZM9V7+nv3N)BbQS7^n2qwsL9UBl3KIR z=5*Zcfa9J(GWaSZ|ES4iNP^=lqi_W~c!0*phM<;i%}e+vL)YSs5>n{Wa46%R5lZ+d z(^f{LU5^2h!CM#^F&l>h1mh>>8ZjLlv|=C4?55Zz#@s|)$1crVZsLzRF@&GmDFZS# z1ig&Z0Aa(qnR=Trtssq^##oIS*+ktlsIjIc5`s!#KS4Kzxs7kr{9$}rIH=K=J6Aq5 z?<)GzhV7wSM`WIDq|X9l+|Za;-AlcMltykYZ6&4iZXrZ7<=+t69YHl`OhFKAW?^!p zF&c9684<|~KFmn@lDHxKc)dgjiwC9jq`fo=r5MsWjw?;Vt+|>;VQ#)i&tCkKN$(M` z3{tnERLEwP!1|Co=GPDeOI`Rz&%ec6b&-6eFQnmgYqQ`k`%FGoBe-4h06Gt@4&7Q= zI)UZ7trrad3}PS$L_b*#2|^>1DZ8Pa$_X?gk}~9n>#10csUCOzPd9P^(?1UQ zOa?F|Li~eHpf(`#VbgGnQ6E- zuM-Uvplwl!+)=R^qIxxj49;l;av%f*rtw6|c7d8l068H3#ZV17wI+=^3KWc`Qj9hc z^ZZ1z14f6^2Tc*5m*2RbR;O9g2`wn3;3Gm4ytsTX5&cINBBA8Ll&PUjq`pV>s}a+w z9PiHby8>UE%_jZMOG>=Dnwz1W)t(}5hPj(D@O!uRZI3!Fra$&+oK~i_ zOO4pH3jWI$l02rpKs*FoY#QK+s>iUEv5-O>$MZX5q&lQ6B=$2WDs3S#phvN+AN(a$ zQ(B3O%3sKLTen2uH))6=3gAukY1WpLW_3XI`g2cG{A3r^oGmEZ&r#0snf$c}@9_ zl*4JuNP5U`rY&p0Q!6@+UoWl_P_*rEEoh;GzD3gzM(Fn)U7(?U4G){j_8lpe;bjk2 zqW?aPCH#K?lkcDSg5cX2yQ`YN+n$1yENRgNs|RXblYe;y`Ll1us+*8W(@AcjuVj3Ye}x*+C;Zlh zz+z&ue@6*FhahSH0W}t%NjEy7p!L&Yvt@)FFsP+D}t11|PxxMd>=E zkf}M@dd_fj)EVc@^a7Bx(v1|5TjN&t;-Xle71b`S#N<$VYja})`*M6D|*z=mSJj~ad*I3 z?>V?IYXczOO=b91@?)!umf{8f(Ii?vW6^x~4(}$y2XjFi9J6uybJ~i(z6RyfO+ZXp zr$L*hA%nQ(E$VPNy5Yg~{5>=Z;x84{_k&38q<@;t$(o6-qlehYAIc5z&vLzZUUe7;9MUH#3Yw*_5Vl;tm)(7I5wv0NsV1{N(hFZm~ zbxj*AyQFwKx?~cLHK9RY-OgQd=^>*_Ub!1xG7NFtC3YhB)FrgQq{4YfD3>7PB-d#R?%l?Kyf9!H98@*_u(Zt+zN6t+*JOBV8EbW_8>PL9TS}`T1e?g(L*?^FFRxDpvw6< z0HnlRaEuSi!d3cxGl40g3!mV3?Mw01E0i^an>*~A@0r=+k-u&N2Eqj=$SXGDi69@~ znuI8)F@~?Lk5DLGMk@90zgn(Q`xSyHl3OT7GZJS1hikgo|8;PDC?G}&Y+&pX2v3lGuB!)kU`7Hex=UZ_b z6|E{Msg~}6EJknO+ImcbrOhN1K!cgW@Iv52%mzoUXC}l=yd5_h@)cESxc>y))}wvy z(YC{bE-+>w6oEA0x0b*(<~=#YZkLv=mo`9kObg&d8eQrZu^n4WbaNkCMocZoIr;9X zJNSSC4b@z6FEM9K+*!_z+>P2EFx6S)@%hgv<2as=(8p|(a05s98n^qial7G|c;`l` z-meiiP)z6=x0W$Zy2p)x=`sH>JM@FA%*|E0yG_f+d`3xHRhcJYw!FU+AM*arKSP?p zC9Q&PV|_%t`Z8INO>sM+?rf|avLKDCY;wYEq?(0DBQL+noHLO#A`8+}dsNvVYE8Oq zO~zHzH6}9}-Hj{KnSI$yPuGj6vAc0=1YS z4^slVb4G#Ctetx~HU#bfeJ4pr?|ja((0p2d>6~S-vf{dn`pI+93g`Hmk&$mJSIUZ# zoRVhHKriweVPkNbt~mS=g;b*yPBxFTjB2K>;8+eTl3^`fDT_*SjE`vk08q->oIi}L zW>-ov7dMQe+VGc=2{l2Kk!fu0Ef*|p)lg;}-VJ5uyNRe;(N~5kR|bu0W#6zumP<_uC8+uwXA%jD`HB#0 zW5@_2k=?#wQ5c3L9B;T_9c8;BBua+pXM&bH^}>smfy%Z%7xlL;T867RtGPeMMDe&R zGltX%Y|TR|KUz`sXm3LrSmc-Zj4BPz$&NGC_s zfWRYXMDU0j;Vo%Im+7xvvSbX+DU>i}XVS;zBib}fc4(n7qDE%fi=6PX5p5bKd$MVm z98v>NGwy19TsEYUArhx3WJGA(<&YXMBzjSN(;g=GN zU|r586Rd#6hxgI8B#b}t!7@vO(09S5tbIdU{N07+_#E@QCh%fbl{znNcYAl!O-$SI z3M9QP&f>Csh?g2{&~>yp%Up4mvfJ=t-{6U}DEpeEv~Q@QV3o2Yk3IXp%Ox|N`@dv5 z-!v0g>u*;O72d~n=Q>_!nlmyZVgLqBLPE}vJoaK_>W<-l=!kwWnr=xRd&%9;`#lyh z5Jn?9zRp$F)>U?wS#;e=iw{G=l@MpqD<84M$Kwh#$Tkolpg_Seoe*7UGft}^G3%~C z@S1aK|SqY~7Vt&DlL}0j6!vLGdi&2CkuR+69!D_cgY9 zP#3-+!B7FD&86zX*zATCVSN#tyf$Q@c^g}(A?x|-Zl2UTnVfG3$t))%`D?Q{xtWspa z03qCO9t9T~ZtSUy&g?XfMI;0<`nt*FcMIiMbJ{8-pr97KZp zYd2z2!1`vtJNH=LkyCjstmE|&FW7pR_g6gFOF7#@+Er+_2cL)NnX;1{Hs&A)-ODm5 zi<;Tsf%a#bxVI-XkoJlZaFe*1q-qHKq!yZ$t$iaNb0nPIR**nZ@cee)^%Bh7=m zycg|--$QI32B(bUo&KA#)!MvK zN2Wh<$@wmILsk)%9l;Tgw#?E#At~8i-*t??^#~wGmfck=w*E?rRBnWu`WU*x=tX}a zL@Q2Mfb4ExlostpfR8S|kDiA&dGUR8K3Ne}-yYwTHV6QSoS0 z4C}e`xC%S(=hrh3(%tQEJsw>=+TSkIUSWY{3;w>fWF=k1)#8^5z8mH5GQypE9yLvO z8&$VuX=g|_ZLD>3uJvi7!nTj#S%)sIr+UEN0hg8^K?n@XacOxi-i%AjcjA>VEte_9 zw#`7TP#L(^sA(B5pCK=*B2>m>P0BzKv%*C@*jU6pcvU0IxC>>#7^SsVBYWZ!y20rU z4m41-E^nmIwe6BqyEMN8DaW-czO6cGPq4~+cA$UQ67_p`ZZ(wM)G$}V4EJyzOnB^v z8AUw~^|hwu8{F#DiC=!9`c|U-F4B@$7L3GD?t)LUCPTRu0dgoe;LRAyf8aH8DF3>N zRN(+ao1BvfM^m?u;uKA|rsVMa8?yYZZ!s=W!ZfzObo`O_-g;cO4j=nSr#sRVyPE29 z{FIfu+wT8?>aK};8$;A;j#NW|Q}zx*>8iY(`WDn~jvMW7J)dS?duB;ex}b zl*&>5b|Vm;h;Vr4fumCDi+J%3C=1t+>g7)Q01xXeCDr+(+?q>8AHXSqdyL;R4@aZm zuVlB(^z)BbLL|!)hPW~Wu^#SkP|zY}8W@kyb2_)sJce5_kKdY!iTtrE>5%k12C$)7 zoOk4NeAQ%mD=m3PL|&_dS>A8Wpd2}u3QlmJrBrIQc_WWZ-*yQtb77k$oq7jdLwkYb zyU3OB$9PfGKt>qnvm6%+UCwP>ZqhG#;p?!_S@Z1`x6KFg9YBfpiduKD&M41uAS0p{Hd%u6l54D$vcKw{o? zcr!3B9NEb(kM*j|56yTE9^Rt@DC=@_b4jRNQmwiBPw8YIXJs zxXz{d7&fT7=O=0c_VqER=0jUrn2?M_l!7J$<`nIR?-4g5Sp*O`S4I*w?69T_qN$Fp zkvB`b1iLasXJP+SF(TlItT4gNu@ptXiS|n1pZrL5C`_1@a+unN?e`FaTwhKMvQBK} z6fm7yB-vKVesSM-&^KRxIZe~sn~3w`U;0GoVv4?-OZ-l=Wx{2z!2#0|;yjaXF=zQ_ zS+ccL%mRrex(iy0l|ZGygIK?l|Mhj0*Z22?S^V6ZFauAIPyKrW>4m^Q>82x6UC~WP z#8<`HPS}RCR}4x3reXUVyjpDdo(}@Ch2L>8qcicgogs~?5PeY;SZT`PU=?Pd2S*Vx z^C2U=`Mm6cEzlE5HHLT|z<~!TDyMDWv^A8rnA6sXrAW){8$*Dr0LAmefMS>-lQQ6$ zsPAaMYZiv+Xym+TL(Ce&G%+SK5Yj7m`w)T zh7bz2jeWEHgmmWFLnNiI6+t92WRY|t^f1_nu6uxwn?pKbj=dLOaJ^_iNUH~-@ z_d><9WC2+#cHnXt(EK6p+;c#{K_OCOb8A0`cr=VWNPPJaDXA-DFWBCM(>c1S3Y`EB z{TjZc5bzS-3?blIyfTBPPSDA-TSx+MYxN=M68Jm@&JqB+*mi3qaMk7sL(go4K0Ffo zjexH0p#B*M^tWgEZ>K)mAo~dS)r4`R=1Ls-4BdkZtRZ@ad*`{;)H`+JuQ-3;RK1r5 zVmfK(=XVYnvs^`Xz|CoMoXT$8mH>ng2@fqjT0Kh?Qht!ag&{;N0&GL*A*{LwF8#=e zKqA#$_>xF9LGKgBDi1=#Sd|4Qdc@12!6gwcS?`dh#Ov=RE8WyoyZ>|WzHn^~LEvQ; z1Yjuu?*C{8UyBLe&)$DULx31O#A{;Zld|cUVl6S-c+m#l!nqt`n^{T6@UZ59^~2-X zZ3*e4{hk6aeO$4*TLOABfFo0vK{_BI?SdDewdRwx<_)-j<~Q9 z#V4l(^L%Y#lu4Yb7jaV(!y*`J`=lle70H>U>M;(blgbRTg}nk-@euC|v%Yt~HJi}* zLEx}30fXHM_%$wX`xx^(HgR}N;p@X-`!r(7mLhx<8YcEECy)F z>nF!9T3g;MmzEuz+Hs7h-hGr32k*EUw1#wq){yWyM{CF<`nXZb4Jww3f4YRLbMu^< zHNjpH$aCzOqu49Jbidz-nt_YYOi0fvf>MJPk=fd55)4x7yFsw-Z>&A`hy#KKfmg?L z;X8ArHOnRKn8plg6}}|GF2|dJu)gJr8p7CVq7;7Vv3}HQaXoo%6(lgQCYGP}NY<36 zHA~s69dc`DQODUySjA?B7a^6j!p2H6sT#+oKnpTyF$rLfgN2``>3=CKy6;J#52a7_bW{3%!cd|7bAj z2Y+_Zae&rc!=%2;1qsmK&IRP(;Y!-OR8LP=lGJqUNNII>&Lh){FQrrhI@*`$)Tjh% zYP?wgZ7}Hg#htqw%P6Ua@mpP(KS{s1{A10an=TY-tlofgHF-zAb!z*tdb>o*CtAG) zc=cu}I3Zi$bYRVO#_svmpJ{gM6K+&`s2N`o?uChcrzMfy)(&5?+uGpG=r$W(!)RwA zhk;D&x58?>aoP0jQW7gK()p0|JYNbVk--p;FmH3Q>9^aRv?t5o&Vv^8DMq)k9L*}3 z?U+b)m`Eh8(t4`k>qz3C1Nj%T6XYK&v5TT@d zm9d5bJ(Br@NI^o)k6$1X)Q#6hsjM`rMU>>cp|#O|N|Xcch57kCt*@})h5D2e!XF9v z#y;^Hr6E$iD1j&|QRrJgp`hpG4W30ii3CSpZL6;y32kNnh5DwEO8>OX0S3K2jin4q z=@wGyL9P!e^4yLOh}ce)#1;p*Ld3SOdAEM^C?!pO_!3C^Jd?|DQMJ1K^UT?~-)tjl znpFgiE6v?3Xhh;b!V)C)IUv@+q=hVf4Z<^fk;H%;rk)SsOJczNcr!3yDqal?$PstF zO5%J_it`scp}!^3!~)mpC@{@Q#LbA1K5x5E@90wcUALRm1fWr{QlCsyi17T2ujLfl zi+Vb>pfl-WhW}6c!!Bh))_s>~Qt)6vnvr>Brn`~J<@g~hcUNJ$b1NwsJbqV($(8hl zE9r*}f3`VqDdq;vj~pQVe!X>u(#8CZeqDyrxs@j&NHw|yN~w>_P?By=If>hEh{pjp zc7XIVZX6Kmsqlgt<9ZQoBg_a13ilwEMmydZ?dX&G)(oY)ekwy5+BLQx(Gx>^{9W6~ zxv@{H7?i2(H@Aa6$Hf|Mr4nRTff1p$UBB*SrB7$c%n_e~RjszS>Xa1;Y=QT1s$Rd% ztqipilh>EJm1S)+(L%);+^bZ&aBDHyOkAg@WGUCF&N53@aC-&}PB)Xt5#t|aAv27q z2tk4EOrSb7;}635i3~>--J@(Dhm}8+sQJS^Ngs~(TW?J{DTz(C;~>b4Y6Bx_tz

    0lp=YlXvli-aj8+!M{s9%d5j-@*@vBq&6gIV}<0twpV-!bBWgo1IgPzy)o!wmfsOGzX*gK{)S$>AaXBsy}4x0YB;yuA5C zM#|wlUs8s)T|0&&MR|^kbB2(->o}^mMbEy1QcPL6f52XW8r3Tx-}+*Y(m_3t=kh63 z{_Tz*5LlR#4Go5WJQlDuQe0LX4JvxyLV30;xIq-PY*3ryX}!h>%k#CY3dH2O(yq;K z?sPP`?BlA*dE$19;XyUaVmQq2h}#C@UGio~6eKdXov}n$W5a*f2 zXts3n=_q%SV$hAmL~%nnbQiR|(@tdj{&)~~R5I3P`<~b%CZQUT=;dS^fND^$LbVyW z+|R#gMzF$}f|YIQj?12$jd_GkoQ=|d%u(8@BRHMy!cnVKbJ2owj|jw5Lj@7(L|M$1 zC@RMGKGMPLs+JxRs+Y4cxkl_hT-0ooNKjq^6q4(iC_F9!{1r*u{;q5B!1^nwqJc+@ zExdOC#JN@C(MgwYdP2M#I>1>7kkTzMC?se^--G-Z>H?GFM-588L-ujFpO>iJ2vgrr}6njN?Y^w*sVCu4ScMy6k=IA={ETVz6Q=pbYJi5n-e;D!j5|1Mus(^U9 zXN}kmtryiqyuhQOuM#Ua610-J|A_nh;p!pxG%fn4c8@^2!OeN<4UwAdwt$^-23weU zF6s*d+>|(a|i3bcLE9sIa=_EA^%*DQhybhpG9C;(P*6@bE3!fxOHmVUI zU7QT7va;;1{lbEpN644Q3Z-lD^zN zAKtJrPtfH9K$fYsMN)5RO$l-hNNLG6C^}3vV3a3y#>6Hy$O+eAbYl&iku|u9YY+r) zYOpnSU!6Fr&cmmM{T+-a&7VBq0C6ww2LQqC^=NzA0PqeQWeV>AI2}9ZfNfo_TU!b! zB!)hZzRlJyLPJI@G7*YlkVl67Z`yK86!&A}n+Fclf>7aC^*9MnC?`y=G60L{?Ja-> zY?8Xj_oN0QT)JEiKU}e6qT)6y=FS)OHj|Y0$|}6{oup`%>mRT9 z*CZv+Z24z`-sMhZq~((YecGLhyOR|fMQGn(1#vEcfDE49n;!l9^{Lo?r?T5@nVz7Z z%u|+Bbey78wy?DARDrMcF$0UvUx4`o*)4A(*Li_IH-4k0rocUo-=taz+;YC+-8stj z$|ODkQS@CUQo&^?bjvt)`4e1<;`Dk0xI#ia8yO+0S?Hy?eM1YFG#Q_x%&qpo6ii+n zPPS!i?IhmN$3CHqR&zfI!o48bvWenpmkYG> z-<%1blQ`b#EHICs{&S&GFx&eP@fT>_j$uoO_H%fT#(qd2FkguswULt={BQ{;%?37n zV5H@}pZ9^Ep_6WI-~2=|9`60Hl!QaUII-}MzHq)WScL}^+;f5{KmtH$` zBMiSg>WTy(8VSDU^M`lGT#;atkzkihka$IcRWiXH;$@j4?urz13=m$KApVL36O9D- zasq6CLw!z0M0W>r5TdV8m7YMs9)(Kb)(Q2y! zCOyYam`(NFHE8vYRtYPxYgPK0UFQ5|c`e#G>zfm8?9mR2`9l|jHF<}2L$^}6NF@#Y zH2lp#%XdC&1%tI$#O}C*V0QuRR@!!tcGv-{roHxYK*vh*NDRmOm@{3=kqQz`R??Ad zN0oi?3#hZb;y_?6o^V)_5Km%66`YuDVMVRu(|I_lya17COiNUr+xG`*;FS9*>{r|) z&kb}3ci1WRhG}1 z5a^9>tWZW0uaC=iY^Sx}?)o-h57tpE0DVn_9XeY(DQoN{iAWzfj4-s4uXF^#zXsx! zV;{Bo!^Y;mfiz?M?VMRjCyWMUk9TZGQ&lTut+v^Y9kM7aD=mQkNE{1UIGBiIH89tq zeayPRgZK<9|F3zOc&94?)TUhmUC)*%9{;D_%Bytg#VcIe$>c*>@gNpBQ2@=&k70bm zdVRIIr>=UHRP}&(_Yh>Nlk@M45Ez>WYp^~)&P(gQ+7338frtu=D0_u#oMpB5lsRR4 z()P+zFfHBVJR)H5S4lSNsi8wW2VlUSDsmaJ`lg&-VNW6vtMUC$Prf6%(|=jf{?-4# zDtcr$LH_-U&iq3gc?d*1w>c3g2p;AieL{TtJrQ1BCd5!XLGYpfV_LGq8?@3mB-{}#R0TjhQuJLz~rQJZsj$Q-<^}oS&AaPcir3()i57pm zxeXbnt3p+;R{!4xJH9E{sH=kguc4};+FpFGNo`{a9Y!C7IydC49D>8=FpT%#d;zy9 zws-r-Skb%r(P(%vckQ#J(% zS9ygAT!ncR9rAV)u0p)@$9}krq(bm`tgFZ)M!H!4SAC^VNgqArCyNREPf@m4HCQ0k zV^b@`?%5ATTmC$czspdka@L@09*q=ppo3A+s;yS`CT%~|;K0?6P~~A`t-g=mMpJs< zSc%B2;EvVKUOS17XnV49E2}=UxQ{e=PrPW1r6)of@SX9I@GLwCu*h~5Y?~h zMNcX1%sup?n_- zo|Oh7s2NH7=k|--ObXSkx4BS1q$wRb{(fKludC8&hwO_p-~gyf+<#e>szv`}Rj&W< zdqp4VS8mWRwN$!vymL61u2jdEK!M0!hmM*ZsnUD(vN~I@8$g3Hh13rYeO51Pcip~N z$?ltsV2DKbMDD}6wRxss4P;x%dq9VJ>zPZG-uj=GD6y^U;F|#Q&Fda5BTApRK(X1qxs?5!eG+jO5*%j1;(}Zb-Bj zmr-Tt1o9%7JAloN1eqzV8vM4D!hf?$&P3%QQ=k7;K_HOOre4WX<^ z!!;+^2E$etBu?`1pQTM8n?g5i=GFohop+ojqPIZ^T#>Mz0V0RPy zcL|9MTnq)!Y@i;Wr`-dNFJ5^qA_QdjxOiHIpqy;bSDc$`5bOWKi6{t61=~Q!MY$0~ zO(Cay4dSj4B!6rw4hy?A6{j{&i3|jj^-4VIjOB!w0AE|sD3ny<>c{F}Rmx#r+p%6@ zGrI&jAa*amx>D^~j;|H3%6Mp^S?5+`s7apS=o+z5Mt4^cE2CIBEGBb&otS}m(3>s^ zhE8w4k$)KCp~Mw#8E@$HAiW#$T{ym0BqE-KBrM2)5L`z74S-)Oa`nGhInQ_ZQ$@c|Y^t*MXPp8qei9sTJ zRFE`Jl>W4l{@IB1X9_m!b@+~h#R&`eK{D8()D1@Jma7_}2a$RNzC)=&bBxr6@UA}| zk@_T3!)ot({nRSuAxqr}ecWoLPxo6JEg?MG&GG*Ogf05E)kUCK8oYG$f&B(`JA^zryp@L@D?YPO#gZP%n$ z6$uL{Gsg$EP%B%xp$XzZM^wSJZVjG>aGgg%lr|>D;f~2~&a$|68hco{uA@#^za#Va z`cWPYw#B!{-@(3{;@L;&?VtvrBFBaKP4cr>aOeu%z>J}CKIG#nLh`u*?I3k5p>x+# z1QqREi%&Y0J>{;IGpy~#f_f)|P8*ApA8B!taV)$z!HQ#NGU*Xr8gAFYW?n-e%S`*sryLB{jBr6u56`c3oQgD~`^|F35RvaI%BOCT2u9(UQm+F_vpRWDl_z`4%Rl;w{tpn+n20v!6&>X;LNQ7 z8G5bD!7b}xjgN|RmSuqWb=05hXwB>pgD*3_z!TbrvnJ)~dpcOtW3C8|i?fb2b5583 zLY%c({P=8;0{SLZilbP*6xVXvpZVT z^dTLs?a?;8XXAm&jKiZ|J{+B_WBX4V>+h22R6e|H$1PiPVi9uHf_gL-v;hUtLZY!z zkoD3{6)_#HkC}f5bj*JTnid9^gFfBq_d&<~cc4i-c{%9y-F_c*{BMA6711I3rPq|6 z`h|VwmL0`}lQd(Gdo)@(#8rJ^#lfJt@%HZen?0@F)GTcIjQKbi^p7$Fb+LTizh7<@ zgC{vB80McqdEmb7b@;O|7ASKOQeB)t>lAaZjQjCw#jEf2+8^*5h1W|TZC-`f)_8sN zwt+Etr5%~mpB>&oUYi10ek~O`zid@I>-62V2!AW^w^SVZ8}W%1(w-IKITau#|DB7k zt)V@$uG{dC8^oGzJKkOXQQhbpB-^%`;{35&UWRawpi~6C$wA;u6p)U9R}tXB77z!h zZiJL01SdpJ5mP`SNISOWGU!VLAx6l@ck*%CNH}lv&r}n;( zjGyvjv2a>cAv-|F!)_j%sk98}{sN&qEisVbAQkb@(ugWX6rujoTh`HxGC+qf-}XGp z(ePmX7)MJ{8hhi0C|$wP2gD9UJFx`)-amSXIK1mZP>Z(WZfH!n0)kqAjcO}o;Qz>T zs5q$~U5{5(RgI9E%iJwVaNeEX3IcR%{6*uhO`^-+WeAR!4)OSNdO0`2x4Q{4GZZu# zjv7gKNk2r|A?TIy3X0;&zPe)B<*0F%jr=Y$mm11OJm@#FNn0@Jg0j5dnG1Z_wCD3L zhV{e-IN2rExg8`S!#@>q=o>If+N6CJal|uUMVzy``B0iTQUD|RK?2zI?%6DX5rv7c z5h60Qf4^oMI1m)a2eTAg@<9Ej^~!+QPjOTg`Y+MEMbB$5QNA80RkZsNPvVGm?7H)5(u&d*`i?;RFsJX z0}PM^(l!HlK?-+VuFKVn8xjPSge^b_Ac&$Ui&wNm*ijZi=6_FBPZq@Q`hVa5Jb#{N z(%n_3s!mm%sycP*oKwGkATN=sqS(9-Mp#94Ecsh?SShDU!QciKR?7n`GyMCKi#cv|>?P<%az}!uc+rO4C#vWUA9c($rZt z5^&B$WkMSo6Dg?}Z|rUSk=X&AE~MogVGFj(t=wwjku){&gi||ACdggWJc)0^17mtb zsUQG2&9cvHjtQc<4Mv1GIZy(lOiuVN|CyvAu&5XqNiuaJ?-K^3yhtHU9V{!`k+RZaqd6&NSMQI<(ChagBYjRcf)U zL#UdVMm@vEibC|4m$I4LHS4U=qk>?GCw#^tC_xV23jsE^8GjvPmPgb(qpsz7|t)!PwwvfOex!LkY7 z8L?-g?I7)#R<>)|%pJn_u!3LMc)*TE|N2o3{Rq&md6iPbHIyQTRGMQQ^n_vop3!I1 zp-5@xb|lxqEho`}=otY&AxpzbQ3gDTP%3ykAi)xKd#qE9&^J5>EX4djW`Y|QIae&j zz2Fjc;VlBU6n4RT!7b!sj#P;mkS$AX2(rh%$8)t5-Gwh$4Z^n8vXt%+g~;!-urd?@ zePmZd7`w}sqH_|#+~N|sAMsA9w*tgc^p)|ojeog!8lT(wC%GFXP}1tR6#WZdR2j_l zq^OsnZP&gg-(Z;W{PcxgAg?=8u+86640S=AxmG|v2k$J62@{~SzHupD9A!VlThavz z-&+5Z%6F8xd9=1OVjJxbmc}6`SVWpOQ4ypNH}+kU(9qEuJlE%JOUr@fWF?k(r4#^| zQuO>KCcgp2-MS0&BKa5*Q6rkwUwa+XtQtU@sDKP?abv54YEDj1kB7@yoN%}ry1DUZ z)mD@Tt!Mz}Hs@N`Xv%ao+-`YxM-|THsbwvNs|Y7ehzV%|r!%47(H@z6l1d62PP5dd zbqLAg6ID{!SemJvSHDbx_3m1vrYBKfy2rtXU1pLSA#Wf=KMai$BJoUWaH1D|7&M<~ z805fq0!K5V%Dz}fL1}=a;KVgO2>0kw^YNa(iFD`)Uu(w}@;Q3p~WwK)RI=(FfU z{4a2CQ?#$43LF-Kaw6cCe<^QSoy{kagr)dh0Le!LNK}0gmjO~-2PC{c$WRWFH%&f5 zMc63vvabm3YrW@uuN$T04J5gtKh{_}!h96-s<1-v1UCK^fZFYPjDf z7Sj`SFX9R+(Zzu{rSq827xA&8!K@9V&yIG$G`MvX4Ye1sqTqYsb{gJwmTH>P+06>M zb+?z`@x|C4EI@72wbRi@;b#vQ7r_3C25ywza}5a5ld!*trRV zeZ^Sz>|U(MP4ijlUfC+GcC+Bqax2pqb`hU#f1r5>Qb2gHOE3e^D&oWSAs|9$yq-Ic zb*YrQwdxJEFu0jX#r@(6HicX8}ObEV^kmhL$Jth+2iq}2_H-~{w2CB;z zvz7bh*v@Yh)T#G2QSXgdq^aJ$ef93Mh+Wz*cZ&Z7Ge4c#R_LFj=V^S3g_LMv`Y>AA zakn#y#o~@deR>oYb`qX(LdZNIx0Ew(%Ix6-a%gMb`xi67(?c2-m8{02<7_B1u;HOo8V7G~llh+I8Hwiv0u}0Xu*7uMa zw1bfQhR_i^m?xYI?9-$2MPn@+#T&~un+O;uUPL*sECA&^ipfZ?q9%+#VkvrsE&WnX zZM%LpZ@e8dh;-m+LC{Wbs0ea)i=F>c?%gUCmM|DSm|Yg21)yRn+A?b#>vs^{bimC< z9+XG+%Y?2QnzlWsG;B)r=F`3a$X-6ijIwargcow1fE^gBu&OsF(#M({tQ0^B6H@vPkv?M%?FhamWsK7tZVQrT_@adAQujKJ2DdiQm@v!`q zwC;Wu^R?V7eD?j^2u7GvJ&X@Xare+_!l$Fr2*8-qhu+Vg`C4w@r>d{)_h}&7l z3VY0H|3sId8raCe1_jf1!i1-&kuKB)TJ6M+k?K~lnwROPv(HKmqRq@^sEoFqv~5aqa}%=qSHCu zSPJh!EV|B8Xyq?hjT`fqzOE2jn!RXx@BU(cr*~(O*S(|aJiaOYWhlfHT(enHa zd@uT84GMF5+@hdQTZk_6M>9C9yZFin*wX+rVl73_Befo~hYw{ZQ9tqq-W&2TojyZ+ zIJW9^z&M8&yGwtBw6?T8)FM+60f!#13^DmtJwVw!i#L?NidU3yECVY`i4!QX(EvbT z03pzbqh~2<$1|?bJEAbb4p|C=IDA6STtQa?H1{sdHZg=Ep0UAr;wlkO!r?vw2yF`r zwG@@Q;qU`>jv**sA&63_ldD)0*%%);`IEv9E&)4$M>24! zIXj^_0QZ^KaE9UvlVSB2rZ|dWB99;(q$Guz=q-@mAfKWsO3TdFdqSM$8L7Z6@-^%Q zE~R6$CI5;wk_|d0-`&)Ygk3-_)03#A=af6wFxN4;gM052NR+BLbLIl+HBR{g$;gI> z*(ndtV5Qg}LLaRh#oCMK%f%@sdJim4k@S^F=*&qck7s4bfP8iE&gIz69>Wd!TkRCEFz zWgkT{}!dJ%C>lc&kxeGOC#VIr z-@!VPxGIXf6Tu}t`gv>!(8^06ll|D-eR98G|4DWEzVsBakJ~#@?Bg=02?lOmvS8p? zGTITV4hZ;YE-p@p+=M$Zsz>oH8Ry@8+<%203)Jtx{f{P5O)b}U7REMx4 zQ4$?`%W3Mpbbd``YCXwo9Ga!FKbmFX_64XUZw{^tff>C*-)85$U+z0e#ht(c&3Kf( z{F59q=3x|3!`S8FBoDAC`E^`?q0i^Of&?!&kw@*)AAm)wV7YRG$lt*-pjMU2BlqYF z@j+4>5I}|g0c(0nZZT{wc9evCWLco;r!&r^G(y!DS?V3V=RFC!2?%cGP^F!CHYliNP`$ zolx%Z=>*3)+Q?wq00K@5V{Lzy+sPz7>5mLz5%gVn%K0~M1kWKFMlyoK(duYUaXv8xboS*}2 zT`TmRmq%1&vT)t^3Ohhp2r_28CLY{+bh%a>;N)2|O zc9(srh|2(=lqf$^ZvjF_5#Gr(Dg9=Yg3T#Z1h?ydAkv|2uR3e=VHNN2O8peV`9{9# z5MsEzM;uKAJ5^S$%T3)&e=@qYFl;S~MhOG@Q^&%UmQ!1zM+87L2mWEn1^zZ5P+M{h zZ|Z*|)KGgx3{+0xbSIp5%Ld0tbQrhrZJizAEIxGpN zP~NDjq%DcU7ZMVIy_-+6xQ^Xsvw2mrBe-mQT>=ZRvwc-^msZ6H<@?Y!*oVS1iXmYM z$|2EQh%~)Fr#ESMf95zXcL>VQBigRS_|R?r*yE?=7L7Bf5-~vXq2CH+D^J6i^6^k+ ztA>EX8prOdhL;62Tfuen>=Z0-+wVi$^A7MMau|l+82obZn~vWs{HT|lI8Ir3rWhSkkRVm}VaKZF){QIrD1P98 zaQ~TOJTsqxlbthTQF|~2YY@7LC`X4sE`(U)0qXPw+3?U?PN5)vh|;Sf{-(FwJQ~6D zr!7vp5#YB4(s(~O_FL@4hw4zY#p(PdP+OeIUmD}ydHxb?EW6!^kqJ-I*!yQ-DZk=M z03Ukx&2aqemj)#=Txf+b91Vg17OxerAb`bhi`Njmejr{!0gE?^*KoX+idT%U#k<67 zZ1k$=7x5asG@3HPr2^4Z9&QbYUd5k%DQD#ll02DBKP$Iu_XfqNOQWg0B(*U5 zJv`M_(Nrcv*8}|DXXQ4dUY~@9!CJc(_2&(wnN;2)nnO`}Ye2h4<*lJP8mqiDG)J(? zTf?cX=R$rpVPh|}H7t7+&1vDhfIE^&}A_!_PpGqg$&@0ltV-rd{JJCKY*pPe|P3SFZb!PA-X0G zZd;D({>Usz3w;H_TEzg*`g<~Q+Ng#uk7mcu%N-j|MR>Kp8k!xw@|pH+S>y$|fqP** zA-9Z}zIf~C!HS=yn3z5;f+vkdn;vrww0s_9?X|{bNq(L+CsrbRb1o8U6%o6NcEe>! zmZ^q2>MnMq~mpE@FNNh-KYUo=TR04#TY~3vdR$1oP`ob0y z=NUiN;xBs86AAlBTr&_%jGP8X$W*P^pJ)ybGGQ*($Knn>odgZ~B}VnU4?6OmjiR1; z$W_~K)HAQPp5cYOo@t1N(umXFV`j*Ty>$_?!;8;TeU4ClD)e1_jQTVpP+xt9iTacS z(rd1aGpeE{yqQj~KoHW{@NM2w)%*cP(uc8R_#V&?a27;9gIE|vq5_;ZjMA;lF9kW6 zUq(c_#$|+=V2v$>l}58<5p`!1bx)<1u+tETt<^(K*YW~Md3c|9<=VE^;QmgnC2g4c zpsiYWQ~XwLYunWg4<*Ukudtn+(>Oz$@k1c;&wa7FC7fZW70lfM-zQMSO%Kb&=6lv_ z#0tZ$0QFQB=|4hQ35605Kb4I3vbiQCv6y5E>@+Hgx`k`j-cg3NDH)rp5Wi^F4hV8K zgF37yXj%jY*H+SGP_02x%yCKnKpHfXUA`oDmfDYGZ7$0Tq_rd1%a`RC_ejHB?Ax>) zC#(Y@yQNw?({p}I)AY26y&0I}wL?z1g81u9y<#kM(`CP%i6%))R=%J~*%7hbyDNU@ zd?@ddw!0@^A_M*|7%y#M151=$WB2hchUMcbO4Rp427Bj0%kG~!y6ju8X0>H6ns5w%gOr^bSQV%)yk=>l=BxxqQx>y zbIGvlGx%OeA>)F5HOA1X$7vPbrw@z1qfS#ekKV7xAeu8T{E%S$m~HHlD{^bM4O&as z@SjfCu6s+laeSI~9Xx>E$W41hVrFyA`j3Cz8ZOtq=7#n-lc1v4Jz(-fVdMO$unY-Z$ct-c6qoD=*C;3rwz$9G1eLpC zu0$cOWItUVOKzQ^IR?h%h}#KO142bVJBB(HqL@S2SsGqx^7JWo3Wcu;a@zsfp*|7; z*pxzg4V`U0b_B7lbM6JBgh1S0pnsi3s%KF8Cf`1AM45wg144M&57bK)`tv<#=S^r0ptkCeU)?CNf9#Nh8Vhg%IJibxj|R01|u5 zbVVU_@Y@@OKr`%5_9P8?e5Kw3@#)$dsJNT%dN$Ay zemz4(|H&OrJ04%DMtehocFVExIm;xc3~YVcWAo&!5}TA=v(t&o9_| zp28g(B$P>mTL!~#4jicvp?*h9wJ>5f>mP!6hvT^`g{C4FNoetyTGr) z7@VV}X{k{e+7MfYmI|Oz;McIjKp=j`wG^jtaAahWuDMU>Phx*f(6s8u^6Bz|-V>Ya zG;K)ToI+?wAeSNG#2Mn~{S}%^POs1vqU&WrkCHIupIiK81n0gr?F2$;lY$15$R z^LwA2tt_?OZqCe2kuC7l_w8KeHgo3p!D<-h%!VoQ3KMKpvg;Vzz!sR>A>T2BXp517 zG2<9E#OfAWW9J2;_J3tV>`(OISsJz$ZAmu7Phb&+4IP-siP!m!XEip&=N@8@O0XfG zGvsy~;)ovX1xaZu9qhn9laxfs(}9^}#p-T{tQKyk83b;tsDa4hjBgX{!+f0YP>i{Y zPA$zw3eg=05xyfqs~B(B^wnuJ-uPVBy{Mcf^c_e(-?zuu@5R3?wQ8iL0l4G}PZ zPHnucmWy`7ijJ>roOuRk$vI>!aOWSVEfO8wf(hIS6`DE=oKZ$&95M?g@N;4UwL_ER z+s4JUFg{mLYqfrRVhW#3L)9A$``d1K< zQ)3^cqmBxFG%$m#wgrsGR|7ZgM%7?!#0&O&t5Rq~u8j|OYV&QJPu#DsL0YIN^u@Kj z2pSLB2VL}ao8P3%`-vVZbZ-ZGjs)P~aQ)H2-m%!;r$nV|DF_*fTn9(#T@2I&X7Tkg zD@FPuuidh02izdm-|+}jVuQ*!1+z-C@}YF=(UJxJN~o!M|0KeFSYSaZW>aVa?$;xe z*oOg%%?+KwKx3CT2MzEosingA6Kb58MV`0uBE#HdH#cIfg^2 z8Xf7fhp9E87PE^6UJkpvk+Q4z>1o8y|Jk(t3IDpL?NjlEnaeP3AM4LT8Y^Af!y3HK z5`>`?{9g>vH~S3G?U;2^u;O82@e>|Apf%U|HUsp{?Dxh>ru&phWk(968$ zILrY!C)%fvfSih+CtI?ncPVX$!MgxWpcn|>)tx$a|Pr(fxf;;@y-tj6@rH&K}|<94da!o)nC z!^Zn;xC6NDyJha(m;$)%dw{TgZ>X5WPfgk?vxV7Cbv`u2S8zp+k?aSJGBAVn(}?d_i=2-fd%=+ z3Lw8&GR5MsaWwJXw^>KmEhmXN{FM;)Y={!vZde))r*UUNma^i1uo<@)Qd8KbBm3Ya zHn^2?k4fayszF-LjT#gnHyKc@bp8_*D{7?#2Xr=xh!d-GC9nhFwoP+mV76h_`fn-uV25Azg55b zP<85uh&$J>e#BqY4?zF>`u&yleEs@G{jK`dhpJOQMBKT4^&|eGegOL4*DvZXO!({9 z&-SR6D;o*!cEJ`(WDd(7CJF#^@iirR|mEhGS0AIpUH|fZWY;p(X$sJBVpEArNDY!EH-U`9KC~Azj3IA$PE*;{~qE@5e4^V>R8p@Dm&uwSqTur!qO- zspK|sJni)F>-5hxLtm(l@3jvpUBPv*qAzm22D)NkEpVL+#m)h^LS=)*@*BvZ@OR_5 z>A;B?Txt#!E;Su+teeuFTKfp8;CxSe18mBJY1@7*xQ!_(K^Z^o`){G3~ z;ux&U+!JzAL`7Z~pA4|B&=3G@MXKD`Z3f7k<3NR0V9RsK@^0VK4F8i@WWCAp-_DD0 z)cUixb@d*AmFZ-$n97FqdVM9f^-A^02dWDw>#vU zxL{^pYq<1=rTeZg^#vzQxCb~@Dm^J5I=ukKIRx+U?jrDcUpY2*K;x!+tltdB{Sj>! z;Dgf7MX&6wZe?VGEWXrhGqG%dqnQm-{4N`zjI3}24svTg4_D6>hLN=)*J8oRA6wo< z31^Wn$iePLaKA{03=4eG`g2h@!N6r6j?Lt&A=T5ED*#hGufw-BIBY{{IO4ej4!DAp zP)Nhwjdbj@$Ke?WzY4WdyiV^?_re)J-6KJ|F>8@KY2XXms+Q>Ju-41fuHS*or31tL7@2NEBWeMJ1sG57zg{z*X7UjDp@d{VL%Rqg3!7o68>Yfgm%Y+O>Cx@` z$lDv?Vd1PY5G2*8##?e%B06?+t=!oSmld3(`1TttA6)}M6&iHY>zVL_6!=b&^+i*71* zcyrGs#qUqUg=(z`9tWvpIJAIT16o2krTQNbhSJV|pPq9MjWe9(9?_THqNc#1aE1QJ zzB;3f4p$LKC%7Yk5XC1d+Wo=l>gxu3)})zz&wq6=U<{gcyzyYiwrT3qOZOYc3!@A`xM4BbLC zQ1h%nfJ&F%dsg91+=}5@jaPmv#!^lx`suQp-kq=>f-(et@Yk0Je|<%^(@2`ASW1e* z@HQD@7R!dBPI$E!=Qk-%Y)EDYA;pP}xG6$$Vq;;3kQjvfyN2TGqeu+5VHR`{_EE=^ z;w%43p#et;V!*Q|FPNGbH%)QxBG6;sf@>c^>gWT*y@>ucweddvS6F2ytDQ*T1Ip%9 zi6ffluX4&Jvb|kQj)m7wj`a*DMg?c4g0L6Cdk9}>aT*AXgHk^~w!WM4gPeZ_uIeL| zK|!Me({WLhF;TLY8!6%L=%bFEN*Fre?EECCj*Y&6%SL>ZrdfagRj$<%$$-FHv;Kl0 z>~JC3hH_Z^5AdH3O_75LbQCvkGk5PGkEwggW6Qj*hu(D#1U4+ zHt#Mp05$^y13c+Alm5n=?CE=zQ1@>T=V^c7lCw0ZbmFe5ect`a8Vok~as4M;L+;JM zNX5-y&=$a5fVeGoMwm^11>vNxuvhPRw>X&NXMiN?x~6XpyU)Yi}^p|qJm5Q*lbe8&sl$7>UK9HpE?<;x2q2MNu=J6$c6O}V2sZ(4@c(RfpNx$?inUbpH zO47`jl4EJg1WB^=Ea{)FG?S#?yO#_ftYk^j+Q^c1nMyxd3YOWqVMz6*R1!H{(M(d3 ziESF8#7pkE?CJ<*n$)eZwc`?I3-Qa#mm#{wci z@9wK~X#gKJow<)1_SRN8%ruzoeMq?%{%MA*)5gL@jUIw@BzMWClile%MKk_|e;UWe z=9qD-CgGf&lb+$LW`ysX`>M$X2%OSj3h}Gj3=Py;O2zCgg5PL`V{i&5+J$TW8ecWa zW9+pYrE@rv)#n{95tg*n!jnw4V=aHo#D`KK4mWounj5Teh+NlawgwpEHu;I6X`NW&?TsRi1v8@XCX>iPF3Cig3VDzE;Y=9ZGbfo^u7pBZTG(-3Tl7;_Sz5N6|CPG%g&41H-GZC+iR~}WqYP5?WMJ4)Du0V zqK8?#si^+BJ68W++*1i}5OPlf7XfQ{$e*gekf*PUzooXFYN|ixb||CziNF4-`d_k} zQt> zhtUS`oOcHjcpm33^+}+Zhy1Ax9_Q)nwt<(IU!Md9-4126LG-0RwLuwcJYBg@O8SBg zoUU}0^DH+@W=~g|2gpqu++^+MD;?N}Pbd$_&jgCECVX+gO(>+J`ihzLpQVhI8cbj> z%~E2d7d~Z&W+_7*u5^FDd1g5DuRiIBU%xsJpY!oqT7A&D#_8#1$Ld#1*)K{J4Z#Xr~?VF}87OWoihiY52K%s{a-PfoG&msFk3OVIf#;Y z8|)C>YuN%9dA_QA(tj3D|q=>J^w@~5ciNz&UNu%GjkF7gkio9wPR zN`(AAUU0i(benOXP-%_CnQ|uosNlq0mG}u;K1UfTZOLN4%u%p=MiNf>3!hS69CZ0( zAC~Qo)-+HbaL`|jBc(b)g_?OIzF-Rs1&xpJ2D9GAFzbB>uf;wRd~-IT7{oLf zUr8O*yC&<2-_^_7R4?lru)Psk|AsdsYcj~G$-3&vnyeeJ`{yeC1}%UlhiKb%-`2}| z5I5xUVVzu)b^qF|d)Li6x;E?dK|5ePA#%NZq+YHo#?)<#r|~5aF2tLGa6VpZ5YEdL z2ydFFOqAyT!EEytO}coKo?-0E`B*YvFg`lFF|jyqvT<%@sC4Wd_JLbTmahKJuDX@J z(&pbwq8BKKOwxv|lE6jE6Q;XHzDsyjILi}B3)D|fd6z9MKz90I_CJ8$ib#ssm833*1j8>8w=5utJ*g=VN(VF8x0=$nDUE5B)kGt>!BV9_3O>)4 zEmdwwNw2bH9%X#&YoisvUJodKCHQq3qxh}EZ}nKk?Fj92`I@mq}F z0sJ(x^g0V$raagVJ=*B`sHTv+tED(FY3u>Zg}|z9JEY>LJh*4<)Wgh9Xbc* zbR^K*0t(B(8q&BCu}*Bb5!)NFUh4&z)vM;7c*i(13_Q35n3SswT@F^dTxsOKu0Mu6 zyw*>VLJPX_s_Z9U)5;^tptK`<=G0oHyi<(=wV*u!AUkSxW}uoE%#BVUr+~c;9QHe4 zM+4<29cr|-Mk*s-f%Q2V-K?RMw;8Dn#}$~M8SWwxou1$l?w$f{9n*`lv zTs}D;j*V`}^TG{778_SpKMW^mEnv%O!M@vBP>vKgl)2wjL%F0He#7R#e#Lbx5c%bP zUftMPFgN5}Ec~}E=Ou>DB*B1&`;2jK^~&xoy1M5#<%sHg;2;N>MhUD> zG1a17MAw}On`#>!ssb(4nxCwal4B-Xm2YgvY;!;hB*J{|6&XfN?gvrRLwI#+CMq{t z%Zzd0k_5Pl%ZyqX-{PLQ`A~Gyqkw<|;@&iEFkE4`AS4_>0yxb_+NzN#nsRlh0bEX0 zH4Xvn!atN=?&=o-M!VX}P?8xQ7CeGWL!b>13ym?lj)WU|R8d@ZZoX&KP^hZu2lX3X z2({DHWY`{MJJaB{7f-wK1Qz@|(0HQZ`8S+=;+9u6yPNTZLU6L10%DASD0;#ipB_$i zD(GzoP>bc7jpMeVKpG-5iDUrzJ*hRuwZfevXbhn<=+y2h^5$iy$K?d(2599~V3NQ+ zg~wla<9ej+Rha+$Fa#pXv_akEYY1()(FFSTt6Etb5yc|{$T_25^?VMEko*|S@+8bf zyy2^-Ko1SLRXV&Q0%66$W%$F0rRfV0#M#Mg3UW6CAg?FI;TWFcoD_F=Z5*8AAQIaW zBk#tdWHaw3VoqG!0@LFVUN^>G^*LgdsUb)$NwT zAvh?<8(i4H3yGiB9enB+-@z%htpgHL+V2v_7F;P_>t)AjDU>w~04<6)BQZX;pLx!i$T)m^&p+ zlc;#3%s?$M+$a;-n}BsUAJ)OZ8rohG5RU@lK=f~+qeS7>tS|3?(Pp(OmG^n`j3n(4MKOZ-&~_sB(TdAgjmyWv(zJx#|$obvK046=I9RRUxlo^1=9NOL)mqp_3nSrhlyh4p{X0WRX-a_ye z;)1WFnKmq5K*>_n1;WW1LLU3m4cs6}Q$-%1W7MfMm7u>tz%*}&X=Em!htxbi-PD<$ zeA5u0ZQOO{FFx=1rXxNL;bw-ohXP#zPHkYIQ*DY1jVJ&&k01c6G_FN~@rPl@XqY8$PXcc z1q3_#oYHmplj!E^$B{EI6*&V-C7}#*KtBX)g&LJoiIR^`b>}=@sxah%B z_z$oGHCNs+_w!0fhz;aW<}4@$Ih+RFq{U?g@{>?pDx+B2Z?`;KNIfmgYt!bV|!qYtSV!=QV^N?;Z4@pq@ba=l3^U%v#;6C8zlHGDH z)@QiNMZ;!+negs(c(>E_IyGFy7V-s(NLb8Po5dRpXPeX*&i(^T8T9eev7|x8OHzA~ z8LgJI1C=q@0}OkgwJdRAnWUCwTy-w2}rcV|lMBZ^zq*aVHy5yUBDA69|UbxB5dQETcmwWF9qU zn?}xYu~ndtsuYq>mQ=s4oRWfl_Yf7FLA*KTUS@ww=`Q^{hdut5($ei-isi;-)8Af- zsoZ1zq=L?s;qk?ZF;cEy*>J;KH4yW^UM%8IL8rO8FI=bQ-d~WAXr!GX;;!$imv$4S zg=T=Co-X2_+l>O6L>~dzX5f0>b^Oy(ECQaTIrWnQTrW6?NirN?Q#1%j_8aQ zP!_R=|E08b-{^|sYFc2XiWZ>iJM`5e4;f|%20cMoO~i2WW7IE?$D)3NJj7enWV>o1 z(id#cgw+b|FZ4{2%RExv7nEb<@&e`3Q*B9zA{#TZK{KVi2y8TSz>BQyyGr}Mt?5%W zfBymxHPcs-uVt#Z+=u zwjN6_1*K8&AduU`YfTmzjzsR{1Z+1bZqG+D5!)~v9^|0`aCjZRYwkJJ5_hub*O&0* z*_3NQ84SaG9h$J00a!RTv3ZYSL;kbHn62^^<=02~#< zafrZiBar5ZSqe#o2^S=?@7Cdd9%p>ysWq~nD;zIx;3c=Q_GKC@EDxYhtD7Nk3k5b5 zftwMC{t8?$H`d$G6_40=C~Z%?OeHYrkO#FMfp!8FET;@Y5Hp`*pl1HE=aUNAQ-5aN+t)OPOp$oo;IrEs%o z$sodKl-Jrz;FIFR2hOcF{hL>~SxtqV@%!hVt6D?}>(zM{hU}!zd~+4L*H8cA9W>|| zH0Te{BLDln*28OgeSdc2n^zbGJRl0FZBbr8b;tU-Lwn8|SU>j%k$akVXGS~-YSDJ= zIfxZQM}nxhcAVkSgk}Yrr?7~T)P`oNz2u?F_#>XvR_C${;<|*$P&-K?#1|_dLVO#w z`INIAkg-0E5L}Aj^KS~o{QeE%FE2x#7jUl1 z(x9Q6>^m$)78(TF(y#@{o{qZn(jn5F$t}5&>$dku8?ltj)V$Wpmtc>FG;0BiuAB*X zGSA`_>vA4^Mi-Pd*DvoOIuN;uj6f8WEApC+ym-Exk#xDkY#25?$K3urx7=9b!^yqs zE%K#m9z_W>c;QJ!ka;Lio*IB-fx&p4tfpGL)}=@fDSC}rLe_k$MX$&PsHB00*BU@T zD{Nd#zRx)~P0Js@H0|9d4%AdiF)0 z_MRHJJg@az5mu95(E>D**Sb{%ckqE0Vf9AvyKWI4RufKwbgy+4g3~>bKbT3516w?` zh^BC$uR*m2w>Ls~fvL)4UJ3x1ec12`$&2~(0E&GCJF#RtKJO(x95oq-_U2*YH50E+ zA_6rzi#%pVcFU(6JVBqmEBWcmo1qND<8lHuR}S`g9`gr?Llf-2rjD$F5$s*MDW&{;59t>ArZWdg5A8# z=Xm%a3Rlgm@Bus@A!=4rAsR6An@`kKY@yAa%{_`l4?oWvGN&dg+iM*u!lu-Of%FF44hPb^SA8o7goY*vOh{NQcLFM7Av8au1x$<2ynlG;4Mp88CgCB5=EJO7z7 zu=#ZE-gZ!k*E*Wsz~-4}vy9J`e$tL7ndfsQyOaA($qx*iCp3j#9>UAnx9~C=FJTmd zDu%9nlQsK7>CmYk(7S|3?Ovxs@V%2t#r(bBj7U^_~or*Y{ecBWM65;o-@XgaM? zx~Cu}7^;xbyP-1UwN68*cCDy1w~5y}=^revOi2x)d!LiyJ|-|1L^y+j;qB3D{rMTT zrcCKCRX)SMEyJneL(i~oTa~U-r)OB!R;6de6H91?{|9K$8E=lnNA3i#bp*1)mT)pY z9ps{jHY(q~%s$zwI6}UD9?SIs0RILCAGT~l!SKS1tjRVEs);Lb43_AuR+7|wh$p?S zI2^q6@>Q|d!;SQ*)Q9awy6bk!q9f#=0+FH$@X(aI<=v2 zLNNe`Rs%3&rUwV(=*p?W@)}tuK_ut3W+0#PL|TNz7D}j0vq!co)1~#B*>~HO)_qEr z!Q}{-wL~98Z>X^EwZ4w9By}tmqws7|&y)x}Qq1_r>UX{_50caQn2Xq2vw&&DrH9X}Q5%>**5;ZIW?AB+7Th9COC#`Mq0JLHHB{UJBZCE?jnWmt9II#K;V#R{FaVtN{`qmYVGtMkOR|iAgAH9j03&Q=xETR6eWd`+TewJVE zPb42p%vKY@|IrL`T_Z#Xp##tJw}e38M3n>L`we*-oNu9aHhsi5sAo~BE4DF2X%w#~ z9YF|$VPM#uG(S$L8i|rf>n}Yla0K66gVxQTOr{M-YWvvpg}JN8h6jOUF!;r3XMVrVT?CD3&eS ztwi5n>nb4H8<%i#1_ovv_=VW#z%MkSbq|Q+Vijx2OT-TB@>&l=jr$O@!xm z^F}1%U!jC{Isj1?4;QxgUh9NnVX%*P)FJ*egr^uTC|K%4&h0-}$L=u#uR z8N$y{`0Q?EUu}My!ade^5X4*Cz62JyN0Ec2@J-%cL5q3H-L5$RTLdt#wGCiUk4M}T zqnaPZoIvE84S)e0;O1fukRjs7KP2K)5#N)?e=p)AMSKeSh|=GS_@{XMXB4lR|G=QG zsvs=X&<0e98hRJyrG_@6B&s{~dxGGOLp?#6NUC4Pt_dlB;wYgG5IR!z_C9Y^FGrNT zo#$r$gk}wtJQ5%(K)lvO5&wpW|L21u9zLqAb9j6Q5nssT-DXdY00;v>83&LB>Pg7C z+6qi_C<`@oA_1zQkyM}-)SHrP=05ZQx!aKXbsvUXi+I~L6Bxu$J^@G>AyBmxmJ!eC zv1)*Kd#%q3XmUS{YQK_C8Sb_ez9E2SbCezzFk1!8$37S=n3Pcthsh8yFAJCnJ{WWm z!K8ASE&}E$0n;HL))~C83;`6)fy@GEqyXyU%LsOMlu>66bFz?Ev8R9;lx-m0Ou#hb zFrN#UCPqFq*-^!=5O3|VdNEsgt?uUp&}oWQ&9V>ZTLJV72g(&NI|WQzUtymKm8C|4_)?W_*zDrbA&)=Kh_6cVxFsIlDK9lN zmQQ-2?Zo4OaWwwa&_?3(Co#b!2+(8V%R81QJR!cCin#BnsnpOLB5u2Qd@cfi7LWk~ za+iqPEdqZRUz^2aFCK2x{VblJN+3C}^=Z6f&V6@%+_zHbTxA=Og&d!_SN+ z9EWEpexKu)fL{@Qr||nXem~&%8-6?RtHjTWpBcX({5<%*gx|CHb;Pe1e*eTT7r#&N zI~$6Bf8ZC1Uj}|7@H>oO5DK-v#s(f!CU^0PZ|Pab;&tOp8-zabGe8sks^aS=R(VWm zC*AWecHx-P%~Z-lk1O|BAJ3x8`cB}&MN1I`i$r+$OkkPEm8{gCM$s|V38KCr$j)u$ ziNOGXM|Q7uC@$NHn-!cpJ7GX)Y)V?XrC}0^#t2r4b7dA-Ecxp`<`z`vCcm#9i*p5u=F35yTgAT z0~pK=Goz2vy=YBQiA|Whz=M}+d%V`R$SPg4hL2#&egr}%MzVE3D)H_dq^cshm1cJ0 z5*<3w&2(xBG-JIjx$b@2MKIh&BXq{HuL7UNE;L zX-l$fka}N}U1s<&%8fzFbt2_)h*${oejk(7)N39&e1muBR6_gK*=TAr>+Vq~1M}i+dSY^BP2W+Pvu95!ts9Sho%TA{KyDwcVxOIYYWaUo zDLop+eZjx|R~I^ni7`?I@-CF=q0OG)oLA#btx8|nolm+ z>2)>XxKz2&Q)umA(AjmeZv@Z=%?|*yTr^tF)g0~f%0;&Nw9-u~8^*Su#=LxG zJv)C|iET1A9cYOAq;d4f`c*4kniai70x34L`8?3m^3scNMsd%s$7bi|sAZqj$b zAQWv6v-0FTrKEcC3>-s+!@y4`4*I~5ZbtUITo_KgO! z;b)bO_ipUxkEX<}Mhi>vRt;Usg>TmceSkMh30dx`1F*mpXR^1DU9jrBl;EnIeLxfp{d7H9H4o8yWC~VMniZ4cI$PM|H7O;#lW8eC(n<_M9~k zrqPEX!=-v9uKG?57Js<{>JfH@>el~#^9;izUNskCx7>{`0^tc;P zUh5d7A%wblt%1aELX|-!VhH_*Ci!s|0JMZ);#u+uUX6Xm4 z<9XO}pJGGKD;?Y;GN>?c(#Fwm^j`f|DuN6u5sratrJ?W+>eN&|pYo;RN18sh&rTXnNfd)H zuQh%!3%CHym1e03L9U!D_pMt`5P4(WdYC_Pt3r*;Gz%fLE>J~6aKcXdQ>aSxS#5R} zB!{-R$6_tZPM|MQ3hqX#bmll3YmY;IKRhrv;zD&p$EP$)Iz6L?6BP6|-K zlxp8Z^Q|-I2=cb;8l2{Mt-n1?T!b$Uq-WQkKm-Ljz1IHI>HHj~B=jk+ej1Kg=;JSk z(VE{J9BA*w_BtUL<9SwN=)CPfRB?Zxz=yb{Fa{e9cv!X+wxY0PfLRKg&}#~RmGO#E z_2p^aH=8g|VAyHqSCJ>Up040}I_U?f-#ylO5PH#t6U6bf%LB)=k%!|;8wvo3F=MrY zdK^zUQVnq%${W*vtuKU6&-c&;Yz>ilBLu&paVJ=fG6s4 z+tkOPl3VB7r%gu=fD6jP^B1FZ6rlGNvT8gePf4;R9H&!FKD`=VM{ zf})^Xt+#ms93j%E`B^TIFm%Gea%qWK%yL<}+g+b8v5(w%A`|<9Lb^7d?+XT;MH`X& zVqsV*Vw~C~aACQD9*3#wD$Nwyml(+ALFh#Fy|5#I0L!`h1o<(=U?WdY2PhS{cJh8q zJr3zJJa59)N!43OKa%^-d@;xSrAO z*3)#lU1mA0pycOc4+tCkaxfMSZ!@&_Ok6NCnWg@UVb}%zkXPMITe1MK-qLtS_`DV> z{~-Qm8kTA3;Uh(E8I^ezOKoi_6~7Sovm3@;C;}^=!#gan>T7^#`*b(>6D-2VICc(r zc`dln*0Q7ok)Xbp@(;vRFl0@c~+Rnz&$Sl8Xq`~o*NB$yvTYzmmAfNg=jbobw&S~_F^txr(2`8t3G z+#Il{>#=u$ahfp_8zUE9rCro;Cw}HtLS;}TUY~PlUigm(cR7SkZ!xKXdT~!dW?DX+BFW8l666|(y_vxeSG_NQu)kZy0g|+RVENGDK*V9-7SC~W+r=fH zq1_7TY}spV%y(CM25=&42rVcYs}m_{jmhVdnDh`Dn}_t94KX&8S-&gFJ@?TekTwCl z^nNs=JRwW(gN0uk6)TU*ia%uW1Q;j-O>bsM)iq#TtINq&T){5#H#>XhiZbw_cG!9O zn#nbEfs?b%UYlv1ufi8qi;OP5}y22 z@!N&D+kqdn0Q^Sa+Yirkn-x}mRcXgATveQ@9Z)t#7mk#i+MHmgnlS5{1bsu%m!v~* zD5sWz!;(V`ToypQ*3+ZKnvIPF8_nF;;Jx8SKlb`H<=!^&E5IV5A)-J%;^aubcAf@y z=z|N`*=z6+P};!Uzzu#6(dIXPshJ=aP>4skDB4850rd}*Pun}_OL;yMzKhU^QJ2<1 zLFyjB`@o?n1r=*^5Q2(RO?tVTHgXrJiQ#|_9YMb1)>8JEWuP8`bneK4SlW$Z+uAh& z(muLVZnGVQM(lBV&>-;@?g?Cto!>SG(nh01Y{hLMo#{H^91MU`s5Rme;WTeql+s1B z{&b&g+)THX?q&K`kA^ykX01f9=f!4p^<4D9g*T`e)w=OM2ubI?iCi(sB?%b#vsD-NP zwKk(rkfn{U;WWkRHd2rB1hoqEKH$~ghyaClr`7=>q2(}9@72Bc(f9f=sM``jQ7_fo zb%M{p#uMAlKt5c|znrS+zYEB!AfZbVv)zQ_fFPF-MZA>F zsRnAxYg}$EuXVY#yvF4gFxQWpZ+i9bWT<=PgwBemfm4y@Srtz=Ejl)40Xh8#oR8`o z0m1h=`mK8i|7%V+vBN!}W6=bAR##iRRAK|_YJitt@sb;~iBi*gc6J6~xF33U*ld-SEx0Wo;3 zE9(H%j~~tBwZDvyL;R;(kdxP%lEYT~p|l)eMG%$tHV^G%B&9FGb^S(KO6dMful1t| zf)X+}#S$_{5rc^*o7rwDww9ZX$f!*=ihP4dK01L%x+m6;e1!58+_Mlx&_O+yY@?tk zPG@==&*0$L~j*SEmiZ^oCt?l6YcQOrtz(4>knQAkpIETjLXB(xc7LwF#bE*zowlc*z>i%JfU zbFw}3?Xg1Z9(kW1$AQ=QGFvZz*ZP2sW7|9ckA|+JfS%=pULv5yw>Z6>!n1u&0NyVE zT@RuB!6Hi+6sN2YaUhofvf9~~UZq)gKL9{j2*!>~H9{NI|Gd<{UhL8S&o=viSbGzI zDvR!a{J9UzbJ?!cSoUVC z)>~dRGh9;4jcl{GdZV&>j26m$z5ma7p1FvA>-&Dc-~W%go;hb`&g^Gq&YYQt_3HHi zSj3;&g>1-}opAC2TvxH0R~`mcQxQ~+!x|P9vuxlK_p@-@6mY`WgyqojN%Sq^2TDqz z!O7W`ittdgOn%}W78&1G3k0)xhnY4Rc`obM6Y&XjRJFc<7znLkmcvMv9QBa9F6*I* z++twXvJv2Ehv_UVgQr1+>1PYcM1;E0TN`z+X+dy1w%NY*kS2n(Th3V1y1N2>Hyj5m z=x_LnON+%LhGX*;1Jyqi(t45?kZfZFqkySjjYMeo=T1f6M%NA5vrk7nnMyB%g9W^Wj%_@ivJQ52z@01b6G}Jn)X0^B%;_P8&&j1 z1aUW&8C>+Ki^1hySkNS?XpcXOHjWzGcFqn5N z(;Ir*^~fU4bcNcz+k6bO37fh=4@K|l$&}icMmrY5xo-ETZE7=|6VY3FiqUZ|RUqwkUms>JR zGnX5cYcCio%;iS=!(473*-hCiN2S7C4!h+Fa2D1}4%k#x9Q4O{cXxTf{di~5gRx0t zuc$}K5~s=(wU8tm4Kp=KTjrPow0RMf(`w)Q5C)ZqYCEbv3t_)I6=kGPp1i&it%Hdd z+9vHD<7p_v7MJjM7>h0IgG`{+dN=#$l99W)utj{yEDQ@WSr0Rp)^mbRektzBMalX zI}L64O(XNQF33Qif_AJG_LACHR!*p?Z}JXa%oh7BEC;pm4vR8*44JdqS6ri#&d0M` z*Iarp__a4SX|9q55RPMbp%?3s@D_q<4TN0(3)b7}doyW>YDF`>ldOr(R!fPsCKbRu zcMLz_#rmeqTyf2{i)vEqfTvYs2|^WIUg09za$MW}JZ-1e2Izf_2&-+)K1Q+rUCdLx zS%u%%e1`tX<~oCeKqBgAMwzO)LFn_4y)x-J99W z39uS%ev@W!Iq%`aI?1+jKHP_;$Q3=g!-qx4#h>%Hd|0&n%>DdZ@}E=2J$>0I`AQrg z@5_42%cFUnHXc>20`FColT0`hct=#veOIVqkE(CxbJmIt z9vV0TIQG^w93Y8*sHPRxQS{O?$Q~5b=ZB-u?9se?TwZL%vl&pwUCT;}7~I{1PBN>+ z2i@B5=QXWZH}8C{Q&^{d&rh{tku!?GKHYIS<)*Rd;g?_=;L4-zmg z>xOKeYGHv{TS3Jzvi)TghsN&9@9Q$PR8gTTsYt+#D=J59{=p>iF=RaD| zWyeQzZ-3S+Co-A_E*=X?Tj7#Xx!NU7GT#V6GLWJx4w7WVw^>*GqS4O% z(1R4*6rP7Y5bpO3qkd!kM+0Bu56N26lgw>dtAo0}2THh91Z!S$s3lyl^w9VtN%lnZ zbNe8{a8hB)Zjkk3YxNv7=UQWHA98BG5KDbA2dvOV)2gsJ-lSn@`nz1#9OQvMpuY2C zuk5K7Sg;L0$=e06zUha*!7#-71htA?u)Z474;>SATi7;G72yejzO=fZgN+T28gZc> z`Kh9q=wQ9S<23;+y8Bq{!S$&%Rnw8HhMp)^RcVZR8TF{&F}_^fO8q%{Lu(c|@$_|r zB(6l>O%rb&aP@mI!3zderrp4Zc?XO?^<|1*Wsgyp#sYhqFpRgC=A+MI0gwH&d4~bB z8q9s?;7-6xGkIZa=8(I$;@4ZV;BGS)Qs36s&X?6Blz=AUL=0%tER^8zJ~UQ2?%Lb= zfI!yKVCVM)vi7a9*0f`#jzzT#FNHwDeAf9vsPiAEwJ z8`c+&pYdW4rp}mD5HzjgXVf*2KGb$NQPt++bZAIHN!8S(uu>XA%LVhQ*ANmKs|4JT z+pI;h2F=vuv)zF&qi&*&DVc)p39kWfbssHF=y2W5nz(6v6DbjGq*6w-^9E8@;Mfrf zL0)0S9*A^eJWEWJoM(xGL6%6j<*`aA2XCqZGgU!;Bs!N)(^NkP%E-6M`p67-zSINl=WH8k;Yv7)RAj#73v}r_d!mse-f$l}IsB zLxHg#)0R0r=H02SsSQ`g)jN*903E8i#slm0&=gn8u)a6(;`dV{tl=Sf>Co%@G*6@@?+ z-n&Gj?WkG>qmi_#F$Qe!f(%s|KDgwpF4IP{Fz;c()c=T)=h8P$h8V(`9vDbk()E2Vn^pKg=YfN}hCFnb1%6XB-% zq0AmPXH=Va;Gy}z#2z1FOY5q|$F6_F;R+tv(vAl8Ho2LO&j27ZblLP{G>Nv?i`42& zRLQnM$P?{{O?W%in_M~rs#cfvP59!on%O;WLRX5<0}P5z_DnAE#wP++)|NCi8Z$zE zO{@6YMPK29wDMfsJ_?saQHAQb~^mo5H_+` zR3Yshk81DvV*mOnw!&&1dLMOx7}P^nev2+pmc~1_#{z9k8dVPTPA=;lII~xsp)4Fz zpX|(Mw`bk>{7@F0qvkoH-Ikx&{yQEdTX1~%S3;{$(g@Uq%*Mlr#duj~ZSRj+!{jLFD@oS@Zz3`npt2fEdx#sfJ?D zHFQEpk+Fe-Pa;tLP-iP&T4;~TLLK8T*Ed5}t=c#(E*-&9Nbg&f+d=X`5B_5ys;r=HMrHdEjZEV~VzVM$;;E-S z0?SU@aE26mPnZXm2vaBzRi?#LutCV8W9~|p4G(ZMuZ-6GtoOkWlf&~0A;Ld{ZtWK=I@hPG^nt0l2 zkJk209TB$zYqF!}C$LB{uly$+)Hn{EebS4Vl3s0kC%Unwc#w>kQ;U&|at z{bh~Q0h|HI^#IkNm^jsn&p(CsfX8dP4Q%1y2$Mf7LE;k8f_5H{lfRvK3CWOP~Kg!(wz&%UXD<-Cwp#Faq6P z3)C*JLZ1FN?Q%Kz|JE)~B(}`am*DbWdrH6r1iC#(db@ZK-Q6yX;55CVzJ8 z?U9GjcM71_#x+;<%W#USu7UHv8v=hd0=(~-PqO;9EZAc~e^s!V{s?q?!KkWVNNG{k zfz1-gM@XP1SYMvK@xPZqRef6K=wopCi>e-pK)2_Jdq!YlxYqktRY%=uqgS>0i32d* zSXFQd6Vkb;Zyw!+ zbufIyv%0Vz0VOy$g}8r-I)Dvl8Bcu4E4#41yuJ&Ij9#CwJvB?|Ve4Z}Pwa~KcTn-y z$J26JJ^MUWZk4GDn|^f?kLU`0)1Z8w)s@9|J{Jft%wgzARlPfCPX^u87VWeHz+(OP zJpNo)Hnwa?J<0<`rlzi@xf3RNl4_s?U>J1v5n#KamPdUYSMkiGXo`~d&1=78N(i2D zfOy^MDiuPCX$6j-Kfp|jmh^b& z0!#Yh_8=V^$<;`x@SYsWt=(9+kS-%oQAfsUWrOPcI35VaE0P5^HMlh&-;H$%$S&3@ zR;!STQZK1^9DlSM3oJ0TP~~RZbXBf1Kri?<4MI1pDR?*Qn5G40>f^vrDRcx~k%Gn6 z1PVZDd-7 z%TO{K4;J92x(ar8tTc#!_|bsIc8{pb=j5lBANpj**%8{e`!79ijN8m#*Lt^4LvGvfZ%G~E%T zPShPXb)@Dvsm?=8>iaV^SWLSzcV8N%k#LNp^{fmX!})31kz(Jhg26?|MCiK|dl-mx zx)7!ZNhORz*b(B|ZY_jHW2!+9k)ww{*Glut)v+HmY>CZft{5oYewH!TyTt$)(L;=Y zceDs6)z>u$#nvQj%SjP-_GwUb6&(LPngz>ShH{^tEM8uh%?I{my|c=*W$A1ax@@K+ zJ5{qo{8&Bz3pC0wvBiPaqvZV¥?X=2eHt;R@1xo+BS85%6dn1>Kv?YkM+l;OA3s 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 aJ;nc!C*f<_a*F@PoxxN6X&)NLrv7hKm*Nco -- 2.11.0