Browse Source

Merge branch 'master' into dev

# Conflicts:
#	source/forms/ScpCommander.cpp

#	source/windows/WinConfiguration.h

Source commit: 6303fe6c4c5929ade1127007af6321e256f6940d
Martin Prikryl 3 years ago
parent
commit
d284d93a83
100 changed files with 2578 additions and 1866 deletions
  1. 6 6
      libs/expat/CMake.README
  2. 3 3
      libs/expat/CMakeLists.txt
  3. 95 0
      libs/expat/Changes
  4. 1 0
      libs/expat/Makefile.in
  5. 1 1
      libs/expat/README.md
  6. 2 2
      libs/expat/cmake/autotools/expat-noconfig__windows.cmake.in
  7. 25 12
      libs/expat/configure
  8. 10 2
      libs/expat/configure.ac
  9. 1 2
      libs/expat/doc/Makefile.am
  10. 2 2
      libs/expat/doc/Makefile.in
  11. 1 1
      libs/expat/doc/reference.html
  12. BIN
      libs/expat/doc/valid-xhtml10.png
  13. 1 1
      libs/expat/doc/xmlwf.1
  14. 2 2
      libs/expat/doc/xmlwf.xml
  15. 1 0
      libs/expat/examples/Makefile.in
  16. 1 1
      libs/expat/examples/elements.c
  17. 1 1
      libs/expat/examples/outline.c
  18. 3 3
      libs/expat/expat_config.h
  19. 1 0
      libs/expat/lib/Makefile.in
  20. 1 1
      libs/expat/lib/expat.h
  21. 145 54
      libs/expat/lib/xmlparse.c
  22. 1 1
      libs/expat/lib/xmlrole.c
  23. 2 7
      libs/expat/lib/xmltok.c
  24. 12 8
      libs/expat/lib/xmltok_impl.c
  25. 1 0
      libs/expat/tests/Makefile.in
  26. 1 0
      libs/expat/tests/benchmark/Makefile.in
  27. 1 1
      libs/expat/tests/benchmark/benchmark.c
  28. 247 4
      libs/expat/tests/runtests.c
  29. 7 3
      libs/expat/win32/expat.iss
  30. 1 0
      libs/expat/xmlwf/Makefile.in
  31. 1 1
      libs/expat/xmlwf/xmlfile.c
  32. 4 4
      libs/expat/xmlwf/xmlwf.c
  33. 8 5
      libs/puttyvs/PuTTYVS.vcxproj
  34. 334 183
      source/Putty.cbproj
  35. 1 0
      source/components/UnixDirView.cpp
  36. 15 1
      source/components/UnixDriveView.cpp
  37. 2 0
      source/components/UnixDriveView.h
  38. 0 5
      source/core/Configuration.cpp
  39. 0 1
      source/core/Configuration.h
  40. 3 5
      source/core/FtpFileSystem.cpp
  41. 2 1
      source/core/Interface.h
  42. 54 27
      source/core/PuttyIntf.cpp
  43. 1 1
      source/core/PuttyIntf.h
  44. 11 3
      source/core/RemoteFiles.cpp
  45. 1 1
      source/core/RemoteFiles.h
  46. 82 40
      source/core/SecureShell.cpp
  47. 3 1
      source/core/SecureShell.h
  48. 1 0
      source/core/WebDAVFileSystem.cpp
  49. 1 0
      source/filezilla/FileZillaIntf.cpp
  50. 1 0
      source/filezilla/FileZillaIntf.h
  51. 1 0
      source/filezilla/FtpControlSocket.cpp
  52. 41 35
      source/filezilla/FtpListResult.cpp
  53. 1 0
      source/filezilla/FtpListResult.h
  54. 1 1
      source/filezilla/structures.cpp
  55. 1 0
      source/filezilla/structures.h
  56. 1 1
      source/forms/Cleanup.dfm
  57. 1 1
      source/forms/Copy.dfm
  58. 1 1
      source/forms/Custom.cpp
  59. 1 1
      source/forms/FileSystemInfo.dfm
  60. 1 1
      source/forms/GenerateUrl.dfm
  61. 1 1
      source/forms/ImportSessions.dfm
  62. 3 1
      source/forms/NonVisual.cpp
  63. 8 8
      source/forms/Preferences.dfm
  64. 19 9
      source/forms/ScpCommander.cpp
  65. 1 1
      source/forms/SiteAdvanced.cpp
  66. 3 3
      source/forms/SiteAdvanced.dfm
  67. 6 0
      source/packages/filemng/DirView.pas
  68. 240 68
      source/packages/filemng/DriveView.pas
  69. 2 2
      source/packages/filemng/IEDriveInfo.pas
  70. 118 0
      source/putty/be_list.c
  71. 0 18
      source/putty/be_ssh.c
  72. 0 179
      source/putty/cproxy.c
  73. 14 0
      source/putty/crypto/aes-common.c
  74. 316 0
      source/putty/crypto/aes-ni.c
  75. 99 0
      source/putty/crypto/aes-select.c
  76. 29 927
      source/putty/crypto/aes-sw.c
  77. 126 0
      source/putty/crypto/aes.h
  78. 0 0
      source/putty/crypto/aesold.c
  79. 0 0
      source/putty/crypto/arcfour.c
  80. 1 1
      source/putty/crypto/argon2.c
  81. 5 6
      source/putty/crypto/bcrypt.c
  82. 0 0
      source/putty/crypto/blake2.c
  83. 1 1
      source/putty/crypto/blowfish.c
  84. 1 1
      source/putty/crypto/blowfish.h
  85. 0 0
      source/putty/crypto/chacha20-poly1305.c
  86. 1 1
      source/putty/crypto/des.c
  87. 11 22
      source/putty/crypto/diffie-hellman.c
  88. 129 129
      source/putty/crypto/dsa.c
  89. 4 0
      source/putty/crypto/ecc-arithmetic.c
  90. 6 14
      source/putty/crypto/ecc-ssh.c
  91. 2 2
      source/putty/crypto/ecc.h
  92. 13 0
      source/putty/crypto/hash_simple.c
  93. 6 6
      source/putty/crypto/hmac.c
  94. 0 0
      source/putty/crypto/mac.c
  95. 16 0
      source/putty/crypto/mac_simple.c
  96. 0 0
      source/putty/crypto/md5.c
  97. 212 26
      source/putty/crypto/mpint.c
  98. 1 1
      source/putty/crypto/mpint_i.h
  99. 1 1
      source/putty/crypto/prng.c
  100. 32 0
      source/putty/crypto/pubkey-pem.c

+ 6 - 6
libs/expat/CMake.README

@@ -3,25 +3,25 @@
 The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual
 The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual
 Studio) and should work on all other platform cmake supports.
 Studio) and should work on all other platform cmake supports.
 
 
-Assuming ~/expat-2.4.3 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.4.6 is the source directory of expat, add a subdirectory
 build and change into that directory:
 build and change into that directory:
-~/expat-2.4.3$ mkdir build && cd build
-~/expat-2.4.3/build$
+~/expat-2.4.6$ mkdir build && cd build
+~/expat-2.4.6/build$
 
 
 From that directory, call cmake first, then call make, make test and
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
 make install in the usual way:
-~/expat-2.4.3/build$ cmake ..
+~/expat-2.4.6/build$ cmake ..
 -- The C compiler identification is GNU
 -- The C compiler identification is GNU
 -- The CXX compiler identification is GNU
 -- The CXX compiler identification is GNU
 ....
 ....
 -- Configuring done
 -- Configuring done
 -- Generating done
 -- Generating done
--- Build files have been written to: /home/patrick/expat-2.4.3/build
+-- Build files have been written to: /home/patrick/expat-2.4.6/build
 
 
 If you want to specify the install location for your files, append
 If you want to specify the install location for your files, append
 -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call.
 -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call.
 
 
-~/expat-2.4.3/build$ make && make test && make install
+~/expat-2.4.6/build$ make && make test && make install
 Scanning dependencies of target expat
 Scanning dependencies of target expat
 [  5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o
 [  5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o
 [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o
 [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o

+ 3 - 3
libs/expat/CMakeLists.txt

@@ -64,7 +64,7 @@ endif()
 
 
 project(expat
 project(expat
     VERSION
     VERSION
-        2.4.3
+        2.4.6
     LANGUAGES
     LANGUAGES
         C
         C
 )
 )
@@ -408,7 +408,7 @@ if(EXPAT_WITH_LIBBSD)
 endif()
 endif()
 
 
 set(LIBCURRENT 9)   # sync
 set(LIBCURRENT 9)   # sync
-set(LIBREVISION 3)  # with
+set(LIBREVISION 6)  # with
 set(LIBAGE 8)       # configure.ac!
 set(LIBAGE 8)       # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 
 
@@ -422,7 +422,7 @@ if(WIN32 AND NOT MINGW)
     #       on Windows by resorting to filename libexpat.dll since Expat 1.95.3.
     #       on Windows by resorting to filename libexpat.dll since Expat 1.95.3.
     #       Everything but MSVC is already adding prefix "lib", automatically.
     #       Everything but MSVC is already adding prefix "lib", automatically.
     # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll
     # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll
-    #       files but not *.lib files, so we have to rely on propert OUTPUT_NAME, instead.
+    #       files but not *.lib files, so we have to rely on property OUTPUT_NAME, instead.
     #       Property CMAKE_*_POSTFIX still applies.
     #       Property CMAKE_*_POSTFIX still applies.
     set_property(TARGET expat PROPERTY OUTPUT_NAME libexpat)
     set_property(TARGET expat PROPERTY OUTPUT_NAME libexpat)
 endif()
 endif()

+ 95 - 0
libs/expat/Changes

@@ -2,6 +2,101 @@ NOTE: We are looking for help with a few things:
       https://github.com/libexpat/libexpat/labels/help%20wanted
       https://github.com/libexpat/libexpat/labels/help%20wanted
       If you can help, please get in touch.  Thanks!
       If you can help, please get in touch.  Thanks!
 
 
+Release 2.4.6 Sun February 20 2022
+        Bug fixes:
+            #566  Fix a regression introduced by the fix for CVE-2022-25313
+                    in release 2.4.5 that affects applications that (1)
+                    call function XML_SetElementDeclHandler and (2) are
+                    parsing XML that contains nested element declarations
+                    (e.g. "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>").
+
+        Other changes:
+       #567 #568  Version info bumped from 9:5:8 to 9:6:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Matt Sergeant
+            Samanta Navarro
+            Sergei Trofimovich
+                 and
+            NixOS
+            Perl XML::Parser
+
+Release 2.4.5 Fri February 18 2022
+        Security fixes:
+            #562  CVE-2022-25235 -- Passing malformed 2- and 3-byte UTF-8
+                    sequences (e.g. from start tag names) to the XML
+                    processing application on top of Expat can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on how invalid UTF-8 is handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #561  CVE-2022-25236 -- Passing (one or more) namespace separator
+                    characters in "xmlns[:prefix]" attribute values
+                    made Expat send malformed tag names to the XML
+                    processor on top of Expat which can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on such unexpectable cases are handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #558  CVE-2022-25313 -- Fix stack exhaustion in doctype parsing
+                    that could be triggered by e.g. a 2 megabytes
+                    file with a large number of opening braces.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+            #560  CVE-2022-25314 -- Fix integer overflow in function copyString;
+                    only affects the encoding name parameter at parser creation
+                    time which is often hardcoded (rather than user input),
+                    takes a value in the gigabytes to trigger, and a 64-bit
+                    machine.  Expected impact is denial of service.
+            #559  CVE-2022-25315 -- Fix integer overflow in function storeRawNames;
+                    needs input in the gigabytes and a 64-bit machine.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+
+        Other changes:
+       #557 #564  Version info bumped from 9:4:8 to 9:5:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Ivan Fratric
+            Samanta Navarro
+                 and
+            Google Project Zero
+            JetBrains
+
+Release 2.4.4 Sun January 30 2022
+        Security fixes:
+            #550  CVE-2022-23852 -- Fix signed integer overflow
+                    (undefined behavior) in function XML_GetBuffer
+                    (that is also called by function XML_Parse internally)
+                    for when XML_CONTEXT_BYTES is defined to >0 (which is both
+                    common and default).
+                    Impact is denial of service or more.
+            #551  CVE-2022-23990 -- Fix unsigned integer overflow in function
+                    doProlog triggered by large content in element type
+                    declarations when there is an element declaration handler
+                    present (from a prior call to XML_SetElementDeclHandler).
+                    Impact is denial of service or more.
+
+        Bug fixes:
+       #544 #545  xmlwf: Fix a memory leak on output file opening error
+
+        Other changes:
+            #546  Autotools: Fix broken CMake support under Cygwin
+            #554  Windows: Add missing files to the installer to fix
+                    compilation with CMake from installed sources
+       #552 #554  Version info bumped from 9:3:8 to 9:4:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Carlo Bramini
+            hwt0415
+            Roland Illig
+            Samanta Navarro
+                 and
+            Clang LeakSan and the Clang team
+
 Release 2.4.3 Sun January 16 2022
 Release 2.4.3 Sun January 16 2022
         Security fixes:
         Security fixes:
        #531 #534  CVE-2021-45960 -- Fix issues with left shifts by >=29 places
        #531 #534  CVE-2021-45960 -- Fix issues with left shifts by >=29 places

+ 1 - 0
libs/expat/Makefile.in

@@ -306,6 +306,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/README.md

@@ -5,7 +5,7 @@
 [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
 [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
 
 
 
 
-# Expat, Release 2.4.3
+# Expat, Release 2.4.6
 
 
 This is Expat, a C library for parsing XML, started by
 This is Expat, a C library for parsing XML, started by
 [James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997.
 [James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997.

+ 2 - 2
libs/expat/cmake/autotools/expat-noconfig__windows.cmake.in

@@ -9,11 +9,11 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_target_properties(expat::expat PROPERTIES
 set_target_properties(expat::expat PROPERTIES
   IMPORTED_IMPLIB_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a"
   IMPORTED_IMPLIB_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a"
-  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/bin/libexpat-@[email protected]"
+  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@[email protected]"
   )
   )
 
 
 list(APPEND _IMPORT_CHECK_TARGETS expat::expat )
 list(APPEND _IMPORT_CHECK_TARGETS expat::expat )
-list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a" "${_IMPORT_PREFIX}/bin/libexpat-@[email protected]" )
+list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a" "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@[email protected]" )
 
 
 # Commands beyond this point should not need to know the version.
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)
 set(CMAKE_IMPORT_FILE_VERSION)

+ 25 - 12
libs/expat/configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for expat 2.4.3.
+# Generated by GNU Autoconf 2.71 for expat 2.4.6.
 #
 #
 # Report bugs to <[email protected]>.
 # Report bugs to <[email protected]>.
 #
 #
@@ -621,8 +621,8 @@ MAKEFLAGS=
 # Identity of this package.
 # Identity of this package.
 PACKAGE_NAME='expat'
 PACKAGE_NAME='expat'
 PACKAGE_TARNAME='expat'
 PACKAGE_TARNAME='expat'
-PACKAGE_VERSION='2.4.3'
-PACKAGE_STRING='expat 2.4.3'
+PACKAGE_VERSION='2.4.6'
+PACKAGE_STRING='expat 2.4.6'
 PACKAGE_BUGREPORT='[email protected]'
 PACKAGE_BUGREPORT='[email protected]'
 PACKAGE_URL=''
 PACKAGE_URL=''
 
 
@@ -664,6 +664,7 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LTLIBOBJS
 LIBOBJS
 LIBOBJS
+CMAKE_SHARED_LIBRARY_PREFIX
 AM_LDFLAGS
 AM_LDFLAGS
 AM_CXXFLAGS
 AM_CXXFLAGS
 AM_CFLAGS
 AM_CFLAGS
@@ -1413,7 +1414,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
   cat <<_ACEOF
-\`configure' configures expat 2.4.3 to adapt to many kinds of systems.
+\`configure' configures expat 2.4.6 to adapt to many kinds of systems.
 
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
 
@@ -1484,7 +1485,7 @@ fi
 
 
 if test -n "$ac_init_help"; then
 if test -n "$ac_init_help"; then
   case $ac_init_help in
   case $ac_init_help in
-     short | recursive ) echo "Configuration of expat 2.4.3:";;
+     short | recursive ) echo "Configuration of expat 2.4.6:";;
    esac
    esac
   cat <<\_ACEOF
   cat <<\_ACEOF
 
 
@@ -1618,7 +1619,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
 if $ac_init_version; then
   cat <<\_ACEOF
   cat <<\_ACEOF
-expat configure 2.4.3
+expat configure 2.4.6
 generated by GNU Autoconf 2.71
 generated by GNU Autoconf 2.71
 
 
 Copyright (C) 2021 Free Software Foundation, Inc.
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2249,7 +2250,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 running configure, to aid debugging if configure makes a mistake.
 
 
-It was created by expat $as_me 2.4.3, which was
+It was created by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
 
   $ $0$ac_configure_args_raw
   $ $0$ac_configure_args_raw
@@ -3816,7 +3817,7 @@ fi
 
 
 # Define the identity of the package.
 # Define the identity of the package.
  PACKAGE='expat'
  PACKAGE='expat'
- VERSION='2.4.3'
+ VERSION='2.4.6'
 
 
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -3923,7 +3924,7 @@ fi
 
 
 
 
 LIBCURRENT=9   # sync
 LIBCURRENT=9   # sync
-LIBREVISION=3  # with
+LIBREVISION=6  # with
 LIBAGE=8       # CMakeLists.txt!
 LIBAGE=8       # CMakeLists.txt!
 
 
 ac_config_headers="$ac_config_headers expat_config.h"
 ac_config_headers="$ac_config_headers expat_config.h"
@@ -19638,10 +19639,22 @@ printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h
 
 
 
 
 
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shared library name prefix" >&5
+printf %s "checking for shared library name prefix... " >&6; }
+case "${host_os}" in #(
+  cygwin*) :
+    CMAKE_SHARED_LIBRARY_PREFIX=cyg ;; #(
+  *) :
+    CMAKE_SHARED_LIBRARY_PREFIX=lib ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${CMAKE_SHARED_LIBRARY_PREFIX}" >&5
+printf "%s\n" "${CMAKE_SHARED_LIBRARY_PREFIX}" >&6; }
+
+
 case "${host_os}" in #(
 case "${host_os}" in #(
   darwin*) :
   darwin*) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in ;; #(
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in ;; #(
-  mingw*) :
+  mingw*|cygwin*) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in ;; #(
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in ;; #(
   *) :
   *) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in ;;
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in ;;
@@ -20214,7 +20227,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 # values after options handling.
 ac_log="
 ac_log="
-This file was extended by expat $as_me 2.4.3, which was
+This file was extended by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_FILES    = $CONFIG_FILES
@@ -20282,7 +20295,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
 ac_cs_version="\\
-expat config.status 2.4.3
+expat config.status 2.4.6
 configured by $0, generated by GNU Autoconf 2.71,
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
   with options \\"\$ac_cs_config\\"
 
 

+ 10 - 2
libs/expat/configure.ac

@@ -82,7 +82,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 dnl
 
 
 LIBCURRENT=9   # sync
 LIBCURRENT=9   # sync
-LIBREVISION=3  # with
+LIBREVISION=6  # with
 LIBAGE=8       # CMakeLists.txt!
 LIBAGE=8       # CMakeLists.txt!
 
 
 AC_CONFIG_HEADERS([expat_config.h])
 AC_CONFIG_HEADERS([expat_config.h])
@@ -395,9 +395,17 @@ AC_SUBST([AM_CFLAGS])
 AC_SUBST([AM_CXXFLAGS])
 AC_SUBST([AM_CXXFLAGS])
 AC_SUBST([AM_LDFLAGS])
 AC_SUBST([AM_LDFLAGS])
 
 
+dnl Emulate the use of CMAKE_SHARED_LIBRARY_PREFIX under CMake
+AC_MSG_CHECKING([for shared library name prefix])
+AS_CASE("${host_os}",
+  [cygwin*], [CMAKE_SHARED_LIBRARY_PREFIX=cyg],
+  [CMAKE_SHARED_LIBRARY_PREFIX=lib])
+AC_MSG_RESULT([${CMAKE_SHARED_LIBRARY_PREFIX}])
+AC_SUBST([CMAKE_SHARED_LIBRARY_PREFIX])
+
 AS_CASE("${host_os}",
 AS_CASE("${host_os}",
   [darwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in],
   [darwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in],
-  [mingw*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
+  [mingw*|cygwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
   [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in])
   [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in])
 AC_CONFIG_FILES([Makefile]
 AC_CONFIG_FILES([Makefile]
   [expat.pc]
   [expat.pc]

+ 1 - 2
libs/expat/doc/Makefile.am

@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #                               |_| XML parser
 #
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2022 Sebastian Pipping <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Licensed under the MIT license:
 # Licensed under the MIT license:
@@ -57,5 +57,4 @@ EXTRA_DIST = \
     ok.min.css \
     ok.min.css \
     reference.html \
     reference.html \
     style.css \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml
     xmlwf.xml

+ 2 - 2
libs/expat/doc/Makefile.in

@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #                               |_| XML parser
 #
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2022 Sebastian Pipping <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Licensed under the MIT license:
 # Licensed under the MIT license:
@@ -209,6 +209,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@
@@ -344,7 +345,6 @@ EXTRA_DIST = \
     ok.min.css \
     ok.min.css \
     reference.html \
     reference.html \
     style.css \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml
     xmlwf.xml
 
 
 all: all-am
 all: all-am

+ 1 - 1
libs/expat/doc/reference.html

@@ -49,7 +49,7 @@
   <div>
   <div>
     <h1>
     <h1>
       The Expat XML Parser
       The Expat XML Parser
-      <small>Release 2.4.3</small>
+      <small>Release 2.4.6</small>
     </h1>
     </h1>
   </div>
   </div>
 <div class="content">
 <div class="content">

BIN
libs/expat/doc/valid-xhtml10.png


+ 1 - 1
libs/expat/doc/xmlwf.1

@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 \\$2 \(la\\$1\(ra\\$3
 ..
 ..
 .if \n(.g .mso www.tmac
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "January 16, 2022" "" ""
+.TH XMLWF 1 "February 20, 2022" "" ""
 .SH NAME
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS
 .SH SYNOPSIS

+ 2 - 2
libs/expat/doc/xmlwf.xml

@@ -21,8 +21,8 @@
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!-- Please adjust the date whenever revising the manpage. -->
-  <!ENTITY dhdate      "<date>January 16, 2022</date>">
+  <!ENTITY dhdate      "<date>February 20, 2022</date>">
+  <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>[email protected]</email>">
   <!ENTITY dhemail     "<email>[email protected]</email>">
   <!ENTITY dhusername  "Scott Bronson">
   <!ENTITY dhusername  "Scott Bronson">

+ 1 - 0
libs/expat/examples/Makefile.in

@@ -230,6 +230,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/examples/elements.c

@@ -13,7 +13,7 @@
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      Zhongyuan Zhou <[email protected]>
    Copyright (c) 2019      Zhongyuan Zhou <[email protected]>

+ 1 - 1
libs/expat/examples/outline.c

@@ -10,7 +10,7 @@
 
 
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2005-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2005-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>

+ 3 - 3
libs/expat/expat_config.h

@@ -77,7 +77,7 @@
 #define PACKAGE_NAME "expat"
 #define PACKAGE_NAME "expat"
 
 
 /* Define to the full name and version of this package. */
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.4.3"
+#define PACKAGE_STRING "expat 2.4.6"
 
 
 /* Define to the one symbol short name of this package. */
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
 #define PACKAGE_TARNAME "expat"
@@ -86,7 +86,7 @@
 #define PACKAGE_URL ""
 #define PACKAGE_URL ""
 
 
 /* Define to the version of this package. */
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.4.3"
+#define PACKAGE_VERSION "2.4.6"
 
 
 /* Define to 1 if all of the C90 standard headers exist (not just the ones
 /* Define to 1 if all of the C90 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
    required in a freestanding environment). This macro is provided for
@@ -94,7 +94,7 @@
 #define STDC_HEADERS 1
 #define STDC_HEADERS 1
 
 
 /* Version number of package */
 /* Version number of package */
-#define VERSION "2.4.3"
+#define VERSION "2.4.6"
 
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
    significant byte first (like Motorola and SPARC, unlike Intel). */

+ 1 - 0
libs/expat/lib/Makefile.in

@@ -268,6 +268,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/lib/expat.h

@@ -1041,7 +1041,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
 */
 */
 #define XML_MAJOR_VERSION 2
 #define XML_MAJOR_VERSION 2
 #define XML_MINOR_VERSION 4
 #define XML_MINOR_VERSION 4
-#define XML_MICRO_VERSION 3
+#define XML_MICRO_VERSION 6
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 145 - 54
libs/expat/lib/xmlparse.c

@@ -1,4 +1,4 @@
-/* 9ca2a2fedc35bcb13ba9a134ba5e173020bc2ff5f5a311abf742cec7da1ff26a (2.4.3+)
+/* a30d2613dcfdef81475a9d1a349134d2d42722172fdaa7d5bb12ed2aa74b9596 (2.4.6+)
                             __  __            _
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
                         / _ \\  /| '_ \ / _` | __|
@@ -11,7 +11,7 @@
    Copyright (c) 2000-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2000-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2002 Greg Stein <[email protected]>
    Copyright (c) 2001-2002 Greg Stein <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
    Copyright (c) 2016      Eric Rahm <[email protected]>
    Copyright (c) 2016      Eric Rahm <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Gaurav <[email protected]>
    Copyright (c) 2016      Gaurav <[email protected]>
@@ -33,6 +33,7 @@
    Copyright (c) 2019-2020 Ben Wagner <[email protected]>
    Copyright (c) 2019-2020 Ben Wagner <[email protected]>
    Copyright (c) 2019      Vadim Zeitlin <[email protected]>
    Copyright (c) 2019      Vadim Zeitlin <[email protected]>
    Copyright (c) 2021      Dong-hee Na <[email protected]>
    Copyright (c) 2021      Dong-hee Na <[email protected]>
+   Copyright (c) 2022      Samanta Navarro <[email protected]>
    Licensed under the MIT license:
    Licensed under the MIT license:
 
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -717,8 +718,10 @@ XML_ParserCreate(const XML_Char *encodingName) {
 
 
 XML_Parser XMLCALL
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
+  // WINSCP
   XML_Char tmp[2];
   XML_Char tmp[2];
-  *tmp = nsSep;
+  tmp[0] = nsSep;
+  tmp[1] = 0;
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
 }
 }
 
 
@@ -978,7 +981,7 @@ parserCreate(const XML_Char *encodingName,
 
 
   if (memsuite) {
   if (memsuite) {
     XML_Memory_Handling_Suite *mtemp;
     XML_Memory_Handling_Suite *mtemp;
-    parser = (XML_Parser)memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
     if (parser != NULL) {
     if (parser != NULL) {
       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
       mtemp->malloc_fcn = memsuite->malloc_fcn;
       mtemp->malloc_fcn = memsuite->malloc_fcn;
@@ -1347,8 +1350,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
      would be otherwise.
      would be otherwise.
   */
   */
   if (parser->m_ns) {
   if (parser->m_ns) {
+    // WINSCP
     XML_Char tmp[2];
     XML_Char tmp[2];
-    *tmp = parser->m_namespaceSeparator;
+    tmp[0] = parser->m_namespaceSeparator;
+    tmp[1] = 0;
     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
   } else {
   } else {
     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
@@ -2071,6 +2076,11 @@ XML_GetBuffer(XML_Parser parser, int len) {
     keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
     keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
     if (keep > XML_CONTEXT_BYTES)
     if (keep > XML_CONTEXT_BYTES)
       keep = XML_CONTEXT_BYTES;
       keep = XML_CONTEXT_BYTES;
+    /* Detect and prevent integer overflow */
+    if (keep > INT_MAX - neededSize) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return NULL;
+    }
     neededSize += keep;
     neededSize += keep;
 #endif /* defined XML_CONTEXT_BYTES */
 #endif /* defined XML_CONTEXT_BYTES */
     if (neededSize
     if (neededSize
@@ -2561,6 +2571,7 @@ storeRawNames(XML_Parser parser) {
   while (tag) {
   while (tag) {
     int bufSize;
     int bufSize;
     int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
     int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+    size_t rawNameLen;
     char *rawNameBuf = tag->buf + nameLen;
     char *rawNameBuf = tag->buf + nameLen;
     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
        at the first entry that has already been copied; everything
        at the first entry that has already been copied; everything
@@ -2572,7 +2583,11 @@ storeRawNames(XML_Parser parser) {
     /* For re-use purposes we need to ensure that the
     /* For re-use purposes we need to ensure that the
        size of tag->buf is a multiple of sizeof(XML_Char).
        size of tag->buf is a multiple of sizeof(XML_Char).
     */
     */
-    bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    /* Detect and prevent integer overflow. */
+    if (rawNameLen > (size_t)INT_MAX - nameLen)
+      return XML_FALSE;
+    bufSize = nameLen + (int)rawNameLen;
     if (bufSize > tag->bufEnd - tag->buf) {
     if (bufSize > tag->bufEnd - tag->buf) {
       char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
       char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
       if (temp == NULL)
       if (temp == NULL)
@@ -3756,6 +3771,17 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
     if (! mustBeXML && isXMLNS
     if (! mustBeXML && isXMLNS
         && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
         && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
       isXMLNS = XML_FALSE;
       isXMLNS = XML_FALSE;
+
+    // NOTE: While Expat does not validate namespace URIs against RFC 3986,
+    //       we have to at least make sure that the XML processor on top of
+    //       Expat (that is splitting tag names by namespace separator into
+    //       2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused
+    //       by an attacker putting additional namespace separator characters
+    //       into namespace declarations.  That would be ambiguous and not to
+    //       be expected.
+    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) {
+      return XML_ERROR_SYNTAX;
+    }
   }
   }
   isXML = isXML && len == xmlLen;
   isXML = isXML && len == xmlLen;
   isXMLNS = isXMLNS && len == xmlnsLen;
   isXMLNS = isXMLNS && len == xmlnsLen;
@@ -4100,7 +4126,7 @@ initializeEncoding(XML_Parser parser) {
   const char *s;
   const char *s;
 #ifdef XML_UNICODE
 #ifdef XML_UNICODE
   char encodingBuf[128];
   char encodingBuf[128];
-  /* See comments abount `protoclEncodingName` in parserInit() */
+  /* See comments about `protocolEncodingName` in parserInit() */
   if (! parser->m_protocolEncodingName)
   if (! parser->m_protocolEncodingName)
     s = NULL;
     s = NULL;
   else {
   else {
@@ -5380,7 +5406,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       if (dtd->in_eldecl) {
       if (dtd->in_eldecl) {
         ELEMENT_TYPE *el;
         ELEMENT_TYPE *el;
         const XML_Char *name;
         const XML_Char *name;
-        int nameLen;
+        size_t nameLen;
         const char *nxt
         const char *nxt
             = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
             = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
         int myindex = nextScaffoldPart(parser);
         int myindex = nextScaffoldPart(parser);
@@ -5396,7 +5422,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
         nameLen = 0;
         nameLen = 0;
         for (; name[nameLen++];)
         for (; name[nameLen++];)
           ;
           ;
-        dtd->contentStringLen += nameLen;
+
+        /* Detect and prevent integer overflow */
+        if (nameLen > UINT_MAX - dtd->contentStringLen) {
+          return XML_ERROR_NO_MEMORY;
+        }
+
+        dtd->contentStringLen += (unsigned)nameLen;
         if (parser->m_elementDeclHandler)
         if (parser->m_elementDeclHandler)
           handleDefault = XML_FALSE;
           handleDefault = XML_FALSE;
       }
       }
@@ -6552,7 +6584,7 @@ normalizePublicId(XML_Char *publicId) {
 
 
 static DTD *
 static DTD *
 dtdCreate(const XML_Memory_Handling_Suite *ms) {
 dtdCreate(const XML_Memory_Handling_Suite *ms) {
-  DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD));
+  DTD *p = ms->malloc_fcn(sizeof(DTD));
   if (p == NULL)
   if (p == NULL)
     return p;
     return p;
   poolInit(&(p->pool), ms);
   poolInit(&(p->pool), ms);
@@ -6725,8 +6757,8 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
     if (! newE)
     if (! newE)
       return 0;
       return 0;
     if (oldE->nDefaultAtts) {
     if (oldE->nDefaultAtts) {
-      newE->defaultAtts = (DEFAULT_ATTRIBUTE *)ms->malloc_fcn(
-          oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      newE->defaultAtts
+          = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
       if (! newE->defaultAtts) {
       if (! newE->defaultAtts) {
         return 0;
         return 0;
       }
       }
@@ -6888,7 +6920,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
     /* table->size is a power of 2 */
     /* table->size is a power of 2 */
     table->size = (size_t)1 << INIT_POWER;
     table->size = (size_t)1 << INIT_POWER;
     tsize = table->size * sizeof(NAMED *);
     tsize = table->size * sizeof(NAMED *);
-    table->v = (NAMED **)table->mem->malloc_fcn(tsize);
+    table->v = table->mem->malloc_fcn(tsize);
     if (! table->v) {
     if (! table->v) {
       table->size = 0;
       table->size = 0;
       return NULL;
       return NULL;
@@ -6930,7 +6962,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
 
 
       { // WINSCP
       { // WINSCP
       size_t tsize = newSize * sizeof(NAMED *);
       size_t tsize = newSize * sizeof(NAMED *);
-      NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
+      NAMED **newV = table->mem->malloc_fcn(tsize);
       if (! newV)
       if (! newV)
         return NULL;
         return NULL;
       memset(newV, 0, tsize);
       memset(newV, 0, tsize);
@@ -6961,7 +6993,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
       } // WINSCP
       } // WINSCP
     }
     }
   }
   }
-  table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize);
+  table->v[i] = table->mem->malloc_fcn(createSize);
   if (! table->v[i])
   if (! table->v[i])
     return NULL;
     return NULL;
   memset(table->v[i], 0, createSize);
   memset(table->v[i], 0, createSize);
@@ -7249,7 +7281,7 @@ poolGrow(STRING_POOL *pool) {
     if (bytesToAllocate == 0)
     if (bytesToAllocate == 0)
       return XML_FALSE;
       return XML_FALSE;
 
 
-    tem = (BLOCK *)pool->mem->malloc_fcn(bytesToAllocate);
+    tem = pool->mem->malloc_fcn(bytesToAllocate);
     if (! tem)
     if (! tem)
       return XML_FALSE;
       return XML_FALSE;
     tem->size = blockSize;
     tem->size = blockSize;
@@ -7325,44 +7357,15 @@ nextScaffoldPart(XML_Parser parser) {
   return next;
   return next;
 }
 }
 
 
-static void
-build_node(XML_Parser parser, int src_node, XML_Content *dest,
-           XML_Content **contpos, XML_Char **strpos) {
-  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
-  dest->type = dtd->scaffold[src_node].type;
-  dest->quant = dtd->scaffold[src_node].quant;
-  if (dest->type == XML_CTYPE_NAME) {
-    const XML_Char *src;
-    dest->name = *strpos;
-    src = dtd->scaffold[src_node].name;
-    for (;;) {
-      *(*strpos)++ = *src;
-      if (! *src)
-        break;
-      src++;
-    }
-    dest->numchildren = 0;
-    dest->children = NULL;
-  } else {
-    unsigned int i;
-    int cn;
-    dest->numchildren = dtd->scaffold[src_node].childcnt;
-    dest->children = *contpos;
-    *contpos += dest->numchildren;
-    for (i = 0, cn = dtd->scaffold[src_node].firstchild; i < dest->numchildren;
-         i++, cn = dtd->scaffold[cn].nextsib) {
-      build_node(parser, cn, &(dest->children[i]), contpos, strpos);
-    }
-    dest->name = NULL;
-  }
-}
-
 static XML_Content *
 static XML_Content *
 build_model(XML_Parser parser) {
 build_model(XML_Parser parser) {
+  /* Function build_model transforms the existing parser->m_dtd->scaffold
+   * array of CONTENT_SCAFFOLD tree nodes into a new array of
+   * XML_Content tree nodes followed by a gapless list of zero-terminated
+   * strings. */
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   XML_Content *ret;
   XML_Content *ret;
-  XML_Content *cpos;
-  XML_Char *str;
+  XML_Char *str; /* the current string writing location */
 
 
   /* Detect and prevent integer overflow.
   /* Detect and prevent integer overflow.
    * The preprocessor guard addresses the "always false" warning
    * The preprocessor guard addresses the "always false" warning
@@ -7389,12 +7392,100 @@ build_model(XML_Parser parser) {
   if (! ret)
   if (! ret)
     return NULL;
     return NULL;
 
 
-  str = (XML_Char *)(&ret[dtd->scaffCount]);
-  cpos = &ret[1];
+  /* What follows is an iterative implementation (of what was previously done
+   * recursively in a dedicated function called "build_node".  The old recursive
+   * build_node could be forced into stack exhaustion from input as small as a
+   * few megabyte, and so that was a security issue.  Hence, a function call
+   * stack is avoided now by resolving recursion.)
+   *
+   * The iterative approach works as follows:
+   *
+   * - We have two writing pointers, both walking up the result array; one does
+   *   the work, the other creates "jobs" for its colleague to do, and leads
+   *   the way:
+   *
+   *   - The faster one, pointer jobDest, always leads and writes "what job
+   *     to do" by the other, once they reach that place in the
+   *     array: leader "jobDest" stores the source node array index (relative
+   *     to array dtd->scaffold) in field "numchildren".
+   *
+   *   - The slower one, pointer dest, looks at the value stored in the
+   *     "numchildren" field (which actually holds a source node array index
+   *     at that time) and puts the real data from dtd->scaffold in.
+   *
+   * - Before the loop starts, jobDest writes source array index 0
+   *   (where the root node is located) so that dest will have something to do
+   *   when it starts operation.
+   *
+   * - Whenever nodes with children are encountered, jobDest appends
+   *   them as new jobs, in order.  As a result, tree node siblings are
+   *   adjacent in the resulting array, for example:
+   *
+   *     [0] root, has two children
+   *       [1] first child of 0, has three children
+   *         [3] first child of 1, does not have children
+   *         [4] second child of 1, does not have children
+   *         [5] third child of 1, does not have children
+   *       [2] second child of 0, does not have children
+   *
+   *   Or (the same data) presented in flat array view:
+   *
+   *     [0] root, has two children
+   *
+   *     [1] first child of 0, has three children
+   *     [2] second child of 0, does not have children
+   *
+   *     [3] first child of 1, does not have children
+   *     [4] second child of 1, does not have children
+   *     [5] third child of 1, does not have children
+   *
+   * - The algorithm repeats until all target array indices have been processed.
+   */
+  { // WINSCP
+  XML_Content *dest = ret; /* tree node writing location, moves upwards */
+  XML_Content *const destLimit = &ret[dtd->scaffCount];
+  XML_Content *jobDest = ret; /* next free writing location in target array */
+  str = (XML_Char *)&ret[dtd->scaffCount];
+
+  /* Add the starting job, the root node (index 0) of the source tree  */
+  (jobDest++)->numchildren = 0;
+
+  for (; dest < destLimit; dest++) {
+    /* Retrieve source tree array index from job storage */
+    const int src_node = (int)dest->numchildren;
+
+    /* Convert item */
+    dest->type = dtd->scaffold[src_node].type;
+    dest->quant = dtd->scaffold[src_node].quant;
+    if (dest->type == XML_CTYPE_NAME) {
+      const XML_Char *src;
+      dest->name = str;
+      src = dtd->scaffold[src_node].name;
+      for (;;) {
+        *str++ = *src;
+        if (! *src)
+          break;
+        src++;
+      }
+      dest->numchildren = 0;
+      dest->children = NULL;
+    } else {
+      unsigned int i;
+      int cn;
+      dest->name = NULL;
+      dest->numchildren = dtd->scaffold[src_node].childcnt;
+      dest->children = jobDest;
+
+      /* Append scaffold indices of children to array */
+      for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+           i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
+        (jobDest++)->numchildren = (unsigned int)cn;
+    }
+  }
 
 
-  build_node(parser, 0, ret, &cpos, &str);
   return ret;
   return ret;
   } // WINSCP
   } // WINSCP
+  } // WINSCP
 }
 }
 
 
 static ELEMENT_TYPE *
 static ELEMENT_TYPE *
@@ -7422,7 +7513,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
 
 
 static XML_Char *
 static XML_Char *
 copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
 copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
-  int charsRequired = 0;
+  size_t charsRequired = 0;
   XML_Char *result;
   XML_Char *result;
 
 
   /* First determine how long the string is */
   /* First determine how long the string is */

+ 1 - 1
libs/expat/lib/xmlrole.c

@@ -11,7 +11,7 @@
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>

+ 2 - 7
libs/expat/lib/xmltok.c

@@ -11,8 +11,8 @@
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Pascal Cuoq <[email protected]>
    Copyright (c) 2016      Pascal Cuoq <[email protected]>
    Copyright (c) 2016      Don Lewis <[email protected]>
    Copyright (c) 2016      Don Lewis <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
@@ -98,11 +98,6 @@
         + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)]                 \
         + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)]                 \
    & (1u << (((byte)[2]) & 0x1F)))
    & (1u << (((byte)[2]) & 0x1F)))
 
 
-#define UTF8_GET_NAMING(pages, p, n)                                           \
-  ((n) == 2                                                                    \
-       ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p))                   \
-       : ((n) == 3 ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) : 0))
-
 /* Detection of invalid UTF-8 sequences is based on Table 3.1B
 /* Detection of invalid UTF-8 sequences is based on Table 3.1B
    of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
    of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
    with the additional restriction of not allowing the Unicode
    with the additional restriction of not allowing the Unicode

+ 12 - 8
libs/expat/lib/xmltok_impl.c

@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2002      Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002      Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2018      Benjamin Peterson <[email protected]>
    Copyright (c) 2018      Benjamin Peterson <[email protected]>
    Copyright (c) 2018      Anton Maklakov <[email protected]>
    Copyright (c) 2018      Anton Maklakov <[email protected]>
@@ -69,7 +69,7 @@
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NAME_CHAR(enc, ptr, n)) {                                         \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) {         \
       *nextTokPtr = ptr;                                                       \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
     }                                                                          \
@@ -98,7 +98,7 @@
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NMSTRT_CHAR(enc, ptr, n)) {                                       \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) {       \
       *nextTokPtr = ptr;                                                       \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
     }                                                                          \
@@ -1142,6 +1142,10 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
       return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
     if (IS_NMSTRT_CHAR(enc, ptr, n)) {                                         \
     if (IS_NMSTRT_CHAR(enc, ptr, n)) {                                         \
       ptr += n;                                                                \
       ptr += n;                                                                \
       tok = XML_TOK_NAME;                                                      \
       tok = XML_TOK_NAME;                                                      \
@@ -1270,7 +1274,7 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
     switch (BYTE_TYPE(enc, ptr)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
     break;
       LEAD_CASE(2)
       LEAD_CASE(2)
       LEAD_CASE(3)
       LEAD_CASE(3)
@@ -1339,7 +1343,7 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
     switch (BYTE_TYPE(enc, ptr)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
     break;
       LEAD_CASE(2)
       LEAD_CASE(2)
       LEAD_CASE(3)
       LEAD_CASE(3)
@@ -1518,7 +1522,7 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax,
       state = inName;                                                          \
       state = inName;                                                          \
     }
     }
 #  define LEAD_CASE(n)                                                         \
 #  define LEAD_CASE(n)                                                         \
-  case BT_LEAD##n:                                                             \
+  case BT_LEAD##n: /* NOTE: The encoding has already been validated. */        \
     START_NAME ptr += (n - MINBPC(enc));                                       \
     START_NAME ptr += (n - MINBPC(enc));                                       \
     break;
     break;
       LEAD_CASE(2)
       LEAD_CASE(2)
@@ -1730,7 +1734,7 @@ PREFIX(nameLength)(const ENCODING *enc, const char *ptr) {
     switch (BYTE_TYPE(enc, ptr)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
     break;
       LEAD_CASE(2)
       LEAD_CASE(2)
       LEAD_CASE(3)
       LEAD_CASE(3)
@@ -1779,7 +1783,7 @@ PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end,
       if (end - ptr < n) { \
       if (end - ptr < n) { \
         return; \
         return; \
       } \
       } \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     pos->columnNumber++;                                                       \
     pos->columnNumber++;                                                       \
     break;
     break;
       LEAD_CASE(2)
       LEAD_CASE(2)

+ 1 - 0
libs/expat/tests/Makefile.in

@@ -516,6 +516,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 0
libs/expat/tests/benchmark/Makefile.in

@@ -227,6 +227,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/tests/benchmark/benchmark.c

@@ -7,7 +7,7 @@
                                  |_| XML parser
                                  |_| XML parser
 
 
    Copyright (c) 2003-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2003-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2017      Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Licensed under the MIT license:
    Licensed under the MIT license:

+ 247 - 4
libs/expat/tests/runtests.c

@@ -8,7 +8,7 @@
 
 
    Copyright (c) 2001-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2003      Greg Stein <[email protected]>
    Copyright (c) 2003      Greg Stein <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2005-2012 Karl Waclawek <[email protected]>
    Copyright (c) 2005-2012 Karl Waclawek <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017-2018 Rhodri James <[email protected]>
    Copyright (c) 2017-2018 Rhodri James <[email protected]>
@@ -2664,6 +2664,82 @@ START_TEST(test_dtd_elements) {
 }
 }
 END_TEST
 END_TEST
 
 
+static void XMLCALL
+element_decl_check_model(void *userData, const XML_Char *name,
+                         XML_Content *model) {
+  UNUSED_P(userData);
+  uint32_t errorFlags = 0;
+
+  /* Expected model array structure is this:
+   * [0] (type 6, quant 0)
+   *   [1] (type 5, quant 0)
+   *     [3] (type 4, quant 0, name "bar")
+   *     [4] (type 4, quant 0, name "foo")
+   *     [5] (type 4, quant 3, name "xyz")
+   *   [2] (type 4, quant 2, name "zebra")
+   */
+  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
+  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
+
+  errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
+  errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
+  errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
+  errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
+  errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
+
+  errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
+  errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
+  errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
+  errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
+  errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
+
+  errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
+  errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
+  errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
+  errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
+  errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
+
+  errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
+  errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
+  errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
+  errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
+  errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
+
+  errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
+  errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
+  errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
+  errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
+  errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
+
+  errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
+  errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
+  errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
+  errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
+  errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags);
+  XML_FreeContentModel(g_parser, model);
+}
+
+START_TEST(test_dtd_elements_nesting) {
+  // Payload inspired by a test in Perl's XML::Parser
+  const char *text = "<!DOCTYPE foo [\n"
+                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
+                     "]>\n"
+                     "<foo/>";
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)-1);
+
+  XML_SetElementDeclHandler(g_parser, element_decl_check_model);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0)
+    fail("Element declaration model regression detected");
+}
+END_TEST
+
 /* Test foreign DTD handling */
 /* Test foreign DTD handling */
 START_TEST(test_set_foreign_dtd) {
 START_TEST(test_set_foreign_dtd) {
   const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
   const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
@@ -3847,6 +3923,30 @@ START_TEST(test_get_buffer_2) {
 }
 }
 END_TEST
 END_TEST
 
 
+/* Test for signed integer overflow CVE-2022-23852 */
+#if defined(XML_CONTEXT_BYTES)
+START_TEST(test_get_buffer_3_overflow) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert(parser != NULL);
+
+  const char *const text = "\n";
+  const int expectedKeepValue = (int)strlen(text);
+
+  // After this call, variable "keep" in XML_GetBuffer will
+  // have value expectedKeepValue
+  if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  assert(expectedKeepValue > 0);
+  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
+    fail("enlarging buffer not failed");
+
+  XML_ParserFree(parser);
+}
+END_TEST
+#endif // defined(XML_CONTEXT_BYTES)
+
 /* Test position information macros */
 /* Test position information macros */
 START_TEST(test_byte_info_at_end) {
 START_TEST(test_byte_info_at_end) {
   const char *text = "<doc></doc>";
   const char *text = "<doc></doc>";
@@ -5974,6 +6074,105 @@ START_TEST(test_utf8_in_cdata_section_2) {
 }
 }
 END_TEST
 END_TEST
 
 
+START_TEST(test_utf8_in_start_tags) {
+  struct test_case {
+    bool goodName;
+    bool goodNameStart;
+    const char *tagName;
+  };
+
+  // The idea with the tests below is this:
+  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
+  // go to isNever and are hence not a concern.
+  //
+  // We start with a character that is a valid name character
+  // (or even name-start character, see XML 1.0r4 spec) and then we flip
+  // single bits at places where (1) the result leaves the UTF-8 encoding space
+  // and (2) we stay in the same n-byte sequence family.
+  //
+  // The flipped bits are highlighted in angle brackets in comments,
+  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
+  // the most significant bit to 1 to leave UTF-8 encoding space.
+  struct test_case cases[] = {
+      // 1-byte UTF-8: [0xxx xxxx]
+      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
+      {false, false, "\xBA"}, // [<1>011 1010]
+      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
+      {false, false, "\xB9"}, // [<1>011 1001]
+
+      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
+      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
+                                  // Arabic small waw U+06E5
+      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
+      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
+      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
+      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
+                                  // combining char U+0301
+      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
+      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
+      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
+
+      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
+      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
+                                      // Devanagari Letter A U+0905
+      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
+      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
+      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
+      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
+      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
+      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
+                                      // combining char U+0901
+      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
+      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
+      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
+      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
+      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
+  };
+  const bool atNameStart[] = {true, false};
+
+  size_t i = 0;
+  char doc[1024];
+  size_t failCount = 0;
+
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    size_t j = 0;
+    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
+      const bool expectedSuccess
+          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
+      sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
+      XML_Parser parser = XML_ParserCreate(NULL);
+
+      const enum XML_Status status
+          = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
+
+      bool success = true;
+      if ((status == XML_STATUS_OK) != expectedSuccess) {
+        success = false;
+      }
+      if ((status == XML_STATUS_ERROR)
+          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
+        success = false;
+      }
+
+      if (! success) {
+        fprintf(
+            stderr,
+            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
+            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
+            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
+        failCount++;
+      }
+
+      XML_ParserFree(parser);
+    }
+  }
+
+  if (failCount > 0) {
+    fail("UTF-8 regression detected");
+  }
+}
+END_TEST
+
 /* Test trailing spaces in elements are accepted */
 /* Test trailing spaces in elements are accepted */
 static void XMLCALL
 static void XMLCALL
 record_element_end_handler(void *userData, const XML_Char *name) {
 record_element_end_handler(void *userData, const XML_Char *name) {
@@ -6151,6 +6350,14 @@ START_TEST(test_bad_doctype) {
 }
 }
 END_TEST
 END_TEST
 
 
+START_TEST(test_bad_doctype_utf8) {
+  const char *text = "<!DOCTYPE \xDB\x25"
+                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid UTF-8 in DOCTYPE not faulted");
+}
+END_TEST
+
 START_TEST(test_bad_doctype_utf16) {
 START_TEST(test_bad_doctype_utf16) {
   const char text[] =
   const char text[] =
       /* <!DOCTYPE doc [ \x06f2 ]><doc/>
       /* <!DOCTYPE doc [ \x06f2 ]><doc/>
@@ -7196,6 +7403,35 @@ START_TEST(test_ns_double_colon_doctype) {
 }
 }
 END_TEST
 END_TEST
 
 
+START_TEST(test_ns_separator_in_uri) {
+  struct test_case {
+    enum XML_Status expectedStatus;
+    const char *doc;
+  };
+  struct test_case cases[] = {
+      {XML_STATUS_OK, "<doc xmlns='one_two' />"},
+      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />"},
+  };
+
+  size_t i = 0;
+  size_t failCount = 0;
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    XML_Parser parser = XML_ParserCreateNS(NULL, '\n');
+    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
+    if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc),
+                  /*isFinal*/ XML_TRUE)
+        != cases[i].expectedStatus) {
+      failCount++;
+    }
+    XML_ParserFree(parser);
+  }
+
+  if (failCount) {
+    fail("Namespace separator handling is broken");
+  }
+}
+END_TEST
+
 /* Control variable; the number of times duff_allocator() will successfully
 /* Control variable; the number of times duff_allocator() will successfully
  * allocate */
  * allocate */
 #define ALLOC_ALWAYS_SUCCEED (-1)
 #define ALLOC_ALWAYS_SUCCEED (-1)
@@ -7352,7 +7588,7 @@ START_TEST(test_misc_version) {
     fail("Version mismatch");
     fail("Version mismatch");
 
 
 #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
 #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
-  if (xcstrcmp(version_text, XCS("expat_2.4.3"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.4.6"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
     fail("XML_*_VERSION in expat.h out of sync?\n");
 #else
 #else
   /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
   /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
@@ -11286,7 +11522,7 @@ START_TEST(test_accounting_precision) {
       {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
       {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
        filled_later},
        filled_later},
       {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
       {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
       {"<e1 xmlns='https://example.org/'>\n"
       {"<e1 xmlns='https://example.org/'>\n"
        "  <e2 xmlns=''/>\n"
        "  <e2 xmlns=''/>\n"
        "</e1>",
        "</e1>",
@@ -11296,7 +11532,7 @@ START_TEST(test_accounting_precision) {
       {"<e>text</e>", NULL, NULL, 0, filled_later},
       {"<e>text</e>", NULL, NULL, 0, filled_later},
       {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
       {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
       {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
       {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
       {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
       {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
 
 
       /* Prolog */
       /* Prolog */
@@ -11703,6 +11939,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_memory_allocation);
   tcase_add_test(tc_basic, test_memory_allocation);
   tcase_add_test(tc_basic, test_default_current);
   tcase_add_test(tc_basic, test_default_current);
   tcase_add_test(tc_basic, test_dtd_elements);
   tcase_add_test(tc_basic, test_dtd_elements);
+  tcase_add_test(tc_basic, test_dtd_elements_nesting);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
@@ -11731,6 +11968,9 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_empty_parse);
   tcase_add_test(tc_basic, test_empty_parse);
   tcase_add_test(tc_basic, test_get_buffer_1);
   tcase_add_test(tc_basic, test_get_buffer_1);
   tcase_add_test(tc_basic, test_get_buffer_2);
   tcase_add_test(tc_basic, test_get_buffer_2);
+#if defined(XML_CONTEXT_BYTES)
+  tcase_add_test(tc_basic, test_get_buffer_3_overflow);
+#endif
   tcase_add_test(tc_basic, test_byte_info_at_end);
   tcase_add_test(tc_basic, test_byte_info_at_end);
   tcase_add_test(tc_basic, test_byte_info_at_error);
   tcase_add_test(tc_basic, test_byte_info_at_error);
   tcase_add_test(tc_basic, test_byte_info_at_cdata);
   tcase_add_test(tc_basic, test_byte_info_at_cdata);
@@ -11814,6 +12054,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
   tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
+  tcase_add_test(tc_basic, test_utf8_in_start_tags);
   tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
   tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
   tcase_add_test(tc_basic, test_utf16_attribute);
   tcase_add_test(tc_basic, test_utf16_attribute);
   tcase_add_test(tc_basic, test_utf16_second_attr);
   tcase_add_test(tc_basic, test_utf16_second_attr);
@@ -11822,6 +12063,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
   tcase_add_test(tc_basic, test_bad_doctype);
   tcase_add_test(tc_basic, test_bad_doctype);
+  tcase_add_test(tc_basic, test_bad_doctype_utf8);
   tcase_add_test(tc_basic, test_bad_doctype_utf16);
   tcase_add_test(tc_basic, test_bad_doctype_utf16);
   tcase_add_test(tc_basic, test_bad_doctype_plus);
   tcase_add_test(tc_basic, test_bad_doctype_plus);
   tcase_add_test(tc_basic, test_bad_doctype_star);
   tcase_add_test(tc_basic, test_bad_doctype_star);
@@ -11878,6 +12120,7 @@ make_suite(void) {
   tcase_add_test(tc_namespace, test_ns_utf16_doctype);
   tcase_add_test(tc_namespace, test_ns_utf16_doctype);
   tcase_add_test(tc_namespace, test_ns_invalid_doctype);
   tcase_add_test(tc_namespace, test_ns_invalid_doctype);
   tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
   tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
+  tcase_add_test(tc_namespace, test_ns_separator_in_uri);
 
 
   suite_add_tcase(s, tc_misc);
   suite_add_tcase(s, tc_misc);
   tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);
   tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);

+ 7 - 3
libs/expat/win32/expat.iss

@@ -36,14 +36,14 @@
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
-#define expatVer "2.4.3"
+#define expatVer "2.4.6"
 
 
 [Setup]
 [Setup]
 AppName=Expat
 AppName=Expat
 AppId=expat
 AppId=expat
 AppVersion={#expatVer}
 AppVersion={#expatVer}
 AppVerName=Expat {#expatVer}
 AppVerName=Expat {#expatVer}
-AppCopyright=Copyright © 1997-2021 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
+AppCopyright=Copyright © 1997-2022 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
 AppPublisher=The Expat Developers
 AppPublisher=The Expat Developers
 AppPublisherURL=https://libexpat.github.io/
 AppPublisherURL=https://libexpat.github.io/
 AppSupportURL=https://libexpat.github.io/
 AppSupportURL=https://libexpat.github.io/
@@ -73,16 +73,20 @@ Flags: ignoreversion; Source: COPYING;                      DestDir: "{app}"; De
 Flags: ignoreversion; Source: README.md;                    DestDir: "{app}"; DestName: README.txt
 Flags: ignoreversion; Source: README.md;                    DestDir: "{app}"; DestName: README.txt
 Flags: ignoreversion; Source: doc\*.html;                   DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.html;                   DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
-Flags: ignoreversion; Source: doc\*.png;                    DestDir: "{app}\Doc"
+Flags: ignoreversion; Source: doc\*.xml;                    DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: win32\bin\Release\*.dll;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.dll;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.lib;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.lib;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\README.txt;             DestDir: "{app}\Source"
 Flags: ignoreversion; Source: win32\README.txt;             DestDir: "{app}\Source"
+Flags: ignoreversion; Source: AUTHORS;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: Changes;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: Changes;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMake.README;                 DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMake.README;                 DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMakeLists.txt;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMakeLists.txt;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: ConfigureChecks.cmake;        DestDir: "{app}\Source"
 Flags: ignoreversion; Source: ConfigureChecks.cmake;        DestDir: "{app}\Source"
+Flags: ignoreversion; Source: expat.pc.cmake;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: expat_config.h.cmake;         DestDir: "{app}\Source"
 Flags: ignoreversion; Source: expat_config.h.cmake;         DestDir: "{app}\Source"
+Flags: ignoreversion; Source: run.sh.in;                    DestDir: "{app}\Source"
 Flags: ignoreversion; Source: cmake\expat-config.cmake.in;  DestDir: "{app}\Source\cmake"
 Flags: ignoreversion; Source: cmake\expat-config.cmake.in;  DestDir: "{app}\Source\cmake"
+Flags: ignoreversion; Source: fuzz\*.c;                     DestDir: "{app}\Source\fuzz"
 Flags: ignoreversion; Source: lib\*.c;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.c;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.h;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.h;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.def;                    DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.def;                    DestDir: "{app}\Source\lib"

+ 1 - 0
libs/expat/xmlwf/Makefile.in

@@ -235,6 +235,7 @@ AWK = @AWK@
 CC = @CC@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/xmlwf/xmlfile.c

@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>

+ 4 - 4
libs/expat/xmlwf/xmlwf.c

@@ -10,8 +10,8 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2009 Karl Waclawek <[email protected]>
    Copyright (c) 2004-2009 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2020      Joe Orton <[email protected]>
    Copyright (c) 2020      Joe Orton <[email protected]>
@@ -1175,9 +1175,9 @@ tmain(int argc, XML_Char **argv) {
       if (! userData.fp) {
       if (! userData.fp) {
         tperror(outName);
         tperror(outName);
         exitCode = XMLWF_EXIT_OUTPUT_ERROR;
         exitCode = XMLWF_EXIT_OUTPUT_ERROR;
+        free(outName);
+        XML_ParserFree(parser);
         if (continueOnError) {
         if (continueOnError) {
-          free(outName);
-          cleanupUserData(&userData);
           continue;
           continue;
         } else {
         } else {
           break;
           break;

+ 8 - 5
libs/puttyvs/PuTTYVS.vcxproj

@@ -48,9 +48,10 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WINSCP;WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <AdditionalIncludeDirectories>..\..\source\putty</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -65,10 +66,11 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WINSCP;WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+      <AdditionalIncludeDirectories>..\..\source\putty</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -78,9 +80,10 @@
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
-    <ClCompile Include="..\..\source\putty\sshaes.c" />
-    <ClCompile Include="..\..\source\putty\sshargon2.c" />
-    <ClCompile Include="..\..\source\putty\sshsh256.c" />
+    <ClCompile Include="..\..\source\putty\crypto\aes-ni.c" />
+    <ClCompile Include="..\..\source\putty\crypto\aes-sw.c" />
+    <ClCompile Include="..\..\source\putty\crypto\argon2.c" />
+    <ClCompile Include="..\..\source\putty\crypto\sha256-sw.c" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">

+ 334 - 183
source/Putty.cbproj

@@ -59,14 +59,14 @@
 		<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace)</DCC_Namespace>
 		<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace)</DCC_Namespace>
 		<Defines>SECURITY_WIN32;WINSCP;MPEXT;PLATFORM_HAS_SMEMCLR;_WINDOWS;$(Defines)</Defines>
 		<Defines>SECURITY_WIN32;WINSCP;MPEXT;PLATFORM_HAS_SMEMCLR;_WINDOWS;$(Defines)</Defines>
 		<FinalOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</FinalOutputDir>
 		<FinalOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</FinalOutputDir>
-		<IncludePath>putty\;putty\windows\;$(BDS)\include;$(IncludePath)</IncludePath>
+		<IncludePath>putty\windows\utils\;putty\utils\;putty\stubs\;putty\ssh\;putty\proxy\;putty\crypto\;putty\;putty\windows\;$(BDS)\include;$(IncludePath)</IncludePath>
 		<IntermediateOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</IntermediateOutputDir>
 		<IntermediateOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</IntermediateOutputDir>
 		<Manifest_File>None</Manifest_File>
 		<Manifest_File>None</Manifest_File>
 		<Multithreaded>true</Multithreaded>
 		<Multithreaded>true</Multithreaded>
 		<OutputExt>lib</OutputExt>
 		<OutputExt>lib</OutputExt>
 		<ProjectType>CppStaticLibrary</ProjectType>
 		<ProjectType>CppStaticLibrary</ProjectType>
 		<SanitizedProjectName>Putty</SanitizedProjectName>
 		<SanitizedProjectName>Putty</SanitizedProjectName>
-		<TLIB_PageSize>128</TLIB_PageSize>
+		<TLIB_PageSize>256</TLIB_PageSize>
 		<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
 		<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
 		<VerInfo_Locale>1033</VerInfo_Locale>
 		<VerInfo_Locale>1033</VerInfo_Locale>
 	</PropertyGroup>
 	</PropertyGroup>
@@ -94,289 +94,440 @@
 		<TASM_Debugging>None</TASM_Debugging>
 		<TASM_Debugging>None</TASM_Debugging>
 	</PropertyGroup>
 	</PropertyGroup>
 	<ItemGroup>
 	<ItemGroup>
-		<CppCompile Include="putty\agentf.c">
-			<BuildOrder>60</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\be_misc.c">
-			<BuildOrder>47</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\be_ssh.c">
-			<BuildOrder>84</BuildOrder>
+		<CppCompile Include="putty\be_list.c">
+			<BuildOrder>0</BuildOrder>
 		</CppCompile>
 		</CppCompile>
 		<CppCompile Include="putty\callback.c">
 		<CppCompile Include="putty\callback.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\conf.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\cproxy.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\ecc.c">
-			<BuildOrder>73</BuildOrder>
+			<BuildOrder>1</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\errsock.c">
-			<BuildOrder>44</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-common.c">
+			<BuildOrder>9</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\import.c">
-			<BuildOrder>46</BuildOrder>
+		<CppCompile Include="putty\crypto\aesold.c">
+			<BuildOrder>150</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\logging.c">
-			<BuildOrder>27</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-select.c">
 			<BuildOrder>11</BuildOrder>
 			<BuildOrder>11</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\mainchan.c">
-			<BuildOrder>69</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\marshal.c">
-			<BuildOrder>55</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-sw.c">
+			<BuildOrder>12</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\memory.c">
-			<BuildOrder>73</BuildOrder>
+		<CppCompile Include="putty\crypto\arcfour.c">
+			<BuildOrder>13</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\misc.c">
-			<BuildOrder>26</BuildOrder>
+		<CppCompile Include="putty\crypto\argon2.c">
 			<BuildOrder>14</BuildOrder>
 			<BuildOrder>14</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\miscucs.c">
-			<BuildOrder>51</BuildOrder>
+		<CppCompile Include="putty\crypto\bcrypt.c">
+			<BuildOrder>15</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\mpint.c">
-			<BuildOrder>74</BuildOrder>
+		<CppCompile Include="putty\crypto\blake2.c">
+			<BuildOrder>16</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\noshare.c">
-			<BuildOrder>41</BuildOrder>
+		<CppCompile Include="putty\crypto\blowfish.c">
+			<BuildOrder>17</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\nullplug.c">
-			<BuildOrder>56</BuildOrder>
+		<CppCompile Include="putty\crypto\chacha20-poly1305.c">
+			<BuildOrder>24</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\pgssapi.c">
-			<BuildOrder>37</BuildOrder>
+		<CppCompile Include="putty\crypto\des.c">
+			<BuildOrder>18</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\portfwd.c">
-			<BuildOrder>23</BuildOrder>
-			<BuildOrder>17</BuildOrder>
+		<CppCompile Include="putty\crypto\diffie-hellman.c">
+			<BuildOrder>19</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\proxy.c">
+		<CppCompile Include="putty\crypto\dsa.c">
 			<BuildOrder>20</BuildOrder>
 			<BuildOrder>20</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\settings.c">
-			<BuildOrder>82</BuildOrder>
+		<CppCompile Include="putty\crypto\ecc-arithmetic.c">
+			<BuildOrder>21</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\crypto\ecc-ssh.c">
+			<BuildOrder>22</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\crypto\hash_simple.c">
+			<BuildOrder>150</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh.c">
+		<CppCompile Include="putty\crypto\hmac.c">
 			<BuildOrder>23</BuildOrder>
 			<BuildOrder>23</BuildOrder>
-			<BuildOrder>19</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2bpp.c">
-			<BuildOrder>54</BuildOrder>
+		<CppCompile Include="putty\crypto\mac.c">
+			<BuildOrder>25</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2bpp-bare.c">
-			<BuildOrder>59</BuildOrder>
+		<CppCompile Include="putty\crypto\mac_simple.c">
+			<BuildOrder>26</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2censor.c">
-			<BuildOrder>58</BuildOrder>
+		<CppCompile Include="putty\crypto\md5.c">
+			<BuildOrder>27</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2connection.c">
-			<BuildOrder>66</BuildOrder>
+		<CppCompile Include="putty\crypto\mpint.c">
+			<BuildOrder>28</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2connection-client.c">
-			<BuildOrder>72</BuildOrder>
+		<CppCompile Include="putty\crypto\prng.c">
+			<BuildOrder>29</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2kex-client.c">
-			<BuildOrder>73</BuildOrder>
+		<CppCompile Include="putty\crypto\pubkey-pem.c">
+			<BuildOrder>30</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2transhk.c">
-			<BuildOrder>70</BuildOrder>
+		<CppCompile Include="putty\crypto\pubkey-ppk.c">
+			<BuildOrder>31</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2transport.c">
-			<BuildOrder>67</BuildOrder>
+		<CppCompile Include="putty\crypto\rsa.c">
+			<BuildOrder>32</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2userauth.c">
-			<BuildOrder>68</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-common.c">
+			<BuildOrder>33</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshaes.c">
-			<BuildOrder>26</BuildOrder>
-			<BuildOrder>22</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-select.c">
+			<BuildOrder>34</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshaesold.c">
-			<BuildOrder>77</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-sw.c">
+			<BuildOrder>35</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\ssharcf.c">
-			<BuildOrder>29</BuildOrder>
-			<BuildOrder>21</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-common.c">
+			<BuildOrder>37</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshargon2.c">
-			<BuildOrder>29</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-select.c">
+			<BuildOrder>38</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshauxcrypt.c">
-			<BuildOrder>78</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-sw.c">
+			<BuildOrder>39</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshbcrypt.c">
-			<BuildOrder>50</BuildOrder>
+		<CppCompile Include="putty\crypto\sha3.c">
+			<BuildOrder>36</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshblake2.c">
-			<BuildOrder>50</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-common.c">
+			<BuildOrder>40</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshblowf.c">
-			<BuildOrder>34</BuildOrder>
-			<BuildOrder>32</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-select.c">
+			<BuildOrder>41</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshccp.c">
-			<BuildOrder>49</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-sw.c">
+			<BuildOrder>42</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshcommon.c">
-			<BuildOrder>62</BuildOrder>
+		<CppCompile Include="putty\errsock.c">
+			<BuildOrder>2</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshdes.c">
+		<CppCompile Include="putty\import.c">
+			<BuildOrder>3</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\logging.c">
+			<BuildOrder>4</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\proxy\cproxy.c">
 			<BuildOrder>44</BuildOrder>
 			<BuildOrder>44</BuildOrder>
-			<BuildOrder>32</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshdh.c">
-			<BuildOrder>47</BuildOrder>
-			<BuildOrder>29</BuildOrder>
+		<CppCompile Include="putty\proxy\http_p.c">
+			<BuildOrder>45</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshdss.c">
-			<BuildOrder>50</BuildOrder>
-			<BuildOrder>28</BuildOrder>
+		<CppCompile Include="putty\proxy\interactor.c">
+			<BuildOrder>46</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\proxy\local.c">
+			<BuildOrder>47</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshecc.c">
+		<CppCompile Include="putty\proxy\nosshproxy.c">
 			<BuildOrder>48</BuildOrder>
 			<BuildOrder>48</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshgssc.c">
-			<BuildOrder>38</BuildOrder>
+		<CppCompile Include="putty\proxy\proxy.c">
+			<BuildOrder>50</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshhmac.c">
-			<BuildOrder>79</BuildOrder>
+		<CppCompile Include="putty\proxy\socks4.c">
+			<BuildOrder>51</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshmac.c">
-			<BuildOrder>61</BuildOrder>
+		<CppCompile Include="putty\proxy\socks5.c">
+			<BuildOrder>52</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshmd5.c">
-			<BuildOrder>53</BuildOrder>
-			<BuildOrder>31</BuildOrder>
+		<CppCompile Include="putty\proxy\telnet.c">
+			<BuildOrder>54</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshprng.c">
-			<BuildOrder>80</BuildOrder>
+		<CppCompile Include="putty\settings.c">
+			<BuildOrder>5</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshpubk.c">
+		<CppCompile Include="putty\ssh\agentf.c">
+			<BuildOrder>55</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\bpp2.c">
 			<BuildOrder>56</BuildOrder>
 			<BuildOrder>56</BuildOrder>
-			<BuildOrder>30</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshrand.c">
+		<CppCompile Include="putty\ssh\bpp-bare.c">
+			<BuildOrder>57</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\censor2.c">
+			<BuildOrder>58</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\common_p.c">
 			<BuildOrder>59</BuildOrder>
 			<BuildOrder>59</BuildOrder>
-			<BuildOrder>18</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshrsa.c">
+		<CppCompile Include="putty\ssh\connection2.c">
+			<BuildOrder>60</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\connection2-client.c">
+			<BuildOrder>61</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\gssc.c">
 			<BuildOrder>62</BuildOrder>
 			<BuildOrder>62</BuildOrder>
-			<BuildOrder>6</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshsh256.c">
+		<CppCompile Include="putty\ssh\kex2-client.c">
+			<BuildOrder>63</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\mainchan.c">
+			<BuildOrder>64</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\nosharing.c">
 			<BuildOrder>65</BuildOrder>
 			<BuildOrder>65</BuildOrder>
-			<BuildOrder>5</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshsh512.c">
-			<BuildOrder>8</BuildOrder>
+		<CppCompile Include="putty\ssh\pgssapi.c">
+			<BuildOrder>66</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\portfwd.c">
+			<BuildOrder>67</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\sharing.c">
 			<BuildOrder>68</BuildOrder>
 			<BuildOrder>68</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshsha.c">
+		<CppCompile Include="putty\ssh\ssh.c">
+			<BuildOrder>69</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\transient-hostkey-cache.c">
+			<BuildOrder>70</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\transport2.c">
 			<BuildOrder>71</BuildOrder>
 			<BuildOrder>71</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\userauth2-client.c">
+			<BuildOrder>72</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\verstring.c">
+			<BuildOrder>73</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\zlib.c">
+			<BuildOrder>75</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\sshpubk.c">
 			<BuildOrder>7</BuildOrder>
 			<BuildOrder>7</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshsha3.c">
-			<BuildOrder>42</BuildOrder>
+		<CppCompile Include="putty\sshrand.c">
+			<BuildOrder>8</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshshare.c">
-			<BuildOrder>42</BuildOrder>
+		<CppCompile Include="putty\stubs\nullplug.c">
+			<BuildOrder>76</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshutils.c">
-			<BuildOrder>63</BuildOrder>
+		<CppCompile Include="putty\utils\antispoof.c">
+			<BuildOrder>77</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshverstring.c">
-			<BuildOrder>63</BuildOrder>
+		<CppCompile Include="putty\utils\backend_socket_log.c">
+			<BuildOrder>78</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\sshzlib.c">
-			<BuildOrder>74</BuildOrder>
-			<BuildOrder>4</BuildOrder>
+		<CppCompile Include="putty\utils\base64_decode_atom.c">
+			<BuildOrder>79</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\base64_encode_atom.c">
+			<BuildOrder>80</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\stripctrl.c">
+		<CppCompile Include="putty\utils\bufchain.c">
 			<BuildOrder>81</BuildOrder>
 			<BuildOrder>81</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\tree234.c">
-			<BuildOrder>77</BuildOrder>
-			<BuildOrder>1</BuildOrder>
+		<CppCompile Include="putty\utils\burnstr.c">
+			<BuildOrder>151</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\utils.c">
-			<BuildOrder>75</BuildOrder>
+		<CppCompile Include="putty\utils\conf.c">
+			<BuildOrder>82</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\wildcard.c">
+		<CppCompile Include="putty\utils\conf_launchable.c">
 			<BuildOrder>83</BuildOrder>
 			<BuildOrder>83</BuildOrder>
-			<BuildOrder>3</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wincapi.c">
-			<BuildOrder>83</BuildOrder>
+		<CppCompile Include="putty\utils\ctrlparse.c">
+			<BuildOrder>84</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\windefs.c">
-			<BuildOrder>83</BuildOrder>
+		<CppCompile Include="putty\utils\default_description.c">
+			<BuildOrder>85</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wingss.c">
+		<CppCompile Include="putty\utils\dup_mb_to_wc.c">
 			<BuildOrder>86</BuildOrder>
 			<BuildOrder>86</BuildOrder>
-			<BuildOrder>2</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winhandl.c">
-			<BuildOrder>43</BuildOrder>
+		<CppCompile Include="putty\utils\dupcat.c">
+			<BuildOrder>148</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winhsock.c">
-			<BuildOrder>45</BuildOrder>
+		<CppCompile Include="putty\utils\dupprintf.c">
+			<BuildOrder>87</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\dupstr.c">
+			<BuildOrder>152</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winmisc.c">
+		<CppCompile Include="putty\utils\host_strchr.c">
 			<BuildOrder>92</BuildOrder>
 			<BuildOrder>92</BuildOrder>
-			<BuildOrder>14</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winmiscs.c">
-			<BuildOrder>76</BuildOrder>
+		<CppCompile Include="putty\utils\host_strchr_internal.c">
+			<BuildOrder>93</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\host_strcspn.c">
+			<BuildOrder>90</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\host_strduptrim.c">
+			<BuildOrder>91</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnet.c">
+		<CppCompile Include="putty\utils\host_strrchr.c">
+			<BuildOrder>94</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\log_proxy_stderr.c">
 			<BuildOrder>95</BuildOrder>
 			<BuildOrder>95</BuildOrder>
-			<BuildOrder>17</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnoise.c">
+		<CppCompile Include="putty\utils\make_spr_sw_abort_static.c">
+			<BuildOrder>153</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\marshal.c">
+			<BuildOrder>96</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\memory.c">
+			<BuildOrder>97</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\memxor.c">
 			<BuildOrder>98</BuildOrder>
 			<BuildOrder>98</BuildOrder>
-			<BuildOrder>16</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnojmp.c">
-			<BuildOrder>39</BuildOrder>
+		<CppCompile Include="putty\utils\null_lp.c">
+			<BuildOrder>99</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnpc.c">
-			<BuildOrder>39</BuildOrder>
+		<CppCompile Include="putty\utils\nullseat.c">
+			<BuildOrder>100</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winpgntc.c">
-			<BuildOrder>13</BuildOrder>
+		<CppCompile Include="putty\utils\nullstrcmp.c">
 			<BuildOrder>101</BuildOrder>
 			<BuildOrder>101</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winproxy.c">
+		<CppCompile Include="putty\utils\out_of_memory.c">
+			<BuildOrder>102</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\parse_blocksize.c">
+			<BuildOrder>103</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\prompts.c">
 			<BuildOrder>104</BuildOrder>
 			<BuildOrder>104</BuildOrder>
-			<BuildOrder>10</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winsecur.c">
-			<BuildOrder>43</BuildOrder>
+		<CppCompile Include="putty\utils\ptrlen.c">
+			<BuildOrder>105</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winstore.c">
-			<BuildOrder>9</BuildOrder>
+		<CppCompile Include="putty\utils\seat_connection_fatal.c">
 			<BuildOrder>107</BuildOrder>
 			<BuildOrder>107</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wintime.c">
-			<BuildOrder>12</BuildOrder>
+		<CppCompile Include="putty\utils\sk_free_peer_info.c">
+			<BuildOrder>108</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\smemclr.c">
+			<BuildOrder>109</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\smemeq.c">
 			<BuildOrder>110</BuildOrder>
 			<BuildOrder>110</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winucs.c">
-			<BuildOrder>52</BuildOrder>
+		<CppCompile Include="putty\utils\spr_get_error_message.c">
+			<BuildOrder>111</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\ssh2_pick_fingerprint.c">
+			<BuildOrder>112</BuildOrder>
 		</CppCompile>
 		</CppCompile>
-		<CppCompile Include="putty\x11fwd.c">
+		<CppCompile Include="putty\utils\sshutils.c">
 			<BuildOrder>113</BuildOrder>
 			<BuildOrder>113</BuildOrder>
-			<BuildOrder>11</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\strbuf.c">
+			<BuildOrder>114</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\string_length_for_printf.c">
+			<BuildOrder>154</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\stripctrl.c">
+			<BuildOrder>115</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\tempseat.c">
+			<BuildOrder>116</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\tree234.c">
+			<BuildOrder>117</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\version.c">
+			<BuildOrder>119</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\wildcard.c">
+			<BuildOrder>120</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\write_c_string_literal.c">
+			<BuildOrder>121</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\agent-client.c">
+			<BuildOrder>127</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\gss.c">
+			<BuildOrder>128</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-io.c">
+			<BuildOrder>129</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-socket.c">
+			<BuildOrder>130</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-wait.c">
+			<BuildOrder>151</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\local-proxy.c">
+			<BuildOrder>131</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\named-pipe-client.c">
+			<BuildOrder>132</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\network.c">
+			<BuildOrder>133</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\noise.c">
+			<BuildOrder>134</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\no-jump-list.c">
+			<BuildOrder>135</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\storage.c">
+			<BuildOrder>136</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\unicode.c">
+			<BuildOrder>137</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\agent_named_pipe_name.c">
+			<BuildOrder>138</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\cryptoapi.c">
+			<BuildOrder>139</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\defaults.c">
+			<BuildOrder>140</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\dll_hijacking_protection.c">
+			<BuildOrder>141</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\escape_registry_key.c">
+			<BuildOrder>142</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\filename.c">
+			<BuildOrder>143</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\fontspec.c">
+			<BuildOrder>144</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\get_system_dir.c">
+			<BuildOrder>147</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\get_username.c">
+			<BuildOrder>145</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\load_system32_dll.c">
+			<BuildOrder>154</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\ltime.c">
+			<BuildOrder>147</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\open_for_write_would_lose_data.c">
+			<BuildOrder>149</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\security_p.c">
+			<BuildOrder>150</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\win_strerror.c">
+			<BuildOrder>152</BuildOrder>
 		</CppCompile>
 		</CppCompile>
 		<BuildConfiguration Include="Base">
 		<BuildConfiguration Include="Base">
 			<Key>Base</Key>
 			<Key>Base</Key>

+ 1 - 0
source/components/UnixDirView.cpp

@@ -1021,6 +1021,7 @@ TDateTime __fastcall TUnixDirView::ItemFileTime(TListItem * Item,
       break;
       break;
 
 
     case mfMDHM:
     case mfMDHM:
+    case mfYMDHM:
       Precision = tpMinute;
       Precision = tpMinute;
       break;
       break;
 
 

+ 15 - 1
source/components/UnixDriveView.cpp

@@ -52,6 +52,7 @@ __fastcall TCustomUnixDriveView::TCustomUnixDriveView(TComponent* Owner) :
   FDummyDragFile = NULL;
   FDummyDragFile = NULL;
   FPendingDelete = new TList();
   FPendingDelete = new TList();
   FDragDropFilesEx->PreferCopy = true;
   FDragDropFilesEx->PreferCopy = true;
+  FChangingDirectory = false;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 __fastcall TCustomUnixDriveView::~TCustomUnixDriveView()
 __fastcall TCustomUnixDriveView::~TCustomUnixDriveView()
@@ -473,6 +474,13 @@ void __fastcall TCustomUnixDriveView::Delete(TTreeNode * Node)
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+bool __fastcall TCustomUnixDriveView::CanChange(TTreeNode * Node)
+{
+  return
+    TCustomDriveView::CanChange(Node) &&
+    !FChangingDirectory;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
 void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
 {
 {
   #ifndef DESIGN_ONLY
   #ifndef DESIGN_ONLY
@@ -512,7 +520,13 @@ void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
         }
         }
         try
         try
         {
         {
-          Terminal->ChangeDirectory(NodePathName(Node));
+          {
+            // Prevent curther changes while loading the folder.
+            // Particularly prevent user from trying to proceed with incremental search.
+            TValueRestorer<bool> ChangingDirectoryRestorer(FChangingDirectory);
+            FChangingDirectory = true;
+            Terminal->ChangeDirectory(NodePathName(Node));
+          }
           TCustomDriveView::Change(Node);
           TCustomDriveView::Change(Node);
         }
         }
         __finally
         __finally

+ 2 - 0
source/components/UnixDriveView.h

@@ -33,6 +33,7 @@ protected:
   DYNAMIC void __fastcall Change(TTreeNode * Node);
   DYNAMIC void __fastcall Change(TTreeNode * Node);
   virtual void __fastcall CreateWnd();
   virtual void __fastcall CreateWnd();
   virtual void __fastcall DestroyWnd();
   virtual void __fastcall DestroyWnd();
+  DYNAMIC bool __fastcall CanChange(TTreeNode * Node);
 
 
   void __fastcall LoadDirectory();
   void __fastcall LoadDirectory();
   TTreeNode * __fastcall LoadPath(UnicodeString Path);
   TTreeNode * __fastcall LoadPath(UnicodeString Path);
@@ -100,6 +101,7 @@ private:
   bool FShowInaccesibleDirectories;
   bool FShowInaccesibleDirectories;
   TRemoteFile * FDummyDragFile;
   TRemoteFile * FDummyDragFile;
   TList * FPendingDelete;
   TList * FPendingDelete;
+  bool FChangingDirectory;
 
 
   bool __fastcall IsRootNameStored();
   bool __fastcall IsRootNameStored();
   void __fastcall SetShowInaccesibleDirectories(bool value);
   void __fastcall SetShowInaccesibleDirectories(bool value);

+ 0 - 5
source/core/Configuration.cpp

@@ -1169,11 +1169,6 @@ void __fastcall TConfiguration::SetNulStorage()
   FStorage = stNul;
   FStorage = stNul;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-void __fastcall TConfiguration::SetDefaultStorage()
-{
-  FStorage = stDetect;
-}
-//---------------------------------------------------------------------------
 void __fastcall TConfiguration::SetExplicitIniFileStorageName(const UnicodeString & FileName)
 void __fastcall TConfiguration::SetExplicitIniFileStorageName(const UnicodeString & FileName)
 {
 {
   FIniFileStorageName = FileName;
   FIniFileStorageName = FileName;

+ 0 - 1
source/core/Configuration.h

@@ -225,7 +225,6 @@ public:
   void __fastcall ScheduleCustomIniFileStorageUse(const UnicodeString & ACustomIniFileStorageName);
   void __fastcall ScheduleCustomIniFileStorageUse(const UnicodeString & ACustomIniFileStorageName);
   void __fastcall SetExplicitIniFileStorageName(const UnicodeString & FileName);
   void __fastcall SetExplicitIniFileStorageName(const UnicodeString & FileName);
   void __fastcall SetNulStorage();
   void __fastcall SetNulStorage();
-  void __fastcall SetDefaultStorage();
   UnicodeString __fastcall GetAutomaticIniFileStorageName(bool ReadingOnly);
   UnicodeString __fastcall GetAutomaticIniFileStorageName(bool ReadingOnly);
   UnicodeString __fastcall GetDefaultIniFileExportPath();
   UnicodeString __fastcall GetDefaultIniFileExportPath();
   UnicodeString __fastcall GetIniFileParamValue();
   UnicodeString __fastcall GetIniFileParamValue();

+ 3 - 5
source/core/FtpFileSystem.cpp

@@ -4344,9 +4344,7 @@ void __fastcall TFTPFileSystem::RemoteFileTimeToDateTimeAndPrecision(const TRemo
       DateTime = DateTime +
       DateTime = DateTime +
         EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
         EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
           (unsigned short)Source.Second, 0);
           (unsigned short)Source.Second, 0);
-      // not exact as we got year as well, but it is most probably
-      // guessed by FZAPI anyway
-      ModificationFmt = Source.HasSeconds ? mfFull : mfMDHM;
+      ModificationFmt = Source.HasSeconds ? mfFull : (Source.HasYear ? mfYMDHM : mfMDHM);
 
 
       // With IIS, the Utc should be false only for MDTM
       // With IIS, the Utc should be false only for MDTM
       if (FWindowsServer && !Source.Utc)
       if (FWindowsServer && !Source.Utc)
@@ -4469,11 +4467,11 @@ bool __fastcall TFTPFileSystem::HandleListData(const wchar_t * Path,
       catch (Exception & E)
       catch (Exception & E)
       {
       {
         UnicodeString EntryData =
         UnicodeString EntryData =
-          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
+          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
             (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->Owner, Entry->Group, Entry->OwnerGroup, IntToStr(Entry->Size),
             (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->Owner, Entry->Group, Entry->OwnerGroup, IntToStr(Entry->Size),
              int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
              int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
              Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
              Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
-             int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
+             int(Entry->Time.HasYear), int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
         throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
         throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
       }
       }
 
 

+ 2 - 1
source/core/Interface.h

@@ -141,7 +141,8 @@ enum TPromptKind
   pkCryptoCard,
   pkCryptoCard,
   pkKeybInteractive,
   pkKeybInteractive,
   pkPassword,
   pkPassword,
-  pkNewPassword
+  pkNewPassword,
+  pkProxyAuth
 };
 };
 
 
 enum TPromptUserParam { pupEcho = 0x01, pupRemember = 0x02 };
 enum TPromptUserParam { pupEcho = 0x01, pupRemember = 0x02 };

+ 54 - 27
source/core/PuttyIntf.cpp

@@ -25,7 +25,7 @@ THierarchicalStorage * PuttyStorage = NULL;
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 extern "C"
 extern "C"
 {
 {
-#include <winstuff.h>
+#include <windows\platform.h>
 }
 }
 const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
 const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
 const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
 const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
@@ -92,6 +92,18 @@ bool RandomSeedExists()
     FileExists(ApiPath(Configuration->RandomSeedFileName));
     FileExists(ApiPath(Configuration->RandomSeedFileName));
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+TSecureShell * GetSeatSecureShell(Seat * seat)
+{
+  DebugAssert(seat != NULL);
+  if (is_tempseat(seat))
+  {
+    seat = tempseat_get_real(seat);
+  }
+
+  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
+  return SecureShell;
+}
+//---------------------------------------------------------------------------
 TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
 TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
 {
 {
   if (!is_ssh(plug) && !is_pfwd(plug))
   if (!is_ssh(plug) && !is_pfwd(plug))
@@ -113,9 +125,7 @@ TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
     seat = get_ssh_seat(plug);
     seat = get_ssh_seat(plug);
   }
   }
   DebugAssert(seat != NULL);
   DebugAssert(seat != NULL);
-
-  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  return SecureShell;
+  return GetSeatSecureShell(seat);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 struct callback_set * get_callback_set(Plug * plug)
 struct callback_set * get_callback_set(Plug * plug)
@@ -127,7 +137,7 @@ struct callback_set * get_callback_set(Plug * plug)
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 struct callback_set * get_seat_callback_set(Seat * seat)
 struct callback_set * get_seat_callback_set(Seat * seat)
 {
 {
-  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
+  TSecureShell * SecureShell = GetSeatSecureShell(seat);
   return SecureShell->GetCallbackSet();
   return SecureShell->GetCallbackSet();
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
@@ -147,14 +157,14 @@ extern "C" const char * do_select(Plug * plug, SOCKET skt, bool enable)
   return NULL;
   return NULL;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-static size_t output(Seat * seat, bool is_stderr, const void * data, size_t len)
+static size_t output(Seat * seat, SeatOutputType type, const void * data, size_t len)
 {
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  if (static_cast<int>(static_cast<char>(is_stderr)) == -1)
+  if (static_cast<int>(static_cast<char>(type)) == -1)
   {
   {
     SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
     SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
   }
   }
-  else if (!is_stderr)
+  else if (type != SEAT_OUTPUT_STDERR)
   {
   {
     SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
     SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
   }
   }
@@ -170,13 +180,13 @@ static bool eof(Seat *)
   return false;
   return false;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-static int get_userpass_input(Seat * seat, prompts_t * p, bufchain * DebugUsedArg(input))
+static SeatPromptResult get_userpass_input(Seat * seat, prompts_t * p)
 {
 {
   DebugAssert(p != NULL);
   DebugAssert(p != NULL);
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   DebugAssert(SecureShell != NULL);
   DebugAssert(SecureShell != NULL);
 
 
-  int Result;
+  SeatPromptResult Result;
   TStrings * Prompts = new TStringList();
   TStrings * Prompts = new TStringList();
   TStrings * Results = new TStringList();
   TStrings * Results = new TStringList();
   try
   try
@@ -223,11 +233,11 @@ static int get_userpass_input(Seat * seat, prompts_t * p, bufchain * DebugUsedAr
         }
         }
         prompt_set_result(Prompt, S.c_str());
         prompt_set_result(Prompt, S.c_str());
       }
       }
-      Result = 1;
+      Result = SPR_OK;
     }
     }
     else
     else
     {
     {
-      Result = 0;
+      Result = SPR_USER_ABORT;
     }
     }
   }
   }
   __finally
   __finally
@@ -246,9 +256,9 @@ static void connection_fatal(Seat * seat, const char * message)
   SecureShell->PuttyFatalError(UnicodeString(AnsiString(message)));
   SecureShell->PuttyFatalError(UnicodeString(AnsiString(message)));
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-int verify_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
-  char * keystr, const char * DebugUsedArg(keydisp), char ** key_fingerprints,
-  void (*DebugUsedArg(callback))(void *ctx, int result), void * DebugUsedArg(ctx))
+SeatPromptResult confirm_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
+  char * keystr, const char * DebugUsedArg(keydisp), char ** key_fingerprints, bool DebugUsedArg(mismatch),
+  void (*DebugUsedArg(callback))(void *ctx, SeatPromptResult result), void * DebugUsedArg(ctx))
 {
 {
   UnicodeString FingerprintSHA256, FingerprintMD5;
   UnicodeString FingerprintSHA256, FingerprintMD5;
   if (key_fingerprints[SSH_FPTYPE_SHA256] != NULL)
   if (key_fingerprints[SSH_FPTYPE_SHA256] != NULL)
@@ -263,7 +273,7 @@ int verify_ssh_host_key(Seat * seat, const char * host, int port, const char * k
   SecureShell->VerifyHostKey(host, port, keytype, keystr, FingerprintSHA256, FingerprintMD5);
   SecureShell->VerifyHostKey(host, port, keytype, keystr, FingerprintSHA256, FingerprintMD5);
 
 
   // We should return 0 when key was not confirmed, we throw exception instead.
   // We should return 0 when key was not confirmed, we throw exception instead.
-  return 1;
+  return SPR_OK;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
 bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
@@ -273,20 +283,20 @@ bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
   return SecureShell->HaveHostKey(hostname, port, keytype) ? 1 : 0;
   return SecureShell->HaveHostKey(hostname, port, keytype) ? 1 : 0;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-int confirm_weak_crypto_primitive(Seat * seat, const char * algtype, const char * algname,
-  void (*/*callback*/)(void * ctx, int result), void * /*ctx*/)
+SeatPromptResult confirm_weak_crypto_primitive(Seat * seat, const char * algtype, const char * algname,
+  void (*/*callback*/)(void * ctx, SeatPromptResult result), void * /*ctx*/)
 {
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   SecureShell->AskAlg(algtype, algname);
   SecureShell->AskAlg(algtype, algname);
 
 
   // We should return 0 when alg was not confirmed, we throw exception instead.
   // We should return 0 when alg was not confirmed, we throw exception instead.
-  return 1;
+  return SPR_OK;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-int confirm_weak_cached_hostkey(Seat *, const char * /*algname*/, const char * /*betteralgs*/,
-  void (*/*callback*/)(void *ctx, int result), void * /*ctx*/)
+SeatPromptResult confirm_weak_cached_hostkey(Seat *, const char * /*algname*/, const char * /*betteralgs*/,
+  void (*/*callback*/)(void *ctx, SeatPromptResult result), void * /*ctx*/)
 {
 {
-  return 1;
+  return SPR_OK;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void old_keyfile_warning(void)
 void old_keyfile_warning(void)
@@ -294,11 +304,22 @@ void old_keyfile_warning(void)
   // no reference to TSecureShell instance available
   // no reference to TSecureShell instance available
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-void display_banner(Seat * seat, const char * banner, int size)
+size_t banner(Seat * seat, const void * data, size_t len)
 {
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  UnicodeString Banner(UTF8String(banner, size));
+  UnicodeString Banner(UTF8String(static_cast<const char *>(data), len));
   SecureShell->DisplayBanner(Banner);
   SecureShell->DisplayBanner(Banner);
+  return 0; // PuTTY never uses the value
+}
+//---------------------------------------------------------------------------
+uintmax_t strtoumax(const char *nptr, char **endptr, int base)
+{
+  if (DebugAlwaysFalse(endptr != NULL) ||
+      DebugAlwaysFalse(base != 10))
+  {
+    Abort();
+  }
+  return StrToInt64(UnicodeString(AnsiString(nptr)));
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 static void SSHFatalError(const char * Format, va_list Param)
 static void SSHFatalError(const char * Format, va_list Param)
@@ -384,13 +405,17 @@ static const SeatVtable ScpSeatVtable =
   {
   {
     output,
     output,
     eof,
     eof,
+    nullseat_sent,
+    banner,
     get_userpass_input,
     get_userpass_input,
+    nullseat_notify_session_started,
     nullseat_notify_remote_exit,
     nullseat_notify_remote_exit,
+    nullseat_notify_remote_disconnect,
     connection_fatal,
     connection_fatal,
     nullseat_update_specials_menu,
     nullseat_update_specials_menu,
     nullseat_get_ttymode,
     nullseat_get_ttymode,
     nullseat_set_busy_status,
     nullseat_set_busy_status,
-    verify_ssh_host_key,
+    confirm_ssh_host_key,
     confirm_weak_crypto_primitive,
     confirm_weak_crypto_primitive,
     confirm_weak_cached_hostkey,
     confirm_weak_cached_hostkey,
     nullseat_is_always_utf8,
     nullseat_is_always_utf8,
@@ -399,7 +424,9 @@ static const SeatVtable ScpSeatVtable =
     nullseat_get_windowid,
     nullseat_get_windowid,
     nullseat_get_window_pixel_size,
     nullseat_get_window_pixel_size,
     nullseat_stripctrl_new,
     nullseat_stripctrl_new,
-    nullseat_set_trust_status_vacuously,
+    nullseat_set_trust_status,
+    nullseat_can_set_trust_status_yes,
+    nullseat_has_mixed_input_stream_yes,
     nullseat_verbose_yes,
     nullseat_verbose_yes,
     nullseat_interactive_no,
     nullseat_interactive_no,
     nullseat_get_cursor_position,
     nullseat_get_cursor_position,
@@ -989,7 +1016,7 @@ UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const s
 UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
 UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
 {
 {
   UnicodeString Result;
   UnicodeString Result;
-  if (KeyType == ssh_dss.cache_id)
+  if (KeyType == ssh_dsa.cache_id)
   {
   {
     Result = L"DSA";
     Result = L"DSA";
   }
   }

+ 1 - 1
source/core/PuttyIntf.h

@@ -18,7 +18,7 @@ extern "C"
 #include <ssh.h>
 #include <ssh.h>
 #undef new
 #undef new
 #include <puttyexp.h>
 #include <puttyexp.h>
-#include <proxy.h>
+#include <proxy\proxy.h>
 #include <storage.h>
 #include <storage.h>
 // Defined in misc.h - Conflicts with std::min/max
 // Defined in misc.h - Conflicts with std::min/max
 #undef min
 #undef min

+ 11 - 3
source/core/RemoteFiles.cpp

@@ -407,6 +407,7 @@ TDateTime __fastcall ReduceDateTimePrecision(TDateTime DateTime,
     DecodeTime(DateTime, H, N, S, MS);
     DecodeTime(DateTime, H, N, S, MS);
     switch (Precision)
     switch (Precision)
     {
     {
+      case mfYMDHM:
       case mfMDHM:
       case mfMDHM:
         S = 0;
         S = 0;
         MS = 0;
         MS = 0;
@@ -443,6 +444,7 @@ UnicodeString __fastcall UserModificationStr(TDateTime DateTime,
       return L"";
       return L"";
     case mfMDY:
     case mfMDY:
       return FormatDateTime(L"ddddd", DateTime);
       return FormatDateTime(L"ddddd", DateTime);
+    case mfYMDHM:
     case mfMDHM:
     case mfMDHM:
       return FormatDateTime(L"ddddd t", DateTime);
       return FormatDateTime(L"ddddd t", DateTime);
     case mfFull:
     case mfFull:
@@ -469,6 +471,10 @@ UnicodeString __fastcall ModificationStr(TDateTime DateTime,
       return FORMAT(L"%3s %2d %2d:%2.2d",
       return FORMAT(L"%3s %2d %2d:%2.2d",
         (EngShortMonthNames[Month-1], Day, Hour, Min));
         (EngShortMonthNames[Month-1], Day, Hour, Min));
 
 
+    case mfYMDHM:
+      return FORMAT(L"%3s %2d %2d:%2.2d %4d",
+        (EngShortMonthNames[Month-1], Day, Hour, Min, Year));
+
     default:
     default:
       DebugFail();
       DebugFail();
       // fall thru
       // fall thru
@@ -1024,7 +1030,7 @@ bool __fastcall TRemoteFile::IsTimeShiftingApplicable()
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 bool __fastcall TRemoteFile::IsTimeShiftingApplicable(TModificationFmt ModificationFmt)
 bool __fastcall TRemoteFile::IsTimeShiftingApplicable(TModificationFmt ModificationFmt)
 {
 {
-  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfFull);
+  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfYMDHM) || (ModificationFmt == mfFull);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TRemoteFile::ShiftTimeInSeconds(__int64 Seconds)
 void __fastcall TRemoteFile::ShiftTimeInSeconds(__int64 Seconds)
@@ -1296,7 +1302,7 @@ void __fastcall TRemoteFile::SetListingStr(UnicodeString value)
             {
             {
               Year = (Word)StrToInt(Col);
               Year = (Word)StrToInt(Col);
               if (Year > 10000) Abort();
               if (Year > 10000) Abort();
-              // When we don't got time we assume midnight
+              // When we didn't get time we assume midnight
               Hour = 0; Min = 0; Sec = 0;
               Hour = 0; Min = 0; Sec = 0;
               FModificationFmt = mfMDY;
               FModificationFmt = mfMDY;
             }
             }
@@ -1306,7 +1312,9 @@ void __fastcall TRemoteFile::SetListingStr(UnicodeString value)
         FModification = EncodeDateVerbose(Year, Month, Day) + EncodeTimeVerbose(Hour, Min, Sec, 0);
         FModification = EncodeDateVerbose(Year, Month, Day) + EncodeTimeVerbose(Hour, Min, Sec, 0);
         // adjust only when time is known,
         // adjust only when time is known,
         // adjusting default "midnight" time makes no sense
         // adjusting default "midnight" time makes no sense
-        if ((FModificationFmt == mfMDHM) || (FModificationFmt == mfFull))
+        if ((FModificationFmt == mfMDHM) ||
+             DebugAlwaysFalse(FModificationFmt == mfYMDHM) ||
+             (FModificationFmt == mfFull))
         {
         {
           DebugAssert(Terminal != NULL);
           DebugAssert(Terminal != NULL);
           FModification = AdjustDateTimeFromUnix(FModification,
           FModification = AdjustDateTimeFromUnix(FModification,

+ 1 - 1
source/core/RemoteFiles.h

@@ -5,7 +5,7 @@
 #include <vector>
 #include <vector>
 #include <map>
 #include <map>
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-enum TModificationFmt { mfNone, mfMDHM, mfMDY, mfFull };
+enum TModificationFmt { mfNone, mfMDHM, mfYMDHM, mfMDY, mfFull };
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 #define SYMLINKSTR L" -> "
 #define SYMLINKSTR L" -> "
 #define ROOTDIRECTORY L"/"
 #define ROOTDIRECTORY L"/"

+ 82 - 40
source/core/SecureShell.cpp

@@ -77,15 +77,16 @@ __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
   FWaitingForData = 0;
   FWaitingForData = 0;
   FCallbackSet.reset(new callback_set());
   FCallbackSet.reset(new callback_set());
   memset(FCallbackSet.get(), 0, sizeof(callback_set));
   memset(FCallbackSet.get(), 0, sizeof(callback_set));
-  FCallbackSet->handles_by_evtomain = new_handles_by_evtomain();
+  FCallbackSet->ready_event = INVALID_HANDLE_VALUE;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 __fastcall TSecureShell::~TSecureShell()
 __fastcall TSecureShell::~TSecureShell()
 {
 {
-  freetree234(FCallbackSet->handles_by_evtomain);
-  FCallbackSet->handles_by_evtomain = NULL;
   DebugAssert(FWaiting == 0);
   DebugAssert(FWaiting == 0);
-  Active = false;
+  if (Active)
+  {
+    Close();
+  }
   ResetConnection();
   ResetConnection();
   CloseHandle(FSocketEvent);
   CloseHandle(FSocketEvent);
 }
 }
@@ -115,6 +116,7 @@ void __fastcall TSecureShell::ResetConnection()
   }
   }
   FLogCtx = NULL;
   FLogCtx = NULL;
   FClosed = false;
   FClosed = false;
+  FLoggedKnownHostKeys.clear();
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::ResetSessionInfo()
 void __fastcall TSecureShell::ResetSessionInfo()
@@ -410,7 +412,7 @@ void __fastcall TSecureShell::Open()
   // do not use UTF-8 until decided otherwise (see TSCPFileSystem::DetectUtf())
   // do not use UTF-8 until decided otherwise (see TSCPFileSystem::DetectUtf())
   FUtfStrings = false;
   FUtfStrings = false;
 
 
-  Active = false;
+  FActive = false;
 
 
   FAuthenticationLog = L"";
   FAuthenticationLog = L"";
   FNoConnectionResponse = false;
   FNoConnectionResponse = false;
@@ -446,6 +448,10 @@ void __fastcall TSecureShell::Open()
       PuttyFatalError(InitError);
       PuttyFatalError(InitError);
     }
     }
     FUI->Information(LoadStr(STATUS_CONNECT), true);
     FUI->Information(LoadStr(STATUS_CONNECT), true);
+    if (!Active && DebugAlwaysTrue(HasLocalProxy()))
+    {
+      FActive = true;
+    }
     Init();
     Init();
 
 
     CheckConnection(CONNECTION_FAILED);
     CheckConnection(CONNECTION_FAILED);
@@ -702,6 +708,7 @@ TPromptKind __fastcall TSecureShell::IdentifyPromptKind(UnicodeString & Name)
     { L"SSH server authentication", SERVER_PROMPT_TITLE },
     { L"SSH server authentication", SERVER_PROMPT_TITLE },
     { L"SSH password", PASSWORD_TITLE },
     { L"SSH password", PASSWORD_TITLE },
     { L"New SSH password", NEW_PASSWORD_TITLE },
     { L"New SSH password", NEW_PASSWORD_TITLE },
+    { L"SOCKS proxy authentication", PROXY_AUTH_TITLE },
   };
   };
 
 
   int Index = TranslatePuttyMessage(NameTranslation, LENOF(NameTranslation), Name);
   int Index = TranslatePuttyMessage(NameTranslation, LENOF(NameTranslation), Name);
@@ -735,6 +742,10 @@ TPromptKind __fastcall TSecureShell::IdentifyPromptKind(UnicodeString & Name)
   {
   {
     PromptKind = pkNewPassword;
     PromptKind = pkNewPassword;
   }
   }
+  else if (Index == 8)
+  {
+    PromptKind = pkProxyAuth;
+  }
   else
   else
   {
   {
     PromptKind = pkPrompt;
     PromptKind = pkPrompt;
@@ -839,6 +850,16 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
     PromptTranslationCount = LENOF(NewPasswordPromptTranslation);
     PromptTranslationCount = LENOF(NewPasswordPromptTranslation);
     PromptDesc = L"new password";
     PromptDesc = L"new password";
   }
   }
+  else if (PromptKind == pkProxyAuth)
+  {
+    static const TPuttyTranslation ProxyAuthPromptTranslation[] = {
+      { L"Proxy username: ", PROXY_AUTH_USERNAME_PROMPT },
+      { L"Proxy password: ", PROXY_AUTH_PASSWORD_PROMPT },
+    };
+    PromptTranslation = ProxyAuthPromptTranslation;
+    PromptTranslationCount = LENOF(ProxyAuthPromptTranslation);
+    PromptDesc = L"proxy authentication";
+  }
   else
   else
   {
   {
     PromptDesc = L"unknown";
     PromptDesc = L"unknown";
@@ -849,7 +870,7 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
     (Instructions.IsEmpty() ? UnicodeString(L"<no instructions>") : FORMAT(L"\"%s\"", (Instructions)));
     (Instructions.IsEmpty() ? UnicodeString(L"<no instructions>") : FORMAT(L"\"%s\"", (Instructions)));
   UnicodeString PromptsLog =
   UnicodeString PromptsLog =
     (Prompts->Count > 0 ? FORMAT(L"\"%s\"", (Prompts->Strings[0])) : UnicodeString(L"<no prompt>")) +
     (Prompts->Count > 0 ? FORMAT(L"\"%s\"", (Prompts->Strings[0])) : UnicodeString(L"<no prompt>")) +
-    (Prompts->Count > 1 ? FORMAT(L"%d more", (Prompts->Count - 1)) : UnicodeString());
+    (Prompts->Count > 1 ? FORMAT(L" + %d more", (Prompts->Count - 1)) : UnicodeString());
   LogEvent(FORMAT(L"Prompt (%s, \"%s\", %s, %s)", (PromptDesc, AName, InstructionsLog, PromptsLog)));
   LogEvent(FORMAT(L"Prompt (%s, \"%s\", %s, %s)", (PromptDesc, AName, InstructionsLog, PromptsLog)));
 
 
   Name = Name.Trim();
   Name = Name.Trim();
@@ -1339,7 +1360,8 @@ void __fastcall TSecureShell::DispatchSendBuffer(int BufSize)
 void __fastcall TSecureShell::Send(const unsigned char * Buf, Integer Len)
 void __fastcall TSecureShell::Send(const unsigned char * Buf, Integer Len)
 {
 {
   CheckConnection();
   CheckConnection();
-  int BufSize = backend_send(FBackendHandle, const_cast<char *>(reinterpret_cast<const char *>(Buf)), Len);
+  backend_send(FBackendHandle, const_cast<char *>(reinterpret_cast<const char *>(Buf)), Len);
+  int BufSize = backend_sendbuffer(FBackendHandle);
   if (Configuration->ActualLogProtocol >= 1)
   if (Configuration->ActualLogProtocol >= 1)
   {
   {
     LogEvent(FORMAT(L"Sent %u bytes", (static_cast<int>(Len))));
     LogEvent(FORMAT(L"Sent %u bytes", (static_cast<int>(Len))));
@@ -1596,6 +1618,11 @@ void __fastcall TSecureShell::SocketEventSelect(SOCKET Socket, HANDLE Event, boo
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+bool TSecureShell::HasLocalProxy()
+{
+  return (FSessionData->ProxyMethod == pmCmd);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Enable)
 void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Enable)
 {
 {
   if (!FActive && !Enable)
   if (!FActive && !Enable)
@@ -1619,7 +1646,7 @@ void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Enable)
     }
     }
     else
     else
     {
     {
-      DebugAssert(FSessionData->ProxyMethod == pmCmd);
+      DebugAssert(HasLocalProxy());
     }
     }
 
 
     if (Enable)
     if (Enable)
@@ -1654,21 +1681,6 @@ void __fastcall TSecureShell::UpdatePortFwdSocket(SOCKET value, bool Enable)
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-void __fastcall TSecureShell::SetActive(bool value)
-{
-  if (FActive != value)
-  {
-    if (value)
-    {
-      Open();
-    }
-    else
-    {
-      Close();
-    }
-  }
-}
-//---------------------------------------------------------------------------
 void __fastcall TSecureShell::FreeBackend()
 void __fastcall TSecureShell::FreeBackend()
 {
 {
   if (FBackendHandle != NULL)
   if (FBackendHandle != NULL)
@@ -1697,6 +1709,25 @@ void __fastcall TSecureShell::FreeBackend()
       sfree(FCallbackSet->pktin_freeq_head);
       sfree(FCallbackSet->pktin_freeq_head);
       FCallbackSet->pktin_freeq_head = NULL;
       FCallbackSet->pktin_freeq_head = NULL;
     }
     }
+
+    if (FCallbackSet->handlewaits_tree_real != NULL)
+    {
+      DebugAssert(count234(FCallbackSet->handlewaits_tree_real) <= 1);
+      while (count234(FCallbackSet->handlewaits_tree_real) > 0)
+      {
+        HandleWait * AHandleWait = static_cast<HandleWait *>(index234(FCallbackSet->handlewaits_tree_real, 0));
+        delete_handle_wait(FCallbackSet.get(), AHandleWait);
+      }
+
+      freetree234(FCallbackSet->handlewaits_tree_real);
+      FCallbackSet->handlewaits_tree_real = NULL;
+    }
+
+    if (FCallbackSet->ready_event != INVALID_HANDLE_VALUE)
+    {
+      CloseHandle(FCallbackSet->ready_event);
+      FCallbackSet->ready_event = INVALID_HANDLE_VALUE;
+    }
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
@@ -1971,8 +2002,6 @@ bool __fastcall TSecureShell::ProcessNetworkEvents(SOCKET Socket)
 bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventRequired,
 bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventRequired,
   WSANETWORKEVENTS * Events)
   WSANETWORKEVENTS * Events)
 {
 {
-  CheckConnection();
-
   bool Result = false;
   bool Result = false;
 
 
   do
   do
@@ -1982,8 +2011,8 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       LogEvent(L"Looking for network events");
       LogEvent(L"Looking for network events");
     }
     }
     unsigned int TicksBefore = GetTickCount();
     unsigned int TicksBefore = GetTickCount();
-    int HandleCount;
-    HANDLE * Handles = NULL;
+    HandleWaitList * WaitList = NULL;
+
     try
     try
     {
     {
       unsigned int Timeout = MSec;
       unsigned int Timeout = MSec;
@@ -1998,15 +2027,15 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
           TimeoutStep = 0;
           TimeoutStep = 0;
         }
         }
         Timeout -= TimeoutStep;
         Timeout -= TimeoutStep;
-        if (Handles != NULL)
+        if (WaitList != NULL)
         {
         {
-          sfree(Handles);
+          handle_wait_list_free(WaitList);
         }
         }
         // It returns only busy handles, so the set can change with every call to run_toplevel_callbacks.
         // It returns only busy handles, so the set can change with every call to run_toplevel_callbacks.
-        Handles = handle_get_events(FCallbackSet->handles_by_evtomain, &HandleCount);
-        Handles = sresize(Handles, HandleCount + 1, HANDLE);
-        Handles[HandleCount] = FSocketEvent;
-        WaitResult = WaitForMultipleObjects(HandleCount + 1, Handles, FALSE, TimeoutStep);
+        WaitList = get_handle_wait_list(FCallbackSet.get());
+        DebugAssert(WaitList->nhandles < MAXIMUM_WAIT_OBJECTS);
+        WaitList->handles[WaitList->nhandles] = FSocketEvent;
+        WaitResult = WaitForMultipleObjects(WaitList->nhandles + 1, WaitList->handles, FALSE, TimeoutStep);
         FUI->ProcessGUI();
         FUI->ProcessGUI();
         // run_toplevel_callbacks can cause processing of pending raw data, so:
         // run_toplevel_callbacks can cause processing of pending raw data, so:
         // 1) Check for changes in our pending buffer - wait criteria in Receive()
         // 1) Check for changes in our pending buffer - wait criteria in Receive()
@@ -2022,20 +2051,21 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
         }
         }
       } while ((WaitResult == WAIT_TIMEOUT) && (Timeout > 0) && !Result);
       } while ((WaitResult == WAIT_TIMEOUT) && (Timeout > 0) && !Result);
 
 
-      if (WaitResult < WAIT_OBJECT_0 + HandleCount)
+      if (WaitResult < WAIT_OBJECT_0 + WaitList->nhandles)
       {
       {
-        if (handle_got_event(FCallbackSet->handles_by_evtomain, Handles[WaitResult - WAIT_OBJECT_0]))
+        if (handle_wait_activate(FCallbackSet.get(), WaitList, WaitResult - WAIT_OBJECT_0))
         {
         {
           Result = true;
           Result = true;
         }
         }
       }
       }
-      else if (WaitResult == WAIT_OBJECT_0 + HandleCount)
+      else if (WaitResult == WAIT_OBJECT_0 + WaitList->nhandles)
       {
       {
         if (Configuration->ActualLogProtocol >= 1)
         if (Configuration->ActualLogProtocol >= 1)
         {
         {
           LogEvent(L"Detected network event");
           LogEvent(L"Detected network event");
         }
         }
 
 
+        DebugAssert(FSocket != INVALID_SOCKET);
         if (Events == NULL)
         if (Events == NULL)
         {
         {
           if (ProcessNetworkEvents(FSocket))
           if (ProcessNetworkEvents(FSocket))
@@ -2081,7 +2111,10 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
     }
     }
     __finally
     __finally
     {
     {
-      sfree(Handles);
+      if (WaitList != NULL)
+      {
+        handle_wait_list_free(WaitList);
+      }
     }
     }
 
 
 
 
@@ -2100,7 +2133,8 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       }
       }
     }
     }
 
 
-    if ((FSendBuf > 0) && (TicksAfter - FLastSendBufferUpdate >= 1000))
+    if ((FSocket != INVALID_SOCKET) &&
+        (FSendBuf > 0) && (TicksAfter - FLastSendBufferUpdate >= 1000))
     {
     {
       DWORD BufferLen = 0;
       DWORD BufferLen = 0;
       DWORD OutLen = 0;
       DWORD OutLen = 0;
@@ -2658,9 +2692,13 @@ bool __fastcall TSecureShell::HaveHostKey(UnicodeString Host, int Port, const Un
     }
     }
   }
   }
 
 
-  if (Result)
+  if (Result &&
+      (FLoggedKnownHostKeys.find(KeyType) == FLoggedKnownHostKeys.end()))
   {
   {
     LogEvent(FORMAT(L"Have a known host key of type %s", (KeyType)));
     LogEvent(FORMAT(L"Have a known host key of type %s", (KeyType)));
+    // This is called multiple times for the same cached key since PuTTY 0.76 (support for rsa-sha2*?).
+    // Avoid repeated log entries.
+    FLoggedKnownHostKeys.insert(KeyType);
   }
   }
 
 
   return Result;
   return Result;
@@ -2690,7 +2728,11 @@ void __fastcall TSecureShell::AskAlg(UnicodeString AlgType, UnicodeString AlgNam
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::DisplayBanner(const UnicodeString & Banner)
 void __fastcall TSecureShell::DisplayBanner(const UnicodeString & Banner)
 {
 {
-  FUI->DisplayBanner(Banner);
+  // Since 0.77 PuTTY calls this again with CRLF if the actual banner does not end with one.
+  if (!Banner.Trim().IsEmpty())
+  {
+    FUI->DisplayBanner(Banner);
+  }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::OldKeyfileWarning()
 void __fastcall TSecureShell::OldKeyfileWarning()

+ 3 - 1
source/core/SecureShell.h

@@ -76,6 +76,7 @@ private:
   ScpLogPolicy * FLogPolicy;
   ScpLogPolicy * FLogPolicy;
   ScpSeat * FSeat;
   ScpSeat * FSeat;
   LogContext * FLogCtx;
   LogContext * FLogCtx;
+  std::set<UnicodeString> FLoggedKnownHostKeys;
 
 
   void __fastcall Init();
   void __fastcall Init();
   void __fastcall SetActive(bool value);
   void __fastcall SetActive(bool value);
@@ -110,6 +111,7 @@ private:
     const UnicodeString & StoredKeys, const UnicodeString & KeyStr, const UnicodeString & FingerprintMD5, const UnicodeString & FingerprintSHA256);
     const UnicodeString & StoredKeys, const UnicodeString & KeyStr, const UnicodeString & FingerprintMD5, const UnicodeString & FingerprintSHA256);
   UnicodeString StoreHostKey(
   UnicodeString StoreHostKey(
     const UnicodeString & Host, int Port, const UnicodeString & KeyType, const UnicodeString & KeyStr);
     const UnicodeString & Host, int Port, const UnicodeString & KeyType, const UnicodeString & KeyStr);
+  bool HasLocalProxy();
 
 
 protected:
 protected:
   TCaptureOutputEvent FOnCaptureOutput;
   TCaptureOutputEvent FOnCaptureOutput;
@@ -178,7 +180,7 @@ public:
   UnicodeString __fastcall ConvertFromPutty(const char * Str, int Length);
   UnicodeString __fastcall ConvertFromPutty(const char * Str, int Length);
   struct callback_set * GetCallbackSet();
   struct callback_set * GetCallbackSet();
 
 
-  __property bool Active = { read = FActive, write = SetActive };
+  __property bool Active = { read = FActive };
   __property bool Ready = { read = GetReady };
   __property bool Ready = { read = GetReady };
   __property TCaptureOutputEvent OnCaptureOutput = { read = FOnCaptureOutput, write = FOnCaptureOutput };
   __property TCaptureOutputEvent OnCaptureOutput = { read = FOnCaptureOutput, write = FOnCaptureOutput };
   __property TDateTime LastDataSent = { read = FLastDataSent };
   __property TDateTime LastDataSent = { read = FLastDataSent };

+ 1 - 0
source/core/WebDAVFileSystem.cpp

@@ -892,6 +892,7 @@ void __fastcall TWebDAVFileSystem::ParsePropResultSet(TRemoteFile * File,
           EncodeDateVerbose((unsigned short)Year, (unsigned short)Month, (unsigned short)Day) +
           EncodeDateVerbose((unsigned short)Year, (unsigned short)Month, (unsigned short)Day) +
           EncodeTimeVerbose((unsigned short)Hour, (unsigned short)Min, (unsigned short)Sec, 0);
           EncodeTimeVerbose((unsigned short)Hour, (unsigned short)Min, (unsigned short)Sec, 0);
         File->Modification = ConvertTimestampFromUTC(Modification);
         File->Modification = ConvertTimestampFromUTC(Modification);
+        // Should use mfYMDHM or mfMDY when appropriate according to Filled
         File->ModificationFmt = mfFull;
         File->ModificationFmt = mfFull;
       }
       }
       else
       else

+ 1 - 0
source/filezilla/FileZillaIntf.cpp

@@ -293,6 +293,7 @@ void __fastcall CopyFileTime(TRemoteFileTime & Dest, const t_directory::t_dirent
   Dest.Second = Source.second;
   Dest.Second = Source.second;
   Dest.HasTime = Source.hastime;
   Dest.HasTime = Source.hastime;
   Dest.HasDate = Source.hasdate;
   Dest.HasDate = Source.hasdate;
+  Dest.HasYear = Source.hasyear;
   Dest.HasSeconds = Source.hasseconds;
   Dest.HasSeconds = Source.hasseconds;
   Dest.Utc = Source.utc;
   Dest.Utc = Source.utc;
 }
 }

+ 1 - 0
source/filezilla/FileZillaIntf.h

@@ -20,6 +20,7 @@ struct TRemoteFileTime
   int Minute;
   int Minute;
   int Second;
   int Second;
   bool HasTime;
   bool HasTime;
+  bool HasYear;
   bool HasSeconds;
   bool HasSeconds;
   bool HasDate;
   bool HasDate;
   bool Utc;
   bool Utc;

+ 1 - 0
source/filezilla/FtpControlSocket.cpp

@@ -4416,6 +4416,7 @@ bool CFtpControlSocket::HandleMdtm(int code, t_directory::t_direntry::t_date & d
             date.minute = m;
             date.minute = m;
             date.second = s;
             date.second = s;
             date.hastime = true;
             date.hastime = true;
+            date.hasyear = true;
             date.hasseconds = hasseconds;
             date.hasseconds = hasseconds;
             date.hasdate = true;
             date.hasdate = true;
             date.utc = true;
             date.utc = true;

+ 41 - 35
source/filezilla/FtpListResult.cpp

@@ -674,6 +674,7 @@ bool CFtpListResult::ParseShortDate(const char *str, int len, t_directory::t_dir
   }
   }
 
 
   date.hasdate = TRUE;
   date.hasdate = TRUE;
+  date.hasyear = TRUE;
   return true;
   return true;
 }
 }
 
 
@@ -803,6 +804,7 @@ BOOL CFtpListResult::parseAsVMS(const char *line, const int linelen, t_directory
   p++;
   p++;
 
 
   dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));
   dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));
+  dir.date.hasyear = TRUE;
 
 
   //Get time
   //Get time
   str = GetNextToken(line, linelen, tokenlen, pos, 0);
   str = GetNextToken(line, linelen, tokenlen, pos, 0);
@@ -1169,6 +1171,7 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
   if (result)
   if (result)
   {
   {
     date.year = Year;
     date.year = Year;
+    date.hasyear = TRUE;
     date.month = Month;
     date.month = Month;
     date.day = Day;
     date.day = Day;
     date.hour = Hours;
     date.hour = Hours;
@@ -1179,6 +1182,32 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
   return result;
   return result;
 }
 }
 
 
+void CFtpListResult::GuessYearIfUnknown(t_directory::t_direntry::t_date & Date)
+{
+  // Problem: Some servers use times only for files newer than 6 months,
+  // others use one year as limit. IIS shows time for files from the current year (jan-dec).
+  // So there is no support for files with time
+  // dated in the near future. Under normal conditions there should not be such files.
+  if (!Date.year) // might use direntry.date.hasyear now?
+  {
+    CTime curtime = CTime::GetCurrentTime();
+    int curday = curtime.GetDay();
+    int curmonth = curtime.GetMonth();
+    int curyear = curtime.GetYear();
+    int now = curmonth * 31 + curday;
+    int file = Date.month * 31 + Date.day;
+    if ((now + 1) >= file)
+    {
+      Date.year = curyear;
+    }
+    else
+    {
+      Date.year = curyear - 1;
+    }
+    // year is guessed, not setting hasyear
+  }
+}
+
 BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_directory::t_direntry &direntry)
 BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_directory::t_direntry &direntry)
 {
 {
   int pos = 0;
   int pos = 0;
@@ -1467,6 +1496,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth == 4) //2002-10-14
     else if (p-smonth == 4) //2002-10-14
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1488,6 +1518,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth) //14-10-2002 or 01-jun-99
     else if (p-smonth) //14-10-2002 or 01-jun-99
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sday = smonth;
       sdaylen = p - smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1529,6 +1560,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==4)
     else if (p-smonth==4)
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1550,6 +1582,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==2)
     else if (p-smonth==2)
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sday = smonth;
       sdaylen = p - smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1652,6 +1685,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
   {
   {
     gotYear = true;
     gotYear = true;
     direntry.date.year = year;
     direntry.date.year = year;
+    direntry.date.hasyear = TRUE;
   }
   }
   else
   else
   {
   {
@@ -1729,23 +1763,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     }
     }
     direntry.date.hastime = TRUE;
     direntry.date.hastime = TRUE;
 
 
-    //Problem: Some servers use times only for files newer than 6 months,
-    //others use one year as limit. IIS shows time for files from the current year (jan-dec).
-    //So there is no support for files with time
-    //dated in the near future. Under normal conditions there should not be such files.
-    if (!direntry.date.year)
-    {
-      CTime curtime = CTime::GetCurrentTime();
-      int curday = curtime.GetDay();
-      int curmonth = curtime.GetMonth();
-      int curyear = curtime.GetYear();
-      int now = curmonth*31+curday;
-      int file = direntry.date.month*31+direntry.date.day;
-      if ((now+1)>=file)
-        direntry.date.year = curyear;
-      else
-        direntry.date.year = curyear-1;
-    }
+    GuessYearIfUnknown(direntry.date);
     bCouldBeVShell = FALSE;
     bCouldBeVShell = FALSE;
   }
   }
   else
   else
@@ -1758,12 +1776,13 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
         return false;
         return false;
       }
       }
     }
     }
-    else if (!direntry.date.year)
+    else if (!direntry.date.year) // might use direntry.date.hasyear now?
     {
     {
       //No delimiters -> year
       //No delimiters -> year
 
 
       direntry.date.hastime = FALSE;
       direntry.date.hastime = FALSE;
       direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));
       direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));
+      direntry.date.hasyear = TRUE;
     }
     }
     else
     else
     {
     {
@@ -1772,7 +1791,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     }
     }
   }
   }
 
 
-  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid!
+  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid! might use direntry.date.hasyear now?
   {
   {
     return FALSE;
     return FALSE;
   }
   }
@@ -1918,6 +1937,7 @@ void CFtpListResult::TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date &
 {
 {
   tm * sTime = gmtime(&TimeT);
   tm * sTime = gmtime(&TimeT);
   Date.year = sTime->tm_year + 1900;
   Date.year = sTime->tm_year + 1900;
+  Date.hasyear = TRUE;
   Date.month = sTime->tm_mon+1;
   Date.month = sTime->tm_mon+1;
   Date.day = sTime->tm_mday;
   Date.day = sTime->tm_mday;
   Date.hour = sTime->tm_hour;
   Date.hour = sTime->tm_hour;
@@ -2044,22 +2064,7 @@ BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directo
         direntry.date.minute = static_cast<int>(strntoi64(strpos+1, tokenlen - (strpos - str) - 1));
         direntry.date.minute = static_cast<int>(strntoi64(strpos+1, tokenlen - (strpos - str) - 1));
         direntry.date.hastime = TRUE;
         direntry.date.hastime = TRUE;
 
 
-        //Problem: Some servers use times only for files newer than 6 months,
-        //others use one year as limit. So there is no support for files with time
-        //dated in the near future. Under normal conditions there should not be such files
-        if (!direntry.date.year)
-        {
-          CTime curtime = CTime::GetCurrentTime();
-          int curday = curtime.GetDay();
-          int curmonth = curtime.GetMonth();
-          int curyear = curtime.GetYear();
-          int now = curmonth*31+curday;
-          int file = direntry.date.month*31+direntry.date.day;
-          if ((now+1)>=file)
-            direntry.date.year = curyear;
-          else
-            direntry.date.year = curyear-1;
-        }
+        GuessYearIfUnknown(direntry.date);
       }
       }
 
 
       str = GetNextToken(line, linelen, tokenlen, pos, 1);
       str = GetNextToken(line, linelen, tokenlen, pos, 1);
@@ -2111,6 +2116,7 @@ BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directo
         return FALSE;
         return FALSE;
 
 
       direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));
       direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));
+      direntry.date.hasyear = TRUE;
       if (direntry.date.year < 50)
       if (direntry.date.year < 50)
         direntry.date.year += 2000;
         direntry.date.year += 2000;
       else if (direntry.date.year < 1000)
       else if (direntry.date.year < 1000)

+ 1 - 0
source/filezilla/FtpListResult.h

@@ -59,6 +59,7 @@ private:
   bool parseTime(const char * str, int len, t_directory::t_direntry::t_date & date) const;
   bool parseTime(const char * str, int len, t_directory::t_direntry::t_date & date) const;
   bool ParseSize(const char * str, int len, __int64 & size) const;
   bool ParseSize(const char * str, int len, __int64 & size) const;
   void TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date & date) const;
   void TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date & date) const;
+  static void GuessYearIfUnknown(t_directory::t_direntry::t_date & Date);
 
 
   bool parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date & date) const;
   bool parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date & date) const;
 
 

+ 1 - 1
source/filezilla/structures.cpp

@@ -47,5 +47,5 @@ t_directory::t_direntry::t_direntry()
 t_directory::t_direntry::t_date::t_date()
 t_directory::t_direntry::t_date::t_date()
 {
 {
   year=month=day=hour=minute=second=0;
   year=month=day=hour=minute=second=0;
-  hasdate=hastime=hasseconds=utc=FALSE;
+  hasdate=hastime=hasyear=hasseconds=utc=FALSE;
 }
 }

+ 1 - 0
source/filezilla/structures.h

@@ -33,6 +33,7 @@ public:
       t_date();
       t_date();
       int year,month,day,hour,minute,second;
       int year,month,day,hour,minute,second;
       bool hastime;
       bool hastime;
+      bool hasyear; // ignored and assumed true when hasseconds
       bool hasseconds;
       bool hasseconds;
       bool hasdate;
       bool hasdate;
       bool utc;
       bool utc;

+ 1 - 1
source/forms/Cleanup.dfm

@@ -89,7 +89,7 @@ object CleanupDialog: TCleanupDialog
   object CheckAllButton: TButton
   object CheckAllButton: TButton
     Left = 8
     Left = 8
     Top = 266
     Top = 266
-    Width = 108
+    Width = 113
     Height = 25
     Height = 25
     Anchors = [akLeft, akBottom]
     Anchors = [akLeft, akBottom]
     Caption = 'Un/check &all'
     Caption = 'Un/check &all'

+ 1 - 1
source/forms/Copy.dfm

@@ -93,7 +93,7 @@ object CopyDialog: TCopyDialog
   object QueueCheck2: TCheckBox
   object QueueCheck2: TCheckBox
     Left = 12
     Left = 12
     Top = 112
     Top = 112
-    Width = 297
+    Width = 317
     Height = 17
     Height = 17
     Caption = 'Transfer on background (add to transfer &queue) X'
     Caption = 'Transfer on background (add to transfer &queue) X'
     TabOrder = 4
     TabOrder = 4

+ 1 - 1
source/forms/Custom.cpp

@@ -1397,7 +1397,7 @@ __fastcall TUsageStatisticsDialog::TUsageStatisticsDialog() :
 
 
   ClipboardButton = new TButton(this);
   ClipboardButton = new TButton(this);
   ClipboardButton->Caption = LoadStr(USAGE_COPY);
   ClipboardButton->Caption = LoadStr(USAGE_COPY);
-  ClipboardButton->Width = ScaleByTextHeight(this, 121);
+  ClipboardButton->Width = ScaleByTextHeight(this, 161);
   ClipboardButton->OnClick = ClipboardButtonClick;
   ClipboardButton->OnClick = ClipboardButtonClick;
   AddDialogButton(ClipboardButton);
   AddDialogButton(ClipboardButton);
 
 

+ 1 - 1
source/forms/FileSystemInfo.dfm

@@ -356,7 +356,7 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
   object ClipboardButton: TButton
   object ClipboardButton: TButton
     Left = 8
     Left = 8
     Top = 364
     Top = 364
-    Width = 121
+    Width = 161
     Height = 25
     Height = 25
     Anchors = [akRight, akBottom]
     Anchors = [akRight, akBottom]
     Caption = '&Copy to Clipboard'
     Caption = '&Copy to Clipboard'

+ 1 - 1
source/forms/GenerateUrl.dfm

@@ -222,7 +222,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
   object ClipboardButton: TButton
   object ClipboardButton: TButton
     Left = 8
     Left = 8
     Top = 305
     Top = 305
-    Width = 145
+    Width = 165
     Height = 25
     Height = 25
     Anchors = [akLeft, akBottom]
     Anchors = [akLeft, akBottom]
     Caption = '&Copy to Clipboard'
     Caption = '&Copy to Clipboard'

+ 1 - 1
source/forms/ImportSessions.dfm

@@ -76,7 +76,7 @@ object ImportSessionsDialog: TImportSessionsDialog
   object CheckAllButton: TButton
   object CheckAllButton: TButton
     Left = 8
     Left = 8
     Top = 242
     Top = 242
-    Width = 108
+    Width = 113
     Height = 25
     Height = 25
     Anchors = [akLeft, akBottom]
     Anchors = [akLeft, akBottom]
     Caption = 'Un/check &all'
     Caption = 'Un/check &all'

+ 3 - 1
source/forms/NonVisual.cpp

@@ -1149,7 +1149,9 @@ static void GiveItemPriority(TTBCustomItem * Item)
 {
 {
   DebugAssert(Item->GetTopComponent() != NULL);
   DebugAssert(Item->GetTopComponent() != NULL);
   TTBCustomToolbar * ToolbarComponent = dynamic_cast<TTBCustomToolbar *>(Item->GetTopComponent());
   TTBCustomToolbar * ToolbarComponent = dynamic_cast<TTBCustomToolbar *>(Item->GetTopComponent());
-  if (ToolbarComponent != NULL)
+  if ((ToolbarComponent != NULL) &&
+      // Only for top-level buttons on custom command toolbar, not for submenus of the custom commands menu in the main menu
+      (Item->Parent == ToolbarComponent->Items))
   {
   {
     TTBItemViewer * Viewer = ToolbarComponent->View->Find(Item);
     TTBItemViewer * Viewer = ToolbarComponent->View->Find(Item);
     ToolbarComponent->View->GivePriority(Viewer);
     ToolbarComponent->View->GivePriority(Viewer);

+ 8 - 8
source/forms/Preferences.dfm

@@ -736,9 +736,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = ControlChange
             OnClick = ControlChange
           end
           end
           object PanelSearchCombo: TComboBox
           object PanelSearchCombo: TComboBox
-            Left = 216
+            Left = 196
             Top = 191
             Top = 191
-            Width = 156
+            Width = 176
             Height = 21
             Height = 21
             Style = csDropDownList
             Style = csDropDownList
             Anchors = [akTop, akRight]
             Anchors = [akTop, akRight]
@@ -1291,7 +1291,7 @@ object PreferencesDialog: TPreferencesDialog
           object AddCommandButton: TButton
           object AddCommandButton: TButton
             Left = 16
             Left = 16
             Top = 313
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Height = 25
             Anchors = [akLeft, akBottom]
             Anchors = [akLeft, akBottom]
             Caption = '&Add...'
             Caption = '&Add...'
@@ -1303,7 +1303,7 @@ object PreferencesDialog: TPreferencesDialog
           object RemoveCommandButton: TButton
           object RemoveCommandButton: TButton
             Left = 16
             Left = 16
             Top = 344
             Top = 344
-            Width = 83
+            Width = 98
             Height = 25
             Height = 25
             Anchors = [akLeft, akBottom]
             Anchors = [akLeft, akBottom]
             Caption = '&Remove'
             Caption = '&Remove'
@@ -1331,9 +1331,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = UpDownCommandButtonClick
             OnClick = UpDownCommandButtonClick
           end
           end
           object EditCommandButton: TButton
           object EditCommandButton: TButton
-            Left = 112
+            Left = 127
             Top = 313
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Height = 25
             Anchors = [akLeft, akBottom]
             Anchors = [akLeft, akBottom]
             Caption = '&Edit...'
             Caption = '&Edit...'
@@ -1341,9 +1341,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = EditCommandButtonClick
             OnClick = EditCommandButtonClick
           end
           end
           object ConfigureCommandButton: TButton
           object ConfigureCommandButton: TButton
-            Left = 112
+            Left = 127
             Top = 313
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Height = 25
             Anchors = [akLeft, akBottom]
             Anchors = [akLeft, akBottom]
             Caption = '&Configure...'
             Caption = '&Configure...'

+ 19 - 9
source/forms/ScpCommander.cpp

@@ -2325,23 +2325,33 @@ void __fastcall TScpCommanderForm::DoLocalPathComboBoxItemClick(TDirView * ADirV
   DebugAssert((PathComboBox->ItemIndex >= 0) && (PathComboBox->ItemIndex < FLocalPathComboBoxPaths->Count));
   DebugAssert((PathComboBox->ItemIndex >= 0) && (PathComboBox->ItemIndex < FLocalPathComboBoxPaths->Count));
 
 
   UnicodeString Path = FLocalPathComboBoxPaths->Strings[PathComboBox->ItemIndex];
   UnicodeString Path = FLocalPathComboBoxPaths->Strings[PathComboBox->ItemIndex];
-  if (PathComboBox->ItemIndex >= FLocalSpecialPaths)
+  try
   {
   {
-    UnicodeString Drive = DriveInfo->GetDriveKey(Path);
-    TDirView * CurrentDirView = dynamic_cast<TDirView *>(DirView(osCurrent));
-    if (IsLocalBrowserMode() && (ADirView != CurrentDirView) &&
-        SamePaths(DriveInfo->GetDriveKey(CurrentDirView->PathName), Drive))
+    if (PathComboBox->ItemIndex >= FLocalSpecialPaths)
     {
     {
-      ADirView->Path = CurrentDirView->PathName;
+      UnicodeString Drive = DriveInfo->GetDriveKey(Path);
+      TDirView * CurrentDirView = dynamic_cast<TDirView *>(DirView(osCurrent));
+      if (IsLocalBrowserMode() && (ADirView != CurrentDirView) &&
+        SamePaths(DriveInfo->GetDriveKey(CurrentDirView->PathName), Drive))
+      {
+        ADirView->Path = CurrentDirView->PathName;
+      }
+      else
+      {
+        ADirView->ExecuteDrive(Drive);
+      }
     }
     }
     else
     else
     {
     {
-      ADirView->ExecuteDrive(Drive);
+      ADirView->Path = Path;
     }
     }
   }
   }
-  else
+  catch (...)
   {
   {
-    ADirView->Path = Path;
+    // Changing the path failed, reset the combo box back.
+    // Does not recurse, so infinite recursion should not happen.
+    DoLocalPathComboBoxItemClick(ADirView, PathComboBox);
+    throw;
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------

+ 1 - 1
source/forms/SiteAdvanced.cpp

@@ -836,7 +836,7 @@ void __fastcall TSiteAdvancedDialog::UpdateControls()
       FtpPasvModeCheck->Checked = true;
       FtpPasvModeCheck->Checked = true;
       MessageDialog(MainInstructions(LoadStr(FTP_PASV_MODE_REQUIRED)), qtInformation, qaOK);
       MessageDialog(MainInstructions(LoadStr(FTP_PASV_MODE_REQUIRED)), qtInformation, qaOK);
     }
     }
-    EnableControl(BufferSizeCheck, SshProtocol || FtpProtocol);
+    EnableControl(BufferSizeCheck, SshProtocol || FtpProtocol || S3Protocol);
     PingGroup->Visible = !FtpProtocol;
     PingGroup->Visible = !FtpProtocol;
     EnableControl(PingGroup, SshProtocol);
     EnableControl(PingGroup, SshProtocol);
     EnableControl(PingIntervalSecEdit, PingGroup->Enabled && !PingOffButton->Checked);
     EnableControl(PingIntervalSecEdit, PingGroup->Enabled && !PingOffButton->Checked);

+ 3 - 3
source/forms/SiteAdvanced.dfm

@@ -527,7 +527,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           object ShowEncryptionKeyCheck: TCheckBox
           object ShowEncryptionKeyCheck: TCheckBox
             Left = 12
             Left = 12
             Top = 61
             Top = 61
-            Width = 97
+            Width = 117
             Height = 17
             Height = 17
             Caption = '&Show key'
             Caption = '&Show key'
             TabOrder = 2
             TabOrder = 2
@@ -2327,7 +2327,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
             OnChange = DataChange
             OnChange = DataChange
           end
           end
           object PrivateKeyToolsButton: TButton
           object PrivateKeyToolsButton: TButton
-            Left = 151
+            Left = 201
             Top = 86
             Top = 86
             Width = 101
             Width = 101
             Height = 25
             Height = 25
@@ -2338,7 +2338,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           object PrivateKeyViewButton: TButton
           object PrivateKeyViewButton: TButton
             Left = 12
             Left = 12
             Top = 86
             Top = 86
-            Width = 133
+            Width = 183
             Height = 25
             Height = 25
             Caption = '&Display Public Key'
             Caption = '&Display Public Key'
             TabOrder = 2
             TabOrder = 2

+ 6 - 0
source/packages/filemng/DirView.pas

@@ -2793,6 +2793,7 @@ end;
 procedure TDirView.ExecuteDrive(Drive: string);
 procedure TDirView.ExecuteDrive(Drive: string);
 var
 var
   APath: string;
   APath: string;
+  DriveRoot: string;
 begin
 begin
   if Assigned(FLastPath) and FLastPath.ContainsKey(Drive) then
   if Assigned(FLastPath) and FLastPath.ContainsKey(Drive) then
   begin
   begin
@@ -2810,6 +2811,11 @@ begin
     if DriveInfo.IsRealDrive(Drive) then
     if DriveInfo.IsRealDrive(Drive) then
     begin
     begin
       GetDir(Integer(Drive[1]) - Integer('A') + 1, APath);
       GetDir(Integer(Drive[1]) - Integer('A') + 1, APath);
+      DriveRoot := DriveInfo.GetDriveRoot(Drive);
+      // When the drive is not valid, the GetDir returns the current drive working directory, detect that,
+      // and let it fail later when trying to open root of the invalid drive.
+      if not StartsText(DriveRoot, APath) then
+        APath := DriveRoot;
       APath := ExcludeTrailingPathDelimiter(APath);
       APath := ExcludeTrailingPathDelimiter(APath);
     end
     end
       else
       else

+ 240 - 68
source/packages/filemng/DriveView.pas

@@ -56,6 +56,8 @@ const
   dvdsFloppy          = 8;  {Include floppy drives}
   dvdsFloppy          = 8;  {Include floppy drives}
   dvdsRereadAllways   = 16; {Refresh drivestatus in any case}
   dvdsRereadAllways   = 16; {Refresh drivestatus in any case}
 
 
+  WM_USER_SHCHANGENOTIFY = WM_USER + $2000 + 13;
+
 type
 type
   EInvalidDirName  = class(Exception);
   EInvalidDirName  = class(Exception);
   ENodeNotAssigned = class(Exception);
   ENodeNotAssigned = class(Exception);
@@ -68,8 +70,12 @@ type
     DiscMonitor: TDiscMonitor; {Monitor thread}
     DiscMonitor: TDiscMonitor; {Monitor thread}
     ChangeTimer: TTimer;       {Change timer for the monitor thread}
     ChangeTimer: TTimer;       {Change timer for the monitor thread}
     DefaultDir: string;        {Current directory}
     DefaultDir: string;        {Current directory}
+    DriveHandle: THandle;
+    NotificationHandle: HDEVNOTIFY;
   end;
   end;
 
 
+  TDriveStatusPair = TPair<string, TDriveStatus>;
+
   TScanDirInfo = record
   TScanDirInfo = record
     SearchNewDirs: Boolean;
     SearchNewDirs: Boolean;
     StartNode: TTreeNode;
     StartNode: TTreeNode;
@@ -130,6 +136,7 @@ type
     FRenameNode: TTreeNode;
     FRenameNode: TTreeNode;
     FLastRenameName: string;
     FLastRenameName: string;
     FInternalWindowHandle: HWND;
     FInternalWindowHandle: HWND;
+    FChangeNotify: ULONG;
     FPrevSelected: TTreeNode;
     FPrevSelected: TTreeNode;
     FPrevSelectedIndex: Integer;
     FPrevSelectedIndex: Integer;
     FChangeTimerSuspended: Integer;
     FChangeTimerSuspended: Integer;
@@ -183,6 +190,11 @@ type
     function WatchThreadActive: Boolean; overload;
     function WatchThreadActive: Boolean; overload;
     function WatchThreadActive(Drive: string): Boolean; overload;
     function WatchThreadActive(Drive: string): Boolean; overload;
     procedure InternalWndProc(var Msg: TMessage);
     procedure InternalWndProc(var Msg: TMessage);
+    procedure UpdateDriveNotifications(Drive: string);
+    procedure DriveRemoved(Drive: string);
+    procedure DriveRemoving(Drive: string);
+    procedure CancelDriveRefresh;
+    procedure ScheduleDriveRefresh;
 
 
     function DirAttrMask: Integer;
     function DirAttrMask: Integer;
     function CreateDriveStatus: TDriveStatus;
     function CreateDriveStatus: TDriveStatus;
@@ -459,6 +471,8 @@ end;
 constructor TDriveView.Create(AOwner: TComponent);
 constructor TDriveView.Create(AOwner: TComponent);
 var
 var
   Drive: TRealDrive;
   Drive: TRealDrive;
+  ChangeNotifyEntry: TSHChangeNotifyEntry;
+  Dummy: string;
 begin
 begin
   inherited;
   inherited;
 
 
@@ -495,6 +509,19 @@ begin
 
 
   FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
   FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
 
 
+  // Source: petr.solin 2022-02-25
+  FChangeNotify := 0;
+  if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
+  begin
+    ChangeNotifyEntry.fRecursive := False;
+
+    FChangeNotify :=
+      SHChangeNotifyRegister(
+        FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
+        SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
+        WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
+  end;
+
   with FDragDropFilesEx do
   with FDragDropFilesEx do
   begin
   begin
     ShellExtensions.DragDropHandler := True;
     ShellExtensions.DragDropHandler := True;
@@ -503,19 +530,21 @@ end; {Create}
 
 
 destructor TDriveView.Destroy;
 destructor TDriveView.Destroy;
 var
 var
-  DriveStatus: TDriveStatus;
+  DriveStatusPair: TDriveStatusPair;
 begin
 begin
+  if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
   Classes.DeallocateHWnd(FInternalWindowHandle);
   Classes.DeallocateHWnd(FInternalWindowHandle);
 
 
-  for DriveStatus in FDriveStatus.Values do
+  for DriveStatusPair in FDriveStatus do
   begin
   begin
-    with DriveStatus do
+    with DriveStatusPair.Value do
     begin
     begin
       if Assigned(DiscMonitor) then
       if Assigned(DiscMonitor) then
-        DiscMonitor.Free;
+        FreeAndNil(DiscMonitor);
       if Assigned(ChangeTimer) then
       if Assigned(ChangeTimer) then
-        ChangeTimer.Free;
+        FreeAndNil(ChangeTimer);
     end;
     end;
+    UpdateDriveNotifications(DriveStatusPair.Key);
   end;
   end;
   FDriveStatus.Free;
   FDriveStatus.Free;
 
 
@@ -541,9 +570,17 @@ begin
     ChangeTimer.Interval := 0;
     ChangeTimer.Interval := 0;
     ChangeTimer.Enabled := False;
     ChangeTimer.Enabled := False;
     ChangeTimer.OnTimer := ChangeTimerOnTimer;
     ChangeTimer.OnTimer := ChangeTimerOnTimer;
+    DriveHandle := INVALID_HANDLE_VALUE;
+    NotificationHandle := nil;
   end;
   end;
 end;
 end;
 
 
+procedure TDriveView.DriveRemoving(Drive: string);
+begin
+  DriveRemoved(Drive);
+  TerminateWatchThread(Drive);
+end;
+
 type
 type
   PDevBroadcastHdr = ^TDevBroadcastHdr;
   PDevBroadcastHdr = ^TDevBroadcastHdr;
   TDevBroadcastHdr = record
   TDevBroadcastHdr = record
@@ -561,37 +598,79 @@ type
     dbcv_flags: WORD;
     dbcv_flags: WORD;
   end;
   end;
 
 
+  PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
+  DEV_BROADCAST_HANDLE = record
+    dbch_size       : DWORD;
+    dbch_devicetype : DWORD;
+    dbch_reserved   : DWORD;
+    dbch_handle     : THandle;
+    dbch_hdevnotify : HDEVNOTIFY  ;
+    dbch_eventguid  : TGUID;
+    dbch_nameoffset : LongInt;
+    dbch_data       : Byte;
+  end;
+
+  PPItemIDList = ^PItemIDList;
+
 const
 const
+  DBT_DEVTYP_HANDLE = $00000006;
   DBT_CONFIGCHANGED = $0018;
   DBT_CONFIGCHANGED = $0018;
   DBT_DEVICEARRIVAL = $8000;
   DBT_DEVICEARRIVAL = $8000;
+  DBT_DEVICEQUERYREMOVE = $8001;
   DBT_DEVICEREMOVEPENDING = $8003;
   DBT_DEVICEREMOVEPENDING = $8003;
   DBT_DEVICEREMOVECOMPLETE = $8004;
   DBT_DEVICEREMOVECOMPLETE = $8004;
   DBT_DEVTYP_VOLUME = $00000002;
   DBT_DEVTYP_VOLUME = $00000002;
 
 
+// WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
+function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
+  var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
+external 'shell32.dll' name 'SHChangeNotification_Lock';
+
 procedure TDriveView.InternalWndProc(var Msg: TMessage);
 procedure TDriveView.InternalWndProc(var Msg: TMessage);
 var
 var
+  DeviceType: DWORD;
   UnitMask: DWORD;
   UnitMask: DWORD;
+  DeviceHandle: THandle;
   Drive: Char;
   Drive: Char;
+  DriveStatusPair: TDriveStatusPair;
+  PPIDL: PPItemIDList;
+  Event: LONG;
+  Lock: THandle;
 begin
 begin
   with Msg do
   with Msg do
   begin
   begin
+    if Msg = WM_USER_SHCHANGENOTIFY then
+    begin
+      Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
+      try
+        if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
+           (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
+           (Event = SHCNE_MEDIAREMOVED) then
+        begin
+          ScheduleDriveRefresh;
+        end;
+      finally
+        SHChangeNotification_Unlock(Lock);
+      end;
+    end
+      else
     if Msg = WM_DEVICECHANGE then
     if Msg = WM_DEVICECHANGE then
     begin
     begin
-       if (wParam = DBT_CONFIGCHANGED) or
-          (wParam = DBT_DEVICEARRIVAL) or
-          (wParam = DBT_DEVICEREMOVECOMPLETE) then
+      if (wParam = DBT_CONFIGCHANGED) or
+         (wParam = DBT_DEVICEARRIVAL) or
+         (wParam = DBT_DEVICEREMOVECOMPLETE) then
       begin
       begin
-        // Delay refreshing drives for a sec.
-        // Particularly with CD/DVD drives, if we query display name
-        // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
-        // Actually one sec does not help usually, but we do not want to wait any longer,
-        // because we want to add USB drives asap.
-        SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
+        ScheduleDriveRefresh;
       end
       end
         else
         else
-      if wParam = DBT_DEVICEREMOVEPENDING then
+      if (wParam = DBT_DEVICEQUERYREMOVE) or
+         (wParam = DBT_DEVICEREMOVEPENDING) then
       begin
       begin
-        if PDevBroadcastHdr(lParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then
+        DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
+        // This is specifically for VeraCrypt.
+        // For normal drives, see DBT_DEVTYP_HANDLE below
+        // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
+        if DeviceType = DBT_DEVTYP_VOLUME then
         begin
         begin
           UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
           UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
           Drive := FirstDrive;
           Drive := FirstDrive;
@@ -599,30 +678,28 @@ begin
           begin
           begin
             if UnitMask and $01 <> 0 then
             if UnitMask and $01 <> 0 then
             begin
             begin
-              // Disable disk monitor to release the handle to the drive.
-              // It may happen that the dirve is not removed in the end. In this case we do not currently resume the
-              // monitoring. We can watch for DBT_DEVICEQUERYREMOVEFAILED to resume the monitoring.
-              // But currently we implement this for VeraCrypt, which does not send this notification.
-              with GetDriveStatus(Drive) do
-              begin
-                if Assigned(DiscMonitor) then
-                begin
-                  DiscMonitor.Enabled := False;
-                  DiscMonitor.Free;
-                  DiscMonitor := nil;
-                end;
-              end;
+              DriveRemoving(Drive);
             end;
             end;
             UnitMask := UnitMask shr 1;
             UnitMask := UnitMask shr 1;
             Drive := Chr(Ord(Drive) + 1);
             Drive := Chr(Ord(Drive) + 1);
           end;
           end;
+        end
+          else
+        if DeviceType = DBT_DEVTYP_HANDLE then
+        begin
+          DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
+          for DriveStatusPair in FDriveStatus do
+            if DriveStatusPair.Value.DriveHandle = DeviceHandle then
+            begin
+              DriveRemoving(DriveStatusPair.Key);
+            end;
         end;
         end;
       end;
       end;
     end
     end
       else
       else
     if Msg = WM_TIMER then
     if Msg = WM_TIMER then
     begin
     begin
-      KillTimer(FInternalWindowHandle, 1);
+      CancelDriveRefresh;
       try
       try
         //DriveInfo.Load;
         //DriveInfo.Load;
         RefreshRootNodes(dsAll or dvdsRereadAllways);
         RefreshRootNodes(dsAll or dvdsRereadAllways);
@@ -637,6 +714,23 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TDriveView.CancelDriveRefresh;
+begin
+  KillTimer(FInternalWindowHandle, 1);
+end;
+
+procedure TDriveView.ScheduleDriveRefresh;
+begin
+  CancelDriveRefresh;
+  // Delay refreshing drives for a sec.
+  // Particularly with CD/DVD drives, if we query display name
+  // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
+  // Actually one sec does not help usually, but we do not want to wait any longer,
+  // because we want to add USB drives asap.
+  // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
+  SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
+end;
+
 procedure TDriveView.CreateWnd;
 procedure TDriveView.CreateWnd;
 var
 var
   DriveStatus: TDriveStatus;
   DriveStatus: TDriveStatus;
@@ -1276,7 +1370,7 @@ end;
 
 
 function TDriveView.GetDrives: TStrings;
 function TDriveView.GetDrives: TStrings;
 var
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
   Drives: TStringList;
   Drives: TStringList;
 begin
 begin
   Drives := TStringList.Create;
   Drives := TStringList.Create;
@@ -1289,6 +1383,41 @@ begin
   Result := Drives;
   Result := Drives;
 end;
 end;
 
 
+procedure TDriveView.DriveRemoved(Drive: string);
+var
+  NewDrive: Char;
+begin
+  if (Directory <> '') and (Directory[1] = Drive) then
+  begin
+    if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
+      else NewDrive := SystemDrive;
+
+    repeat
+      if NewDrive < SystemDrive then NewDrive := SystemDrive
+        else
+      if NewDrive = SystemDrive then NewDrive := LastDrive
+        else Dec(NewDrive);
+      DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
+
+      if NewDrive = Drive then
+      begin
+        Break;
+      end;
+
+      if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
+      begin
+        Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
+        break;
+      end;
+    until False;
+
+    if not Assigned(Selected) then
+    begin
+      Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
+    end;
+  end;
+end;
+
 procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
 procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
 var
 var
   Drives: TStrings;
   Drives: TStrings;
@@ -1296,7 +1425,6 @@ var
   SaveCursor: TCursor;
   SaveCursor: TCursor;
   WasValid: Boolean;
   WasValid: Boolean;
   NodeData: TNodeData;
   NodeData: TNodeData;
-  NewDrive: Char;
   DriveStatus: TDriveStatus;
   DriveStatus: TDriveStatus;
   NextDriveNode: TTreeNode;
   NextDriveNode: TTreeNode;
   Index: Integer;
   Index: Integer;
@@ -1373,35 +1501,7 @@ begin
           if WasValid then
           if WasValid then
           {Drive has been removed => delete rootnode:}
           {Drive has been removed => delete rootnode:}
           begin
           begin
-            if (Directory <> '') and (Directory[1] = Drive) then
-            begin
-              if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
-                else NewDrive := SystemDrive;
-
-              repeat
-                if NewDrive < SystemDrive then NewDrive := SystemDrive
-                  else
-                if NewDrive = SystemDrive then NewDrive := LastDrive
-                  else Dec(NewDrive);
-                DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
-
-                if NewDrive = Drive then
-                begin
-                  Break;
-                end;
-
-                if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
-                begin
-                  Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
-                  break;
-                end;
-              until False;
-
-              if not Assigned(Selected) then
-              begin
-                Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
-              end;
-            end;
+            DriveRemoved(Drive);
             Scanned := False;
             Scanned := False;
             Verified := False;
             Verified := False;
             RootNode.Delete;
             RootNode.Delete;
@@ -1962,6 +2062,7 @@ begin
       DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
       DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
       DiscMonitor.Open;
       DiscMonitor.Open;
     end;
     end;
+    UpdateDriveNotifications(Drive); // probably noop, as the monitor is not enabled yet
   end;
   end;
 end; {CreateWatchThread}
 end; {CreateWatchThread}
 
 
@@ -1999,13 +2100,14 @@ end; {NodeWatched}
 procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
 procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
   const ErrorStr: string);
   const ErrorStr: string);
 var
 var
-  Dir: string;
+  Drive: string;
 begin
 begin
-  Dir := (Sender as TDiscMonitor).Directories[0];
-  with GetDriveStatus(DriveInfo.GetDriveKey(Dir)) do
+  Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
+  with GetDriveStatus(Drive) do
   begin
   begin
     DiscMonitor.Close;
     DiscMonitor.Close;
   end;
   end;
+  UpdateDriveNotifications(Drive);
 end; {DirWatchChangeInvalid}
 end; {DirWatchChangeInvalid}
 
 
 procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
 procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
@@ -2030,7 +2132,7 @@ end; {DirWatchChangeDetected}
 
 
 procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
 procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
 var
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
 begin
 begin
   if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
   if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
   begin
   begin
@@ -2062,6 +2164,60 @@ begin
   end;
   end;
 end; {ChangeTimerOnTimer}
 end; {ChangeTimerOnTimer}
 
 
+procedure TDriveView.UpdateDriveNotifications(Drive: string);
+var
+  NeedNotifications: Boolean;
+  Path: string;
+  DevBroadcastHandle: DEV_BROADCAST_HANDLE;
+  Size: Integer;
+begin
+  if DriveInfo.IsFixedDrive(Drive) then
+  begin
+    with GetDriveStatus(Drive) do
+    begin
+      NeedNotifications :=
+        WatchThreadActive(Drive) and
+        (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
+        DriveInfo.Get(Drive).DriveReady;
+
+      if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
+      begin
+        if NeedNotifications then
+        begin
+          Path := DriveInfo.GetDriveRoot(Drive);
+          DriveHandle :=
+            CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
+            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
+          if DriveHandle <> INVALID_HANDLE_VALUE then
+          begin
+            Size := SizeOf(DevBroadcastHandle);
+            ZeroMemory(@DevBroadcastHandle, Size);
+            DevBroadcastHandle.dbch_size := Size;
+            DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
+            DevBroadcastHandle.dbch_handle := DriveHandle;
+
+            NotificationHandle :=
+              RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
+            if NotificationHandle = nil then
+            begin
+              CloseHandle(DriveHandle);
+              DriveHandle := INVALID_HANDLE_VALUE;
+            end;
+          end;
+        end
+          else
+        begin
+          UnregisterDeviceNotification(NotificationHandle);
+          NotificationHandle := nil;
+
+          CloseHandle(DriveHandle);
+          DriveHandle := INVALID_HANDLE_VALUE;
+        end;
+      end;
+    end;
+  end;
+end;
+
 procedure TDriveView.StartWatchThread;
 procedure TDriveView.StartWatchThread;
 var
 var
   Drive: string;
   Drive: string;
@@ -2079,14 +2235,22 @@ begin
     if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
     if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
       DiscMonitor.Enabled := True;
       DiscMonitor.Enabled := True;
   end;
   end;
+  UpdateDriveNotifications(Drive);
 end; {StartWatchThread}
 end; {StartWatchThread}
 
 
 procedure TDriveView.StopWatchThread;
 procedure TDriveView.StopWatchThread;
+var
+  Drive: string;
 begin
 begin
   if Assigned(Selected) then
   if Assigned(Selected) then
-    with GetDriveStatus(GetDriveToNode(Selected)) do
+  begin
+    Drive := GetDriveToNode(Selected);
+    with GetDriveStatus(Drive) do
       if Assigned(DiscMonitor) then
       if Assigned(DiscMonitor) then
         DiscMonitor.Enabled := False;
         DiscMonitor.Enabled := False;
+
+    UpdateDriveNotifications(Drive);
+  end;
 end; {StopWatchThread}
 end; {StopWatchThread}
 
 
 procedure TDriveView.SuspendChangeTimer;
 procedure TDriveView.SuspendChangeTimer;
@@ -2108,11 +2272,13 @@ begin
       DiscMonitor.Free;
       DiscMonitor.Free;
       DiscMonitor := nil;
       DiscMonitor := nil;
     end;
     end;
+
+  UpdateDriveNotifications(Drive);
 end; {StopWatchThread}
 end; {StopWatchThread}
 
 
 procedure TDriveView.StartAllWatchThreads;
 procedure TDriveView.StartAllWatchThreads;
 var
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
   Drive: string;
   Drive: string;
 begin
 begin
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
@@ -2125,7 +2291,10 @@ begin
         if not Assigned(DiscMonitor) then
         if not Assigned(DiscMonitor) then
           CreateWatchThread(DriveStatusPair.Key);
           CreateWatchThread(DriveStatusPair.Key);
         if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
         if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
+        begin
           DiscMonitor.Open;
           DiscMonitor.Open;
+          UpdateDriveNotifications(Drive);
+        end;
       end;
       end;
 
 
   if Assigned(Selected) then
   if Assigned(Selected) then
@@ -2140,7 +2309,7 @@ end; {StartAllWatchThreads}
 
 
 procedure TDriveView.StopAllWatchThreads;
 procedure TDriveView.StopAllWatchThreads;
 var
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
 begin
 begin
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
      Exit;
      Exit;
@@ -2149,7 +2318,10 @@ begin
     with DriveStatusPair.Value do
     with DriveStatusPair.Value do
     begin
     begin
       if Assigned(DiscMonitor) then
       if Assigned(DiscMonitor) then
+      begin
         DiscMonitor.Close;
         DiscMonitor.Close;
+        UpdateDriveNotifications(DriveStatusPair.Key);
+      end;
     end;
     end;
 end; {StopAllWatchThreads}
 end; {StopAllWatchThreads}
 
 

+ 2 - 2
source/packages/filemng/IEDriveInfo.pas

@@ -552,7 +552,7 @@ begin
       end;
       end;
 
 
       {DisplayName:}
       {DisplayName:}
-      if (Flags and dsDisplayName <> 0) then
+      if (Flags and dsDisplayName) <> 0 then
       begin
       begin
         {Fetch drives displayname:}
         {Fetch drives displayname:}
         SimpleName := GetSimpleName(Drive);
         SimpleName := GetSimpleName(Drive);
@@ -589,7 +589,7 @@ begin
       end;
       end;
 
 
       {ImageIndex:}
       {ImageIndex:}
-      if ((Flags and dsImageIndex) <> 0) and (ImageIndex < 5) then
+      if (Flags and dsImageIndex) <> 0 then
       begin
       begin
         if Assigned(PIDL) then
         if Assigned(PIDL) then
         begin
         begin

+ 118 - 0
source/putty/be_list.c

@@ -0,0 +1,118 @@
+/*
+ * Source file that is rebuilt per application, and provides the list
+ * of backends, the default protocol, and the application name.
+ *
+ * This file expects the build system to provide some per-application
+ * definitions on the compiler command line. So you don't just add it
+ * directly to the sources list for an application. Instead you call
+ * the be_list() function defined in setup.cmake, e.g.
+ *
+ *    be_list(target-name AppName [SSH] [SERIAL] [OTHERBACKENDS])
+ *
+ * This translates into the following command-line macro definitions
+ * used by the code below:
+ *
+ *  - APPNAME should be defined to the name of the program, in
+ *    user-facing capitalisation (e.g. PuTTY rather than putty).
+ *    Unquoted: it's easier to stringify it in the preprocessor than
+ *    to persuade cmake to put the right quotes on the command line on
+ *    all build platforms.
+ *
+ *  - The following macros should each be defined to 1 if a given set
+ *    of backends should be added to the backends[] list, or 0 if they
+ *    should not be:
+ *
+ *     * SSH: the two SSH backends (SSH proper, and bare-ssh-connection)
+ *
+ *     * SERIAL: the serial port backend
+ *
+ *     * OTHERBACKENDS: the non-cryptographic network protocol backends
+ *       (Telnet, Rlogin, SUPDUP, Raw)
+ */
+
+#include <stdio.h>
+#include "putty.h"
+
+const char *const appname = STR(APPNAME);
+
+/*
+ * Define the default protocol for the application. This is always a
+ * network backend (serial ports come second behind network, in every
+ * case). Applications that don't have either (such as pterm) don't
+ * need this variable anyway, so just set it to -1.
+ */
+#if SSH
+const int be_default_protocol = PROT_SSH;
+#elif OTHERBACKENDS
+const int be_default_protocol = PROT_TELNET;
+#else
+const int be_default_protocol = -1;
+#endif
+
+/*
+ * List all the configured backends, in the order they should appear
+ * in the config box.
+ */
+const struct BackendVtable *const backends[] = {
+    /*
+     * Start with the most-preferred network-remote-login protocol.
+     * That's SSH if present, otherwise Telnet if present.
+     */
+#if SSH
+    &ssh_backend,
+#elif OTHERBACKENDS
+    &telnet_backend,         /* Telnet at the top if SSH is absent  */
+#endif
+
+    /*
+     * Second on the list is the serial-port backend, if available.
+     */
+#if SERIAL
+    &serial_backend,
+#endif
+
+    /*
+     * After that come the remaining network protocols: Telnet if it
+     * hasn't already appeared above, and Rlogin, SUPDUP and Raw.
+     */
+#if OTHERBACKENDS && SSH
+    &telnet_backend,         /* only if SSH displaced it at the top  */
+#endif
+#if OTHERBACKENDS
+    &rlogin_backend,
+    &supdup_backend,
+    &raw_backend,
+#endif
+
+    /*
+     * Bare ssh-connection / PSUSAN is a niche protocol and goes well
+     * down the list.
+     */
+#if SSH
+    &sshconn_backend,
+#endif
+
+    /*
+     * Done. Null pointer to mark the end of the list.
+     */
+    NULL
+};
+
+/*
+ * Number of backends at the start of the above list that should have
+ * radio buttons in the config UI.
+ *
+ * The rule is: the most-preferred network backend, and Serial, each
+ * get a radio button if present.
+ *
+ * The rest will be relegated to a dropdown list.
+ */
+const size_t n_ui_backends =
+    0
+#if SSH || OTHERBACKENDS
+    + 1
+#endif
+#if SERIAL
+    + 1
+#endif
+    ;

+ 0 - 18
source/putty/be_ssh.c

@@ -1,18 +0,0 @@
-/*
- * Linking module for programs that are restricted to only using
- * SSH-type protocols (pscp and psftp). These still have a choice of
- * two actual backends, because they can also speak PROT_SSHCONN.
- */
-
-#include <stdio.h>
-#include "putty.h"
-
-const int be_default_protocol = PROT_SSH;
-
-const struct BackendVtable *const backends[] = {
-    &ssh_backend,
-    &sshconn_backend,
-    NULL
-};
-
-const size_t n_ui_backends = 0;  /* not used in programs with a config UI */

+ 0 - 179
source/putty/cproxy.c

@@ -1,179 +0,0 @@
-/*
- * Routines to do cryptographic interaction with proxies in PuTTY.
- * This is in a separate module from proxy.c, so that it can be
- * conveniently removed in PuTTYtel by replacing this module with
- * the stub version nocproxy.c.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "putty.h"
-#include "ssh.h" /* For MD5 support */
-#include "network.h"
-#include "proxy.h"
-#include "marshal.h"
-
-static void hmacmd5_chap(const unsigned char *challenge, int challen,
-                         const char *passwd, unsigned char *response)
-{
-    mac_simple(&ssh_hmac_md5, ptrlen_from_asciz(passwd),
-               make_ptrlen(challenge, challen), response);
-}
-
-void proxy_socks5_offerencryptedauth(BinarySink *bs)
-{
-    put_byte(bs, 0x03);              /* CHAP */
-}
-
-int proxy_socks5_handlechap (ProxySocket *p)
-{
-
-    /* CHAP authentication reply format:
-     *  version number (1 bytes) = 1
-     *  number of commands (1 byte)
-     *
-     * For each command:
-     *  command identifier (1 byte)
-     *  data length (1 byte)
-     */
-    unsigned char data[260];
-    unsigned char outbuf[20];
-
-    while(p->chap_num_attributes == 0 ||
-          p->chap_num_attributes_processed < p->chap_num_attributes) {
-        if (p->chap_num_attributes == 0 ||
-            p->chap_current_attribute == -1) {
-            /* CHAP normally reads in two bytes, either at the
-             * beginning or for each attribute/value pair.  But if
-             * we're waiting for the value's data, we might not want
-             * to read 2 bytes.
-             */
-
-            if (bufchain_size(&p->pending_input_data) < 2)
-                return 1;              /* not got anything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data, 2);
-            bufchain_consume(&p->pending_input_data, 2);
-        }
-
-        if (p->chap_num_attributes == 0) {
-            /* If there are no attributes, this is our first msg
-             * with the server, where we negotiate version and
-             * number of attributes
-             */
-            if (data[0] != 0x01) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy wants"
-                             " a different CHAP version",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            if (data[1] == 0x00) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy won't"
-                             " negotiate CHAP with us",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            p->chap_num_attributes = data[1];
-        } else {
-            if (p->chap_current_attribute == -1) {
-                /* We have to read in each attribute/value pair -
-                 * those we don't understand can be ignored, but
-                 * there are a few we'll need to handle.
-                 */
-                p->chap_current_attribute = data[0];
-                p->chap_current_datalen = data[1];
-            }
-            if (bufchain_size(&p->pending_input_data) <
-                p->chap_current_datalen)
-                return 1;              /* not got everything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data,
-                           p->chap_current_datalen);
-
-            bufchain_consume(&p->pending_input_data,
-                             p->chap_current_datalen);
-
-            switch (p->chap_current_attribute) {
-              case 0x00:
-                /* Successful authentication */
-                if (data[0] == 0x00)
-                    p->state = 2;
-                else {
-                    plug_closing(p->plug, "Proxy error: SOCKS proxy"
-                                 " refused CHAP authentication",
-                                 PROXY_ERROR_GENERAL, 0);
-                    return 1;
-                }
-              break;
-              case 0x03:
-                outbuf[0] = 0x01; /* Version */
-                outbuf[1] = 0x01; /* One attribute */
-                outbuf[2] = 0x04; /* Response */
-                outbuf[3] = 0x10; /* Length */
-                hmacmd5_chap(data, p->chap_current_datalen,
-                             conf_get_str(p->conf, CONF_proxy_password),
-                             &outbuf[4]);
-                sk_write(p->sub_socket, outbuf, 20);
-              break;
-              case 0x11:
-                /* Chose a protocol */
-                if (data[0] != 0x85) {
-                    plug_closing(p->plug, "Proxy error: Server chose "
-                                 "CHAP of other than HMAC-MD5 but we "
-                                 "didn't offer it!",
-                                 PROXY_ERROR_GENERAL, 0);
-                    return 1;
-                }
-              break;
-            }
-            p->chap_current_attribute = -1;
-            p->chap_num_attributes_processed++;
-        }
-        if (p->state == 8 &&
-            p->chap_num_attributes_processed >= p->chap_num_attributes) {
-            p->chap_num_attributes = 0;
-            p->chap_num_attributes_processed = 0;
-            p->chap_current_datalen = 0;
-        }
-    }
-    return 0;
-}
-
-int proxy_socks5_selectchap(ProxySocket *p)
-{
-    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 */
-        chapbuf[1] = '\x02'; /* Number of attributes sent */
-        chapbuf[2] = '\x11'; /* First attribute - algorithms list */
-        chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
-        chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
-        chapbuf[5] = '\x02'; /* Second attribute - username */
-
-        ulen = strlen(username);
-        if (ulen > 255) ulen = 255;
-        if (ulen < 1) ulen = 1;
-
-        chapbuf[6] = ulen;
-        memcpy(chapbuf+7, username, ulen);
-
-        sk_write(p->sub_socket, chapbuf, ulen + 7);
-        p->chap_num_attributes = 0;
-        p->chap_num_attributes_processed = 0;
-        p->chap_current_attribute = -1;
-        p->chap_current_datalen = 0;
-
-        p->state = 8;
-    } else
-        plug_closing(p->plug, "Proxy error: Server chose "
-                     "CHAP authentication but we didn't offer it!",
-                 PROXY_ERROR_GENERAL, 0);
-    return 1;
-}

+ 14 - 0
source/putty/crypto/aes-common.c

@@ -0,0 +1,14 @@
+/*
+ * Common variable definitions across all the AES implementations.
+ */
+
+#include "ssh.h"
+#include "aes.h"
+
+const uint8_t aes_key_setup_round_constants[10] = {
+    /* The first few powers of X in GF(2^8), used during key setup.
+     * This can safely be a lookup table without side channel risks,
+     * because key setup iterates through it once in a standard way
+     * regardless of the key. */
+    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
+};

+ 316 - 0
source/putty/crypto/aes-ni.c

@@ -0,0 +1,316 @@
+/*
+ * Hardware-accelerated implementation of AES using x86 AES-NI.
+ */
+
+#include "ssh.h"
+#include "aes.h"
+
+#ifndef WINSCP_VS
+bool aes_ni_available(void);
+ssh_cipher *aes_ni_new(const ssh_cipheralg *alg);
+void aes_ni_free(ssh_cipher *ciph);
+void aes_ni_setiv_cbc(ssh_cipher *ciph, const void *iv);
+void aes_ni_setkey(ssh_cipher *ciph, const void *vkey);
+void aes_ni_setiv_sdctr(ssh_cipher *ciph, const void *iv);
+#define NI_ENC_DEC_H(len)                                               \
+    void aes##len##_ni_cbc_encrypt(                                     \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+    void aes##len##_ni_cbc_decrypt(                                     \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+    void aes##len##_ni_sdctr(                                           \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+
+NI_ENC_DEC_H(128)
+NI_ENC_DEC_H(192)
+NI_ENC_DEC_H(256)
+#else
+
+#include <wmmintrin.h>
+#include <smmintrin.h>
+
+#if defined(__clang__) || defined(__GNUC__)
+#include <cpuid.h>
+#define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3])
+#else
+#define GET_CPU_ID(out) __cpuid(out, 1)
+#endif
+
+/*static WINSCP*/ bool aes_ni_available(void)
+{
+    /*
+     * Determine if AES is available on this CPU, by checking that
+     * both AES itself and SSE4.1 are supported.
+     */
+    unsigned int CPUInfo[4];
+    GET_CPU_ID(CPUInfo);
+    return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19));
+}
+
+/*
+ * Core AES-NI encrypt/decrypt functions, one per length and direction.
+ */
+
+#define NI_CIPHER(len, dir, dirlong, repmacro)                          \
+    static inline __m128i aes_ni_##len##_##dir(                         \
+        __m128i v, const __m128i *keysched)                             \
+    {                                                                   \
+        v = _mm_xor_si128(v, *keysched++);                              \
+        repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++););        \
+        return _mm_aes##dirlong##last_si128(v, *keysched);              \
+    }
+
+NI_CIPHER(128, e, enc, REP9)
+NI_CIPHER(128, d, dec, REP9)
+NI_CIPHER(192, e, enc, REP11)
+NI_CIPHER(192, d, dec, REP11)
+NI_CIPHER(256, e, enc, REP13)
+NI_CIPHER(256, d, dec, REP13)
+
+/*
+ * The main key expansion.
+ */
+static void aes_ni_key_expand(
+    const unsigned char *key, size_t key_words,
+    __m128i *keysched_e, __m128i *keysched_d)
+{
+    size_t rounds = key_words + 6;
+    size_t sched_words = (rounds + 1) * 4;
+
+    /*
+     * Store the key schedule as 32-bit integers during expansion, so
+     * that it's easy to refer back to individual previous words. We
+     * collect them into the final __m128i form at the end.
+     */
+    uint32_t sched[MAXROUNDKEYS * 4];
+
+    unsigned rconpos = 0;
+
+    for (size_t i = 0; i < sched_words; i++) {
+        if (i < key_words) {
+            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
+        } else {
+            uint32_t temp = sched[i - 1];
+
+            bool rotate_and_round_constant = (i % key_words == 0);
+            bool only_sub = (key_words == 8 && i % 8 == 4);
+
+            if (rotate_and_round_constant) {
+                __m128i v = _mm_setr_epi32(0,temp,0,0);
+                v = _mm_aeskeygenassist_si128(v, 0);
+                temp = _mm_extract_epi32(v, 1);
+
+                assert(rconpos < lenof(aes_key_setup_round_constants));
+                temp ^= aes_key_setup_round_constants[rconpos++];
+            } else if (only_sub) {
+                __m128i v = _mm_setr_epi32(0,temp,0,0);
+                v = _mm_aeskeygenassist_si128(v, 0);
+                temp = _mm_extract_epi32(v, 0);
+            }
+
+            sched[i] = sched[i - key_words] ^ temp;
+        }
+    }
+
+    /*
+     * Combine the key schedule words into __m128i vectors and store
+     * them in the output context.
+     */
+    for (size_t round = 0; round <= rounds; round++)
+        keysched_e[round] = _mm_setr_epi32(
+            sched[4*round  ], sched[4*round+1],
+            sched[4*round+2], sched[4*round+3]);
+
+    smemclr(sched, sizeof(sched));
+
+    /*
+     * Now prepare the modified keys for the inverse cipher.
+     */
+    for (size_t eround = 0; eround <= rounds; eround++) {
+        size_t dround = rounds - eround;
+        __m128i rkey = keysched_e[eround];
+        if (eround && dround)      /* neither first nor last */
+            rkey = _mm_aesimc_si128(rkey);
+        keysched_d[dround] = rkey;
+    }
+}
+
+// WINSCP
+// WORKAROUND
+// Cannot use _mm_setr_epi* - it results in the constant being stored in .rdata segment.
+// objconv reports:
+// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
+// Despite that the code crashes.
+// This macro is based on:
+// Based on https://stackoverflow.com/q/35268036/850848
+#define _MM_SETR_EPI8(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
+    { (char)a0, (char)a1, (char)a2, (char)a3, (char)a4, (char)a5, (char)a6, (char)a7, \
+      (char)a8, (char)a9, (char)aa, (char)ab, (char)ac, (char)ad, (char)ae, (char)af }
+
+/*
+ * Auxiliary routine to increment the 128-bit counter used in SDCTR
+ * mode.
+ */
+static inline __m128i aes_ni_sdctr_increment(__m128i v)
+{
+    const __m128i ONE = _MM_SETR_EPI8(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); // WINSCP
+    const __m128i ZERO = _mm_setzero_si128();
+
+    /* Increment the low-order 64 bits of v */
+    v  = _mm_add_epi64(v, ONE);
+    /* Check if they've become zero */
+    __m128i cmp = _mm_cmpeq_epi64(v, ZERO);
+    /* If so, the low half of cmp is all 1s. Pack that into the high
+     * half of addend with zero in the low half. */
+    __m128i addend = _mm_unpacklo_epi64(ZERO, cmp);
+    /* And subtract that from v, which increments the high 64 bits iff
+     * the low 64 wrapped round. */
+    v = _mm_sub_epi64(v, addend);
+
+    return v;
+}
+
+/*
+ * Auxiliary routine to reverse the byte order of a vector, so that
+ * the SDCTR IV can be made big-endian for feeding to the cipher.
+ */
+static inline __m128i aes_ni_sdctr_reverse(__m128i v)
+{
+    const __m128i R = _MM_SETR_EPI8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); // WINSCP
+    v = _mm_shuffle_epi8(
+        v, R); // WINSCP
+    return v;
+}
+
+/*
+ * The SSH interface and the cipher modes.
+ */
+
+typedef struct aes_ni_context aes_ni_context;
+struct aes_ni_context {
+    __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
+
+    void *pointer_to_free;
+    ssh_cipher ciph;
+};
+
+/*static WINSCP*/ ssh_cipher *aes_ni_new(const ssh_cipheralg *alg)
+{
+    const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
+    if (!check_availability(extra))
+        return NULL;
+
+    /*
+     * The __m128i variables in the context structure need to be
+     * 16-byte aligned, but not all malloc implementations that this
+     * code has to work with will guarantee to return a 16-byte
+     * aligned pointer. So we over-allocate, manually realign the
+     * pointer ourselves, and store the original one inside the
+     * context so we know how to free it later.
+     */
+    void *allocation = smalloc(sizeof(aes_ni_context) + 15);
+    uintptr_t alloc_address = (uintptr_t)allocation;
+    uintptr_t aligned_address = (alloc_address + 15) & ~15;
+    aes_ni_context *ctx = (aes_ni_context *)aligned_address;
+
+    ctx->ciph.vt = alg;
+    ctx->pointer_to_free = allocation;
+    return &ctx->ciph;
+}
+
+/*static WINSCP*/ void aes_ni_free(ssh_cipher *ciph)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    void *allocation = ctx->pointer_to_free;
+    smemclr(ctx, sizeof(*ctx));
+    sfree(allocation);
+}
+
+/*static WINSCP*/ void aes_ni_setkey(ssh_cipher *ciph, const void *vkey)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    const unsigned char *key = (const unsigned char *)vkey;
+
+    aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32,
+                      ctx->keysched_e, ctx->keysched_d);
+}
+
+/*static WINSCP*/ void aes_ni_setiv_cbc(ssh_cipher *ciph, const void *iv)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    ctx->iv = _mm_loadu_si128(iv);
+}
+
+/*static WINSCP*/ void aes_ni_setiv_sdctr(ssh_cipher *ciph, const void *iv)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    __m128i counter = _mm_loadu_si128(iv);
+    ctx->iv = aes_ni_sdctr_reverse(counter);
+}
+
+typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
+
+static inline void aes_cbc_ni_encrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i plaintext = _mm_loadu_si128((const __m128i *)blk);
+        __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv);
+        __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e);
+        _mm_storeu_si128((__m128i *)blk, ciphertext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static inline void aes_cbc_ni_decrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk);
+        __m128i decrypted = decrypt(ciphertext, ctx->keysched_d);
+        __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv);
+        _mm_storeu_si128((__m128i *)blk, plaintext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static inline void aes_sdctr_ni(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i counter = aes_ni_sdctr_reverse(ctx->iv);
+        __m128i keystream = encrypt(counter, ctx->keysched_e);
+        __m128i input = _mm_loadu_si128((const __m128i *)blk);
+        __m128i output = _mm_xor_si128(input, keystream);
+        _mm_storeu_si128((__m128i *)blk, output);
+        ctx->iv = aes_ni_sdctr_increment(ctx->iv);
+    }
+}
+
+#define NI_ENC_DEC(len)                                                 \
+    /*static WINSCP*/ void aes##len##_ni_cbc_encrypt(                              \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
+    /*static WINSCP*/ void aes##len##_ni_cbc_decrypt(                              \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
+    /*static WINSCP*/ void aes##len##_ni_sdctr(                                    \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
+
+NI_ENC_DEC(128)
+NI_ENC_DEC(192)
+NI_ENC_DEC(256)
+
+#endif // WINSCP_VS
+
+AES_EXTRA(_ni);
+AES_ALL_VTABLES(_ni, "AES-NI accelerated");

+ 99 - 0
source/putty/crypto/aes-select.c

@@ -0,0 +1,99 @@
+/*
+ * Top-level vtables to select an AES implementation.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "aes.h"
+
+static ssh_cipher *aes_select(const ssh_cipheralg *alg)
+{
+    const ssh_cipheralg *const *real_algs = (const ssh_cipheralg **)alg->extra;
+
+    { // WINSCP
+    size_t i;
+    for (i = 0; real_algs[i]; i++) {
+        const ssh_cipheralg *alg = real_algs[i];
+        const struct aes_extra *alg_extra =
+            (const struct aes_extra *)alg->extra;
+        if (check_availability(alg_extra))
+            return ssh_cipher_new(alg);
+    }
+
+    /* We should never reach the NULL at the end of the list, because
+     * the last non-NULL entry should be software-only AES, which is
+     * always available. */
+    unreachable("aes_select ran off the end of its list");
+    } // WINSCP
+}
+
+#if HAVE_AES_NI
+#define IF_NI(...) __VA_ARGS__
+#else
+#define IF_NI(...)
+#endif
+
+#if HAVE_NEON_CRYPTO
+#define IF_NEON(...) __VA_ARGS__
+#else
+#define IF_NEON(...)
+#endif
+
+#define AES_SELECTOR_VTABLE(mode_c, mode_protocol, mode_display, bits)  \
+    static const ssh_cipheralg *                                        \
+    ssh_aes ## bits ## _ ## mode_c ## _impls[] = {                      \
+        IF_NI(&ssh_aes ## bits ## _ ## mode_c ## _ni,)                  \
+        IF_NEON(&ssh_aes ## bits ## _ ## mode_c ## _neon,)              \
+        &ssh_aes ## bits ## _ ## mode_c ## _sw,                         \
+        NULL,                                                           \
+    };                                                                  \
+    const ssh_cipheralg ssh_aes ## bits ## _ ## mode_c = {              \
+        /* WINSCP */ \
+        /*.new =*/ aes_select,                                              \
+        NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+        /*.ssh2_id =*/ "aes" #bits "-" mode_protocol,                       \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        0, \
+        /*.text_name =*/ "AES-" #bits " " mode_display                      \
+        " (dummy selector vtable)",                                     \
+        NULL, \
+        /*.extra =*/ ssh_aes ## bits ## _ ## mode_c ## _impls,              \
+    }
+
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 128);
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 192);
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 256);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 128);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 192);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 256);
+
+static const ssh_cipheralg ssh_rijndael_lysator = {
+    /* Same as aes256_cbc, but with a different protocol ID */
+    /*.new =*/ aes_select,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    /*.ssh2_id =*/ "[email protected]",
+    /*.blksize =*/ 16,
+    /*.real_keybits =*/ 256,
+    /*.padded_keybytes =*/ 256/8,
+    0,
+    /*.text_name =*/ "AES-256 CBC (dummy selector vtable)",
+    NULL,
+    /*.extra =*/ ssh_aes256_cbc_impls,
+};
+
+static const ssh_cipheralg *const aes_list[] = {
+    &ssh_aes256_sdctr,
+    &ssh_aes256_cbc,
+    &ssh_rijndael_lysator,
+    &ssh_aes192_sdctr,
+    &ssh_aes192_cbc,
+    &ssh_aes128_sdctr,
+    &ssh_aes128_cbc,
+};
+
+const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list };

+ 29 - 927
source/putty/sshaes.c → source/putty/crypto/aes-sw.c

@@ -1,265 +1,4 @@
 /*
 /*
- * sshaes.c - implementation of AES
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "ssh.h"
-#include "mpint_i.h"               /* we reuse the BignumInt system */
-
-/*
- * Start by deciding whether we can support hardware AES at all.
- */
-#define HW_AES_NONE 0
-#define HW_AES_NI 1
-#define HW_AES_NEON 2
-
-#ifdef _FORCE_AES_NI
-#   define HW_AES HW_AES_NI
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<wmmintrin.h>) &&       \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#   endif
-#elif defined(__GNUC__)
-#    if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#    endif
-#elif defined (_MSC_VER)
-#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
-#      define HW_AES HW_AES_NI
-#   endif
-#endif
-
-#define HW_AES HW_AES_NI // WINSCP
-
-#ifdef _FORCE_AES_NEON
-#   define HW_AES HW_AES_NEON
-#elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-    /* Arm can potentially support both endiannesses, but this code
-     * hasn't been tested on anything but little. If anyone wants to
-     * run big-endian, they'll need to fix it first. */
-#elif defined __ARM_FEATURE_CRYPTO
-    /* If the Arm crypto extension is available already, we can
-     * support NEON AES without having to enable anything by hand */
-#   define HW_AES HW_AES_NEON
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<arm_neon.h>) &&       \
-    (defined(__aarch64__))
-        /* clang can enable the crypto extension in AArch64 using
-         * __attribute__((target)) */
-#       define HW_AES HW_AES_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#elif defined _MSC_VER
-#   if defined _M_ARM64
-#       define HW_AES HW_AES_NEON
-        /* 64-bit Visual Studio uses the header <arm64_neon.h> in place
-         * of the standard <arm_neon.h> */
-#       define USE_ARM64_NEON_H
-#   elif defined _M_ARM
-#       define HW_AES HW_AES_NEON
-        /* 32-bit Visual Studio uses the right header name, but requires
-         * this #define to enable a set of intrinsic definitions that
-         * do not omit one of the parameters for vaes[ed]q_u8 */
-#       define _ARM_USE_NEW_NEON_INTRINSICS
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_AES || !defined HW_AES
-#   undef HW_AES
-#   define HW_AES HW_AES_NONE
-#endif
-
-#if HW_AES == HW_AES_NI
-#define HW_NAME_SUFFIX " (AES-NI accelerated)"
-#elif HW_AES == HW_AES_NEON
-#define HW_NAME_SUFFIX " (NEON accelerated)"
-#else
-#define HW_NAME_SUFFIX " (!NONEXISTENT ACCELERATED VERSION!)"
-#endif
-
-/*
- * Vtable collection for AES. For each SSH-level cipher id (i.e.
- * combination of key length and cipher mode), we provide three
- * vtables: one for the pure software implementation, one using
- * hardware acceleration (if available), and a top-level one which is
- * never actually instantiated, and only contains a new() method whose
- * job is to decide which of the other two to return an actual
- * instance of.
- */
-
-static ssh_cipher *aes_select(const ssh_cipheralg *alg);
-static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg);
-static void aes_sw_free(ssh_cipher *);
-static void aes_sw_setiv_cbc(ssh_cipher *, const void *iv);
-static void aes_sw_setiv_sdctr(ssh_cipher *, const void *iv);
-static void aes_sw_setkey(ssh_cipher *, const void *key);
-/*WINSCP static*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg);
-/*WINSCP static*/ void aes_hw_free(ssh_cipher *);
-/*WINSCP static*/ void aes_hw_setiv_cbc(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setkey(ssh_cipher *, const void *key);
-
-#ifndef WINSCP_VS
-struct aes_extra {
-    const ssh_cipheralg *sw, *hw;
-};
-
-#define VTABLES_INNER(cid, pid, bits, name, encsuffix,                  \
-                      decsuffix, setivsuffix, flagsval)                 \
-    /*WINSCP static*/ void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_sw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_sw_new,                                              \
-        /*.free =*/ aes_sw_free,                                            \
-        /*.setiv =*/ aes_sw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_sw_setkey,                                        \
-        /*.encrypt =*/ cid##_sw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_sw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (unaccelerated)",                           \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    /*WINSCP static*/ void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_hw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_hw_new,                                              \
-        /*.free =*/ aes_hw_free,                                            \
-        /*.setiv =*/ aes_hw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_hw_setkey,                                        \
-        /*.encrypt =*/ cid##_hw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_hw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name HW_NAME_SUFFIX,                               \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    static const struct aes_extra extra_##cid = {                       \
-        &ssh_##cid##_sw, &ssh_##cid##_hw };                             \
-                                                                        \
-    const ssh_cipheralg ssh_##cid = {                                   \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_select,                                              \
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL,                           \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (dummy selector vtable)",                   \
-        NULL,                                                               \
-        /*.extra =*/ &extra_##cid                                           \
-    };                                                                  \
-
-#define VTABLES(keylen)                                                 \
-    VTABLES_INNER(aes ## keylen ## _cbc, "aes" #keylen "-cbc",          \
-                  keylen, "AES-" #keylen " CBC", _encrypt, _decrypt,    \
-                  setiv_cbc, SSH_CIPHER_IS_CBC)                         \
-    VTABLES_INNER(aes ## keylen ## _sdctr, "aes" #keylen "-ctr",        \
-                  keylen, "AES-" #keylen " SDCTR",,, setiv_sdctr, 0)
-
-VTABLES(128)
-VTABLES(192)
-VTABLES(256)
-
-static const ssh_cipheralg ssh_rijndael_lysator = {
-    /* Same as aes256_cbc, but with a different protocol ID */
-    // WINSCP
-    /*.new =*/ aes_select,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "[email protected]",
-    /*.blksize =*/ 16,
-    /*.real_keybits =*/ 256,
-    /*.padded_keybytes =*/ 256/8,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "AES-256 CBC (dummy selector vtable)",
-    NULL, // WINSCP
-    /*.extra =*/ &extra_aes256_cbc,
-};
-
-static const ssh_cipheralg *const aes_list[] = {
-    &ssh_aes256_sdctr,
-    &ssh_aes256_cbc,
-    &ssh_rijndael_lysator,
-    &ssh_aes192_sdctr,
-    &ssh_aes192_cbc,
-    &ssh_aes128_sdctr,
-    &ssh_aes128_cbc,
-};
-
-const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list };
-#endif
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-/*WINSCP static*/ bool aes_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * aes_hw_available() so it only has to run once.
- */
-static bool aes_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = aes_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-#ifndef WINSCP_VS
-static ssh_cipher *aes_select(const ssh_cipheralg *alg)
-{
-    const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
-    const ssh_cipheralg *real_alg =
-        aes_hw_available_cached() ? extra->hw : extra->sw;
-
-    return ssh_cipher_new(real_alg);
-}
-#endif
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-#define REP2(x) x x
-#define REP4(x) REP2(REP2(x))
-#define REP8(x) REP2(REP4(x))
-#define REP9(x) REP8(x) x
-#define REP11(x) REP8(x) REP2(x) x
-#define REP13(x) REP8(x) REP4(x) x
-
-static const uint8_t key_setup_round_constants[] = {
-    /* The first few powers of X in GF(2^8), used during key setup.
-     * This can safely be a lookup table without side channel risks,
-     * because key setup iterates through it once in a standard way
-     * regardless of the key. */
-    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
-};
-
-#define MAXROUNDKEYS 15
-
-/* ----------------------------------------------------------------------
  * Software implementation of AES.
  * Software implementation of AES.
  *
  *
  * This implementation uses a bit-sliced representation. Instead of
  * This implementation uses a bit-sliced representation. Instead of
@@ -275,6 +14,16 @@ static const uint8_t key_setup_round_constants[] = {
  * ops you get 64 S-box lookups, not just one.
  * ops you get 64 S-box lookups, not just one.
  */
  */
 
 
+#include "ssh.h"
+#include "aes.h"
+#include "mpint_i.h"               /* we reuse the BignumInt system */
+
+static bool aes_sw_available(void)
+{
+    /* Software AES is always available */
+    return true;
+}
+
 #define SLICE_PARALLELISM (BIGNUM_INT_BYTES / 2)
 #define SLICE_PARALLELISM (BIGNUM_INT_BYTES / 2)
 
 
 #ifdef WINSCP_VS
 #ifdef WINSCP_VS
@@ -947,8 +696,8 @@ struct aes_sliced_key {
             }
             }
 
 
             if (rotate_and_round_constant) {
             if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                uint8_t rcon = key_setup_round_constants[rconpos++];
+                assert(rconpos < lenof(aes_key_setup_round_constants));
+                uint8_t rcon = aes_key_setup_round_constants[rconpos++];
                 for (size_t i = 0; i < 8; i++)
                 for (size_t i = 0; i < 8; i++)
                     slices[i] ^= 1 & (rcon >> i);
                     slices[i] ^= 1 & (rcon >> i);
             }
             }
@@ -1291,684 +1040,37 @@ static inline void aes_sdctr_sw(
 }
 }
 
 
 #define SW_ENC_DEC(len)                                 \
 #define SW_ENC_DEC(len)                                 \
-    /*WINSCP static*/ void aes##len##_cbc_sw_encrypt(              \
+    /*WINSCP static*/ void aes##len##_sw_cbc_encrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
     { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_cbc_sw_decrypt(              \
+    /*WINSCP static*/ void aes##len##_sw_cbc_decrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
     { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_sdctr_sw(                    \
+    /*WINSCP static*/ void aes##len##_sw_sdctr(                    \
         ssh_cipher *ciph, void *vblk, int blklen)       \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_sdctr_sw(ciph, vblk, blklen); }
     { aes_sdctr_sw(ciph, vblk, blklen); }
 
 
-SW_ENC_DEC(128)
-SW_ENC_DEC(192)
-SW_ENC_DEC(256)
-
-#endif
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using x86 AES-NI.
- */
-
-#if HW_AES == HW_AES_NI
-
-#ifdef WINSCP_VS
-
-/*
- * Set target architecture for Clang and GCC
- */
-#if !defined(__clang__) && defined(__GNUC__)
-#    pragma GCC target("aes")
-#    pragma GCC target("sse4.1")
-#endif
-
-#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
-#    define FUNC_ISA __attribute__ ((target("sse4.1,aes")))
-#else
-#    define FUNC_ISA
-#endif
-
-#include <wmmintrin.h>
-#include <smmintrin.h>
-
-#if defined(__clang__) || defined(__GNUC__)
-#include <cpuid.h>
-#define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3])
-#else
-#define GET_CPU_ID(out) __cpuid(out, 1)
-#endif
-
-bool aes_hw_available(void)
-{
-    /*
-     * Determine if AES is available on this CPU, by checking that
-     * both AES itself and SSE4.1 are supported.
-     */
-    unsigned int CPUInfo[4];
-    GET_CPU_ID(CPUInfo);
-    return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19));
-}
-
-/*
- * Core AES-NI encrypt/decrypt functions, one per length and direction.
- */
-
-#define NI_CIPHER(len, dir, dirlong, repmacro)                          \
-    static FUNC_ISA inline __m128i aes_ni_##len##_##dir(                \
-        __m128i v, const __m128i *keysched)                             \
-    {                                                                   \
-        v = _mm_xor_si128(v, *keysched++);                              \
-        repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++););        \
-        return _mm_aes##dirlong##last_si128(v, *keysched);              \
-    }
-
-NI_CIPHER(128, e, enc, REP9)
-NI_CIPHER(128, d, dec, REP9)
-NI_CIPHER(192, e, enc, REP11)
-NI_CIPHER(192, d, dec, REP11)
-NI_CIPHER(256, e, enc, REP13)
-NI_CIPHER(256, d, dec, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_ni_key_expand(
-    const unsigned char *key, size_t key_words,
-    __m128i *keysched_e, __m128i *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final __m128i form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool only_sub = (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 1);
-
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            } else if (only_sub) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 0);
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into __m128i vectors and store
-     * them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = _mm_setr_epi32(
-            sched[4*round  ], sched[4*round+1],
-            sched[4*round+2], sched[4*round+3]);
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        __m128i rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = _mm_aesimc_si128(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-// WINSCP
-// WORKAROUND
-// Cannot use _mm_setr_epi* - it results in the constant being stored in .rdata segment.
-// objconv reports:
-// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
-// Despite that the code crashes.
-// This macro is based on:
-// Based on https://stackoverflow.com/q/35268036/850848
-#define _MM_SETR_EPI8(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
-    { (char)a0, (char)a1, (char)a2, (char)a3, (char)a4, (char)a5, (char)a6, (char)a7, \
-      (char)a8, (char)a9, (char)aa, (char)ab, (char)ac, (char)ad, (char)ae, (char)af }
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_increment(__m128i v)
-{
-    const __m128i ONE = _MM_SETR_EPI8(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); // WINSCP
-    const __m128i ZERO = _mm_setzero_si128();
-
-    /* Increment the low-order 64 bits of v */
-    v  = _mm_add_epi64(v, ONE);
-    /* Check if they've become zero */
-    __m128i cmp = _mm_cmpeq_epi64(v, ZERO);
-    /* If so, the low half of cmp is all 1s. Pack that into the high
-     * half of addend with zero in the low half. */
-    __m128i addend = _mm_unpacklo_epi64(ZERO, cmp);
-    /* And subtract that from v, which increments the high 64 bits iff
-     * the low 64 wrapped round. */
-    v = _mm_sub_epi64(v, addend);
-
-    return v;
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_reverse(__m128i v)
-{
-    const __m128i R = _MM_SETR_EPI8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); // WINSCP
-    v = _mm_shuffle_epi8(
-        v, R); // WINSCP
-    return v;
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_ni_context aes_ni_context;
-struct aes_ni_context {
-    __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    void *pointer_to_free;
-    ssh_cipher ciph;
-};
-
-/*static WINSCP*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    /*
-     * The __m128i variables in the context structure need to be
-     * 16-byte aligned, but not all malloc implementations that this
-     * code has to work with will guarantee to return a 16-byte
-     * aligned pointer. So we over-allocate, manually realign the
-     * pointer ourselves, and store the original one inside the
-     * context so we know how to free it later.
-     */
-    void *allocation = smalloc(sizeof(aes_ni_context) + 15);
-    uintptr_t alloc_address = (uintptr_t)allocation;
-    uintptr_t aligned_address = (alloc_address + 15) & ~15;
-    aes_ni_context *ctx = (aes_ni_context *)aligned_address;
-
-    ctx->ciph.vt = alg;
-    ctx->pointer_to_free = allocation;
-    return &ctx->ciph;
-}
-
-/*static WINSCP*/ void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    void *allocation = ctx->pointer_to_free;
-    smemclr(ctx, sizeof(*ctx));
-    sfree(allocation);
-}
-
-/*static WINSCP*/ void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    ctx->iv = _mm_loadu_si128(iv);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    __m128i counter = _mm_loadu_si128(iv);
-    ctx->iv = aes_ni_sdctr_reverse(counter);
-}
-
-typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
-
-static FUNC_ISA inline void aes_cbc_ni_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i plaintext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv);
-        __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        _mm_storeu_si128((__m128i *)blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
+#else // WINSCP_VS
 
 
-static FUNC_ISA inline void aes_cbc_ni_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i decrypted = decrypt(ciphertext, ctx->keysched_d);
-        __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv);
-        _mm_storeu_si128((__m128i *)blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_ni(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i counter = aes_ni_sdctr_reverse(ctx->iv);
-        __m128i keystream = encrypt(counter, ctx->keysched_e);
-        __m128i input = _mm_loadu_si128((const __m128i *)blk);
-        __m128i output = _mm_xor_si128(input, keystream);
-        _mm_storeu_si128((__m128i *)blk, output);
-        ctx->iv = aes_ni_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NI_ENC_DEC(len)                                                 \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_encrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_decrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_sdctr_hw(                \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
-
-NI_ENC_DEC(128)
-NI_ENC_DEC(192)
-NI_ENC_DEC(256)
+#define SW_ENC_DEC(len)                                 \
+    void aes##len##_sw_cbc_encrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
+    void aes##len##_sw_cbc_decrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
+    void aes##len##_sw_sdctr(                           \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
 
 
 #endif // WINSCP_VS
 #endif // WINSCP_VS
 
 
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using Arm NEON.
- */
-
-#elif HW_AES == HW_AES_NEON
-
-/*
- * Manually set the target architecture, if we decided above that we
- * need to.
- */
-#ifdef USE_CLANG_ATTR_TARGET_AARCH64
-/*
- * A spot of cheating: redefine some ACLE feature macros before
- * including arm_neon.h. Otherwise we won't get the AES intrinsics
- * defined by that header, because it will be looking at the settings
- * for the whole translation unit rather than the ones we're going to
- * put on some particular functions using __attribute__((target)).
- */
-#define __ARM_NEON 1
-#define __ARM_FEATURE_CRYPTO 1
-#define __ARM_FEATURE_AES 1
-#define FUNC_ISA __attribute__ ((target("neon,crypto")))
-#endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
-
-#ifndef FUNC_ISA
-#define FUNC_ISA
-#endif
-
-#ifdef USE_ARM64_NEON_H
-#include <arm64_neon.h>
-#else
-#include <arm_neon.h>
-#endif
-
-static bool aes_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform AES detection function,
-     * because it has to be implemented by asking the operating system
-     * rather than directly querying the CPU.
-     *
-     * That's because Arm systems commonly have multiple cores that
-     * are not all alike, so any method of querying whether NEON
-     * crypto instructions work on the _current_ CPU - even one as
-     * crude as just trying one and catching the SIGILL - wouldn't
-     * give an answer that you could still rely on the first time the
-     * OS migrated your process to another CPU.
-     */
-    return platform_aes_hw_available();
-}
-
-/*
- * Core NEON encrypt/decrypt functions, one per length and direction.
- */
-
-#define NEON_CIPHER(len, repmacro)                              \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_e(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesmcq_u8(vaeseq_u8(v, *keysched++)););   \
-        v = vaeseq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }                                                           \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_d(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesimcq_u8(vaesdq_u8(v, *keysched++)););  \
-        v = vaesdq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }
-
-NEON_CIPHER(128, REP9)
-NEON_CIPHER(192, REP11)
-NEON_CIPHER(256, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_neon_key_expand(
-    const unsigned char *key, size_t key_words,
-    uint8x16_t *keysched_e, uint8x16_t *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final uint8x16_t form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool sub = rotate_and_round_constant ||
-                (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant)
-                temp = (temp << 24) | (temp >> 8);
-
-            if (sub) {
-                uint32x4_t v32 = vdupq_n_u32(temp);
-                uint8x16_t v8 = vreinterpretq_u8_u32(v32);
-                v8 = vaeseq_u8(v8, vdupq_n_u8(0));
-                v32 = vreinterpretq_u32_u8(v8);
-                temp = vget_lane_u32(vget_low_u32(v32), 0);
-            }
-
-            if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into uint8x16_t vectors and
-     * store them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = vreinterpretq_u8_u32(vld1q_u32(sched + 4*round));
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        uint8x16_t rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = vaesimcq_u8(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- *
- * In fact we don't need to reverse the vector _all_ the way; we leave
- * the two lanes in MSW,LSW order, because that makes no difference to
- * the efficiency of the increment. That way we only have to reverse
- * bytes within each lane in this function.
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_reverse(uint8x16_t v)
-{
-    return vrev64q_u8(v);
-}
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode. There's no instruction to treat a 128-bit vector as a single
- * long integer, so instead we have to increment the bottom half
- * unconditionally, and the top half if the bottom half started off as
- * all 1s (in which case there was about to be a carry).
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_increment(uint8x16_t in)
-{
-#ifdef __aarch64__
-    /* There will be a carry if the low 64 bits are all 1s. */
-    uint64x1_t all1 = vcreate_u64(0xFFFFFFFFFFFFFFFF);
-    uint64x1_t carry = vceq_u64(vget_high_u64(vreinterpretq_u64_u8(in)), all1);
-
-    /* Make a word whose bottom half is unconditionally all 1s, and
-     * the top half is 'carry', i.e. all 0s most of the time but all
-     * 1s if we need to increment the top half. Then that word is what
-     * we need to _subtract_ from the input counter. */
-    uint64x2_t subtrahend = vcombine_u64(carry, all1);
-#else
-    /* AArch32 doesn't have comparisons that operate on a 64-bit lane,
-     * so we start by comparing each 32-bit half of the low 64 bits
-     * _separately_ to all-1s. */
-    uint32x2_t all1 = vdup_n_u32(0xFFFFFFFF);
-    uint32x2_t carry = vceq_u32(
-        vget_high_u32(vreinterpretq_u32_u8(in)), all1);
-
-    /* Swap the 32-bit words of the compare output, and AND with the
-     * unswapped version. Now carry is all 1s iff the bottom half of
-     * the input counter was all 1s, and all 0s otherwise. */
-    carry = vand_u32(carry, vrev64_u32(carry));
-
-    /* Now make the vector to subtract in the same way as above. */
-    uint64x2_t subtrahend = vreinterpretq_u64_u32(vcombine_u32(carry, all1));
-#endif
-
-    return vreinterpretq_u8_u64(
-        vsubq_u64(vreinterpretq_u64_u8(in), subtrahend));
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_neon_context aes_neon_context;
-struct aes_neon_context {
-    uint8x16_t keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    aes_neon_context *ctx = snew(aes_neon_context);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_neon_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    ctx->iv = vld1q_u8(iv);
-}
-
-static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    uint8x16_t counter = vld1q_u8(iv);
-    ctx->iv = aes_neon_sdctr_reverse(counter);
-}
-
-typedef uint8x16_t (*aes_neon_fn)(uint8x16_t v, const uint8x16_t *keysched);
-
-static FUNC_ISA inline void aes_cbc_neon_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t plaintext = vld1q_u8(blk);
-        uint8x16_t cipher_input = veorq_u8(plaintext, ctx->iv);
-        uint8x16_t ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        vst1q_u8(blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_cbc_neon_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn decrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t ciphertext = vld1q_u8(blk);
-        uint8x16_t decrypted = decrypt(ciphertext, ctx->keysched_d);
-        uint8x16_t plaintext = veorq_u8(decrypted, ctx->iv);
-        vst1q_u8(blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_neon(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t counter = aes_neon_sdctr_reverse(ctx->iv);
-        uint8x16_t keystream = encrypt(counter, ctx->keysched_e);
-        uint8x16_t input = vld1q_u8(blk);
-        uint8x16_t output = veorq_u8(input, keystream);
-        vst1q_u8(blk, output);
-        ctx->iv = aes_neon_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NEON_ENC_DEC(len)                                               \
-    static FUNC_ISA void aes##len##_cbc_hw_encrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_encrypt(ciph, vblk, blklen, aes_neon_##len##_e); }   \
-    static FUNC_ISA void aes##len##_cbc_hw_decrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_decrypt(ciph, vblk, blklen, aes_neon_##len##_d); }   \
-    static FUNC_ISA void aes##len##_sdctr_hw(                           \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_neon(ciph, vblk, blklen, aes_neon_##len##_e); }         \
-
-NEON_ENC_DEC(128)
-NEON_ENC_DEC(192)
-NEON_ENC_DEC(256)
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated AES. In this
- * case, aes_hw_new returns NULL (though it should also never be
- * selected by aes_select, so the only thing that should even be
- * _able_ to call it is testcrypt). As a result, the remaining vtable
- * functions should never be called at all.
- */
-
-#elif HW_AES == HW_AES_NONE
-
-bool aes_hw_available(void)
-{
-    return false;
-}
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void aes_hw_free(ssh_cipher *ciph) STUB_BODY
-static void aes_hw_setkey(ssh_cipher *ciph, const void *key) STUB_BODY
-static void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) STUB_BODY
-static void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) STUB_BODY
-#define STUB_ENC_DEC(len)                                       \
-    static void aes##len##_cbc_hw_encrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_cbc_hw_decrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_sdctr_hw(                            \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY
-
-STUB_ENC_DEC(128)
-STUB_ENC_DEC(192)
-STUB_ENC_DEC(256)
-
-#endif /* HW_AES */
+SW_ENC_DEC(128)
+SW_ENC_DEC(192)
+SW_ENC_DEC(256)
 
 
 #ifndef WINSCP_VS
 #ifndef WINSCP_VS
 
 
+AES_EXTRA(_sw);
+AES_ALL_VTABLES(_sw, "unaccelerated");
+
 #ifdef MPEXT
 #ifdef MPEXT
 
 
 #include "puttyexp.h"
 #include "puttyexp.h"

+ 126 - 0
source/putty/crypto/aes.h

@@ -0,0 +1,126 @@
+/*
+ * Definitions likely to be helpful to multiple AES implementations.
+ */
+
+/*
+ * The 'extra' structure used by AES implementations is used to
+ * include information about how to check if a given implementation is
+ * available at run time, and whether we've already checked.
+ */
+struct aes_extra_mutable;
+struct aes_extra {
+    /* Function to check availability. Might be expensive, so we don't
+     * want to call it more than once. */
+    bool (*check_available)(void);
+
+    /* Point to a writable substructure. */
+    struct aes_extra_mutable *mut;
+};
+struct aes_extra_mutable {
+    bool checked_availability;
+    bool is_available;
+};
+static inline bool check_availability(const struct aes_extra *extra)
+{
+    if (!extra->mut->checked_availability) {
+        extra->mut->is_available = extra->check_available();
+        extra->mut->checked_availability = true;
+    }
+
+    return extra->mut->is_available;
+}
+
+/*
+ * Macros to define vtables for AES variants. There are a lot of
+ * these, because of the cross product between cipher modes, key
+ * sizes, and assorted HW/SW implementations, so it's worth spending
+ * some effort here to reduce the boilerplate in the sub-files.
+ */
+
+#define AES_EXTRA(impl_c)                                               \
+    static struct aes_extra_mutable aes ## impl_c ## _extra_mut;        \
+    static const struct aes_extra aes ## impl_c ## _extra = {           \
+        /* WINSCP */ \
+        /*.check_available =*/ aes ## impl_c ## _available,                 \
+        /*.mut =*/ &aes ## impl_c ## _extra_mut,                            \
+    }
+
+// WINSCP string constants are for avoiding 
+// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
+// in objconv
+
+#define AES_CBC_VTABLE(impl_c, impl_display, bits)                      \
+    const char ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id[] = "aes" #bits "-cbc"; /*WINSCP*/ \
+    const char ssh_aes ## bits ## _cbc ## impl_c ## text_name[] = "AES-" #bits " CBC (" impl_display ")"; /*WINSCP*/ \
+    const ssh_cipheralg ssh_aes ## bits ## _cbc ## impl_c = {           \
+        /*WINSCP*/ \
+        /*.new =*/ aes ## impl_c ## _new,                                   \
+        /*.free =*/ aes ## impl_c ## _free,                                 \
+        /*.setiv =*/ aes ## impl_c ## _setiv_cbc,                           \
+        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
+        /*.encrypt =*/ aes ## bits ## impl_c ## _cbc_encrypt,               \
+        /*.decrypt =*/ aes ## bits ## impl_c ## _cbc_decrypt,               \
+        NULL, \
+        NULL, \
+        /*.ssh2_id =*/ ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id, /*WINSCP*/ \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        /*.flags =*/ SSH_CIPHER_IS_CBC,                                     \
+        /*.text_name =*/ ssh_aes ## bits ## _cbc ## impl_c ## text_name, /*WINSCP*/ \
+        NULL, \
+        /*.extra =*/ &aes ## impl_c ## _extra,                              \
+    }
+
+#define AES_SDCTR_VTABLE(impl_c, impl_display, bits)                    \
+    const char ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id[] = "aes" #bits "-ctr"; /*WINSCP*/ \
+    const char ssh_aes ## bits ## _sdctr ## impl_c ## text_name[] = "AES-" #bits " SDCTR (" impl_display ")"; /*WINSCP*/ \
+    const ssh_cipheralg ssh_aes ## bits ## _sdctr ## impl_c = {         \
+        /*WINSCP*/ \
+        /*.new =*/ aes ## impl_c ## _new,                                   \
+        /*.free =*/ aes ## impl_c ## _free,                                 \
+        /*.setiv =*/ aes ## impl_c ## _setiv_sdctr,                         \
+        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
+        /*.encrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
+        /*.decrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
+        NULL, \
+        NULL, \
+        /*.ssh2_id =*/ ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id, /*WINSCP*/ \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        /*.flags =*/ 0,                                                     \
+        /*.text_name =*/ ssh_aes ## bits ## _sdctr ## impl_c ## text_name, /*WINSCP*/ \
+        NULL, \
+        /*.extra =*/ &aes ## impl_c ## _extra,                              \
+    }
+
+#define AES_ALL_VTABLES(impl_c, impl_display)           \
+    AES_CBC_VTABLE(impl_c, impl_display, 128);          \
+    AES_CBC_VTABLE(impl_c, impl_display, 192);          \
+    AES_CBC_VTABLE(impl_c, impl_display, 256);          \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 128);        \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 192);        \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 256)
+
+/*
+ * Macros to repeat a piece of code particular numbers of times that
+ * correspond to 1 fewer than the number of AES rounds. (Because the
+ * last round is different.)
+ */
+#define REP2(x) x x
+#define REP4(x) REP2(REP2(x))
+#define REP8(x) REP2(REP4(x))
+#define REP9(x) REP8(x) x
+#define REP11(x) REP8(x) REP2(x) x
+#define REP13(x) REP8(x) REP4(x) x
+
+/*
+ * The round constants used in key schedule expansion.
+ */
+extern const uint8_t aes_key_setup_round_constants[10];
+
+/*
+ * The largest number of round keys ever needed.
+ */
+#define MAXROUNDKEYS 15

+ 0 - 0
source/putty/sshaesold.c → source/putty/crypto/aesold.c


+ 0 - 0
source/putty/ssharcf.c → source/putty/crypto/arcfour.c


+ 1 - 1
source/putty/sshargon2.c → source/putty/crypto/argon2.c

@@ -289,7 +289,7 @@ static void argon2_internal(uint32_t p, uint32_t T, uint32_t m, uint32_t t,
      * that in the initial slice on the first pass, we've already written
      * that in the initial slice on the first pass, we've already written
      * values into the first two columns during the initial setup above. So
      * values into the first two columns during the initial setup above. So
      * 'jstart' indicates the starting index in each segment we process; it
      * 'jstart' indicates the starting index in each segment we process; it
-     * starts off as 2 so that we don't overwrite the inital setup, and then
+     * starts off as 2 so that we don't overwrite the initial setup, and then
      * after the first slice is done, we set it to 0, and it stays there.
      * after the first slice is done, we set it to 0, and it stays there.
      *
      *
      * d_mode indicates whether we're being data-dependent (true) or
      * d_mode indicates whether we're being data-dependent (true) or

+ 5 - 6
source/putty/sshbcrypt.c → source/putty/crypto/bcrypt.c

@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <stddef.h>
 #include <string.h>
 #include <string.h>
 #include "ssh.h"
 #include "ssh.h"
-#include "sshblowf.h"
+#include "blowfish.h"
 
 
 BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes,
 BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes,
                               const unsigned char *salt, int saltbytes)
                               const unsigned char *salt, int saltbytes)
@@ -69,8 +69,7 @@ void bcrypt_genblock(int counter,
     smemclr(&hashed_salt, sizeof(hashed_salt));
     smemclr(&hashed_salt, sizeof(hashed_salt));
 }
 }
 
 
-void openssh_bcrypt(const char *passphrase,
-                    const unsigned char *salt, int saltbytes,
+void openssh_bcrypt(ptrlen passphrase, ptrlen salt,
                     int rounds, unsigned char *out, int outbytes)
                     int rounds, unsigned char *out, int outbytes)
 {
 {
     unsigned char hashed_passphrase[64];
     unsigned char hashed_passphrase[64];
@@ -80,7 +79,7 @@ void openssh_bcrypt(const char *passphrase,
     int modulus, residue, i, j, round;
     int modulus, residue, i, j, round;
 
 
     /* Hash the passphrase to get the bcrypt key material */
     /* Hash the passphrase to get the bcrypt key material */
-    hash_simple(&ssh_sha512, ptrlen_from_asciz(passphrase), hashed_passphrase);
+    hash_simple(&ssh_sha512, passphrase, hashed_passphrase);
 
 
     /* We output key bytes in a scattered fashion to meld all output
     /* We output key bytes in a scattered fashion to meld all output
      * key blocks into all parts of the output. To do this, we pick a
      * key blocks into all parts of the output. To do this, we pick a
@@ -97,8 +96,8 @@ void openssh_bcrypt(const char *passphrase,
          * by bcrypt in the following loop */
          * by bcrypt in the following loop */
         memset(outblock, 0, sizeof(outblock));
         memset(outblock, 0, sizeof(outblock));
 
 
-        thissalt = salt;
-        thissaltbytes = saltbytes;
+        thissalt = salt.ptr;
+        thissaltbytes = salt.len;
         for (round = 0; round < rounds; round++) {
         for (round = 0; round < rounds; round++) {
             bcrypt_genblock(round == 0 ? residue+1 : 0,
             bcrypt_genblock(round == 0 ? residue+1 : 0,
                             hashed_passphrase,
                             hashed_passphrase,

+ 0 - 0
source/putty/sshblake2.c → source/putty/crypto/blake2.c


+ 1 - 1
source/putty/sshblowf.c → source/putty/crypto/blowfish.c

@@ -7,7 +7,7 @@
 #include <assert.h>
 #include <assert.h>
 #include <stdio.h>
 #include <stdio.h>
 #include "ssh.h"
 #include "ssh.h"
-#include "sshblowf.h"
+#include "blowfish.h"
 
 
 struct BlowfishContext {
 struct BlowfishContext {
     uint32_t S0[256], S1[256], S2[256], S3[256], P[18];
     uint32_t S0[256], S1[256], S2[256], S3[256], P[18];

+ 1 - 1
source/putty/sshblowf.h → source/putty/crypto/blowfish.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Header file shared between sshblowf.c and sshbcrypt.c. Exposes the
+ * Header file shared between blowfish.c and bcrypt.c. Exposes the
  * internal Blowfish routines needed by bcrypt.
  * internal Blowfish routines needed by bcrypt.
  */
  */
 
 

+ 0 - 0
source/putty/sshccp.c → source/putty/crypto/chacha20-poly1305.c


+ 1 - 1
source/putty/sshdes.c → source/putty/crypto/des.c

@@ -1,5 +1,5 @@
 /*
 /*
- * sshdes.c: implementation of DES.
+ * Implementation of DES.
  */
  */
 
 
 /*
 /*

+ 11 - 22
source/putty/sshdh.c → source/putty/crypto/diffie-hellman.c

@@ -19,12 +19,18 @@ struct dh_extra {
 
 
 static void dh_group1_construct(dh_ctx *ctx)
 static void dh_group1_construct(dh_ctx *ctx)
 {
 {
+    /* Command to recompute, from the expression in RFC 2412 section E.2:
+spigot -B16 '2^1024 - 2^960 - 1 + 2^64 * ( floor(2^894 pi) + 129093 )'
+     */
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF");
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF");
     ctx->g = mp_from_integer(2);
     ctx->g = mp_from_integer(2);
 }
 }
 
 
 static void dh_group14_construct(dh_ctx *ctx)
 static void dh_group14_construct(dh_ctx *ctx)
 {
 {
+    /* Command to recompute, from the expression in RFC 3526 section 3:
+spigot -B16 '2^2048 - 2^1984 - 1 + 2^64 * ( floor(2^1918 pi) + 124476 )'
+     */
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF");
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF");
     ctx->g = mp_from_integer(2);
     ctx->g = mp_from_integer(2);
 }
 }
@@ -33,7 +39,7 @@ static const struct dh_extra extra_group1 = {
     false, dh_group1_construct,
     false, dh_group1_construct,
 };
 };
 
 
-static const ssh_kex ssh_diffiehellman_group1_sha1 = {
+const ssh_kex ssh_diffiehellman_group1_sha1 = {
     "diffie-hellman-group1-sha1", "group1",
     "diffie-hellman-group1-sha1", "group1",
     KEXTYPE_DH, &ssh_sha1, &extra_group1,
     KEXTYPE_DH, &ssh_sha1, &extra_group1,
 };
 };
@@ -48,12 +54,12 @@ static const struct dh_extra extra_group14 = {
     false, dh_group14_construct,
     false, dh_group14_construct,
 };
 };
 
 
-static const ssh_kex ssh_diffiehellman_group14_sha256 = {
+const ssh_kex ssh_diffiehellman_group14_sha256 = {
     "diffie-hellman-group14-sha256", "group14",
     "diffie-hellman-group14-sha256", "group14",
     KEXTYPE_DH, &ssh_sha256, &extra_group14,
     KEXTYPE_DH, &ssh_sha256, &extra_group14,
 };
 };
 
 
-static const ssh_kex ssh_diffiehellman_group14_sha1 = {
+const ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
     "diffie-hellman-group14-sha1", "group14",
     KEXTYPE_DH, &ssh_sha1, &extra_group14,
     KEXTYPE_DH, &ssh_sha1, &extra_group14,
 };
 };
@@ -96,7 +102,7 @@ const ssh_kexes ssh_diffiehellman_gex = { lenof(gex_list), gex_list };
  * Kerberos v5.
  * Kerberos v5.
  *
  *
  * (The same encoded OID, minus the two-byte DER header, is defined in
  * (The same encoded OID, minus the two-byte DER header, is defined in
- * pgssapi.c as GSS_MECH_KRB5.)
+ * ssh/pgssapi.c as GSS_MECH_KRB5.)
  */
  */
 #define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
 #define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
 
 
@@ -194,19 +200,8 @@ void dh_cleanup(dh_ctx *ctx)
 /*
 /*
  * DH stage 1: invent a number x between 1 and q, and compute e =
  * DH stage 1: invent a number x between 1 and q, and compute e =
  * g^x mod p. Return e.
  * g^x mod p. Return e.
- *
- * If `nbits' is greater than zero, it is used as an upper limit
- * for the number of bits in x. This is safe provided that (a) you
- * use twice as many bits in x as the number of bits you expect to
- * use in your session key, and (b) the DH group is a safe prime
- * (which SSH demands that it must be).
- *
- * P. C. van Oorschot, M. J. Wiener
- * "On Diffie-Hellman Key Agreement with Short Exponents".
- * Advances in Cryptology: Proceedings of Eurocrypt '96
- * Springer-Verlag, May 1996.
  */
  */
-mp_int *dh_create_e(dh_ctx *ctx, int nbits)
+mp_int *dh_create_e(dh_ctx *ctx)
 {
 {
     /*
     /*
      * Lower limit is just 2.
      * Lower limit is just 2.
@@ -218,12 +213,6 @@ mp_int *dh_create_e(dh_ctx *ctx, int nbits)
      */
      */
     mp_int *hi = mp_copy(ctx->q);
     mp_int *hi = mp_copy(ctx->q);
     mp_sub_integer_into(hi, hi, 1);
     mp_sub_integer_into(hi, hi, 1);
-    if (nbits) {
-        mp_int *pow2 = mp_power_2(nbits+1);
-        mp_min_into(pow2, pow2, hi);
-        mp_free(hi);
-        hi = pow2;
-    }
 
 
     /*
     /*
      * Make a random number in that range.
      * Make a random number in that range.

+ 129 - 129
source/putty/sshdss.c → source/putty/crypto/dsa.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Digital Signature Standard implementation for PuTTY.
+ * Digital Signature Algorithm implementation for PuTTY.
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
@@ -10,49 +10,49 @@
 #include "mpint.h"
 #include "mpint.h"
 #include "misc.h"
 #include "misc.h"
 
 
-static void dss_freekey(ssh_key *key);    /* forward reference */
+static void dsa_freekey(ssh_key *key);    /* forward reference */
 
 
-static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data)
+static ssh_key *dsa_new_pub(const ssh_keyalg *self, ptrlen data)
 {
 {
     BinarySource src[1];
     BinarySource src[1];
-    struct dss_key *dss;
+    struct dsa_key *dsa;
 
 
     BinarySource_BARE_INIT_PL(src, data);
     BinarySource_BARE_INIT_PL(src, data);
     if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
     if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
         return NULL;
         return NULL;
 
 
-    dss = snew(struct dss_key);
-    dss->sshk.vt = &ssh_dss;
-    dss->p = get_mp_ssh2(src);
-    dss->q = get_mp_ssh2(src);
-    dss->g = get_mp_ssh2(src);
-    dss->y = get_mp_ssh2(src);
-    dss->x = NULL;
+    dsa = snew(struct dsa_key);
+    dsa->sshk.vt = &ssh_dsa;
+    dsa->p = get_mp_ssh2(src);
+    dsa->q = get_mp_ssh2(src);
+    dsa->g = get_mp_ssh2(src);
+    dsa->y = get_mp_ssh2(src);
+    dsa->x = NULL;
 
 
     if (get_err(src) ||
     if (get_err(src) ||
-        mp_eq_integer(dss->p, 0) || mp_eq_integer(dss->q, 0)) {
+        mp_eq_integer(dsa->p, 0) || mp_eq_integer(dsa->q, 0)) {
         /* Invalid key. */
         /* Invalid key. */
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
         return NULL;
     }
     }
 
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 }
 
 
-static void dss_freekey(ssh_key *key)
+static void dsa_freekey(ssh_key *key)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-    if (dss->p)
-        mp_free(dss->p);
-    if (dss->q)
-        mp_free(dss->q);
-    if (dss->g)
-        mp_free(dss->g);
-    if (dss->y)
-        mp_free(dss->y);
-    if (dss->x)
-        mp_free(dss->x);
-    sfree(dss);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
+    if (dsa->p)
+        mp_free(dsa->p);
+    if (dsa->q)
+        mp_free(dsa->q);
+    if (dsa->g)
+        mp_free(dsa->g);
+    if (dsa->y)
+        mp_free(dsa->y);
+    if (dsa->x)
+        mp_free(dsa->x);
+    sfree(dsa);
 }
 }
 
 
 static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
 static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
@@ -69,55 +69,55 @@ static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
     } // WINSCP
     } // WINSCP
 }
 }
 
 
-static char *dss_cache_str(ssh_key *key)
+static char *dsa_cache_str(ssh_key *key)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     strbuf *sb = strbuf_new();
     strbuf *sb = strbuf_new();
 
 
-    if (!dss->p) {
+    if (!dsa->p) {
         strbuf_free(sb);
         strbuf_free(sb);
         return NULL;
         return NULL;
     }
     }
 
 
-    append_hex_to_strbuf(sb, dss->p);
-    append_hex_to_strbuf(sb, dss->q);
-    append_hex_to_strbuf(sb, dss->g);
-    append_hex_to_strbuf(sb, dss->y);
+    append_hex_to_strbuf(sb, dsa->p);
+    append_hex_to_strbuf(sb, dsa->q);
+    append_hex_to_strbuf(sb, dsa->g);
+    append_hex_to_strbuf(sb, dsa->y);
 
 
     return strbuf_to_str(sb);
     return strbuf_to_str(sb);
 }
 }
 
 
-static key_components *dss_components(ssh_key *key)
+static key_components *dsa_components(ssh_key *key)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     key_components *kc = key_components_new();
     key_components *kc = key_components_new();
 
 
     key_components_add_text(kc, "key_type", "DSA");
     key_components_add_text(kc, "key_type", "DSA");
-    assert(dss->p);
-    key_components_add_mp(kc, "p", dss->p);
-    key_components_add_mp(kc, "q", dss->q);
-    key_components_add_mp(kc, "g", dss->g);
-    key_components_add_mp(kc, "public_y", dss->y);
-    if (dss->x)
-        key_components_add_mp(kc, "private_x", dss->x);
+    assert(dsa->p);
+    key_components_add_mp(kc, "p", dsa->p);
+    key_components_add_mp(kc, "q", dsa->q);
+    key_components_add_mp(kc, "g", dsa->g);
+    key_components_add_mp(kc, "public_y", dsa->y);
+    if (dsa->x)
+        key_components_add_mp(kc, "private_x", dsa->x);
 
 
     return kc;
     return kc;
 }
 }
 
 
-static char *dss_invalid(ssh_key *key, unsigned flags)
+static char *dsa_invalid(ssh_key *key, unsigned flags)
 {
 {
     /* No validity criterion will stop us from using a DSA key at all */
     /* No validity criterion will stop us from using a DSA key at all */
     return NULL;
     return NULL;
 }
 }
 
 
-static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
+static bool dsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     BinarySource src[1];
     BinarySource src[1];
     unsigned char hash[20];
     unsigned char hash[20];
     bool toret;
     bool toret;
 
 
-    if (!dss->p)
+    if (!dsa->p)
         return false;
         return false;
 
 
     BinarySource_BARE_INIT_PL(src, sig);
     BinarySource_BARE_INIT_PL(src, sig);
@@ -159,8 +159,8 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     unsigned invalid = 0;
     unsigned invalid = 0;
     invalid |= mp_eq_integer(r, 0);
     invalid |= mp_eq_integer(r, 0);
     invalid |= mp_eq_integer(s, 0);
     invalid |= mp_eq_integer(s, 0);
-    invalid |= mp_cmp_hs(r, dss->q);
-    invalid |= mp_cmp_hs(s, dss->q);
+    invalid |= mp_cmp_hs(r, dsa->q);
+    invalid |= mp_cmp_hs(s, dsa->q);
     if (invalid) {
     if (invalid) {
         mp_free(r);
         mp_free(r);
         mp_free(s);
         mp_free(s);
@@ -171,7 +171,7 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
      * Step 1. w <- s^-1 mod q.
      * Step 1. w <- s^-1 mod q.
      */
      */
     { // WINSCP
     { // WINSCP
-    mp_int *w = mp_invert(s, dss->q);
+    mp_int *w = mp_invert(s, dsa->q);
     if (!w) {
     if (!w) {
         mp_free(r);
         mp_free(r);
         mp_free(s);
         mp_free(s);
@@ -184,20 +184,20 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     hash_simple(&ssh_sha1, data, hash);
     hash_simple(&ssh_sha1, data, hash);
     { // WINSCP
     { // WINSCP
     mp_int *sha = mp_from_bytes_be(make_ptrlen(hash, 20));
     mp_int *sha = mp_from_bytes_be(make_ptrlen(hash, 20));
-    mp_int *u1 = mp_modmul(sha, w, dss->q);
+    mp_int *u1 = mp_modmul(sha, w, dsa->q);
 
 
     /*
     /*
      * Step 3. u2 <- r * w mod q.
      * Step 3. u2 <- r * w mod q.
      */
      */
-    mp_int *u2 = mp_modmul(r, w, dss->q);
+    mp_int *u2 = mp_modmul(r, w, dsa->q);
 
 
     /*
     /*
      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.
      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.
      */
      */
-    mp_int *gu1p = mp_modpow(dss->g, u1, dss->p);
-    mp_int *yu2p = mp_modpow(dss->y, u2, dss->p);
-    mp_int *gu1yu2p = mp_modmul(gu1p, yu2p, dss->p);
-    mp_int *v = mp_mod(gu1yu2p, dss->q);
+    mp_int *gu1p = mp_modpow(dsa->g, u1, dsa->p);
+    mp_int *yu2p = mp_modpow(dsa->y, u2, dsa->p);
+    mp_int *gu1yu2p = mp_modmul(gu1p, yu2p, dsa->p);
+    mp_int *v = mp_mod(gu1yu2p, dsa->q);
 
 
     /*
     /*
      * Step 5. v should now be equal to r.
      * Step 5. v should now be equal to r.
@@ -223,57 +223,57 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     return toret;
     return toret;
 }
 }
 
 
-static void dss_public_blob(ssh_key *key, BinarySink *bs)
+static void dsa_public_blob(ssh_key *key, BinarySink *bs)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
 
     put_stringz(bs, "ssh-dss");
     put_stringz(bs, "ssh-dss");
-    put_mp_ssh2(bs, dss->p);
-    put_mp_ssh2(bs, dss->q);
-    put_mp_ssh2(bs, dss->g);
-    put_mp_ssh2(bs, dss->y);
+    put_mp_ssh2(bs, dsa->p);
+    put_mp_ssh2(bs, dsa->q);
+    put_mp_ssh2(bs, dsa->g);
+    put_mp_ssh2(bs, dsa->y);
 }
 }
 
 
-static void dss_private_blob(ssh_key *key, BinarySink *bs)
+static void dsa_private_blob(ssh_key *key, BinarySink *bs)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
 
-    put_mp_ssh2(bs, dss->x);
+    put_mp_ssh2(bs, dsa->x);
 }
 }
 
 
-static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
+static ssh_key *dsa_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
 {
 {
     BinarySource src[1];
     BinarySource src[1];
     ssh_key *sshk;
     ssh_key *sshk;
-    struct dss_key *dss;
+    struct dsa_key *dsa;
     ptrlen hash;
     ptrlen hash;
     unsigned char digest[20];
     unsigned char digest[20];
     mp_int *ytest;
     mp_int *ytest;
 
 
-    sshk = dss_new_pub(self, pub);
+    sshk = dsa_new_pub(self, pub);
     if (!sshk)
     if (!sshk)
         return NULL;
         return NULL;
 
 
-    dss = container_of(sshk, struct dss_key, sshk);
+    dsa = container_of(sshk, struct dsa_key, sshk);
     BinarySource_BARE_INIT_PL(src, priv);
     BinarySource_BARE_INIT_PL(src, priv);
-    dss->x = get_mp_ssh2(src);
+    dsa->x = get_mp_ssh2(src);
     if (get_err(src)) {
     if (get_err(src)) {
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
         return NULL;
     }
     }
 
 
     /*
     /*
-     * Check the obsolete hash in the old DSS key format.
+     * Check the obsolete hash in the old DSA key format.
      */
      */
     hash = get_string(src);
     hash = get_string(src);
     if (hash.len == 20) {
     if (hash.len == 20) {
         ssh_hash *h = ssh_hash_new(&ssh_sha1);
         ssh_hash *h = ssh_hash_new(&ssh_sha1);
-        put_mp_ssh2(h, dss->p);
-        put_mp_ssh2(h, dss->q);
-        put_mp_ssh2(h, dss->g);
+        put_mp_ssh2(h, dsa->p);
+        put_mp_ssh2(h, dsa->q);
+        put_mp_ssh2(h, dsa->g);
         ssh_hash_final(h, digest);
         ssh_hash_final(h, digest);
         if (!smemeq(hash.ptr, digest, 20)) {
         if (!smemeq(hash.ptr, digest, 20)) {
-            dss_freekey(&dss->sshk);
+            dsa_freekey(&dsa->sshk);
             return NULL;
             return NULL;
         }
         }
     }
     }
@@ -281,75 +281,75 @@ static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
     /*
     /*
      * Now ensure g^x mod p really is y.
      * Now ensure g^x mod p really is y.
      */
      */
-    ytest = mp_modpow(dss->g, dss->x, dss->p);
-    if (!mp_cmp_eq(ytest, dss->y)) {
+    ytest = mp_modpow(dsa->g, dsa->x, dsa->p);
+    if (!mp_cmp_eq(ytest, dsa->y)) {
         mp_free(ytest);
         mp_free(ytest);
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
         return NULL;
     }
     }
     mp_free(ytest);
     mp_free(ytest);
 
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 }
 
 
-static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self,
+static ssh_key *dsa_new_priv_openssh(const ssh_keyalg *self,
                                      BinarySource *src)
                                      BinarySource *src)
 {
 {
-    struct dss_key *dss;
+    struct dsa_key *dsa;
 
 
-    dss = snew(struct dss_key);
-    dss->sshk.vt = &ssh_dss;
+    dsa = snew(struct dsa_key);
+    dsa->sshk.vt = &ssh_dsa;
 
 
-    dss->p = get_mp_ssh2(src);
-    dss->q = get_mp_ssh2(src);
-    dss->g = get_mp_ssh2(src);
-    dss->y = get_mp_ssh2(src);
-    dss->x = get_mp_ssh2(src);
+    dsa->p = get_mp_ssh2(src);
+    dsa->q = get_mp_ssh2(src);
+    dsa->g = get_mp_ssh2(src);
+    dsa->y = get_mp_ssh2(src);
+    dsa->x = get_mp_ssh2(src);
 
 
     if (get_err(src) ||
     if (get_err(src) ||
-        mp_eq_integer(dss->q, 0) || mp_eq_integer(dss->p, 0)) {
+        mp_eq_integer(dsa->q, 0) || mp_eq_integer(dsa->p, 0)) {
         /* Invalid key. */
         /* Invalid key. */
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
         return NULL;
     }
     }
 
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 }
 
 
-static void dss_openssh_blob(ssh_key *key, BinarySink *bs)
+static void dsa_openssh_blob(ssh_key *key, BinarySink *bs)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
 
-    put_mp_ssh2(bs, dss->p);
-    put_mp_ssh2(bs, dss->q);
-    put_mp_ssh2(bs, dss->g);
-    put_mp_ssh2(bs, dss->y);
-    put_mp_ssh2(bs, dss->x);
+    put_mp_ssh2(bs, dsa->p);
+    put_mp_ssh2(bs, dsa->q);
+    put_mp_ssh2(bs, dsa->g);
+    put_mp_ssh2(bs, dsa->y);
+    put_mp_ssh2(bs, dsa->x);
 }
 }
 
 
-static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
+static int dsa_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
 {
 {
     ssh_key *sshk;
     ssh_key *sshk;
-    struct dss_key *dss;
+    struct dsa_key *dsa;
     int ret;
     int ret;
 
 
-    sshk = dss_new_pub(self, pub);
+    sshk = dsa_new_pub(self, pub);
     if (!sshk)
     if (!sshk)
         return -1;
         return -1;
 
 
-    dss = container_of(sshk, struct dss_key, sshk);
-    ret = mp_get_nbits(dss->p);
-    dss_freekey(&dss->sshk);
+    dsa = container_of(sshk, struct dsa_key, sshk);
+    ret = mp_get_nbits(dsa->p);
+    dsa_freekey(&dsa->sshk);
 
 
     return ret;
     return ret;
 }
 }
 
 
-mp_int *dss_gen_k(const char *id_string, mp_int *modulus,
+mp_int *dsa_gen_k(const char *id_string, mp_int *modulus,
                      mp_int *private_key,
                      mp_int *private_key,
                      unsigned char *digest, int digest_len)
                      unsigned char *digest, int digest_len)
 {
 {
     /*
     /*
-     * The basic DSS signing algorithm is:
+     * The basic DSA signing algorithm is:
      *
      *
      *  - invent a random k between 1 and q-1 (exclusive).
      *  - invent a random k between 1 and q-1 (exclusive).
      *  - Compute r = (g^k mod p) mod q.
      *  - Compute r = (g^k mod p) mod q.
@@ -459,32 +459,32 @@ mp_int *dss_gen_k(const char *id_string, mp_int *modulus,
     } // WINSCP
     } // WINSCP
 }
 }
 
 
-static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
+static void dsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
 {
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     unsigned char digest[20];
     unsigned char digest[20];
     int i;
     int i;
 
 
     hash_simple(&ssh_sha1, data, digest);
     hash_simple(&ssh_sha1, data, digest);
 
 
     { // WINSCP
     { // WINSCP
-    mp_int *k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,
+    mp_int *k = dsa_gen_k("DSA deterministic k generator", dsa->q, dsa->x,
                           digest, sizeof(digest));
                           digest, sizeof(digest));
-    mp_int *kinv = mp_invert(k, dss->q);       /* k^-1 mod q */
+    mp_int *kinv = mp_invert(k, dsa->q);       /* k^-1 mod q */
 
 
     /*
     /*
      * Now we have k, so just go ahead and compute the signature.
      * Now we have k, so just go ahead and compute the signature.
      */
      */
-    mp_int *gkp = mp_modpow(dss->g, k, dss->p); /* g^k mod p */
-    mp_int *r = mp_mod(gkp, dss->q);        /* r = (g^k mod p) mod q */
+    mp_int *gkp = mp_modpow(dsa->g, k, dsa->p); /* g^k mod p */
+    mp_int *r = mp_mod(gkp, dsa->q);        /* r = (g^k mod p) mod q */
     mp_free(gkp);
     mp_free(gkp);
 
 
     { // WINSCP
     { // WINSCP
     mp_int *hash = mp_from_bytes_be(make_ptrlen(digest, 20));
     mp_int *hash = mp_from_bytes_be(make_ptrlen(digest, 20));
-    mp_int *xr = mp_mul(dss->x, r);
+    mp_int *xr = mp_mul(dsa->x, r);
     mp_int *hxr = mp_add(xr, hash);         /* hash + x*r */
     mp_int *hxr = mp_add(xr, hash);         /* hash + x*r */
     { // WINSCP
     { // WINSCP
-    mp_int *s = mp_modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash+x*r) mod q */
+    mp_int *s = mp_modmul(kinv, hxr, dsa->q); /* s = k^-1 * (hash+x*r) mod q */
     mp_free(hxr);
     mp_free(hxr);
     mp_free(xr);
     mp_free(xr);
     mp_free(kinv);
     mp_free(kinv);
@@ -504,21 +504,21 @@ static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
     } // WINSCP
     } // WINSCP
 }
 }
 
 
-const ssh_keyalg ssh_dss = {
+const ssh_keyalg ssh_dsa = {
     // WINSCP
     // WINSCP
-    /*.new_pub =*/ dss_new_pub,
-    /*.new_priv =*/ dss_new_priv,
-    /*.new_priv_openssh =*/ dss_new_priv_openssh,
-    /*.freekey =*/ dss_freekey,
-    /*.invalid =*/ dss_invalid,
-    /*.sign =*/ dss_sign,
-    /*.verify =*/ dss_verify,
-    /*.public_blob =*/ dss_public_blob,
-    /*.private_blob =*/ dss_private_blob,
-    /*.openssh_blob =*/ dss_openssh_blob,
-    /*.cache_str =*/ dss_cache_str,
-    /*.components =*/ dss_components,
-    /*.pubkey_bits =*/ dss_pubkey_bits,
+    /*.new_pub =*/ dsa_new_pub,
+    /*.new_priv =*/ dsa_new_priv,
+    /*.new_priv_openssh =*/ dsa_new_priv_openssh,
+    /*.freekey =*/ dsa_freekey,
+    /*.invalid =*/ dsa_invalid,
+    /*.sign =*/ dsa_sign,
+    /*.verify =*/ dsa_verify,
+    /*.public_blob =*/ dsa_public_blob,
+    /*.private_blob =*/ dsa_private_blob,
+    /*.openssh_blob =*/ dsa_openssh_blob,
+    /*.cache_str =*/ dsa_cache_str,
+    /*.components =*/ dsa_components,
+    /*.pubkey_bits =*/ dsa_pubkey_bits,
     /*.ssh_id =*/ "ssh-dss",
     /*.ssh_id =*/ "ssh-dss",
     /*.cache_id =*/ "dss",
     /*.cache_id =*/ "dss",
     NULL, NULL,
     NULL, NULL,

+ 4 - 0
source/putty/ecc.c → source/putty/crypto/ecc-arithmetic.c

@@ -1,3 +1,7 @@
+/*
+ * Basic arithmetic for elliptic curves, implementing ecc.h.
+ */
+
 #include <assert.h>
 #include <assert.h>
 
 
 #include "ssh.h"
 #include "ssh.h"

+ 6 - 14
source/putty/sshecc.c → source/putty/crypto/ecc-ssh.c

@@ -1,13 +1,5 @@
 /*
 /*
- * Elliptic-curve crypto module for PuTTY
- * Implements the three required curves, no optional curves
- *
- * NOTE: Only curves on prime field are handled by the maths functions
- *       in Weierstrass form using Jacobian co-ordinates.
- *
- *       Montgomery form curves are supported for DH. (Curve25519)
- *
- *       Edwards form curves are supported for DSA. (Ed25519, Ed448)
+ * Elliptic-curve signing and key exchange for PuTTY's SSH layer.
  */
  */
 
 
 /*
 /*
@@ -652,8 +644,8 @@ static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve)
     mp_free(y);
     mp_free(y);
 
 
     /* A point constructed in this way will always satisfy the curve
     /* A point constructed in this way will always satisfy the curve
-     * equation, unless ecc.c wasn't able to construct one at all, in
-     * which case P is now NULL. Either way, return it. */
+     * equation, unless ecc-arithmetic.c wasn't able to construct one
+     * at all, in which case P is now NULL. Either way, return it. */
     return P;
     return P;
     } // WINSCP
     } // WINSCP
     } // WINSCP
     } // WINSCP
@@ -798,12 +790,12 @@ static char *ecc_cache_str_shared(
     strbuf *sb = strbuf_new();
     strbuf *sb = strbuf_new();
 
 
     if (curve_name)
     if (curve_name)
-        strbuf_catf(sb, "%s,", curve_name);
+        put_fmt(sb, "%s,", curve_name);
 
 
     { // WINSCP
     { // WINSCP
     char *hx = mp_get_hex(x);
     char *hx = mp_get_hex(x);
     char *hy = mp_get_hex(y);
     char *hy = mp_get_hex(y);
-    strbuf_catf(sb, "0x%s,0x%s", hx, hy);
+    put_fmt(sb, "0x%s,0x%s", hx, hy);
     sfree(hx);
     sfree(hx);
     sfree(hy);
     sfree(hy);
     } // WINSCP
     } // WINSCP
@@ -1300,7 +1292,7 @@ static void ecdsa_sign(ssh_key *key, ptrlen data,
     {
     {
         unsigned char digest[20];
         unsigned char digest[20];
         hash_simple(&ssh_sha1, data, digest);
         hash_simple(&ssh_sha1, data, digest);
-        k = dss_gen_k(
+        k = dsa_gen_k(
             "ECDSA deterministic k generator", ek->curve->w.G_order,
             "ECDSA deterministic k generator", ek->curve->w.G_order,
             ek->privateKey, digest, sizeof(digest));
             ek->privateKey, digest, sizeof(digest));
     }
     }

+ 2 - 2
source/putty/ecc.h → source/putty/crypto/ecc.h

@@ -63,7 +63,7 @@ WeierstrassPoint *ecc_weierstrass_point_new_from_x(
 /* Memory management: copy and free points. */
 /* Memory management: copy and free points. */
 void ecc_weierstrass_point_copy_into(
 void ecc_weierstrass_point_copy_into(
     WeierstrassPoint *dest, WeierstrassPoint *src);
     WeierstrassPoint *dest, WeierstrassPoint *src);
-WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *wc);
+WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *orig);
 void ecc_weierstrass_point_free(WeierstrassPoint *point);
 void ecc_weierstrass_point_free(WeierstrassPoint *point);
 
 
 /* Check whether a point is actually on the curve. */
 /* Check whether a point is actually on the curve. */
@@ -223,7 +223,7 @@ EdwardsPoint *ecc_edwards_point_new_from_y(
 
 
 /* Copy and free points. */
 /* Copy and free points. */
 void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src);
 void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src);
-EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *ec);
+EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *orig);
 void ecc_edwards_point_free(EdwardsPoint *point);
 void ecc_edwards_point_free(EdwardsPoint *point);
 
 
 /*
 /*

+ 13 - 0
source/putty/crypto/hash_simple.c

@@ -0,0 +1,13 @@
+/*
+ * Convenience function to hash a single piece of data, wrapping up
+ * the faff of making and freeing an ssh_hash.
+ */
+
+#include "ssh.h"
+
+void hash_simple(const ssh_hashalg *alg, ptrlen data, void *output)
+{
+    ssh_hash *hash = ssh_hash_new(alg);
+    put_datapl(hash, data);
+    ssh_hash_final(hash, output);
+}

+ 6 - 6
source/putty/sshhmac.c → source/putty/crypto/hmac.c

@@ -42,21 +42,21 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
     ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
     ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
 
 
     ctx->text_name = strbuf_new();
     ctx->text_name = strbuf_new();
-    strbuf_catf(ctx->text_name, "HMAC-%s%s",
-                ctx->hashalg->text_basename, extra->suffix);
+    put_fmt(ctx->text_name, "HMAC-%s%s",
+            ctx->hashalg->text_basename, extra->suffix);
     if (extra->annotation || ctx->hashalg->annotation) {
     if (extra->annotation || ctx->hashalg->annotation) {
-        strbuf_catf(ctx->text_name, " (");
+        put_fmt(ctx->text_name, " (");
         { // WINSCP
         { // WINSCP
         const char *sep = "";
         const char *sep = "";
         if (extra->annotation) {
         if (extra->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation);
+            put_fmt(ctx->text_name, "%s%s", sep, extra->annotation);
             sep = ", ";
             sep = ", ";
         }
         }
         if (ctx->hashalg->annotation) {
         if (ctx->hashalg->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
+            put_fmt(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
             sep = ", ";
             sep = ", ";
         }
         }
-        strbuf_catf(ctx->text_name, ")");
+        put_fmt(ctx->text_name, ")");
         } // WINSCP
         } // WINSCP
     }
     }
 
 

+ 0 - 0
source/putty/sshmac.c → source/putty/crypto/mac.c


+ 16 - 0
source/putty/crypto/mac_simple.c

@@ -0,0 +1,16 @@
+/*
+ * Convenience function to MAC a single piece of data, wrapping up
+ * the faff of making and freeing an ssh_mac.
+ */
+
+#include "ssh.h"
+
+void mac_simple(const ssh2_macalg *alg, ptrlen key, ptrlen data, void *output)
+{
+    ssh2_mac *mac = ssh2_mac_new(alg, NULL);
+    ssh2_mac_setkey(mac, key);
+    ssh2_mac_start(mac);
+    put_datapl(mac, data);
+    ssh2_mac_genresult(mac, output);
+    ssh2_mac_free(mac);
+}

+ 0 - 0
source/putty/sshmd5.c → source/putty/crypto/md5.c


+ 212 - 26
source/putty/mpint.c → source/putty/crypto/mpint.c

@@ -1,3 +1,7 @@
+/*
+ * Multiprecision integer arithmetic, implementing mpint.h.
+ */
+
 #include <assert.h>
 #include <assert.h>
 #include <limits.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdio.h>
@@ -1648,43 +1652,137 @@ mp_int *monty_export(MontyContext *mc, mp_int *x)
     return toret;
     return toret;
 }
 }
 
 
-static void monty_reduce(MontyContext *mc, mp_int *x)
-{
-    mp_int reduced = monty_reduce_internal(mc, x, *mc->scratch);
-    mp_copy_into(x, &reduced);
-    mp_clear(mc->scratch);
-}
-
+#define MODPOW_LOG2_WINDOW_SIZE 5
+#define MODPOW_WINDOW_SIZE (1 << MODPOW_LOG2_WINDOW_SIZE)
 mp_int *monty_pow(MontyContext *mc, mp_int *base, mp_int *exponent)
 mp_int *monty_pow(MontyContext *mc, mp_int *base, mp_int *exponent)
 {
 {
-    /* square builds up powers of the form base^{2^i}. */
-    mp_int *square = mp_copy(base);
-    size_t i = 0;
+    /*
+     * Modular exponentiation is done from the top down, using a
+     * fixed-window technique.
+     *
+     * We have a table storing every power of the base from base^0 up
+     * to base^{w-1}, where w is a small power of 2, say 2^k. (k is
+     * defined above as MODPOW_LOG2_WINDOW_SIZE, and w = 2^k is
+     * defined as MODPOW_WINDOW_SIZE.)
+     *
+     * We break the exponent up into k-bit chunks, from the bottom up,
+     * that is
+     *
+     *   exponent = c_0 + 2^k c_1 + 2^{2k} c_2 + ... + 2^{nk} c_n
+     *
+     * and we compute base^exponent by computing in turn
+     *
+     *   base^{c_n}
+     *   base^{2^k c_n + c_{n-1}}
+     *   base^{2^{2k} c_n + 2^k c_{n-1} + c_{n-2}}
+     *   ...
+     *
+     * where each line is obtained by raising the previous line to the
+     * power 2^k (i.e. squaring it k times) and then multiplying in
+     * a value base^{c_i}, which we can look up in our table.
+     *
+     * Side-channel considerations: the exponent is secret, so
+     * actually doing a single table lookup by using a chunk of
+     * exponent bits as an array index would be an obvious leak of
+     * secret information into the cache. So instead, in each
+     * iteration, we read _all_ the table entries, and do a sequence
+     * of mp_select operations to leave just the one we wanted in the
+     * variable that will go into the multiplication. In other
+     * contexts (like software AES) that technique is so prohibitively
+     * slow that it makes you choose a strategy that doesn't use table
+     * lookups at all (we do bitslicing in preference); but here, this
+     * iteration through 2^k table elements is replacing k-1 bignum
+     * _multiplications_ that you'd have to use instead if you did
+     * simple square-and-multiply, and that makes it still a win.
+     */
+
+    /* Table that holds base^0, ..., base^{w-1} */
+    mp_int *table[MODPOW_WINDOW_SIZE];
+    table[0] = mp_copy(monty_identity(mc));
+    { // WINSCP
+    size_t i;
+    for (i = 1; i < MODPOW_WINDOW_SIZE; i++)
+        table[i] = monty_mul(mc, table[i-1], base);
 
 
-    /* out accumulates the output value. Starts at 1 (in Montgomery
-     * representation) and we multiply in each base^{2^i}. */
-    mp_int *out = mp_copy(mc->powers_of_r_mod_m[0]);
+    /* out accumulates the output value */
+    { // WINSCP
+    mp_int *out = mp_make_sized(mc->rw);
+    mp_copy_into(out, monty_identity(mc));
+
+    /* table_entry will hold each value we get out of the table */
+    { // WINSCP
+    mp_int *table_entry = mp_make_sized(mc->rw);
+
+    /* Bit index of the chunk of bits we're working on. Start with the
+     * highest multiple of k strictly less than the size of our
+     * bignum, i.e. the highest-index chunk of bits that might
+     * conceivably contain any nonzero bit. */
+    { // WINSCP
+    size_t i = (exponent->nw * BIGNUM_INT_BITS) - 1;
+    i -= i % MODPOW_LOG2_WINDOW_SIZE;
 
 
-    /* tmp holds each product we compute and reduce. */
-    mp_int *tmp = mp_make_sized(mc->rw * 2);
+    { // WINSCP
+    bool first_iteration = true;
 
 
     while (true) {
     while (true) {
-        mp_mul_into(tmp, out, square);
-        monty_reduce(mc, tmp);
-        mp_select_into(out, out, tmp, mp_get_bit(exponent, i));
+        /* Construct the table index */
+        unsigned table_index = 0;
+        { // WINSCP
+        size_t j;
+        for (j = 0; j < MODPOW_LOG2_WINDOW_SIZE; j++)
+            table_index |= mp_get_bit(exponent, i+j) << j;
+
+        /* Iterate through the table to do a side-channel-safe lookup,
+         * ending up with table_entry = table[table_index] */
+        mp_copy_into(table_entry, table[0]);
+        { // WINSCP
+        size_t j;
+        for (j = 1; j < MODPOW_WINDOW_SIZE; j++) {
+            unsigned not_this_one =
+                ((table_index ^ j) + MODPOW_WINDOW_SIZE - 1)
+                >> MODPOW_LOG2_WINDOW_SIZE;
+            mp_select_into(table_entry, table[j], table_entry, not_this_one);
+        }
 
 
-        if (++i >= exponent->nw * BIGNUM_INT_BITS)
+        if (!first_iteration) {
+            /* Multiply into the output */
+            monty_mul_into(mc, out, out, table_entry);
+        } else {
+            /* On the first iteration, we can save one multiplication
+             * by just copying */
+            mp_copy_into(out, table_entry);
+            first_iteration = false;
+        }
+
+        /* If that was the bottommost chunk of bits, we're done */
+        if (i == 0)
             break;
             break;
 
 
-        mp_mul_into(tmp, square, square);
-        monty_reduce(mc, tmp);
-        mp_copy_into(square, tmp);
+        /* Otherwise, square k times and go round again. */
+        { // WINSCP
+        size_t j;
+        for (j = 0; j < MODPOW_LOG2_WINDOW_SIZE; j++)
+            monty_mul_into(mc, out, out, out);
+
+        i-= MODPOW_LOG2_WINDOW_SIZE;
+        } // WINSCP
+        } // WINSCP
+        } // WINSCP
     }
     }
 
 
-    mp_free(square);
-    mp_free(tmp);
+    { // WINSCP
+    size_t i;
+    for (i = 0; i < MODPOW_WINDOW_SIZE; i++)
+        mp_free(table[i]);
+    mp_free(table_entry);
     mp_clear(mc->scratch);
     mp_clear(mc->scratch);
     return out;
     return out;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 }
 
 
 mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus)
 mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus)
@@ -2281,9 +2379,9 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out)
      */
      */
     { // WINSCP
     { // WINSCP
     mp_int *two_R = mp_make_sized(rw);
     mp_int *two_R = mp_make_sized(rw);
+    BignumInt top_word = (BignumInt)1 << ((log2_R+1) % BIGNUM_INT_BITS);
     mp_add_integer_into_shifted_by_words(
     mp_add_integer_into_shifted_by_words(
-        two_R, two_R, (BignumInt)1 << ((log2_R+1) % BIGNUM_INT_BITS),
-        (log2_R+1) / BIGNUM_INT_BITS);
+        two_R, two_R, top_word, (log2_R+1) / BIGNUM_INT_BITS);
 
 
     /*
     /*
      * Scratch space.
      * Scratch space.
@@ -2408,6 +2506,94 @@ mp_int *mp_mod(mp_int *n, mp_int *d)
     return r;
     return r;
 }
 }
 
 
+uint32_t mp_mod_known_integer(mp_int *x, uint32_t m)
+{
+    uint64_t reciprocal = ((uint64_t)1 << 48) / m;
+    uint64_t accumulator = 0;
+    { // WINSCP
+    size_t i;
+    for (i = mp_max_bytes(x); i-- > 0 ;) {
+        accumulator = 0x100 * accumulator + mp_get_byte(x, i);
+        /*
+         * Let A be the value in 'accumulator' at this point, and let
+         * R be the value it will have after we subtract quot*m below.
+         *
+         * Lemma 1: if A < 2^48, then R < 2m.
+         *
+         * Proof:
+         *
+         * By construction, we have 2^48/m - 1 < reciprocal <= 2^48/m.
+         * Multiplying that by the accumulator gives
+         *
+         *      A/m * 2^48 - A < unshifted_quot <= A/m * 2^48
+         * i.e. 0 <= (A/m * 2^48) - unshifted_quot < A
+         * i.e. 0 <= A/m - unshifted_quot/2^48 < A/2^48
+         *
+         * So when we shift this quotient right by 48 bits, i.e. take
+         * the floor of (unshifted_quot/2^48), the value we take the
+         * floor of is at most A/2^48 less than the true rational
+         * value A/m that we _wanted_ to take the floor of.
+         *
+         * Provided A < 2^48, this is less than 1. So the quotient
+         * 'quot' that we've just produced is either the true quotient
+         * floor(A/m), or one less than it. Hence, the output value R
+         * is less than 2m. []
+         *
+         * Lemma 2: if A < 2^16 m, then the multiplication of
+         * accumulator*reciprocal does not overflow.
+         *
+         * Proof: as above, we have reciprocal <= 2^48/m. Multiplying
+         * by A gives unshifted_quot <= 2^48 * A / m < 2^48 * 2^16 =
+         * 2^64. []
+         */
+        { // WINSCP
+        uint64_t unshifted_quot = accumulator * reciprocal;
+        uint64_t quot = unshifted_quot >> 48;
+        accumulator -= quot * m;
+        } // WINSCP
+    }
+
+    /*
+     * Theorem 1: accumulator < 2m at the end of every iteration of
+     * this loop.
+     *
+     * Proof: induction on the above loop.
+     *
+     * Base case: at the start of the first loop iteration, the
+     * accumulator is 0, which is certainly < 2m.
+     *
+     * Inductive step: in each loop iteration, we take a value at most
+     * 2m-1, multiply it by 2^8, and add another byte less than 2^8 to
+     * generate the input value A to the reduction process above. So
+     * we have A < 2m * 2^8 - 1. We know m < 2^32 (because it was
+     * passed in as a uint32_t), so A < 2^41, which is enough to allow
+     * us to apply Lemma 1, showing that the value of 'accumulator' at
+     * the end of the loop is still < 2m. []
+     *
+     * Corollary: we need at most one final subtraction of m to
+     * produce the canonical residue of x mod m, i.e. in the range
+     * [0,m).
+     *
+     * Theorem 2: no multiplication in the inner loop overflows.
+     *
+     * Proof: in Theorem 1 we established A < 2m * 2^8 - 1 in every
+     * iteration. That is less than m * 2^16, so Lemma 2 applies.
+     *
+     * The other multiplication, of quot * m, cannot overflow because
+     * quot is at most A/m, so quot*m <= A < 2^64. []
+     */
+
+    { // WINSCP
+    uint32_t result = accumulator;
+    uint32_t reduced = result - m;
+    uint32_t select = -(reduced >> 31);
+    result = reduced ^ ((result ^ reduced) & select);
+    assert(result < m);
+    return result;
+    } // WINSCP
+    } // WINSCP
+}
+
 mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out)
 mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out)
 {
 {
     /*
     /*

+ 1 - 1
source/putty/mpint_i.h → source/putty/crypto/mpint_i.h

@@ -266,7 +266,7 @@
 #endif /* DEFINE_BIGNUMDBLINT */
 #endif /* DEFINE_BIGNUMDBLINT */
 
 
 /* ----------------------------------------------------------------------
 /* ----------------------------------------------------------------------
- * Data structures used inside bignum.c.
+ * Data structures used inside mpint.c.
  */
  */
 
 
 struct mp_int {
 struct mp_int {

+ 1 - 1
source/putty/sshprng.c → source/putty/crypto/prng.c

@@ -1,5 +1,5 @@
 /*
 /*
- * sshprng.c: PuTTY's cryptographic pseudorandom number generator.
+ * PuTTY's cryptographic pseudorandom number generator.
  *
  *
  * This module just defines the PRNG object type and its methods. The
  * This module just defines the PRNG object type and its methods. The
  * usual global instance of it is managed by sshrand.c.
  * usual global instance of it is managed by sshrand.c.

+ 32 - 0
source/putty/crypto/pubkey-pem.c

@@ -0,0 +1,32 @@
+/*
+ * Convenience functions to encrypt and decrypt OpenSSH PEM format for
+ * SSH-2 private key files. This uses triple-DES in SSH-2 style (one
+ * CBC layer), with three distinct keys, and an IV also generated from
+ * the passphrase.
+ */
+
+#include "ssh.h"
+
+static ssh_cipher *des3_pubkey_ossh_cipher(const void *vkey, const void *viv)
+{
+    ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh2);
+    ssh_cipher_setkey(c, vkey);
+    ssh_cipher_setiv(c, viv);
+    return c;
+}
+
+void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv,
+                              void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_decrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv,
+                              void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_encrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}

Some files were not shown because too many files changed in this diff