Martin Prikryl пре 10 година
родитељ
комит
4acedf8030
83 измењених фајлова са 929 додато и 758 уклоњено
  1. 0 4
      deployment/winscpsetup.iss
  2. 2 1
      dotnet/Session.cs
  3. 11 2
      dotnet/internal/ExeSessionProcess.cs
  4. 17 3
      dotnet/internal/Logger.cs
  5. 3 3
      dotnet/properties/AssemblyInfo.cs
  6. 14 14
      libs/neon/src/ne_request.c
  7. 2 3
      libs/neon/src/ne_request.h
  8. 4 4
      libs/openssl/crypto/buildinf.h
  9. 1 1
      libs/openssl/crypto/cversion.c
  10. 1 1
      libs/openssl/crypto/ecdsa/ecs_vrf.c
  11. 3 3
      libs/openssl/crypto/opensslv.h
  12. 2 59
      libs/openssl/crypto/rand/rand_win.c
  13. 1 1
      libs/openssl/crypto/x509v3/v3_ncons.c
  14. 3 1
      libs/openssl/e_os.h
  15. 1 1
      source/Console.cbproj
  16. 1 1
      source/DragExt.cbproj
  17. 3 3
      source/DragExt64.rc
  18. 2 3
      source/WinSCP.cbproj
  19. 103 89
      source/components/UnixDirView.cpp
  20. 27 5
      source/core/Configuration.cpp
  21. 6 0
      source/core/Configuration.h
  22. 54 13
      source/core/FtpFileSystem.cpp
  23. 1 0
      source/core/FtpFileSystem.h
  24. 3 0
      source/core/Interface.h
  25. 28 16
      source/core/NamedObjs.cpp
  26. 3 2
      source/core/NamedObjs.h
  27. 43 2
      source/core/Option.cpp
  28. 8 1
      source/core/Option.h
  29. 50 13
      source/core/Script.cpp
  30. 1 0
      source/core/Script.h
  31. 11 0
      source/core/SecureShell.cpp
  32. 23 1
      source/core/SessionData.cpp
  33. 2 1
      source/core/SessionData.h
  34. 43 9
      source/core/SessionInfo.cpp
  35. 2 0
      source/core/SessionInfo.h
  36. 3 2
      source/core/SftpFileSystem.cpp
  37. 15 17
      source/core/WebDAVFileSystem.cpp
  38. 0 2
      source/core/WebDAVFileSystem.h
  39. 14 1
      source/dragext/DragExt.cpp
  40. 6 13
      source/filezilla/ApiLog.cpp
  41. 0 1
      source/filezilla/ApiLog.h
  42. 1 1
      source/filezilla/AsyncSslSocketLayer.cpp
  43. 11 0
      source/filezilla/ControlSocket.cpp
  44. 1 0
      source/filezilla/ControlSocket.h
  45. 4 3
      source/filezilla/FileZillaApi.h
  46. 3 2
      source/filezilla/FileZillaIntf.h
  47. 22 24
      source/filezilla/FtpControlSocket.cpp
  48. 19 23
      source/filezilla/FtpListResult.cpp
  49. 1 1
      source/filezilla/FtpListResult.h
  50. 36 25
      source/filezilla/TransferSocket.cpp
  51. 1 0
      source/filezilla/TransferSocket.h
  52. 39 16
      source/forms/About.cpp
  53. 2 2
      source/forms/About.h
  54. 1 1
      source/forms/Custom.cpp
  55. 24 3
      source/forms/CustomScpExplorer.cpp
  56. 7 7
      source/forms/Editor.dfm
  57. 6 6
      source/forms/GenerateUrl.dfm
  58. 14 0
      source/forms/LocationProfiles.cpp
  59. 1 0
      source/forms/LocationProfiles.h
  60. 33 35
      source/forms/MessageDlg.cpp
  61. 14 0
      source/forms/OpenDirectory.cpp
  62. 1 0
      source/forms/OpenDirectory.h
  63. 7 5
      source/forms/ScpCommander.cpp
  64. 4 4
      source/forms/ScpCommander.dfm
  65. 3 3
      source/forms/ScpExplorer.dfm
  66. 5 2
      source/packages/dragndrop/DragDrop.pas
  67. 5 2
      source/packages/filemng/CustomDirView.pas
  68. 73 283
      source/packages/filemng/DirView.pas
  69. 5 0
      source/putty/windows/WINNET.C
  70. 1 1
      source/resource/Propagation.rc
  71. 2 0
      source/resource/TextsFileZilla.h
  72. 2 0
      source/resource/TextsFileZilla.rc
  73. 1 0
      source/resource/TextsWin.h
  74. 1 0
      source/resource/TextsWin1.rc
  75. 3 3
      source/resource/TextsWin2.rc
  76. 2 0
      source/windows/ConsoleRunner.cpp
  77. 12 7
      source/windows/GUITools.cpp
  78. 1 0
      source/windows/GUITools.h
  79. 5 0
      source/windows/UserInterface.cpp
  80. 1 1
      source/windows/WinConfiguration.cpp
  81. 9 1
      source/windows/WinInterface.cpp
  82. 27 1
      source/windows/WinMain.cpp
  83. 2 0
      source/windows/WinSCP.exe.manifest

+ 0 - 4
deployment/winscpsetup.iss

@@ -1030,10 +1030,6 @@ begin
   if CurPageID = wpLicense then
   begin
     WizardForm.NextButton.Caption := ExpandConstant('{cm:AcceptButton}')
-  end
-    else
-  begin
-    WizardForm.NextButton.Caption := SetupMessage(msgButtonNext);
   end;
 
   if CurPageID = wpSelectDir then

+ 2 - 1
dotnet/Session.cs

@@ -50,6 +50,7 @@ namespace WinSCP
         public TimeSpan ReconnectTime { get { return _reconnectTime; } set { CheckNotOpened(); _reconnectTime = value; } }
         public int ReconnectTimeInMilliseconds { get { return Tools.TimeSpanToMilliseconds(ReconnectTime); } set { ReconnectTime = Tools.MillisecondsToTimeSpan(value); } }
         public string DebugLogPath { get { CheckNotDisposed(); return Logger.LogPath; } set { CheckNotDisposed(); Logger.LogPath = value; } }
+        public int DebugLogLevel { get { CheckNotDisposed(); return Logger.LogLevel; } set { CheckNotDisposed(); Logger.LogLevel = value; } }
         public string SessionLogPath { get { return _sessionLogPath; } set { CheckNotOpened(); _sessionLogPath = value; } }
         public string XmlLogPath { get { return _xmlLogPath; } set { CheckNotOpened(); _xmlLogPath = value; } }
         #if DEBUG
@@ -998,7 +999,7 @@ namespace WinSCP
                 string url = head;
                 string logUrl = head;
 
-                if ((sessionOptions.SecurePassword) != null && (sessionOptions.SecurePassword.Length > 0))
+                if (sessionOptions.SecurePassword != null)
                 {
                     if (!hasUsername)
                     {

+ 11 - 2
dotnet/internal/ExeSessionProcess.cs

@@ -495,9 +495,18 @@ namespace WinSCP
         {
             using (_logger.CreateCallstack())
             {
-                _logger.WriteLine("Waiting for process to exit");
+                int timeout;
 
-                if (!_process.WaitForExit(1000))
+                #if DEBUG
+                // in debug build, we expect the winscp.exe to run in tracing mode, being very slow
+                timeout = 10000;
+                #else
+                timeout = 2000;
+                #endif
+
+                _logger.WriteLine("Waiting for process to exit ({0} ms)", timeout);
+
+                if (!_process.WaitForExit(timeout))
                 {
                     _logger.WriteLine("Killing process");
                     _process.Kill();

+ 17 - 3
dotnet/internal/Logger.cs

@@ -13,6 +13,7 @@ namespace WinSCP
     internal class Logger : IDisposable
     {
         public string LogPath { get { return _logPath; } set { SetLogPath(value); } }
+        public int LogLevel { get { return _logLevel; } set { SetLogLevel(value); } }
         public bool Logging { get { return (_writter != null) && _writter.BaseStream.CanWrite; } }
 
         public string GetAssemblyFilePath()
@@ -156,7 +157,7 @@ namespace WinSCP
 
         public void WriteCounters()
         {
-            if (Logging)
+            if (Logging && (_logLevel >= 1))
             {
                 try
                 {
@@ -178,7 +179,7 @@ namespace WinSCP
 
         public void WriteProcesses()
         {
-            if (Logging)
+            if (Logging && (_logLevel >= 1))
             {
                 try
                 {
@@ -264,7 +265,10 @@ namespace WinSCP
                         _writter = File.CreateText(_logPath);
                         _writter.AutoFlush = true;
                         WriteEnvironmentInfo();
-                        CreateCounters();
+                        if (_logLevel >= 1)
+                        {
+                            CreateCounters();
+                        }
                     }
                 }
             }
@@ -289,11 +293,21 @@ namespace WinSCP
             return new Win32Exception(Marshal.GetLastWin32Error()).Message;
         }
 
+        private void SetLogLevel(int value)
+        {
+            if ((value < 0) || (value > 1))
+            {
+                throw new ArgumentOutOfRangeException(string.Format(CultureInfo.CurrentCulture, "Logging level has to be in range 0-1"));
+            }
+            _logLevel = value;
+        }
+
         private StreamWriter _writter;
         private string _logPath;
         private readonly Dictionary<int, int> _indents = new Dictionary<int, int>();
         private readonly object _logLock = new object();
         private readonly Lock _lock = new Lock();
         private List<PerformanceCounter> _performanceCounters = new List<PerformanceCounter>();
+        private int _logLevel;
     }
 }

+ 3 - 3
dotnet/properties/AssemblyInfo.cs

@@ -19,9 +19,9 @@ using System.Runtime.InteropServices;
 // The following GUID is for the ID of the typelib if this project is exposed to COM
 [assembly: Guid("a0b93468-d98a-4845-a234-8076229ad93f")]
 
-[assembly: AssemblyVersion("1.2.5.0")]
-[assembly: AssemblyFileVersion("1.2.5.0")]
-[assembly: AssemblyInformationalVersionAttribute("5.6.5.0")]
+[assembly: AssemblyVersion("1.2.6.0")]
+[assembly: AssemblyFileVersion("1.2.6.0")]
+[assembly: AssemblyInformationalVersionAttribute("5.7.0.0")]
 
 [assembly: CLSCompliant(true)]
 

+ 14 - 14
libs/neon/src/ne_request.c

@@ -94,6 +94,10 @@ struct ne_request_s {
     /* Request body. */
     ne_provide_body body_cb;
     void *body_ud;
+    #ifdef WINSCP
+    ne_provide_body body_cb_pre;
+    void *body_ud_pre;
+    #endif
 
     /* Request body source: file or buffer (if not callback). */
     union {
@@ -404,7 +408,11 @@ static int send_request_body(ne_request *req, int retry)
         return NE_ERROR;
     }
     
-    while ((bytes = req->body_cb(req->body_ud, start, buflen)) > 0) {
+    while (
+        #ifdef WINSCP
+        ((req->body_cb_pre == NULL) || ((bytes = req->body_cb_pre(req->body_ud_pre, start, buflen)) > 0)) &&
+        #endif
+        (bytes = req->body_cb(req->body_ud, start, buflen)) > 0) {
         req->session->status.sr.progress += bytes;
         if (chunked) {
             /* Overwrite the buffer prefix with the appropriate chunk
@@ -590,20 +598,12 @@ void ne_set_request_body_fd(ne_request *req, int fd,
 
 #include <assert.h>
 
-void ne_set_request_body_provider_proxy(ne_request *req,
-    ne_provide_body provider, void * ud,
-    ne_provide_body * prev_provider, void ** prev_ud)
+void ne_set_request_body_provider_pre(ne_request *req,
+    ne_provide_body provider, void *ud)
 {
-    if ((req->body_cb != provider) ||
-        (req->body_ud != ud))
-    {
-        assert(*prev_provider == NULL);
-        assert(*prev_ud == NULL);
-        *prev_provider = req->body_cb;
-        *prev_ud = req->body_ud;
-        req->body_cb = provider;
-        req->body_ud = ud;
-    }
+    assert((req->body_cb_pre == NULL) || (req->body_cb_pre == provider));
+    req->body_cb_pre = provider;
+    req->body_ud_pre = ud;
 }
 
 int ne_get_request_body_buffer(ne_request *req, const char **buffer,

+ 2 - 3
libs/neon/src/ne_request.h

@@ -85,9 +85,8 @@ void ne_set_request_body_provider(ne_request *req, ne_off_t length,
                                   ne_provide_body provider, void *userdata);
 
 #ifdef WINSCP
-void ne_set_request_body_provider_proxy(ne_request *req,
-  ne_provide_body provider, void * ud,
-  ne_provide_body * prev_provider, void ** prev_ud);
+void ne_set_request_body_provider_pre(ne_request *req,
+  ne_provide_body provider, void * ud);
 
 int ne_get_request_body_buffer(ne_request *req, const char **buffer,
 			       size_t * size);

+ 4 - 4
libs/openssl/crypto/buildinf.h

@@ -7,13 +7,13 @@
 #endif
 #ifdef MK1MF_PLATFORM_VC_WIN32
   /* auto-generated/updated by util/mk1mf.pl for crypto/cversion.c */
-  #define CFLAGS "cl  /MD /Ox /O2 /Ob2 -DOPENSSL_THREADS  -DDSO_WIN32  -DOPENSSL_USE_APPLINK -I. -DOPENSSL_NO_RC5 -DOPENSSL_NO_MD2 -DOPENSSL_NO_KRB5 -DOPENSSL_NO_JPAKE -DOPENSSL_NO_STATIC_ENGINE    "
+  #define CFLAGS "compiler: cl  /MD /Ox /O2 /Ob2 -DOPENSSL_THREADS  -DDSO_WIN32  -DOPENSSL_USE_APPLINK -I. -DOPENSSL_NO_RC5 -DOPENSSL_NO_MD2 -DOPENSSL_NO_KRB5 -DOPENSSL_NO_JPAKE -DOPENSSL_NO_STATIC_ENGINE    "
   #define PLATFORM "VC-WIN32"
-  #define DATE "Fri Jan  9 11:28:31 2015"
+  #define DATE "Wed Jan 28 13:03:40 2015"
 #endif
 #ifdef MK1MF_PLATFORM_BC_NT
   /* auto-generated/updated by util/mk1mf.pl for crypto/cversion.c */
-  #define CFLAGS "bcc32 -DWIN32_LEAN_AND_MEAN -q -w-ccc -w-rch -w-pia -w-aus -w-par -w-inl  -c -tWC -tWM -DOPENSSL_SYSNAME_WIN32 -DL_ENDIAN -DDSO_WIN32 -D_stricmp=stricmp -D_strnicmp=strnicmp -D_timeb=timeb -D_ftime=ftime -O2 -ff -fp -DBN_ASM -DMD5_ASM -DSHA1_ASM -DRMD160_ASM -DOPENSSL_NO_RC5 -DOPENSSL_NO_MD2 -DOPENSSL_NO_KRB5 -DOPENSSL_NO_JPAKE -DOPENSSL_NO_DYNAMIC_ENGINE    "
+  #define CFLAGS "compiler: bcc32 -DWIN32_LEAN_AND_MEAN -q -w-ccc -w-rch -w-pia -w-aus -w-par -w-inl  -c -tWC -tWM -DOPENSSL_SYSNAME_WIN32 -DL_ENDIAN -DDSO_WIN32 -D_stricmp=stricmp -D_strnicmp=strnicmp -D_timeb=timeb -D_ftime=ftime -O2 -ff -fp -DBN_ASM -DMD5_ASM -DSHA1_ASM -DRMD160_ASM -DOPENSSL_NO_RC5 -DOPENSSL_NO_MD2 -DOPENSSL_NO_KRB5 -DOPENSSL_NO_JPAKE -DOPENSSL_NO_DYNAMIC_ENGINE    "
   #define PLATFORM "BC-NT"
-  #define DATE "Fri Jan  9 11:28:31 2015"
+  #define DATE "Wed Jan 28 13:03:40 2015"
 #endif

+ 1 - 1
libs/openssl/crypto/cversion.c

@@ -77,7 +77,7 @@ const char *SSLeay_version(int t)
 	if (t == SSLEAY_CFLAGS)
 		{
 #ifdef CFLAGS
-		return(CFLAGS); // MPEXT
+		return(CFLAGS);
 #else
 		return("compiler: information not available");
 #endif

+ 1 - 1
libs/openssl/crypto/ecdsa/ecs_vrf.c

@@ -57,7 +57,7 @@
  */
 
 #include "ecs_locl.h"
-#include "cryptlib.h"
+#include <string.h>
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif

+ 3 - 3
libs/openssl/crypto/opensslv.h

@@ -29,11 +29,11 @@ extern "C" {
  * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for
  *  major minor fix final patch/beta)
  */
-#define OPENSSL_VERSION_NUMBER	0x100010bfL
+#define OPENSSL_VERSION_NUMBER	0x100010cfL
 #ifdef OPENSSL_FIPS
-#define OPENSSL_VERSION_TEXT	"OpenSSL 1.0.1k-fips 8 Jan 2015"
+#define OPENSSL_VERSION_TEXT	"OpenSSL 1.0.1l-fips 15 Jan 2015"
 #else
-#define OPENSSL_VERSION_TEXT	"OpenSSL 1.0.1k 8 Jan 2015"
+#define OPENSSL_VERSION_TEXT	"OpenSSL 1.0.1l 15 Jan 2015"
 #endif
 #define OPENSSL_VERSION_PTEXT	" part of " OPENSSL_VERSION_TEXT
 

+ 2 - 59
libs/openssl/crypto/rand/rand_win.c

@@ -196,12 +196,6 @@ int RAND_poll(void)
 	DWORD w;
 	int good = 0;
 
-	/* Determine the OS version we are on so we can turn off things 
-	 * that do not work properly.
-	 */
-        OSVERSIONINFO osverinfo ;
-        osverinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ;
-        GetVersionEx( &osverinfo ) ;
 
 #if defined(OPENSSL_SYS_WINCE)
 # if defined(_WIN32_WCE) && _WIN32_WCE>=300
@@ -281,56 +275,6 @@ int RAND_poll(void)
          * at random times on Windows 2000.  Reported by Jeffrey Altman.  
          * Only use it on NT.
 	 */
-	/* Wolfgang Marczy <[email protected]> reports that
-	 * the RegQueryValueEx call below can hang on NT4.0 (SP6).
-	 * So we don't use this at all for now. */
-#if 0
-        if ( osverinfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
-		osverinfo.dwMajorVersion < 5)
-		{
-		/* Read Performance Statistics from NT/2000 registry
-		 * The size of the performance data can vary from call
-		 * to call so we must guess the size of the buffer to use
-		 * and increase its size if we get an ERROR_MORE_DATA
-		 * return instead of ERROR_SUCCESS.
-		 */
-		LONG   rc=ERROR_MORE_DATA;
-		char * buf=NULL;
-		DWORD bufsz=0;
-		DWORD length;
-
-		while (rc == ERROR_MORE_DATA)
-			{
-			buf = realloc(buf,bufsz+8192);
-			if (!buf)
-				break;
-			bufsz += 8192;
-
-			length = bufsz;
-			rc = RegQueryValueEx(HKEY_PERFORMANCE_DATA, TEXT("Global"),
-				NULL, NULL, buf, &length);
-			}
-		if (rc == ERROR_SUCCESS)
-			{
-                        /* For entropy count assume only least significant
-			 * byte of each DWORD is random.
-			 */
-			RAND_add(&length, sizeof(length), 0);
-			RAND_add(buf, length, length / 4.0);
-
-			/* Close the Registry Key to allow Windows to cleanup/close
-			 * the open handle
-			 * Note: The 'HKEY_PERFORMANCE_DATA' key is implicitly opened
-			 *       when the RegQueryValueEx above is done.  However, if
-			 *       it is not explicitly closed, it can cause disk
-			 *       partition manipulation problems.
-			 */
-			RegCloseKey(HKEY_PERFORMANCE_DATA);
-			}
-		if (buf)
-			free(buf);
-		}
-#endif
 
 	if (advapi)
 		{
@@ -383,7 +327,7 @@ int RAND_poll(void)
         if (advapi)
 		FreeLibrary(advapi);
 
-	if ((osverinfo.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+	if ((!check_winnt() ||
 	     !OPENSSL_isservice()) &&
 	    (user = LoadLibrary(TEXT("USER32.DLL"))))
 		{
@@ -407,8 +351,7 @@ int RAND_poll(void)
 			 * on NT4 even though it exists in SP3 (or SP6) and
 			 * higher.
 			 */
-			if ( osverinfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
-				osverinfo.dwMajorVersion < 5)
+			if (check_winnt() && !check_win_minplat(5))
 				cursor = 0;
 			}
 		if (cursor)

+ 1 - 1
libs/openssl/crypto/x509v3/v3_ncons.c

@@ -401,7 +401,7 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
 	if (dns->length > base->length)
 		{
 		dnsptr += dns->length - base->length;
-		if (dnsptr[-1] != '.')
+		if (*baseptr != '.' && dnsptr[-1] != '.')
 			return X509_V_ERR_PERMITTED_VIOLATION;
 		}
 

+ 3 - 1
libs/openssl/e_os.h

@@ -368,11 +368,13 @@ static __inline unsigned int _strlen31(const char *str)
 #    define DEFAULT_HOME  "C:"
 #  endif
 
-/* Avoid Windows 8 SDK GetVersion deprecated problems */
+/* Avoid Visual Studio 13 GetVersion deprecated problems */
 #if defined(_MSC_VER) && _MSC_VER>=1800
 #  define check_winnt() (1)
+#  define check_win_minplat(x) (1)
 #else
 #  define check_winnt() (GetVersion() < 0x80000000)
+#  define check_win_minplat(x) (LOBYTE(LOWORD(GetVersion())) >= (x))
 #endif
 
 #else /* The non-microsoft world */

+ 1 - 1
source/Console.cbproj

@@ -65,7 +65,7 @@
 			<ProjectType>CppConsoleApplication</ProjectType>
 			<SanitizedProjectName>Console</SanitizedProjectName>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.1.0.0;InternalName=console;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.6.5.0;ReleaseType=RC;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.1.0.0;InternalName=console;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.7.0.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>4</VerInfo_MajorVer>
 			<VerInfo_MinorVer>1</VerInfo_MinorVer>

+ 1 - 1
source/DragExt.cbproj

@@ -66,7 +66,7 @@
 			<SanitizedProjectName>DragExt</SanitizedProjectName>
 			<VerInfo_DLL>true</VerInfo_DLL>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Drag&amp;Drop shell extension for WinSCP (32-bit);FileVersion=1.2.1.0;InternalName=dragext32;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.6.5.0;ReleaseType=RC;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Drag&amp;Drop shell extension for WinSCP (32-bit);FileVersion=1.2.1.0;InternalName=dragext32;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.7.0.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MinorVer>2</VerInfo_MinorVer>
 			<VerInfo_Release>1</VerInfo_Release>

+ 3 - 3
source/DragExt64.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
 FILEVERSION 1,2,1,0
-PRODUCTVERSION 5,6,5,0
+PRODUCTVERSION 5,7,0,0
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -16,8 +16,8 @@ FILETYPE 0x2
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext64.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "5.6.5.0\0"
-            VALUE "ReleaseType", "RC\0"
+            VALUE "ProductVersion", "5.7.0.0\0"
+            VALUE "ReleaseType", "stable\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 2 - 3
source/WinSCP.cbproj

@@ -83,11 +83,10 @@
 			<SanitizedProjectName>WinSCP</SanitizedProjectName>
 			<UsingDelphiRTL>true</UsingDelphiRTL>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.6.5.0;InternalName=winscp;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.6.5.0;ReleaseType=RC;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.7.0.0;InternalName=winscp;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.7.0.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>5</VerInfo_MajorVer>
-			<VerInfo_MinorVer>6</VerInfo_MinorVer>
-			<VerInfo_Release>5</VerInfo_Release>
+			<VerInfo_MinorVer>7</VerInfo_MinorVer>
 		</PropertyGroup>
 	<PropertyGroup Condition="'$(Base_Win32)'!=''">
 			<Defines>IDE;STRICT;$(Defines)</Defines>

+ 103 - 89
source/components/UnixDirView.cpp

@@ -39,81 +39,6 @@ namespace Unixdirview
   }
 }
 //---------------------------------------------------------------------------
-#ifndef DESIGN_ONLY
-#define RFILE(N) ((TRemoteFile *)(Item ## N->Data))
-int __stdcall CompareDirectories(TListItem *Item1, TListItem *Item2)
-{
-  // Because CompareDirectories is called from each other compare functions
-  // it's sufficient to check pointers only here (see below)
-  assert(Item1 && RFILE(1) && Item2 && RFILE(2));
-
-  if (RFILE(1)->IsParentDirectory && !RFILE(2)->IsParentDirectory) return -1;
-    else
-  if (!RFILE(1)->IsParentDirectory && RFILE(2)->IsParentDirectory) return 1;
-    else
-  if (RFILE(1)->IsDirectory && !RFILE(2)->IsDirectory) return -1;
-    else
-  if (!RFILE(1)->IsDirectory && RFILE(2)->IsDirectory) return 1;
-    else
-  return 0;
-}
-//---------------------------------------------------------------------------
-int __fastcall CompareExtThenFull(const UnicodeString & Name1, const UnicodeString & Name2)
-{
-  UnicodeString Ext1 = UnixExtractFileExt(Name1);
-  UnicodeString Ext2 = UnixExtractFileExt(Name2);
-  int Result = CompareLogicalText(Ext1, Ext2);
-  if (Result == 0)
-  {
-    Result = CompareLogicalText(Name1, Name2);
-  }
-  return Result;
-}
-//---------------------------------------------------------------------------
-#define DEFINE_COMPARE_FUNC_EX(PROPERTY, NAME, COMPAREFUNCFILE, COMPAREFUNCDIR, FALLBACK) \
-  int __stdcall Compare ## NAME(TListItem *Item1, TListItem *Item2, TUnixDirView *DirView) \
-  { \
-    int Result = CompareDirectories(Item1, Item2); \
-    if (!Result) \
-    { \
-      if (RFILE(1)->IsDirectory) \
-      {  \
-        Result = COMPAREFUNCDIR(RFILE(1)->PROPERTY, RFILE(2)->PROPERTY); \
-      } \
-      else \
-      { \
-        Result = COMPAREFUNCFILE(RFILE(1)->PROPERTY, RFILE(2)->PROPERTY); \
-      } \
-      if (!DirView->UnixColProperties->SortAscending) Result = -Result; \
-      if (Result == 0) \
-      { \
-        Result = FALLBACK(Item1, Item2, DirView); \
-      } \
-    } \
-    return Result; \
-  }
-#define DEFINE_COMPARE_FUNC(PROPERTY, COMPAREFUNC) \
-  DEFINE_COMPARE_FUNC_EX(PROPERTY, PROPERTY, COMPAREFUNC, COMPAREFUNC, CompareItemFileName)
-#define COMPARE_NUMBER(Num1, Num2) ( Num1 < Num2 ? -1 : ( Num1 > Num2 ? 1 : 0) )
-#define COMPARE_DUMMY(X1, X2) 0
-#define COMPARE_ITEM_DUMMY(X1, X2, DV) 0
-#define COMPARE_TOKEN(Token1, Token2) Token1.Compare(Token2)
-//---------------------------------------------------------------------------
-DEFINE_COMPARE_FUNC_EX(FileName, ItemFileName, CompareLogicalText, CompareLogicalText, COMPARE_ITEM_DUMMY);
-DEFINE_COMPARE_FUNC(Size, COMPARE_NUMBER);
-DEFINE_COMPARE_FUNC(Modification, COMPARE_NUMBER);
-DEFINE_COMPARE_FUNC(RightsStr, AnsiCompareText);
-DEFINE_COMPARE_FUNC(Owner, COMPARE_TOKEN);
-DEFINE_COMPARE_FUNC(Group, COMPARE_TOKEN);
-DEFINE_COMPARE_FUNC_EX(Extension, Extension, CompareLogicalText, COMPARE_DUMMY, CompareItemFileName);
-DEFINE_COMPARE_FUNC(LinkTo, CompareLogicalText);
-DEFINE_COMPARE_FUNC_EX(TypeName, TypeName, CompareLogicalText, CompareLogicalText, CompareExtension);
-//---------------------------------------------------------------------------
-#undef DEFINE_COMPARE_FUNC
-#undef COMPARE_NUMBER
-#undef RFILE
-#endif
-//---------------------------------------------------------------------------
 #define HOMEDIRECTORY L""
 //---------------------------------------------------------------------------
 __fastcall TUnixDirView::TUnixDirView(TComponent* Owner)
@@ -699,26 +624,115 @@ void __fastcall TUnixDirView::SetPath(UnicodeString Value)
 #endif
 }
 //---------------------------------------------------------------------------
+#ifndef DESIGN_ONLY
+#define COMPARE_NUMBER(Num1, Num2) ( Num1 < Num2 ? -1 : ( Num1 > Num2 ? 1 : 0) )
+//---------------------------------------------------------------------------
+int __stdcall CompareFile(TListItem * Item1, TListItem * Item2, TUnixDirView * DirView)
+{
+  assert((Item1 != NULL) && (Item2 != NULL));
+  TRemoteFile * File1 = NOT_NULL((TRemoteFile *)(Item1->Data));
+  TRemoteFile * File2 = NOT_NULL((TRemoteFile *)(Item2->Data));
+
+  int Result;
+  if (File1->IsParentDirectory && !File2->IsParentDirectory)
+  {
+    Result = -1;
+  }
+  else if (!File1->IsParentDirectory && File2->IsParentDirectory)
+  {
+    Result = 1;
+  }
+  else if (File1->IsDirectory && !File2->IsDirectory)
+  {
+    Result = -1;
+  }
+  else if (!File1->IsDirectory && File2->IsDirectory)
+  {
+    Result = 1;
+  }
+  else
+  {
+    Result = 0;
+
+    switch (DirView->SortColumn)
+    {
+      case uvName:
+        // fallback
+        break;
+
+      case uvSize:
+        Result = COMPARE_NUMBER(File1->Size, File2->Size);
+        break;
+
+      case uvChanged:
+        Result = COMPARE_NUMBER(File1->Modification, File2->Modification);
+        break;
+
+      case uvRights:
+        Result = AnsiCompareText(File1->RightsStr, File2->RightsStr);
+        break;
+
+      case uvOwner:
+        Result = File1->Owner.Compare(File2->Owner);
+        break;
+
+      case uvGroup:
+        Result = File1->Group.Compare(File2->Group);
+        break;
+
+      case uvExt:
+        // Duplicated in uvType branch
+        if (!File1->IsDirectory)
+        {
+          Result = CompareLogicalText(File1->Extension, File2->Extension);
+        }
+        else
+        {
+          // fallback
+        }
+        break;
+
+      case uvLinkTarget:
+        Result = CompareLogicalText(File1->LinkTo, File2->LinkTo);
+        break;
+
+      case uvType:
+        Result = CompareLogicalText(File1->TypeName, File2->TypeName);
+        // fallback to uvExt
+        if ((Result == 0) && !File1->IsDirectory)
+        {
+          Result = CompareLogicalText(File1->Extension, File2->Extension);
+        }
+        break;
+
+      default:
+        FAIL;
+    }
+
+    if (Result == 0)
+    {
+      Result = CompareLogicalText(File1->FileName, File2->FileName);
+    }
+
+    if (!DirView->UnixColProperties->SortAscending)
+    {
+      Result = -Result;
+    }
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+#undef COMPARE_NUMBER
+#endif
+//---------------------------------------------------------------------------
 void __fastcall TUnixDirView::SortItems()
 {
 #ifndef DESIGN_ONLY
   assert(Terminal);
   if (HandleAllocated())
   {
-    PFNLVCOMPARE SortProc;
-    switch (SortColumn) {
-      case uvName: SortProc = (PFNLVCOMPARE)CompareItemFileName; break;
-      case uvSize: SortProc = (PFNLVCOMPARE)CompareSize; break;
-      case uvChanged: SortProc = (PFNLVCOMPARE)CompareModification; break;
-      case uvRights: SortProc = (PFNLVCOMPARE)CompareRightsStr; break;
-      case uvOwner: SortProc = (PFNLVCOMPARE)CompareOwner; break;
-      case uvGroup: SortProc = (PFNLVCOMPARE)CompareGroup; break;
-      case uvExt: SortProc = (PFNLVCOMPARE)CompareExtension; break;
-      case uvLinkTarget: SortProc = (PFNLVCOMPARE)CompareLinkTo; break;
-      case uvType: SortProc = (PFNLVCOMPARE)CompareTypeName; break;
-      default: FAIL;
-    }
-    CustomSortItems(SortProc);
+    CustomSortItems(CompareFile);
   }
 #endif
 }

+ 27 - 5
source/core/Configuration.cpp

@@ -101,8 +101,10 @@ void __fastcall TConfiguration::Default()
   FPermanentLogFileName = FLogFileName;
   FLogFileAppend = true;
   FLogSensitive = false;
+  FPermanentLogSensitive = FLogSensitive;
   FLogWindowLines = 100;
   FLogProtocol = 0;
+  FPermanentLogProtocol = FLogProtocol;
   UpdateActualLogProtocol();
   FLogActions = false;
   FPermanentLogActions = false;
@@ -208,9 +210,9 @@ UnicodeString __fastcall TConfiguration::PropertyToKey(const UnicodeString & Pro
     KEYEX(Bool,  PermanentLogging, L"Logging"); \
     KEYEX(String,PermanentLogFileName, L"LogFileName"); \
     KEY(Bool,    LogFileAppend); \
-    KEY(Bool,    LogSensitive); \
+    KEYEX(Bool,  PermanentLogSensitive, L"LogSensitive"); \
     KEY(Integer, LogWindowLines); \
-    KEY(Integer, LogProtocol); \
+    KEYEX(Integer,PermanentLogProtocol, L"LogProtocol"); \
     KEYEX(Bool,  PermanentLogActions, L"LogActions"); \
     KEYEX(String,PermanentActionsLogFileName, L"ActionsLogFileName"); \
   );
@@ -1224,6 +1226,16 @@ void __fastcall TConfiguration::TemporaryActionsLogging(const UnicodeString ALog
   FActionsLogFileName = ALogFileName;
 }
 //---------------------------------------------------------------------
+void __fastcall TConfiguration::TemporaryLogProtocol(int ALogProtocol)
+{
+  FLogProtocol = ALogProtocol;
+}
+//---------------------------------------------------------------------
+void __fastcall TConfiguration::TemporaryLogSensitive(bool ALogSensitive)
+{
+  FLogSensitive = ALogSensitive;
+}
+//---------------------------------------------------------------------
 void __fastcall TConfiguration::SetLogging(bool value)
 {
   if (Logging != value)
@@ -1267,8 +1279,13 @@ void __fastcall TConfiguration::UpdateActualLogProtocol()
 //---------------------------------------------------------------------
 void __fastcall TConfiguration::SetLogProtocol(int value)
 {
-  SET_CONFIG_PROPERTY(LogProtocol);
-  UpdateActualLogProtocol();
+  if (LogProtocol != value)
+  {
+    FPermanentLogProtocol = value;
+    FLogProtocol = value;
+    Changed();
+    UpdateActualLogProtocol();
+  }
 }
 //---------------------------------------------------------------------
 void __fastcall TConfiguration::SetLogActions(bool value)
@@ -1288,7 +1305,12 @@ void __fastcall TConfiguration::SetLogFileAppend(bool value)
 //---------------------------------------------------------------------
 void __fastcall TConfiguration::SetLogSensitive(bool value)
 {
-  SET_CONFIG_PROPERTY(LogSensitive);
+  if (LogSensitive != value)
+  {
+    FPermanentLogSensitive = value;
+    FLogSensitive = value;
+    Changed();
+  }
 }
 //---------------------------------------------------------------------
 void __fastcall TConfiguration::SetLogWindowLines(int value)

+ 6 - 0
source/core/Configuration.h

@@ -32,7 +32,9 @@ private:
   int FLogWindowLines;
   bool FLogFileAppend;
   bool FLogSensitive;
+  bool FPermanentLogSensitive;
   int FLogProtocol;
+  int FPermanentLogProtocol;
   int FActualLogProtocol;
   bool FLogActions;
   bool FPermanentLogActions;
@@ -160,6 +162,8 @@ protected:
   __property UnicodeString PermanentLogFileName  = { read=FPermanentLogFileName, write=SetLogFileName };
   __property bool PermanentLogActions  = { read=FPermanentLogActions, write=SetLogActions };
   __property UnicodeString PermanentActionsLogFileName  = { read=FPermanentActionsLogFileName, write=SetActionsLogFileName };
+  __property int PermanentLogProtocol  = { read=FPermanentLogProtocol, write=SetLogProtocol };
+  __property bool PermanentLogSensitive  = { read=FPermanentLogSensitive, write=SetLogSensitive };
 
 public:
   __fastcall TConfiguration();
@@ -190,6 +194,8 @@ public:
   virtual THierarchicalStorage * CreateScpStorage(bool & SessionList);
   void __fastcall TemporaryLogging(const UnicodeString ALogFileName);
   void __fastcall TemporaryActionsLogging(const UnicodeString ALogFileName);
+  void __fastcall TemporaryLogProtocol(int ALogProtocol);
+  void __fastcall TemporaryLogSensitive(bool ALogSensitive);
   virtual RawByteString __fastcall EncryptPassword(UnicodeString Password, UnicodeString Key);
   virtual UnicodeString __fastcall DecryptPassword(RawByteString Password, UnicodeString Key);
   virtual RawByteString __fastcall StronglyRecryptPassword(RawByteString Password, UnicodeString Key);

+ 54 - 13
source/core/FtpFileSystem.cpp

@@ -190,7 +190,7 @@ const wchar_t CertificateStorageKey[] = L"FtpsCertificates";
 const UnicodeString SiteCommand(L"SITE");
 const UnicodeString SymlinkSiteCommand(L"SYMLINK");
 const UnicodeString CopySiteCommand(L"COPY");
-const UnicodeString HashCommand(L"HASH");
+const UnicodeString HashCommand(L"HASH"); // Cerberos + FileZilla servers
 const UnicodeString AvblCommand(L"AVBL");
 const UnicodeString XQuotaCommand(L"XQUOTA");
 //---------------------------------------------------------------------------
@@ -252,7 +252,8 @@ __fastcall TFTPFileSystem::TFTPFileSystem(TTerminal * ATerminal):
   FOnCaptureOutput(NULL),
   FFileSystemInfoValid(false),
   FDoListAll(false),
-  FServerCapabilities(NULL)
+  FServerCapabilities(NULL),
+  FReadCurrentDirectory(false)
 {
   ResetReply();
 
@@ -268,11 +269,12 @@ __fastcall TFTPFileSystem::TFTPFileSystem(TTerminal * ATerminal):
 
   FChecksumAlgs.reset(new TStringList());
   FChecksumCommands.reset(new TStringList());
-  RegisterChecksumAlgCommand(Sha1ChecksumAlg, L"XSHA1");
-  RegisterChecksumAlgCommand(Sha256ChecksumAlg, L"XSHA256");
-  RegisterChecksumAlgCommand(Sha512ChecksumAlg, L"XSHA512");
-  RegisterChecksumAlgCommand(Md5ChecksumAlg, L"XMD5");
-  RegisterChecksumAlgCommand(Crc32ChecksumAlg, L"XCRC");
+  RegisterChecksumAlgCommand(Sha1ChecksumAlg, L"XSHA1"); // e.g. Cerberos FTP
+  RegisterChecksumAlgCommand(Sha256ChecksumAlg, L"XSHA256"); // e.g. Cerberos FTP
+  RegisterChecksumAlgCommand(Sha512ChecksumAlg, L"XSHA512"); // e.g. Cerberos FTP
+  RegisterChecksumAlgCommand(Md5ChecksumAlg, L"XMD5"); // e.g. Cerberos FTP
+  RegisterChecksumAlgCommand(Md5ChecksumAlg, L"MD5"); // e.g. Apache FTP
+  RegisterChecksumAlgCommand(Crc32ChecksumAlg, L"XCRC"); // e.g. Cerberos FTP
 }
 //---------------------------------------------------------------------------
 __fastcall TFTPFileSystem::~TFTPFileSystem()
@@ -317,7 +319,7 @@ void __fastcall TFTPFileSystem::Open()
   DiscardMessages();
 
   ResetCaches();
-  FCurrentDirectory = L"";
+  FReadCurrentDirectory = true;
   FHomeDirectory = L"";
 
   TSessionData * Data = FTerminal->SessionData;
@@ -361,7 +363,7 @@ void __fastcall TFTPFileSystem::Open()
         default:
         case 0:
         case 1:
-          LogLevel = TFileZillaIntf::LOG_WARNING;
+          LogLevel = TFileZillaIntf::LOG_PROGRESS;
           break;
 
         case 2:
@@ -707,6 +709,23 @@ void __fastcall TFTPFileSystem::CollectUsage()
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPComplete");
   }
+  // 220 Core FTP Server Version 1.2, build 567, 64-bit, installed 8 days ago Unregistered
+  // ...
+  // SYST
+  // 215 UNIX Type: L8
+  else if (ContainsText(FWelcomeMessage, L"Core FTP Server"))
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPCore");
+  }
+  // 220 Service ready for new user.
+  // ..
+  // SYST
+  // 215 UNIX Type: Apache FtpServer
+  // (e.g. brickftp.com)
+  else if (ContainsText(FSystem, L"Apache FtpServer"))
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPApache");
+  }
   else
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOther");
@@ -723,6 +742,7 @@ void __fastcall TFTPFileSystem::Idle()
     if ((FTerminal->SessionData->FtpPingType != ptOff) &&
         (double(Now() - FLastDataSent) > double(FTerminal->SessionData->FtpPingIntervalDT) * 4))
     {
+      FTerminal->LogEvent(L"Dummy directory read to keep session alive.");
       FLastDataSent = Now();
 
       TRemoteDirectory * Files = new TRemoteDirectory(FTerminal);
@@ -872,12 +892,13 @@ void __fastcall TFTPFileSystem::ChangeDirectory(const UnicodeString ADirectory)
   DoChangeDirectory(Directory);
 
   // make next ReadCurrentDirectory retrieve actual server-side current directory
-  FCurrentDirectory = L"";
+  FReadCurrentDirectory = true;
 }
 //---------------------------------------------------------------------------
 void __fastcall TFTPFileSystem::CachedChangeDirectory(const UnicodeString Directory)
 {
   FCurrentDirectory = UnixExcludeTrailingBackslash(Directory);
+  FReadCurrentDirectory = false;
 }
 //---------------------------------------------------------------------------
 void __fastcall TFTPFileSystem::ChangeFileProperties(const UnicodeString AFileName,
@@ -959,6 +980,9 @@ bool __fastcall TFTPFileSystem::LoadFilesProperties(TStrings * /*FileList*/)
 UnicodeString __fastcall TFTPFileSystem::DoCalculateFileChecksum(
   bool UsingHashCommand, const UnicodeString & Alg, TRemoteFile * File)
 {
+  // Overview of server supporting various hash commands is at:
+  // https://tools.ietf.org/html/draft-ietf-ftpext2-hash-03#appendix-B
+
   UnicodeString CommandName;
 
   if (UsingHashCommand)
@@ -1034,10 +1058,21 @@ UnicodeString __fastcall TFTPFileSystem::DoCalculateFileChecksum(
       Hash = Range;
     }
   }
-  else // All X<hash> commands
+  else // All hash-specific commands
   {
     // Accepting any 2xx response. Most servers use 213,
     // but for example WS_FTP uses non-sense code 220 (Service ready for new user)
+
+    // MD5 response according to a draft-twine-ftpmd5-00 includes a file name
+    // (implemented by Apache FtpServer).
+    // Other commands (X<hash>) return the hash only.
+    ResponseText = ResponseText.Trim();
+    int P = ResponseText.LastDelimiter(L" ");
+    if (P > 0)
+    {
+      ResponseText.Delete(1, P);
+    }
+
     Hash = ResponseText;
   }
 
@@ -2172,6 +2207,7 @@ void __fastcall TFTPFileSystem::HomeDirectory()
   // of ChangeDirectory, such as EnsureLocation
   DoChangeDirectory(FHomeDirectory);
   FCurrentDirectory = FHomeDirectory;
+  FReadCurrentDirectory = false;
   // make sure FZAPI is aware that we changed current working directory
   FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
 }
@@ -2239,7 +2275,7 @@ void __fastcall TFTPFileSystem::ReadCurrentDirectory()
   // and immediately after call to CWD,
   // later our current directory may be not synchronized with FZAPI current
   // directory anyway, see comments in EnsureLocation
-  if (FCurrentDirectory.IsEmpty())
+  if (FReadCurrentDirectory || ALWAYS_FALSE(FCurrentDirectory.IsEmpty()))
   {
     UnicodeString Command = L"PWD";
     SendCommand(Command);
@@ -2284,6 +2320,7 @@ void __fastcall TFTPFileSystem::ReadCurrentDirectory()
         if (Result)
         {
           FCurrentDirectory = UnixExcludeTrailingBackslash(Path);
+          FReadCurrentDirectory = false;
         }
       }
 
@@ -3643,7 +3680,7 @@ bool __fastcall TFTPFileSystem::HandleStatus(const wchar_t * AStatus, int Type)
       // by setting dummy one
       if (Type == TFileZillaIntf::LOG_ERROR)
       {
-        if (Status == FTimeoutStatus)
+        if (StartsStr(FTimeoutStatus, Status))
         {
           if (NoFinalLastCode())
           {
@@ -3665,6 +3702,10 @@ bool __fastcall TFTPFileSystem::HandleStatus(const wchar_t * AStatus, int Type)
       LogType = llMessage;
       break;
 
+    case TFileZillaIntf::LOG_PROGRESS:
+      LogType = llMessage;
+      break;
+
     case TFileZillaIntf::LOG_REPLY:
       HandleReplyStatus(AStatus);
       LogType = llOutput;

+ 1 - 0
source/core/FtpFileSystem.h

@@ -237,6 +237,7 @@ private:
   UnicodeString FSystem;
   TStrings * FFeatures;
   UnicodeString FCurrentDirectory;
+  bool FReadCurrentDirectory;
   UnicodeString FHomeDirectory;
   TRemoteFileList * FFileList;
   TRemoteFileList * FFileListCache;

+ 3 - 0
source/core/Interface.h

@@ -7,6 +7,8 @@
 #define HELP_NONE ""
 //---------------------------------------------------------------------------
 TConfiguration * __fastcall CreateConfiguration();
+class TOptions;
+TOptions * __fastcall GetGlobalOptions();
 
 void __fastcall ShowExtendedException(Exception * E);
 bool __fastcall AppendExceptionStackTraceAndForget(TStrings *& MoreMessages);
@@ -16,6 +18,7 @@ UnicodeString __fastcall GetRegistryKey();
 void * __fastcall BusyStart();
 void __fastcall BusyEnd(void * Token);
 const unsigned int GUIUpdateInterval = 200;
+void __fastcall SetNoGUI();
 bool __fastcall ProcessGUI(bool Force = false);
 UnicodeString __fastcall AppNameString();
 UnicodeString __fastcall SshVersionString();

+ 28 - 16
source/core/NamedObjs.cpp

@@ -9,13 +9,7 @@
 //---------------------------------------------------------------------------
 int __fastcall NamedObjectSortProc(void * Item1, void * Item2)
 {
-  bool HasPrefix1 = ((TNamedObject *)Item1)->Hidden;
-  bool HasPrefix2 = ((TNamedObject *)Item2)->Hidden;
-  if (HasPrefix1 && !HasPrefix2) return -1;
-    else
-  if (!HasPrefix1 && HasPrefix2) return 1;
-    else
-  return CompareLogicalText(((TNamedObject *)Item1)->Name, ((TNamedObject *)Item2)->Name);
+  return static_cast<TNamedObject *>(Item1)->Compare(static_cast<TNamedObject *>(Item2));
 }
 //--- TNamedObject ----------------------------------------------------------
 __fastcall TNamedObject::TNamedObject(UnicodeString AName)
@@ -29,13 +23,27 @@ void __fastcall TNamedObject::SetName(UnicodeString value)
   FName = value;
 }
 //---------------------------------------------------------------------------
-Integer __fastcall TNamedObject::CompareName(UnicodeString aName,
-  Boolean CaseSensitive)
+int __fastcall TNamedObject::Compare(TNamedObject * Other)
 {
-  if (CaseSensitive)
-    return Name.Compare(aName);
+  int Result;
+  if (Hidden && !Other->Hidden)
+  {
+    Result = -1;
+  }
+  else if (!Hidden && Other->Hidden)
+  {
+    Result = 1;
+  }
   else
-    return Name.CompareIC(aName);
+  {
+    Result = CompareLogicalText(Name, Other->Name);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TNamedObject::IsSameName(const UnicodeString & AName)
+{
+  return (Name.CompareIC(AName) == 0);
 }
 //---------------------------------------------------------------------------
 void __fastcall TNamedObject::MakeUniqueIn(TNamedObjectList * List)
@@ -101,13 +109,17 @@ void __fastcall TNamedObjectList::Notify(void *Ptr, TListNotification Action)
   }
 }
 //---------------------------------------------------------------------------
-TNamedObject * __fastcall TNamedObjectList::FindByName(UnicodeString Name,
-  Boolean CaseSensitive)
+TNamedObject * __fastcall TNamedObjectList::FindByName(const UnicodeString & Name)
 {
   // this should/can be optimized when list is sorted
   for (Integer Index = 0; Index < TObjectList::Count; Index++)
-    if (!((TNamedObject *)Items[Index])->CompareName(Name, CaseSensitive))
-      return (TNamedObject *)Items[Index];
+  {
+    TNamedObject * NamedObject = AtObject(Index);
+    if (NamedObject->IsSameName(Name))
+    {
+      return NamedObject;
+    }
+  }
   return NULL;
 }
 //---------------------------------------------------------------------------

+ 3 - 2
source/core/NamedObjs.h

@@ -12,7 +12,8 @@ public:
   __property UnicodeString Name = { read = FName, write = SetName };
   __property bool Hidden = { read = FHidden };
   __fastcall TNamedObject() {};
-  Integer __fastcall CompareName(UnicodeString aName, Boolean CaseSensitive = False);
+  bool __fastcall IsSameName(const UnicodeString & Name);
+  virtual int __fastcall Compare(TNamedObject * Other);
   __fastcall TNamedObject(UnicodeString aName);
   void __fastcall MakeUniqueIn(TNamedObjectList * List);
 private:
@@ -40,7 +41,7 @@ public:
   __fastcall TNamedObjectList();
   void __fastcall AlphaSort();
   virtual TNamedObject * __fastcall AtObject(Integer Index);
-  TNamedObject * __fastcall FindByName(UnicodeString Name, Boolean CaseSensitive = False);
+  TNamedObject * __fastcall FindByName(const UnicodeString & Name);
   __property int Count = { read = GetCount, write = SetCount };
   __property int CountIncludingHidden = { read = GetCountIncludingHidden };
 };

+ 43 - 2
source/core/Option.cpp

@@ -10,8 +10,8 @@
 //---------------------------------------------------------------------------
 __fastcall TOptions::TOptions()
 {
-  FSwitchMarks = L"-/";
-  FSwitchValueDelimiters = L":=";
+  FSwitchMarks = L"/-";
+  FSwitchValueDelimiters = L"=:";
   FNoMoreSwitches = false;
   FParamCount = 0;
 }
@@ -70,6 +70,8 @@ void __fastcall TOptions::Add(UnicodeString Value)
       ++FParamCount;
     }
   }
+
+  FOriginalOptions = FOptions;
 }
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TOptions::GetParam(int Index)
@@ -249,6 +251,18 @@ bool __fastcall TOptions::UnusedSwitch(UnicodeString & Switch)
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TOptions::WasSwitchAdded(UnicodeString & Switch)
+{
+  bool Result =
+    ALWAYS_TRUE(FOptions.size() > 0) &&
+    (FOptions.back().Type == otSwitch);
+  if (Result)
+  {
+    Switch = FOptions.back().Name;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TOptions::ParamsProcessed(int ParamsStart, int ParamsCount)
 {
   if (ParamsCount > 0)
@@ -278,3 +292,30 @@ void __fastcall TOptions::ParamsProcessed(int ParamsStart, int ParamsCount)
     }
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall TOptions::LogOptions(TLogOptionEvent OnLogOption)
+{
+  for (size_t Index = 0; Index < FOriginalOptions.size(); Index++)
+  {
+    const TOption & Option = FOriginalOptions[Index];
+    UnicodeString LogStr;
+    switch (Option.Type)
+    {
+      case otParam:
+        LogStr = FORMAT(L"Parameter: %s", (Option.Value));
+        assert(Option.Name.IsEmpty());
+        break;
+
+      case otSwitch:
+        LogStr =
+          FORMAT(L"Switch:    %s%s%s%s",
+            (FSwitchMarks[1], Option.Name, (Option.Value.IsEmpty() ? UnicodeString() : FSwitchValueDelimiters.SubString(1, 1)), Option.Value));
+        break;
+
+      default:
+        FAIL;
+        break;
+    }
+    OnLogOption(LogStr);
+  }
+}

+ 8 - 1
source/core/Option.h

@@ -6,6 +6,8 @@
 //---------------------------------------------------------------------------
 enum TOptionType { otParam, otSwitch };
 //---------------------------------------------------------------------------
+typedef void __fastcall (__closure *TLogOptionEvent)(const UnicodeString & LogStr);
+//---------------------------------------------------------------------------
 class TOptions
 {
 public:
@@ -22,6 +24,9 @@ public:
   bool __fastcall SwitchValue(const UnicodeString Switch, bool Default);
   bool __fastcall SwitchValue(const UnicodeString Switch, bool Default, bool DefaultOnNonExistence);
   bool __fastcall UnusedSwitch(UnicodeString & Switch);
+  bool __fastcall WasSwitchAdded(UnicodeString & Switch);
+
+  void __fastcall LogOptions(TLogOptionEvent OnEnumOption);
 
   __property int ParamCount = { read = FParamCount };
   __property UnicodeString Param[int Index] = { read = GetParam };
@@ -45,7 +50,9 @@ private:
     bool Used;
   };
 
-  std::vector<TOption> FOptions;
+  typedef std::vector<TOption> TOptionsVector;
+  TOptionsVector FOptions;
+  TOptionsVector FOriginalOptions;
   bool FNoMoreSwitches;
   int FParamCount;
 

+ 50 - 13
source/core/Script.cpp

@@ -443,6 +443,11 @@ void __fastcall TScript::Log(TLogLineType Type, AnsiString Str)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::LogOption(const UnicodeString & LogStr)
+{
+  Log(llInput, LogStr);
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::LogPendingLines(TTerminal * ATerminal)
 {
   if (IsTerminalLogging(ATerminal) && (FPendingLogLines->Count > 0))
@@ -487,6 +492,16 @@ void __fastcall TScript::Command(UnicodeString Cmd)
         UnicodeString LogCmd = GetLogCmd(FullCmd, Command, Cmd);
         Log(llInput, LogCmd);
 
+        if (Configuration->LogProtocol >= 1)
+        {
+          UnicodeString DummyLogCmd;
+          if (ALWAYS_TRUE(CutToken(LogCmd, DummyLogCmd)))
+          {
+            std::unique_ptr<TScriptProcParams> Parameters(new TScriptProcParams(LogCmd));
+            Parameters->LogOptions(LogOption);
+          }
+        }
+
         if (FEcho)
         {
           PrintLine(LogCmd);
@@ -2300,33 +2315,55 @@ UnicodeString __fastcall TManagementScript::GetLogCmd(const UnicodeString & Full
     UnicodeString AParams = Params;
     std::unique_ptr<TScriptProcParams> Parameters(new TScriptProcParams(L""));
 
-    UnicodeString MaskedParams;
+    UnicodeString Url;
+    UnicodeString MaskedParamsPre;
+    UnicodeString MaskedParamsPost;
+
     UnicodeString Param;
     UnicodeString RawParam;
-    while ((Parameters->ParamCount == 0) && CutToken(AParams, Param, &RawParam))
+    bool AnySensitiveOption = false;
+
+    while (CutToken(AParams, Param, &RawParam))
     {
       Parameters->Add(Param);
-      if (Parameters->ParamCount == 0)
+      if ((Parameters->ParamCount == 1) && Url.IsEmpty())
       {
+        Url = Param;
+      }
+      else
+      {
+        UnicodeString & MaskedParams = Url.IsEmpty() ? MaskedParamsPre : MaskedParamsPost;
+
+        UnicodeString Switch;
+        if (Parameters->WasSwitchAdded(Switch) &&
+            TSessionData::IsSensitiveOption(Switch))
+        {
+          // We should use something like TProgramParams::FormatSwitch here
+          RawParam = FORMAT(L"-%s=***", (Switch));
+          AnySensitiveOption = true;
+        }
         AddToList(MaskedParams, RawParam, L" ");
       }
     }
 
-    if (Parameters->ParamCount > 0)
+    if (!Url.IsEmpty() || AnySensitiveOption)
     {
-      UnicodeString Session = Parameters->Param[1];
-
       UnicodeString MaskedUrl;
       bool DefaultsOnly;
 
-      std::unique_ptr<TSessionData> Data(
-        FStoredSessions->ParseUrl(Session, Parameters.get(), DefaultsOnly, NULL, NULL, &MaskedUrl));
-      if (Session != MaskedUrl)
+      if (!Url.IsEmpty())
+      {
+        std::unique_ptr<TSessionData> Data(
+          FStoredSessions->ParseUrl(Url, Parameters.get(), DefaultsOnly, NULL, NULL, &MaskedUrl));
+      }
+
+      if ((Url != MaskedUrl) || AnySensitiveOption)
       {
-        // todo: quote, if necessary
-        AddToList(MaskedParams, MaskedUrl, L" ");
-        AddToList(MaskedParams, AParams, L" ");
-        Result = Command + L" " + MaskedParams;
+        Result = Command;
+        // AddToList is noop, when respective component is empty
+        AddToList(Result, MaskedParamsPre, L" ");
+        AddToList(Result, MaskedUrl, L" ");
+        AddToList(Result, MaskedParamsPost, L" ");
       }
     }
   }

+ 1 - 0
source/core/Script.h

@@ -173,6 +173,7 @@ private:
   bool __fastcall HasNonDefaultCopyParams();
   void __fastcall CheckDefaultSynchronizeParams();
   void __fastcall NotSupported();
+  void __fastcall LogOption(const UnicodeString & LogStr);
 };
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TScriptInputEvent)(TScript * Script, const UnicodeString Prompt, UnicodeString & Str);

+ 11 - 0
source/core/SecureShell.cpp

@@ -2328,6 +2328,17 @@ void __fastcall TSecureShell::CollectUsage()
   {
     Configuration->Usage->Inc(L"OpenedSessionsSSHComplete");
   }
+  // SSH-2.0-CoreFTP-0.3.3
+  else if (ContainsText(FSessionInfo.SshImplementation, L"CoreFTP"))
+  {
+    Configuration->Usage->Inc(L"OpenedSessionsSSHCore");
+  }
+  // SSH-2.0-SSHD-CORE-0.11.0 (value is configurable, this is a default)
+  // (Apache Mina SSHD, e.g. on brickftp.com)
+  else if (ContainsText(FSessionInfo.SshImplementation, L"SSHD-CORE"))
+  {
+    Configuration->Usage->Inc(L"OpenedSessionsSSHApache");
+  }
   else
   {
     Configuration->Usage->Inc(L"OpenedSessionsSSHOther");

+ 23 - 1
source/core/SessionData.cpp

@@ -52,6 +52,7 @@ const wchar_t UrlParamSeparator = L';';
 const wchar_t UrlParamValueSeparator = L'=';
 const UnicodeString UrlHostKeyParamName(L"fingerprint");
 const UnicodeString UrlSaveParamName(L"save");
+const UnicodeString PassphraseOption(L"passphrase");
 //---------------------------------------------------------------------
 TDateTime __fastcall SecToDateTime(int Sec)
 {
@@ -69,6 +70,22 @@ _fastcall TSessionData::~TSessionData()
 {
 }
 //---------------------------------------------------------------------
+int __fastcall TSessionData::Compare(TNamedObject * Other)
+{
+  int Result;
+  // To avoid using CompareLogicalText on hex names of sessions in workspace.
+  // The session 000A would be sorted before 0001.
+  if (IsWorkspace && NOT_NULL(dynamic_cast<TSessionData *>(Other))->IsWorkspace)
+  {
+    Result = CompareText(Name, Other->Name);
+  }
+  else
+  {
+    Result = TNamedObject::Compare(Other);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::Default()
 {
   HostName = L"";
@@ -1228,6 +1245,11 @@ bool __fastcall TSessionData::IsProtocolUrl(
     DoIsProtocolUrl(Url, WinSCPProtocolPrefix + Protocol, ProtocolLen);
 }
 //---------------------------------------------------------------------
+bool __fastcall TSessionData::IsSensitiveOption(const UnicodeString & Option)
+{
+  return AnsiSameText(Option, PassphraseOption);
+}
+//---------------------------------------------------------------------
 bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
   TStoredSessionList * StoredSessions, bool & DefaultsOnly, UnicodeString * FileName,
   bool * AProtocolDefined, UnicodeString * MaskedUrl)
@@ -1513,7 +1535,7 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
     {
       PublicKeyFile = Value;
     }
-    if (Options->FindSwitch(L"passphrase", Value))
+    if (Options->FindSwitch(PassphraseOption, Value))
     {
       Passphrase = Value;
     }

+ 2 - 1
source/core/SessionData.h

@@ -205,7 +205,6 @@ private:
   UnicodeString __fastcall GetUserNameExpanded();
   void __fastcall SetPassword(UnicodeString value);
   UnicodeString __fastcall GetPassword() const;
-  void __fastcall SetPasswordless(bool value);
   void __fastcall SetPingInterval(int value);
   void __fastcall SetTryAgent(bool value);
   void __fastcall SetAgentFwd(bool value);
@@ -382,6 +381,7 @@ public:
   void __fastcall Remove();
   void __fastcall CacheHostKeyIfNotCached();
   virtual void __fastcall Assign(TPersistent * Source);
+  virtual int __fastcall Compare(TNamedObject * Other);
   void __fastcall CopyData(TSessionData * Source);
   void __fastcall CopyDirectoriesStateData(TSessionData * SourceData);
   bool __fastcall ParseUrl(UnicodeString Url, TOptions * Options,
@@ -401,6 +401,7 @@ public:
   static UnicodeString __fastcall ExtractLocalName(const UnicodeString & Name);
   static UnicodeString __fastcall ExtractFolderName(const UnicodeString & Name);
   static UnicodeString __fastcall ComposePath(const UnicodeString & Path, const UnicodeString & Name);
+  static bool __fastcall IsSensitiveOption(const UnicodeString & Option);
 
   __property UnicodeString HostName  = { read=FHostName, write=SetHostName };
   __property UnicodeString HostNameExpanded  = { read=GetHostNameExpanded };

+ 43 - 9
source/core/SessionInfo.cpp

@@ -956,6 +956,9 @@ UnicodeString __fastcall TSessionLog::LogSensitive(const UnicodeString & Str)
   }
 }
 //---------------------------------------------------------------------------
+#define ADSTR(S) DoAdd(llMessage, S, DoAddToSelf);
+#define ADF(S, F) ADSTR(FORMAT(S, F));
+//---------------------------------------------------------------------------
 void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
 {
   TGuard Guard(FCriticalSection);
@@ -963,8 +966,6 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
   BeginUpdate();
   try
   {
-    #define ADSTR(S) DoAdd(llMessage, S, DoAddToSelf);
-    #define ADF(S, F) ADSTR(FORMAT(S, F));
     if (Data == NULL)
     {
       AddSeparator();
@@ -985,10 +986,32 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
       {
         wcscpy(UserName, L"<Failed to retrieve username>");
       }
+      UnicodeString LogStr;
+      if (FConfiguration->LogProtocol <= 0)
+      {
+        LogStr = L"Normal";
+      }
+      else if (FConfiguration->LogProtocol == 1)
+      {
+        LogStr = L"Debug 1";
+      }
+      else if (FConfiguration->LogProtocol >= 2)
+      {
+        LogStr = L"Debug 2";
+      }
+      if (FConfiguration->LogSensitive)
+      {
+        LogStr += L", Logging passwords";
+      }
+      ADF(L"Log level: %s", (LogStr));
       ADF(L"Local account: %s", (UserName));
       ADF(L"Working directory: %s", (GetCurrentDir()));
       ADF(L"Process ID: %d", (int(GetCurrentProcessId())));
       ADF(L"Command-line: %s", (CmdLine));
+      if (FConfiguration->LogProtocol >= 1)
+      {
+        AddOptions(GetGlobalOptions());
+      }
       ADF(L"Time zone: %s", (GetTimeZoneLogString()));
       if (!AdjustClockForDSTEnabled())
       {
@@ -1077,6 +1100,7 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
         }
         ADF(L"Ciphers: %s; Ssh2DES: %s",
           (Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
+        ADF(L"KEX: %s", (Data->KexList));
         UnicodeString Bugs;
         for (int Index = 0; Index < BUG_COUNT; Index++)
         {
@@ -1157,12 +1181,12 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
       UnicodeString TimeInfo;
       if ((Data->FSProtocol == fsSFTP) || (Data->FSProtocol == fsSFTPonly) || (Data->FSProtocol == fsSCPonly) || (Data->FSProtocol == fsWebDAV))
       {
-        AddToList(TimeInfo, FORMAT("DST mode: %d", (int(Data->DSTMode))), L";");
+        AddToList(TimeInfo, FORMAT(L"DST mode: %d", (int(Data->DSTMode))), L";");
       }
       if ((Data->FSProtocol == fsSCPonly) || (Data->FSProtocol == fsFTP))
       {
         int TimeDifferenceMin = TimeToMinutes(Data->TimeDifference);
-        AddToList(TimeInfo, FORMAT("Timezone offset: %dh %dm", ((TimeDifferenceMin / MinsPerHour), (TimeDifferenceMin % MinsPerHour))), L";");
+        AddToList(TimeInfo, FORMAT(L"Timezone offset: %dh %dm", ((TimeDifferenceMin / MinsPerHour), (TimeDifferenceMin % MinsPerHour))), L";");
       }
       ADSTR(TimeInfo);
 
@@ -1174,9 +1198,6 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
 
       AddSeparator();
     }
-
-    #undef ADF
-    #undef ADSTR
   }
   __finally
   {
@@ -1186,6 +1207,19 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TSessionLog::AddOption(const UnicodeString & LogStr)
+{
+  ADSTR(LogStr);
+}
+//---------------------------------------------------------------------------
+void __fastcall TSessionLog::AddOptions(TOptions * Options)
+{
+  Options->LogOptions(AddOption);
+}
+//---------------------------------------------------------------------------
+#undef ADF
+#undef ADSTR
+//---------------------------------------------------------------------------
 void __fastcall TSessionLog::AddSeparator()
 {
   Add(llMessage, L"--------------------------------------------------------------------------");
@@ -1422,7 +1456,7 @@ void __fastcall TActionLog::BeginGroup(UnicodeString Name)
   assert(!FInGroup);
   FInGroup = true;
   assert(FIndent == L"  ");
-  AddIndented(FORMAT("<group name=\"%s\" start=\"%s\">",
+  AddIndented(FORMAT(L"<group name=\"%s\" start=\"%s\">",
     (XmlAttributeEscape(Name), StandardTimestamp())));
   FIndent = L"    ";
 }
@@ -1437,7 +1471,7 @@ void __fastcall TActionLog::EndGroup()
   // so do not try to close the group, if it has not been opened, to avoid recursion
   if (FFile != NULL)
   {
-    AddIndented("</group>");
+    AddIndented(L"</group>");
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 0
source/core/SessionInfo.h

@@ -286,6 +286,8 @@ private:
   void __fastcall DoAddStartupInfo(TSessionData * Data);
   UnicodeString __fastcall GetTlsVersionName(TTlsVersion TlsVersion);
   UnicodeString __fastcall LogSensitive(const UnicodeString & Str);
+  void __fastcall AddOption(const UnicodeString & LogStr);
+  void __fastcall AddOptions(TOptions * Options);
 };
 //---------------------------------------------------------------------------
 class TActionLog

+ 3 - 2
source/core/SftpFileSystem.cpp

@@ -2008,6 +2008,7 @@ void __fastcall TSFTPFileSystem::Idle()
     if ((FTerminal->SessionData->PingType == ptDummyCommand) &&
         FSecureShell->Ready)
     {
+      FTerminal->LogEvent(L"Sending dummy command to keep session alive.");
       TSFTPPacket Packet(SSH_FXP_REALPATH);
       Packet.AddPathString(L"/", FUtfStrings);
       SendPacketAndReceiveResponse(&Packet, &Packet);
@@ -4554,9 +4555,9 @@ void __fastcall TSFTPFileSystem::SFTPSource(const UnicodeString FileName,
             // if file has all permissions and is small, then it is likely symlink.
             // also it is not likely that such a small file (if it is not symlink)
             // gets overwritten by large file (that would trigger resumable transfer).
-            // - Also never do resumable transfer for file owner by other user
+            // - Also never do resumable transfer for file owned by other user
             // as deleting and recreating the file would change ownership.
-            // This won't for work for SFT-3 (OpenSSH) as it does not provide
+            // This won't for work for SFTP-3 (OpenSSH) as it does not provide
             // owner name (only UID) and we know only logged in user name (not UID)
             if (File->IsSymLink ||
                 ((FVersion < 4) &&

+ 15 - 17
source/core/WebDAVFileSystem.cpp

@@ -181,6 +181,10 @@ void __fastcall NeonInitialize()
   // Even if this fails, we do not want to interrupt WinSCP starting for that.
   // We may possibly remember that and fail opening session later.
   // Anyway, it can hardly fail.
+  // Though it fails on Wine on Debian VM.
+  // Probably because of ne_sspi_init() as we get this message on stderr:
+  // p11-kit: couldn't load module: /usr/lib/i386-linux-gnu/pkcs11/gnome-keyring-pkcs11.so: /usr/lib/i386-linux-gnu/pkcs11/gnome-keyring-pkcs11.so: cannot open shared object file: No such file or directory
+  // err:winediag:SECUR32_initNTLMSP ntlm_auth was not found or is outdated. Make sure that ntlm_auth >= 3.0.25 is in your path. Usually, you can find it in the winbind package of your distribution.
   ALWAYS_TRUE(ne_sock_init() == 0);
 }
 //---------------------------------------------------------------------------
@@ -210,7 +214,6 @@ TWebDAVFileSystem::TWebDAVFileSystem(TTerminal * ATerminal) :
   FNeonSession(NULL),
   FUploading(false),
   FDownloading(false),
-  FUploadBodyProvider(NULL),
   FInitialHandshake(false),
   FIgnoreAuthenticationFailure(iafNo)
 {
@@ -525,9 +528,14 @@ void __fastcall TWebDAVFileSystem::CollectUsage()
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVITHit");
   }
+  // e.g. brickftp.com
+  else if (ContainsText(RemoteSystem, L"nginx"))
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVNginx");
+  }
   else
   {
-    // Web also know OpenDrive, Yandex, iFiles (iOS), Swapper (iOS), SafeSync
+    // We also know OpenDrive, Yandex, iFiles (iOS), Swapper (iOS), SafeSync
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsWebDAVOther");
   }
 }
@@ -1442,8 +1450,6 @@ void __fastcall TWebDAVFileSystem::Source(const UnicodeString FileName,
   }
   __finally
   {
-    FUploadBodyProvider = NULL;
-
     if (FD >= 0)
     {
       // _close calls CloseHandle internally (even doc states, we should not call CloseHandle),
@@ -1675,7 +1681,7 @@ void TWebDAVFileSystem::NeonPreSend(
     // http://lists.manyfish.co.uk/pipermail/neon/2012-April/001452.html
     // It's also supported by Oracle server:
     // https://docs.oracle.com/cd/E19146-01/821-1828/gczya/index.html
-    // We do not know yet of ay server that fails when the header is used,
+    // We do not know yet of any server that fails when the header is used,
     // so it's added unconditionally.
     ne_buffer_zappend(Header, "Translate: f\r\n");
   }
@@ -1688,7 +1694,6 @@ void TWebDAVFileSystem::NeonPreSend(
     {
       // all neon request types that use ne_add_request_header
       // use XML content-type, so it's text-based
-      USEDPARAM(Header);
       assert(ContainsStr(AnsiString(Header->data, Header->used), "Content-Type: " NE_XML_MEDIA_TYPE));
       FileSystem->FTerminal->Log->Add(llInput, UnicodeString(UTF8String(Buffer, Size)));
     }
@@ -1696,14 +1701,8 @@ void TWebDAVFileSystem::NeonPreSend(
 
   if (FileSystem->FUploading)
   {
-    // this may get called multiple times per request,
-    // for example when authentication fails
-    ne_set_request_body_provider_proxy(Request,
-      FileSystem->NeonUploadBodyProvider, FileSystem,
-      &FileSystem->FUploadBodyProvider,
-      &FileSystem->FUploadBodyProviderUserData);
-
-    assert(FileSystem->FUploadBodyProvider != NULL);
+    ne_set_request_body_provider_pre(Request,
+      FileSystem->NeonUploadBodyProvider, FileSystem);
   }
 
   FileSystem->FResponse = L"";
@@ -1720,10 +1719,9 @@ int TWebDAVFileSystem::NeonPostSend(ne_request * /*Req*/, void * UserData,
   return NE_OK;
 }
 //---------------------------------------------------------------------------
-ssize_t TWebDAVFileSystem::NeonUploadBodyProvider(void * UserData, char * Buffer, size_t BufLen)
+ssize_t TWebDAVFileSystem::NeonUploadBodyProvider(void * UserData, char * /*Buffer*/, size_t /*BufLen*/)
 {
   TWebDAVFileSystem * FileSystem = static_cast<TWebDAVFileSystem *>(UserData);
-  assert(FileSystem->FUploadBodyProvider != NULL);
   ssize_t Result;
   if (FileSystem->CancelTransfer())
   {
@@ -1731,7 +1729,7 @@ ssize_t TWebDAVFileSystem::NeonUploadBodyProvider(void * UserData, char * Buffer
   }
   else
   {
-    Result = FileSystem->FUploadBodyProvider(FileSystem->FUploadBodyProviderUserData, Buffer, BufLen);
+    Result = 1;
   }
   return Result;
 }

+ 0 - 2
source/core/WebDAVFileSystem.h

@@ -150,8 +150,6 @@ private:
   ne_session_s * FNeonSession;
   bool FInitialHandshake;
   bool FAuthenticationRequested;
-  ne_provide_body FUploadBodyProvider;
-  void * FUploadBodyProviderUserData;
   UnicodeString FResponse;
   RawByteString FPassword;
   UnicodeString FTlsVersionStr;

+ 14 - 1
source/dragext/DragExt.cpp

@@ -823,7 +823,20 @@ STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND /*Hwnd*/, UINT Func, UINT /*Fla
   }
   else
   {
-    DEBUG(L"CShellExt::CopyCallback NOT copy nor move");
+    if (!GEnabled)
+    {
+      DEBUG(L"CShellExt::CopyCallback Disabled");
+    }
+    else
+    {
+      wchar_t Buf[1024];
+      wcscpy(Buf, L"CShellExt::CopyCallback NOT copy nor move - X");
+      // This is to avoid using swprintf, what's in a lib,
+      // we do not link yet, in 64-bit build (maybe this changes, once
+      // we switch to BCB build)
+      Buf[wcslen(Buf) - 1] = L'0' + Func;
+      DEBUG(Buf);
+    }
   }
 
   DEBUG(L"CShellExt::CopyCallback leave");

+ 6 - 13
source/filezilla/ApiLog.cpp

@@ -76,7 +76,7 @@ bool CApiLog::LoggingMessageType(int nMessageType) const
 
 void CApiLog::LogMessage(int nMessageType, LPCTSTR pMsgFormat, ...) const
 {
-	ASSERT(nMessageType>=0 && nMessageType<=8);
+	ASSERT(nMessageType>=FZ_LOG_STATUS && nMessageType<=FZ_LOG_DEBUG);
 	ASSERT(m_hTargetWnd || m_pApiLogParent);
 	if (!LoggingMessageType(nMessageType))
 		return;
@@ -97,7 +97,7 @@ void CApiLog::LogMessage(int nMessageType, LPCTSTR pMsgFormat, ...) const
 
 void CApiLog::LogMessageRaw(int nMessageType, LPCTSTR pMsg) const
 {
-	ASSERT(nMessageType>=0 && nMessageType<=8);
+	ASSERT(nMessageType>=FZ_LOG_STATUS && nMessageType<=FZ_LOG_DEBUG);
 	ASSERT(m_hTargetWnd || m_pApiLogParent);
 	if (!LoggingMessageType(nMessageType))
 		return;
@@ -111,7 +111,7 @@ void CApiLog::LogMessageRaw(int nMessageType, LPCTSTR pMsg) const
 
 void CApiLog::LogMessage(int nMessageType, UINT nFormatID, ...) const
 {
-	ASSERT(nMessageType>=0 && nMessageType<=8);
+	ASSERT(nMessageType>=FZ_LOG_STATUS && nMessageType<=FZ_LOG_DEBUG);
 	ASSERT(m_hTargetWnd || m_pApiLogParent);
 	if (!LoggingMessageType(nMessageType))
 		return;
@@ -135,7 +135,7 @@ void CApiLog::LogMessage(int nMessageType, UINT nFormatID, ...) const
 
 void CApiLog::LogMessage(CString SourceFile, int nSourceLine, void *pInstance, int nMessageType, LPCTSTR pMsgFormat, ...) const
 {
-	ASSERT(nMessageType>=4 && nMessageType<=8);
+	ASSERT(nMessageType>=FZ_LOG_LIST && nMessageType<=FZ_LOG_DEBUG);
 	ASSERT(m_hTargetWnd || m_pApiLogParent);
 	ASSERT(nSourceLine>0);
 
@@ -171,7 +171,7 @@ BOOL CApiLog::PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) con
 
 void CApiLog::LogMessageRaw(CString SourceFile, int nSourceLine, void *pInstance, int nMessageType, LPCTSTR pMsg) const
 {
-	ASSERT(nMessageType>=4 && nMessageType<=8);
+	ASSERT(nMessageType>=FZ_LOG_LIST && FZ_LOG_DEBUG<=FZ_LOG_DEBUG);
 	ASSERT(m_hTargetWnd || m_pApiLogParent);
 	ASSERT(nSourceLine>0);
 
@@ -240,15 +240,8 @@ BOOL CApiLog::SetDebugLevel(int nDebugLevel)
 {
 	if (m_pApiLogParent)
 		return FALSE;
-	if (nDebugLevel<0 || nDebugLevel>4)
+	if (nDebugLevel<0 || nDebugLevel>5)
 		return FALSE;
 	m_nDebugLevel=nDebugLevel;
 	return TRUE;
 }
-
-int CApiLog::GetDebugLevel()
-{
-	if (m_pApiLogParent)
-		return m_pApiLogParent->m_nDebugLevel;
-	return m_nDebugLevel;
-}

+ 0 - 1
source/filezilla/ApiLog.h

@@ -45,7 +45,6 @@ public:
 	void LogMessageRaw(CString SourceFile, int nSourceLine, void *pInstance, int nMessageType, LPCTSTR pMsg) const;
 
 	BOOL SetDebugLevel(int nLogLevel);
-	int GetDebugLevel();
 protected:
 #ifdef MPEXT
 	virtual BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) const;

+ 1 - 1
source/filezilla/AsyncSslSocketLayer.cpp

@@ -1179,7 +1179,7 @@ void CAsyncSslSocketLayer::apps_ssl_info_callback(const SSL *s, int where, int r
 				{
 					if (SSL_session_reused(pLayer->m_ssl))
 					{
-						pLayer->LogSocketMessage(FZ_LOG_INFO, _T("Session ID reused"));
+						pLayer->LogSocketMessage(FZ_LOG_PROGRESS, _T("Session ID reused"));
 					}
 					else
 					{

+ 11 - 0
source/filezilla/ControlSocket.cpp

@@ -133,6 +133,17 @@ void CControlSocket::ShowStatus(CString status, int type) const
 }
 
 
+void CControlSocket::ShowTimeoutError(UINT nID) const
+{
+	CString str1;
+	str1.LoadString(IDS_ERRORMSG_TIMEOUT);
+	CString str2;
+	str2.LoadString(nID);
+	CString message;
+	message.Format(L"%s (%s)", str1, str2);
+	ShowStatus(message, FZ_LOG_ERROR);
+}
+
 t_server CControlSocket::GetCurrentServer()
 {
 	return m_CurrentServer;

+ 1 - 0
source/filezilla/ControlSocket.h

@@ -112,6 +112,7 @@ public:
 	t_server GetCurrentServer();
 	void ShowStatus(UINT nID, int type) const;
 	void ShowStatus(CString status,int type) const;
+	void ShowTimeoutError(UINT nID) const;
 
 #ifdef MPEXT
 	virtual bool UsingMlsd() = 0;

+ 4 - 3
source/filezilla/FileZillaApi.h

@@ -285,11 +285,12 @@ public:
 #define FZ_LOG_COMMAND 2
 #define FZ_LOG_REPLY 3
 #define FZ_LOG_LIST 4
-//By calling CFileZillaApi::SetDebugLevel, the aplication can enable loggint of the following messages:
+//By calling CFileZillaApi::SetDebugLevel, the aplication can enable logging of the following messages:
 #define FZ_LOG_APIERROR 5
 #define FZ_LOG_WARNING 6
-#define FZ_LOG_INFO 7
-#define FZ_LOG_DEBUG 8
+#define FZ_LOG_PROGRESS 7
+#define FZ_LOG_INFO 8
+#define FZ_LOG_DEBUG 9
 
 class CMainThread;
 class CFileZillaTools;

+ 3 - 2
source/filezilla/FileZillaIntf.h

@@ -102,8 +102,9 @@ public:
     LOG_LIST = 4,
     LOG_APIERROR = 5,
     LOG_WARNING = 6,
-    LOG_INFO = 7,
-    LOG_DEBUG = 8
+    LOG_PROGRESS = 7,
+    LOG_INFO = 8,
+    LOG_DEBUG = 9
   };
 
   enum TMessageType

+ 22 - 24
source/filezilla/FtpControlSocket.cpp

@@ -675,7 +675,6 @@ void CFtpControlSocket::LogOnToServer(BOOL bSkipReply /*=FALSE*/)
 	{
 		#ifdef MPEXT
 		std::string facts;
-		// this is never true, see comment is DiscardLine
 		if (m_serverCapabilities.GetCapabilityString(mlsd_command, &facts) == yes)
 		{
 			ftp_capabilities_t cap = m_serverCapabilities.GetCapabilityString(opts_mlst_command);
@@ -722,14 +721,14 @@ void CFtpControlSocket::LogOnToServer(BOOL bSkipReply /*=FALSE*/)
 					if (!strcmp(fact.c_str(), "type") ||
 						!strcmp(fact.c_str(), "size") ||
 						!strcmp(fact.c_str(), "modify") ||
+						!strcmp(fact.c_str(), "create") ||
 						!strcmp(fact.c_str(), "perm") ||
 						!strcmp(fact.c_str(), "unix.mode") ||
 						!strcmp(fact.c_str(), "unix.owner") ||
 						!strcmp(fact.c_str(), "unix.user") ||
 						!strcmp(fact.c_str(), "unix.group") ||
 						!strcmp(fact.c_str(), "unix.uid") ||
-						!strcmp(fact.c_str(), "unix.gid") ||
-						!strcmp(fact.c_str(), "x.hidden"))
+						!strcmp(fact.c_str(), "unix.gid"))
 					{
 						had_unset |= !enabled;
 						opts_facts += fact.c_str();
@@ -1164,7 +1163,7 @@ void CFtpControlSocket::OnReceive(int nErrorCode)
 		m_MultiLine = "";
 		CString str;
 		str.Format(IDS_STATUSMSG_CONNECTEDWITH, m_ServerName);
-		ShowStatus(str, FZ_LOG_STATUS);
+		ShowStatus(str, FZ_LOG_PROGRESS);
 		m_pOwner->SetConnected(TRUE);
 	}
 	char *buffer = new char[BUFFERSIZE];
@@ -1357,7 +1356,7 @@ void CFtpControlSocket::OnConnect(int nErrorCode)
 				m_pSslLayer ? IDS_STATUSMSG_CONNECTEDWITHSSL :
 #endif
 				IDS_STATUSMSG_CONNECTEDWITH, m_ServerName);
-			ShowStatus(str,FZ_LOG_STATUS);
+			ShowStatus(str,FZ_LOG_PROGRESS);
 		}
 	}
 	else
@@ -1569,7 +1568,7 @@ void CFtpControlSocket::CheckForTimeout()
 	CTimeSpan span=CTime::GetCurrentTime()-m_LastRecvTime;
 	if (span.GetTotalSeconds()>=delay)
 	{
-		ShowStatus(IDS_ERRORMSG_TIMEOUT, FZ_LOG_ERROR);
+		ShowTimeoutError(IDS_CONTROL_CONNECTION);
 		DoClose();
 	}
 }
@@ -1762,7 +1761,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 		}
 		else if (pData->pDirectoryListing && pData->nFinish==1)
 		{
-			ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL,FZ_LOG_STATUS);
+			ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL,FZ_LOG_PROGRESS);
 #ifndef MPEXT_NO_CACHE
 			CDirectoryCache cache;
 			cache.Lock();
@@ -1837,7 +1836,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 								}
 						if (bExact)
 						{
-							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_STATUS);
+							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_PROGRESS);
 							SetDirectoryListing(&dir);
 							ResetOperation(FZ_REPLY_OK);
 							return;
@@ -1893,7 +1892,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 								}
 						if (bExact)
 						{
-							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_STATUS);
+							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_PROGRESS);
 							SetDirectoryListing(&dir);
 							ResetOperation(FZ_REPLY_OK);
 							return;
@@ -1943,7 +1942,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 								}
 						if (bExact)
 						{
-							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_STATUS);
+							ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_PROGRESS);
 							SetDirectoryListing(&dir);
 							ResetOperation(FZ_REPLY_OK);
 							return;
@@ -2064,7 +2063,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 		case LIST_LIST:
 			if (IsMisleadingListResponse())
 			{
-				ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_STATUS);
+				ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_PROGRESS);
 
 				t_directory listing;
 				listing.server = m_CurrentServer;
@@ -2114,7 +2113,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 		pData->path=path;
 		pData->subdir=subdir;
 		m_Operation.pData=pData;
-		ShowStatus(IDS_STATUSMSG_RETRIEVINGDIRLIST, FZ_LOG_STATUS);
+		ShowStatus(IDS_STATUSMSG_RETRIEVINGDIRLIST, FZ_LOG_PROGRESS);
 		pData->nFinish=-1;
 		if (m_pDirectoryListing)
 		{
@@ -2168,7 +2167,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 									}
 							if (bExact)
 							{
-								ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL,FZ_LOG_STATUS);
+								ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL,FZ_LOG_PROGRESS);
 								SetDirectoryListing(&dir);
 								ResetOperation(FZ_REPLY_OK);
 								return;
@@ -2440,7 +2439,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
 			hostname.Format(L"%s:%d", pData->host, pData->port);
 			CString str;
 			str.Format(IDS_STATUSMSG_CONNECTING, hostname);
-			ShowStatus(str, FZ_LOG_STATUS);
+			ShowStatus(str, FZ_LOG_PROGRESS);
 
 			// if PASV create the socket & initiate outbound data channel connection
 			if (!m_pTransferSocket->Connect(pData->host,pData->port))
@@ -3582,7 +3581,7 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi
 		case FILETRANSFER_LIST_LIST:
 			if (IsMisleadingListResponse())
 			{
-				ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_STATUS);
+				ShowStatus(IDS_STATUSMSG_DIRLISTSUCCESSFUL, FZ_LOG_PROGRESS);
 
 				t_directory listing;
 				listing.server = m_CurrentServer;
@@ -5554,6 +5553,7 @@ void CFtpControlSocket::SetFileExistsAction(int nAction, COverwriteRequestData *
 
 void CFtpControlSocket::SendKeepAliveCommand()
 {
+	ShowStatus(L"Sending dummy command to keep session alive.", FZ_LOG_PROGRESS);
 	m_bKeepAliveActive=TRUE;
 	//Choose a random command from the list
 	TCHAR commands[4][7]={_MPT("PWD"),_MPT("REST 0"),_MPT("TYPE A"),_MPT("TYPE I")};
@@ -6354,14 +6354,12 @@ void CFtpControlSocket::DiscardLine(CStringA line)
 		else if (line.Left(4) == _MPAT("MLST"))
 		{
 			USES_CONVERSION;
-			// This is wrong, the -1 for length does not work with
-			// Mid(), so result is always an empty string.
-			// Consequently CONNECT_FEAT state code in
-			// LogOnToServer() is never triggered
-			// and OPTS MLST command is never sent.
-			// Also using 6 index when there are no facts after
-			// MLST (ftp.drivehq.com) triggers an assertion.
-			m_serverCapabilities.SetCapability(mlsd_command, yes, (LPCSTR)line.Mid(5, -1));
+			std::string facts;
+			if (line.GetLength() > 5)
+			{
+				facts = (LPCSTR)line.Mid(5, line.GetLength() - 5);
+			}
+			m_serverCapabilities.SetCapability(mlsd_command, yes, facts);
 		}
 		else if (line == _MPAT("MFMT"))
 		{
@@ -6575,7 +6573,7 @@ bool CFtpControlSocket::CheckForcePasvIp(CString & host)
 		default: // auto
 			if (!GetPeerName(ahost, tmpPort))
 			{
-				LogMessage(__FILE__, __LINE__, this, FZ_LOG_INFO, _T("Error retrieving server address, cannot test if address is routable"));
+				LogMessage(__FILE__, __LINE__, this, FZ_LOG_PROGRESS, _T("Error retrieving server address, cannot test if address is routable"));
 			}
 			else if (!IsRoutableAddress(host) && IsRoutableAddress(ahost))
 			{

+ 19 - 23
source/filezilla/FtpListResult.cpp

@@ -661,6 +661,9 @@ void CFtpListResult::SendToMessageLog(HWND hWnd, UINT nMsg)
 	curpos = listhead;
 	pos=0;
 	char *line = GetLine();
+	// Note that FZ_LOG_INFO here is not checked against debug level, as the direct
+	// call to PostMessage bypasses check in LogMessage.
+	// So we get the listing on any logging level, what is actually what we want
 	if (!line)
 	{
 		//Displays a message in the message log
@@ -1331,6 +1334,8 @@ BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_director
 		CString factname = facts.Left(pos);
 		factname.MakeLower();
 		CString value = facts.Mid(pos + 1, delim - pos - 1);
+		// When adding new facts, update filter in CFtpControlSocket::LogOnToServer
+		// (CONNECT_FEAT state)
 		if (factname == _T("type"))
 		{
 			if (!value.CompareNoCase(_T("dir")))
@@ -1378,7 +1383,7 @@ BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_director
 		else if (factname == _T("modify") ||
 			(!direntry.date.hasdate && factname == _T("create")))
 		{
-			if (!parseMlsdDateTime(value, direntry))
+			if (!parseMlsdDateTime(value, direntry.date))
 				return FALSE;
 		}
 		else if (factname == _T("perm"))
@@ -1436,7 +1441,7 @@ BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_director
 	return TRUE;
 }
 
-bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_direntry &direntry) const
+bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date &date) const
 {
 	if (value.IsEmpty())
 		return FALSE;
@@ -1446,35 +1451,26 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
 	Year=Month=Day=Hours=Minutes=Seconds=0;
 	if (swscanf((LPCWSTR)value, L"%4d%2d%2d%2d%2d%2d", &Year, &Month, &Day, &Hours, &Minutes, &Seconds) == 6)
 	{
-		direntry.date.hasdate = TRUE;
-		direntry.date.hastime = TRUE;
-		direntry.date.hasseconds = TRUE;
+		date.hasdate = TRUE;
+		date.hastime = TRUE;
+		date.hasseconds = TRUE;
 		result = TRUE;
 	}
 	else if (swscanf((LPCWSTR)value, L"%4d%2d%2d", &Year, &Month, &Day) == 3)
 	{
-		direntry.date.hasdate = TRUE;
-		direntry.date.hastime = FALSE;
+		date.hasdate = TRUE;
+		date.hastime = FALSE;
 		result = TRUE;
 	}
 	if (result)
 	{
-		try
-		{
-			direntry.date.year = Year;
-			direntry.date.month = Month;
-			direntry.date.day = Day;
-			direntry.date.hour = Hours;
-			direntry.date.minute = Minutes;
-			direntry.date.second = Seconds;
-			direntry.date.utc = TRUE;
-		}
-		// Does not really seem to ever throw in our version of MFC/ATL
-		catch (CException &)
-		{
-			direntry.date.hasdate = FALSE;
-			direntry.date.hastime = FALSE;
-		}
+		date.year = Year;
+		date.month = Month;
+		date.day = Day;
+		date.hour = Hours;
+		date.minute = Minutes;
+		date.second = Seconds;
+		date.utc = TRUE;
 	}
 	return result;
 }

+ 1 - 1
source/filezilla/FtpListResult.h

@@ -87,7 +87,7 @@ private:
 	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 parseMlsdDateTime(const CString value, t_directory::t_direntry &direntry) const;
+	bool parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date &date) const;
 
 	int pos;
 	struct t_list

+ 36 - 25
source/filezilla/TransferSocket.cpp

@@ -367,21 +367,12 @@ void CTransferSocket::OnReceive(int nErrorCode)
 	}
 }
 
-void CTransferSocket::OnAccept(int nErrorCode)
+void CTransferSocket::SetBuffers()
 {
-	LogMessage(__FILE__, __LINE__, this,FZ_LOG_DEBUG, _T("OnAccept(%d)"), nErrorCode);
-	m_bListening=FALSE;
-	CAsyncSocketEx tmp;
-	Accept(tmp);
-	SOCKET socket=tmp.Detach();
-	CAsyncSocketEx::Close();
-	
-	Attach(socket);
-
 	/* Set internal socket send buffer
 	 * this should fix the speed problems some users have reported
 	 */
-	DWORD value;
+	DWORD value = 0;
 	int len = sizeof(value);
 	GetSockOpt(SO_SNDBUF, &value, &len);
 	// MPEXT
@@ -392,6 +383,35 @@ void CTransferSocket::OnAccept(int nErrorCode)
 		SetSockOpt(SO_SNDBUF, &value, sizeof(value));
 	}
 
+	// For now we increase receive buffer, whenever send buffer is set.
+	// The size is not configurable. The constant taken from FZ.
+	if (sndbuf > 0)
+	{
+		value = 0;
+		len = sizeof(value);
+		GetSockOpt(SO_RCVBUF, &value, &len);
+		int rcvbuf = 4 * 1024 * 1024;
+		if (value < rcvbuf)
+		{
+			value = rcvbuf;
+			SetSockOpt(SO_RCVBUF, &value, sizeof(value));
+		}
+	}
+}
+
+void CTransferSocket::OnAccept(int nErrorCode)
+{
+	LogMessage(__FILE__, __LINE__, this,FZ_LOG_DEBUG, _T("OnAccept(%d)"), nErrorCode);
+	m_bListening=FALSE;
+	CAsyncSocketEx tmp;
+	Accept(tmp);
+	SOCKET socket=tmp.Detach();
+	CAsyncSocketEx::Close();
+	
+	Attach(socket);
+
+	SetBuffers();
+
 	if (m_nTransferState == STATE_STARTING)
 	{
 		m_nTransferState = STATE_STARTED;	
@@ -462,19 +482,8 @@ void CTransferSocket::OnConnect(int nErrorCode)
 	}
 	else
 	{
-		/* Set internal socket send buffer
-		 * this should fix the speed problems some users have reported
-		 */
-		DWORD value;
-		int len = sizeof(value);
-		GetSockOpt(SO_SNDBUF, &value, &len);
-		// MPEXT
-		int sndbuf = COptions::GetOptionVal(OPTION_MPEXT_SNDBUF);
-		if (value < sndbuf)
-		{
-			value = sndbuf;
-			SetSockOpt(SO_SNDBUF, &value, sizeof(value));
-		}
+		SetBuffers();
+		m_pOwner->ShowStatus(L"Data connection opened", FZ_LOG_INFO);
 	}
 	if (m_nTransferState == STATE_WAITING)
 	{
@@ -535,6 +544,8 @@ void CTransferSocket::OnClose(int nErrorCode)
 		m_nNotifyWaiting |= FD_CLOSE;
 		return;
 	}
+	
+	m_pOwner->ShowStatus(L"Data connection closed", FZ_LOG_INFO);
 
 	OnReceive(0);
 	CloseAndEnsureSendClose(0);
@@ -552,7 +563,7 @@ int CTransferSocket::CheckForTimeout(int delay)
 	CTimeSpan span = CTime::GetCurrentTime()-m_LastActiveTime;
 	if (span.GetTotalSeconds()>=delay)
 	{
-		m_pOwner->ShowStatus(IDS_ERRORMSG_TIMEOUT, FZ_LOG_ERROR);
+		m_pOwner->ShowTimeoutError(IDS_DATA_CONNECTION);
 		CloseAndEnsureSendClose(CSMODE_TRANSFERTIMEOUT);
 		return 2;
 	}

+ 1 - 0
source/filezilla/TransferSocket.h

@@ -132,6 +132,7 @@ protected:
 	void EnsureSendClose(int Mode);
 	void CloseOnShutDownOrError(int Mode);
 	void LogError(int Error);
+	void SetBuffers();
 
 	DWORD m_Transfered[SPEED_SECONDS];
 	bool m_UsedForTransfer[SPEED_SECONDS];

+ 39 - 16
source/forms/About.cpp

@@ -30,14 +30,14 @@
 #pragma resource "*.dfm"
 #endif
 //---------------------------------------------------------------------------
-void __fastcall DoAboutDialog(TConfiguration * Configuration,
-  bool AllowLicense, TRegistration * Registration)
+static void __fastcall DoAboutDialog(TConfiguration * Configuration,
+  bool AllowLicense, TRegistration * Registration, bool LoadThirdParty)
 {
   TAboutDialog * AboutDialog = NULL;
   try
   {
     AboutDialog = new TAboutDialog(Application, Configuration, AllowLicense,
-      Registration);
+      Registration, LoadThirdParty);
     AboutDialog->ShowModal();
   }
   __finally
@@ -46,8 +46,24 @@ void __fastcall DoAboutDialog(TConfiguration * Configuration,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall DoAboutDialog(TConfiguration * Configuration,
+  bool AllowLicense, TRegistration * Registration)
+{
+  try
+  {
+    DoAboutDialog(Configuration, AllowLicense, Registration, true);
+  }
+  catch (EOleException & E)
+  {
+    // This happens particularly on Wine that's does not support some
+    // functionality of embedded IE we need.
+    DoAboutDialog(Configuration, AllowLicense, Registration, false);
+  }
+}
+//---------------------------------------------------------------------------
 __fastcall TAboutDialog::TAboutDialog(TComponent * AOwner,
-  TConfiguration * Configuration, bool AllowLicense, TRegistration * Registration)
+  TConfiguration * Configuration, bool AllowLicense, TRegistration * Registration,
+  bool ALoadThirdParty)
   : TForm(AOwner)
 {
   FConfiguration = Configuration;
@@ -123,9 +139,15 @@ __fastcall TAboutDialog::TAboutDialog(TComponent * AOwner,
 
   LicenseButton->Visible = AllowLicense;
 
-  FThirdPartyWebBrowser = CreateBrowserViewer(ThirdPartyPanel, L"");
-
   LoadData();
+  if (ALoadThirdParty)
+  {
+    LoadThirdParty();
+  }
+  else
+  {
+    CreateLabelPanel(ThirdPartyPanel, LoadStr(MESSAGE_DISPLAY_ERROR));
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::LoadData()
@@ -138,16 +160,17 @@ void __fastcall TAboutDialog::LoadData()
       (Version, FConfiguration->ProductName, FConfiguration->ProductVersion));
   }
   VersionLabel->Caption = Version;
-
-  LoadThirdParty();
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::LoadThirdParty()
 {
-  reinterpret_cast<TLabel *>(FThirdPartyWebBrowser)->Color = clBtnFace;
+  TWebBrowserEx * ThirdPartyWebBrowser =
+    CreateBrowserViewer(ThirdPartyPanel, L"");
+
+  reinterpret_cast<TLabel *>(ThirdPartyWebBrowser)->Color = clBtnFace;
 
-  FThirdPartyWebBrowser->Navigate(L"about:blank");
-  while (FThirdPartyWebBrowser->ReadyState < ::READYSTATE_INTERACTIVE)
+  ThirdPartyWebBrowser->Navigate(L"about:blank");
+  while (ThirdPartyWebBrowser->ReadyState < ::READYSTATE_INTERACTIVE)
   {
     Application->ProcessMessages();
   }
@@ -231,7 +254,7 @@ void __fastcall TAboutDialog::LoadThirdParty()
     CreateLink(EXPAT_LICENSE_URL, LoadStr(ABOUT_THIRDPARTY_LICENSE)) + Br +
     CreateLink(LoadStr(EXPAT_URL)));
 
-  AddBrowserLinkHandler(FThirdPartyWebBrowser, EXPAT_LICENSE_URL, ExpatLicenceHandler);
+  AddBrowserLinkHandler(ThirdPartyWebBrowser, EXPAT_LICENSE_URL, ExpatLicenceHandler);
 
 #ifndef NO_COMPONENTS
 
@@ -271,14 +294,14 @@ void __fastcall TAboutDialog::LoadThirdParty()
   ThirdPartyStream->Write(ThirdPartyUTF8.c_str(), ThirdPartyUTF8.Length());
   ThirdPartyStream->Seek(0, 0);
 
-  // For steam-loaded document, when set only after loading from OnDocumentComplete,
+  // For stream-loaded document, when set only after loading from OnDocumentComplete,
   // browser stops working
-  SetBrowserDesignModeOff(FThirdPartyWebBrowser);
+  SetBrowserDesignModeOff(ThirdPartyWebBrowser);
 
   TStreamAdapter * ThirdPartyStreamAdapter = new TStreamAdapter(ThirdPartyStream.get(), soReference);
   IPersistStreamInit * PersistStreamInit = NULL;
-  if (ALWAYS_TRUE(FThirdPartyWebBrowser->Document != NULL) &&
-      SUCCEEDED(FThirdPartyWebBrowser->Document->QueryInterface(IID_IPersistStreamInit, (void **)&PersistStreamInit)) &&
+  if (ALWAYS_TRUE(ThirdPartyWebBrowser->Document != NULL) &&
+      SUCCEEDED(ThirdPartyWebBrowser->Document->QueryInterface(IID_IPersistStreamInit, (void **)&PersistStreamInit)) &&
       ALWAYS_TRUE(PersistStreamInit != NULL))
   {
     PersistStreamInit->Load(static_cast<_di_IStream>(*ThirdPartyStreamAdapter));

+ 2 - 2
source/forms/About.h

@@ -42,7 +42,6 @@ __published:
 private:
   TConfiguration * FConfiguration;
   TNotifyEvent FOnRegistrationLink;
-  TWebBrowserEx * FThirdPartyWebBrowser;
 
   void __fastcall LoadData();
   void __fastcall LoadThirdParty();
@@ -52,7 +51,8 @@ private:
 
 public:
   virtual __fastcall TAboutDialog(TComponent * AOwner,
-    TConfiguration * Configuration, bool AllowLicense, TRegistration * Registration);
+    TConfiguration * Configuration, bool AllowLicense, TRegistration * Registration,
+    bool ALoadThirdParty);
 };
 //----------------------------------------------------------------------------
 #endif

+ 1 - 1
source/forms/Custom.cpp

@@ -441,7 +441,7 @@ void __fastcall SessionNameValidate(const UnicodeString & Text,
       qtError, qaOK, HELP_NONE);
     Abort();
   }
-  else if ((Data != NULL) && (Data->CompareName(OriginalName) != 0) &&
+  else if ((Data != NULL) && !Data->IsSameName(OriginalName) &&
     MessageDialog(MainInstructions(FMTLOAD(CONFIRM_OVERWRITE_SESSION, (Text))),
       qtConfirmation, qaYes | qaNo, HELP_SESSION_SAVE_OVERWRITE) != qaYes)
   {

+ 24 - 3
source/forms/CustomScpExplorer.cpp

@@ -107,6 +107,24 @@ private:
   HANDLE FMutex;
 };
 //---------------------------------------------------------------------------
+class TWindowLock
+{
+public:
+  TWindowLock(TCustomScpExplorerForm * Form) :
+    FForm(Form)
+  {
+    FForm->LockWindow();
+  }
+
+  ~TWindowLock()
+  {
+    FForm->UnlockWindow();
+  }
+
+private:
+  TCustomScpExplorerForm * FForm;
+};
+//---------------------------------------------------------------------------
 struct TTransferOperationParam
 {
   TTransferOperationParam();
@@ -3498,6 +3516,7 @@ void __fastcall TCustomScpExplorerForm::CreateDirectory(TOperationSide Side)
 
   if (DoCreateDirectoryDialog(Name, AProperties, AllowedChanges, SaveSettings))
   {
+    TWindowLock Lock(this);
     if (Side == osRemote)
     {
       if (SaveSettings)
@@ -3515,6 +3534,7 @@ void __fastcall TCustomScpExplorerForm::CreateDirectory(TOperationSide Side)
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::HomeDirectory(TOperationSide Side)
 {
+  TWindowLock Lock(this);
   DirView(Side)->ExecuteHomeDirectory();
 }
 //---------------------------------------------------------------------------
@@ -5125,6 +5145,7 @@ void __fastcall TCustomScpExplorerForm::DoOpenDirectoryDialog(
       if (::DoOpenDirectoryDialog(Mode, Side, Name, VisitedDirectories, Terminal,
             HasDirView[osLocal]))
       {
+        TWindowLock Lock(this);
         DirView(Side)->Path = Name;
       }
     }
@@ -6156,7 +6177,7 @@ void __fastcall TCustomScpExplorerForm::RemoteFileControlDDGiveFeedback(
   DragDropFiles(Sender)->CHCopy = SlippedCopyCursor;
   DragDropFiles(Sender)->CHScrollCopy = SlippedCopyCursor;
 
-  // Remember drop effect so we know (when user dropes files), if we copy or move
+  // Remember drop effect so we know (when user drops files), if we copy or move
   FLastDropEffect = dwEffect;
 }
 //---------------------------------------------------------------------------
@@ -6764,7 +6785,7 @@ void __fastcall TCustomScpExplorerForm::RemoteFileContolDDChooseEffect(
       {
         bool MoveCapable = FTerminal->IsCapable[fcRemoteMove];
         // currently we support copying always (at least via temporary directory);
-        // remove associated checks once this all proves stable andworking
+        // remove associated checks once this all proves stable and working
         bool CopyCapable = true;
         // if we do not support neither of operations, there's no discussion
         if (!MoveCapable && !CopyCapable)
@@ -8265,6 +8286,6 @@ Boolean __fastcall TCustomScpExplorerForm::AllowedAction(TAction * /*Action*/, T
   // so stop it at least here
   return
     (Allowed == aaUpdate) ||
-    !IsBusy();
+    !NonVisualDataModule->Busy;
 }
 //---------------------------------------------------------------------------

+ 7 - 7
source/forms/Editor.dfm

@@ -157,19 +157,19 @@ object EditorForm: TEditorForm
     end
     object EditCut: TEditCut
       Caption = 'Cu&t'
-      Hint = 'Cut|Cuts the selection and puts it on the Clipboard'
+      Hint = 'Cut|Cut the selection and put it on the Clipboard'
       ImageIndex = 1
       ShortCut = 16472
     end
     object EditCopy: TEditCopy
       Caption = '&Copy'
-      Hint = 'Copy|Copies the selection and puts it on the Clipboard'
+      Hint = 'Copy|Copy the selection and put it on the Clipboard'
       ImageIndex = 2
       ShortCut = 16451
     end
     object EditPaste: TEditPaste
       Caption = '&Paste'
-      Hint = 'Paste|Inserts Clipboard contents'
+      Hint = 'Paste|Insert Clipboard contents'
       ImageIndex = 3
       SecondaryShortCuts.Strings = (
         'Shift+Ins'
@@ -178,25 +178,25 @@ object EditorForm: TEditorForm
     end
     object EditSelectAll: TEditSelectAll
       Caption = 'Select &All'
-      Hint = 'Select All|Selects the entire document'
+      Hint = 'Select All|Select the entire document'
       ImageIndex = 6
       ShortCut = 16449
     end
     object EditUndo: TEditUndo
       Caption = '&Undo'
-      Hint = 'Undo|Reverts the last action'
+      Hint = 'Undo|Revert the last action'
       ImageIndex = 4
       ShortCut = 16474
     end
     object EditRedo: TAction
       Caption = 'Re&do'
-      Hint = 'Redo|Reverts the effects of most recent Undo'
+      Hint = 'Redo|Revert the effects of most recent Undo'
       ImageIndex = 14
       ShortCut = 16473
     end
     object EditDelete: TEditDelete
       Caption = '&Delete'
-      Hint = 'Delete|Erases the selection'
+      Hint = 'Delete|Erase the selection'
       ImageIndex = 5
     end
     object PreferencesAction: TAction

+ 6 - 6
source/forms/GenerateUrl.dfm

@@ -29,7 +29,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 1
       Left = 11
       Top = 20
-      Width = 97
+      Width = 216
       Height = 17
       Caption = '&User name'
       TabOrder = 0
@@ -39,7 +39,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 2
       Left = 235
       Top = 20
-      Width = 97
+      Width = 216
       Height = 17
       HelpType = htKeyword
       HelpKeyword = 'ui_generate_url'
@@ -51,7 +51,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 4
       Left = 11
       Top = 43
-      Width = 97
+      Width = 216
       Height = 17
       Caption = 'SSH &host Key'
       TabOrder = 2
@@ -61,7 +61,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 8
       Left = 235
       Top = 43
-      Width = 97
+      Width = 216
       Height = 17
       Caption = 'Initial &directory'
       TabOrder = 3
@@ -71,7 +71,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 16
       Left = 11
       Top = 66
-      Width = 97
+      Width = 216
       Height = 17
       Caption = '&WinSCP-specific'
       TabOrder = 4
@@ -81,7 +81,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
       Tag = 32
       Left = 235
       Top = 66
-      Width = 97
+      Width = 216
       Height = 17
       Caption = '&Save extension'
       TabOrder = 5

+ 14 - 0
source/forms/LocationProfiles.cpp

@@ -410,6 +410,7 @@ bool __fastcall TLocationProfilesDialog::Execute()
 {
   bool Result;
   PageControl->ActivePage = GetProfilesSheet();
+  FBookmarkSelected = false;
   Result = (ShowModal() == DefaultResult(this));
   if (Terminal)
   {
@@ -417,6 +418,17 @@ bool __fastcall TLocationProfilesDialog::Execute()
     WinConfiguration->SharedBookmarks = FSharedBookmarkList;
     WinConfiguration->UseSharedBookmarks = (PageControl->ActivePage == SharedProfilesSheet);
   }
+  if (Result)
+  {
+    if (FBookmarkSelected)
+    {
+      Configuration->Usage->Inc(L"OpenedBookmark");
+    }
+    else
+    {
+      Configuration->Usage->Inc(L"OpenedPath");
+    }
+  }
   return Result;
 }
 //---------------------------------------------------------------------------
@@ -821,6 +833,7 @@ void __fastcall TLocationProfilesDialog::DirectoryEditChange(TObject * /*Sender*
   {
     FindProfile();
     UpdateControls();
+    FBookmarkSelected = false;
   }
 }
 //---------------------------------------------------------------------------
@@ -842,6 +855,7 @@ void __fastcall TLocationProfilesDialog::ProfilesViewChange(
     }
     // try to locate the same profile in the other set
     FindProfile();
+    FBookmarkSelected = true;
   }
   UpdateControls();
 }

+ 1 - 0
source/forms/LocationProfiles.h

@@ -123,6 +123,7 @@ private:
   TTreeViewScrollOnDragOver * FSessionScrollOnDragOver;
   TTreeViewScrollOnDragOver * FSharedScrollOnDragOver;
   UnicodeString FSessionKey;
+  bool FBookmarkSelected;
   #ifdef _DEBUG
   HWND FSessionProfilesViewHandle;
   HWND FSharedProfilesViewHandle;

+ 33 - 35
source/forms/MessageDlg.cpp

@@ -983,16 +983,43 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
       ButtonSpacing * (ButtonControls.size() - 1);
   }
 
+  assert((ButtonHeight > 0) && (ButtonWidths > 0));
+
+  TPanel * Panel = new TPanel(Result);
+  Panel->Name = L"Panel";
+  Panel->Parent = Result;
+  Panel->Color = clWindow;
+  Panel->ParentBackground = false;
+  Panel->Anchors = TAnchors() << akLeft << akRight << akTop;
+  Panel->BevelOuter = bvNone;
+  Panel->BevelKind = bkNone;
+  Panel->Caption = L"";
+
   int IconWidth = 0;
+  int IconHeight = 0;
   const wchar_t * IconID = IconIDs[DlgType];
-  bool HasIcon = (IconID != NULL) || !ImageName.IsEmpty();
-  if (HasIcon)
+
+  if ((IconID != NULL) || !ImageName.IsEmpty())
   {
-    IconWidth = 32 + HorzSpacing;
+    TImage * Image = new TImage(Panel);
+    Image->Name = L"Image";
+    Image->Parent = Panel;
+    if (!ImageName.IsEmpty())
+    {
+      LoadResourceImage(Image, ImageName);
+    }
+    else
+    {
+      // Until Windows 8, LoadIcon for IDI_XXX always returns 32x32 image.
+      // Since Windows 8.1, it returns image adjusted for DPI.
+      // For 125%, it's scaled version. For 150%, there's native larger version.
+      Image->Picture->Icon->Handle = LoadIcon(0, IconID);
+    }
+    Image->SetBounds(HorzMargin, VertMargin, Image->Picture->Width, Image->Picture->Height);
+    IconWidth = Image->Width + HorzSpacing;
+    IconHeight = Image->Height;
   }
 
-  assert((ButtonHeight > 0) && (ButtonWidths > 0));
-
   int MaxTextWidth = ScaleByTextHeightRunTime(Result, mcMaxDialogWidth);
   // if the dialog would be wide anyway (overwrite confirmation on Windows XP),
   // to fit the buttons, do not restrict the text
@@ -1001,16 +1028,6 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
     MaxTextWidth = ButtonGroupWidth - IconWidth;
   }
 
-  TPanel * Panel = new TPanel(Result);
-  Panel->Name = L"Panel";
-  Panel->Parent = Result;
-  Panel->Color = clWindow;
-  Panel->ParentBackground = false;
-  Panel->Anchors = TAnchors() << akLeft << akRight << akTop;
-  Panel->BevelOuter = bvNone;
-  Panel->BevelKind = bkNone;
-  Panel->Caption = L"";
-
   UnicodeString BodyMsg = Msg;
   BodyMsg = RemoveInteractiveMsgTag(BodyMsg);
   UnicodeString MainMsg;
@@ -1119,10 +1136,7 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
 
   assert((IconTextWidth > 0) && (IconTextHeight > 0));
 
-  if (HasIcon && (IconTextHeight < 32))
-  {
-    IconTextHeight = 32;
-  }
+  IconTextHeight = Max(IconTextHeight, IconHeight);
 
   int MoreMessageHeight =
     (HasMoreMessages ?
@@ -1190,22 +1204,6 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
     Result->Caption = Application->Title;
   }
 
-  if ((IconID != NULL) || !ImageName.IsEmpty())
-  {
-    TImage * Image = new TImage(Panel);
-    Image->Name = L"Image";
-    Image->Parent = Panel;
-    if (!ImageName.IsEmpty())
-    {
-      LoadResourceImage(Image, ImageName);
-    }
-    else
-    {
-      Image->Picture->Icon->Handle = LoadIcon(0, IconID);
-    }
-    Image->SetBounds(HorzMargin, VertMargin, 32, 32);
-  }
-
   if (MoreMessagesControl != NULL)
   {
     MoreMessagesControl->SetBounds(

+ 14 - 0
source/forms/OpenDirectory.cpp

@@ -249,6 +249,7 @@ bool __fastcall TOpenDirectoryDialog::Execute()
       AddAsBookmark(PageControl->ActivePage);
     }
   }
+  FBookmarkSelected = false;
   Result = (ShowModal() == DefaultResult(this));
   if (Terminal)
   {
@@ -256,6 +257,17 @@ bool __fastcall TOpenDirectoryDialog::Execute()
     WinConfiguration->SharedBookmarks = FSharedBookmarkList;
     WinConfiguration->UseSharedBookmarks = (PageControl->ActivePage == SharedBookmarksSheet);
   }
+  if (Result)
+  {
+    if (FBookmarkSelected)
+    {
+      Configuration->Usage->Inc(L"OpenedBookmark");
+    }
+    else
+    {
+      Configuration->Usage->Inc(L"OpenedPath");
+    }
+  }
   return Result;
 }
 //---------------------------------------------------------------------------
@@ -400,6 +412,7 @@ void __fastcall TOpenDirectoryDialog::BookmarkSelected(TObject * Sender)
     Directory = BookmarkDirectory(GetBookmark(BookmarksList, BookmarksList->ItemIndex));
   }
   UpdateControls();
+  FBookmarkSelected = true;
 }
 //---------------------------------------------------------------------------
 void __fastcall TOpenDirectoryDialog::BookmarksListClick(TObject * Sender)
@@ -487,6 +500,7 @@ void __fastcall TOpenDirectoryDialog::DirectoryEditChange(TObject * /*Sender*/)
   SelectBookmark(SessionBookmarksList);
   SelectBookmark(SharedBookmarksList);
   UpdateControls();
+  FBookmarkSelected = false;
 }
 //---------------------------------------------------------------------------
 void __fastcall TOpenDirectoryDialog::BookmarksListDblClick(TObject * Sender)

+ 1 - 0
source/forms/OpenDirectory.h

@@ -100,6 +100,7 @@ private:
   bool FAllowSwitch;
   TListBoxScrollOnDragOver * FSessionScrollOnDragOver;
   TListBoxScrollOnDragOver * FSharedScrollOnDragOver;
+  bool FBookmarkSelected;
 
   void __fastcall SetDirectory(UnicodeString value);
   UnicodeString __fastcall GetDirectory();

+ 7 - 5
source/forms/ScpCommander.cpp

@@ -520,6 +520,7 @@ void __fastcall TScpCommanderForm::ConfigurationChanged()
     LocalDirView->Invalidate();
   }
 
+  // See also LocalDirViewDDTargetHasDropHandler
   LocalDirView->DragDropFilesEx->ShellExtensions->DropHandler =
     !WinConfiguration->DDExtEnabled;
   LocalDriveView->DragDropFilesEx->ShellExtensions->DropHandler =
@@ -1321,11 +1322,12 @@ bool __fastcall TScpCommanderForm::OpenBookmark(UnicodeString Local, UnicodeStri
 void __fastcall TScpCommanderForm::LocalDirViewDDTargetHasDropHandler(
   TObject * /*Sender*/, TListItem * Item, int & /*Effect*/, bool & DropHandler)
 {
-  // when drop target is not directory, it is probably file type, which have
-  // associated drop handler (such as ZIP file in WinXP). in this case we
-  // cannot allow downloading when using shellex.
-  // ! this check is duplicated in InternalDDDownload() for non-shellex downloads
-  if ((FDDExtMapFile != NULL) &&
+  // When drop target is not directory, it is probably file type, which have
+  // associated drop handler (such as EXE file). In this case we
+  // cannot allow drop when when using shellex,
+  // as drop handlers are disabled, so drop would error
+  // (see TShellExtension.DropHandler assignment in ConfigurationChanged),
+  if (WinConfiguration->DDExtEnabled &&
       !LocalDirView->ItemIsDirectory(Item))
   {
     DropHandler = false;

+ 4 - 4
source/forms/ScpCommander.dfm

@@ -1022,7 +1022,7 @@ inherited ScpCommanderForm: TScpCommanderForm
           Alignment = taCenter
           Framed = False
           Hint = 'Click to show hidden files'
-          MaxSize = 100
+          MaxSize = 120
           Size = 80
           StretchPriority = 2
           Tag = 0
@@ -1032,7 +1032,7 @@ inherited ScpCommanderForm: TScpCommanderForm
           Alignment = taCenter
           Framed = False
           Hint = 'Click to modify or clear the filter'
-          MaxSize = 100
+          MaxSize = 120
           Size = 80
           StretchPriority = 2
           Tag = 0
@@ -1309,7 +1309,7 @@ inherited ScpCommanderForm: TScpCommanderForm
           Alignment = taCenter
           Framed = False
           Hint = 'Click to show hidden files'
-          MaxSize = 100
+          MaxSize = 120
           Size = 80
           StretchPriority = 2
           Tag = 0
@@ -1319,7 +1319,7 @@ inherited ScpCommanderForm: TScpCommanderForm
           Alignment = taCenter
           Framed = False
           Hint = 'Click to modify or clear the filter'
-          MaxSize = 100
+          MaxSize = 120
           Size = 80
           StretchPriority = 2
           Tag = 0

+ 3 - 3
source/forms/ScpExplorer.dfm

@@ -953,7 +953,7 @@ inherited ScpExplorerForm: TScpExplorerForm
       Images = GlyphsModule.SessionImages
       Panels = <
         item
-          Size = 170
+          Size = 130
           StretchPriority = 1
           Tag = 0
           TextTruncation = twEndEllipsis
@@ -961,14 +961,14 @@ inherited ScpExplorerForm: TScpExplorerForm
         item
           Alignment = taCenter
           Hint = 'Click to show hidden files'
-          Size = 100
+          Size = 120
           Tag = 0
           TextTruncation = twEndEllipsis
         end
         item
           Alignment = taCenter
           Hint = 'Click to modify or clear the filter'
-          Size = 100
+          Size = 120
           Tag = 0
           TextTruncation = twEndEllipsis
         end

+ 5 - 2
source/packages/dragndrop/DragDrop.pas

@@ -902,8 +902,11 @@ begin
           else if dwEffect and DROPEFFECT_Move<>0 then HC:=FOwner.FCHMove
                else if dwEffect and DROPEFFECT_COPY<>0 then HC:=FOwner.FCHCopy
                          else HC:=DefaultCursor;
-     if HC=DefaultCursor then Result:=DRAGDROP_S_USEDEFAULTCURSORS
-     else
+     if HC=DefaultCursor then
+     begin
+          Result:=DRAGDROP_S_USEDEFAULTCURSORS
+     end
+         else
      begin
           Result:=S_Ok;
           Windows.SetCursor(HC);

+ 5 - 2
source/packages/filemng/CustomDirView.pas

@@ -389,7 +389,7 @@ type
     property DimmHiddenFiles: Boolean read FDimmHiddenFiles write SetDimmHiddenFiles default True;
     property DragDropFilesEx: TCustomizableDragDropFilesEx read FDragDropFilesEx;
     property FormatSizeBytes: TFormatBytesStyle read FFormatSizeBytes write SetFormatSizeBytes default fbNone;
-    property WantUseDragImages: Boolean read FWantUseDragImages write FWantUseDragImages default True;
+    property WantUseDragImages: Boolean read FWantUseDragImages write FWantUseDragImages default False;
     property UseDragImages: Boolean read GetUseDragImages stored False;
     property FullDrag default True;
     property TargetPopupMenu: Boolean read GetTargetPopupMenu write SetTargetPopupMenu default True;
@@ -838,7 +838,7 @@ begin
   FDimmHiddenFiles := True;
   FShowHiddenFiles := True;
   FFormatSizeBytes := fbNone;
-  FWantUseDragImages := True;
+  FWantUseDragImages := False;
   FAddParentDir := False;
   FullDrag := True;
   FInvalidNameChars := '\/:*?"<>|';
@@ -2222,7 +2222,9 @@ begin
   end;
 
   if Assigned(OnDDQueryContinueDrag) then
+  begin
     OnDDQueryContinueDrag(Self, FEscapePressed, grfKeyState, Result);
+  end;
 
   try
     if FEscapePressed then
@@ -2549,6 +2551,7 @@ begin
       FDragEnabled := False;
       {Create the dragimage:}
       GlobalDragImageList := DragImageList;
+      // This code is not used anymore
       if UseDragImages and (not AvoidDragImage) then
       begin
         ImageListHandle := ListView_CreateDragImage(Handle, FirstItem.Index, Spot);

+ 73 - 283
source/packages/filemng/DirView.pas

@@ -1920,171 +1920,43 @@ begin
   Result := StrCmpLogicalW(PChar(S1), PChar(S2));
 end;
 
-function CompareFileName(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
+function CompareFileType(I1, I2: TListItem; P1, P2: PFileRec): Integer;
 var
-  P1, P2: PFileRec;
+  Key1, Key2: string;
 begin
-  if I1 = I2 then  Result := fEqual
-    else
-  if I1 = nil then Result := fLess
-    else
-  if I2 = nil then Result := fGreater
-    else
+  if P1.Empty then TDirView(I1.ListView).GetDisplayData(I1, False);
+  if P2.Empty then TDirView(I2.ListView).GetDisplayData(I2, False);
+  if P1.IsDirectory then
   begin
-    P1 := PFileRec(I1.Data);
-    P2 := PFileRec(I2.Data);
-    if P1.isParentDir then
-    begin
-      Result := fLess;
-      Exit;
-    end
-      else
-    if P2.isParentDir then
-    begin
-      Result := fGreater;
-      Exit;
-    end;
-
-    {Directories allways should appear "grouped":}
-    if P1.isDirectory <> P2.isDirectory then
-    begin
-      if P1.isDirectory then
-      begin
-        Result := fLess;
-        Exit;
-      end
-        else
-      begin
-        Result := fGreater;
-        Exit;
-      end;
-    end
-      else Result := CompareLogicalText(P1.DisplayName, P2.DisplayName);
-  end;
-
-  if not AOwner.SortAscending then
-    Result := -Result;
-end; {CompareFileName}
-
-function CompareFileSize(I1, I2: TListItem; AOwner : TDirView): Integer; stdcall;
-var
-  P1, P2: PFileRec;
-begin
-  if I1 = I2 then Result := fEqual
-    else
-  if I1 = nil then Result := fLess
-    else
-  if I2 = nil then Result := fGreater
+    Key1 := P1.TypeName + ' ' + P1.DisplayName;
+    Key2 := P2.TypeName + ' ' + P2.DisplayName;
+  end
     else
   begin
-    P1 := PFileRec(I1.Data);
-    P2 := PFileRec(I2.Data);
-    if P1.isParentDir then
-    begin
-      Result := fLess;
-      Exit;
-    end
-      else
-    if P2.isParentDir then
-    begin
-      Result := fGreater;
-      Exit;
-    end;
-    {Directories always should appear "grouped":}
-    if P1.isDirectory <> P2.isDirectory then
-    begin
-      if P1.isDirectory then
-      begin
-        Result := fLess;
-        Exit;
-      end
-        else
-      begin
-        Result := fGreater;
-        Exit;
-      end;
-    end
-      else
-    begin
-      if P1.Size < P2.Size then Result := fLess
-        else
-      if P1.Size > P2.Size then Result := fGreater
-        else
-      Result := CompareLogicalText(P1.DisplayName, P2.DisplayName);
-    end;
+    Key1 := P1.TypeName + ' ' + P1.FileExt + ' ' + P1.DisplayName;
+    Key2 := P2.TypeName + ' ' + P2.FileExt + ' ' + P2.DisplayName;
   end;
-  if not AOwner.SortAscending then
-    Result := -Result;
-end; {CompareFileSize}
+  Result := CompareLogicalText(Key1, Key2);
+end;
 
-function CompareFileType(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
+function CompareFileTime(P1, P2: PFileRec): Integer;
 var
-  P1, P2: PFileRec;
-  Key1, Key2: string;
+  Time1, Time2: Int64;
 begin
-  if I1 = I2 then  Result := fEqual
-    else
-  if I1 = nil then Result := fLess
+  Time1 := Int64(P1.FileTime.dwHighDateTime) shl 32 + P1.FileTime.dwLowDateTime;
+  Time2 := Int64(P2.FileTime.dwHighDateTime) shl 32 + P2.FileTime.dwLowDateTime;
+  if Time1 < Time2 then Result := fLess
     else
-  if I2 = nil then Result := fGreater
-    else
-  begin
-    P1 := PFileRec(I1.Data);
-    P2 := PFileRec(I2.Data);
-    if P1.isParentDir then
-    begin
-      Result := fLess;
-      Exit;
-    end
-      else
-    if P2.isParentDir then
-    begin
-      Result := fGreater;
-      Exit;
-    end;
-
-    {Directories allways should appear "grouped":}
-    if P1.isDirectory <> P2.isDirectory then
-    begin
-      if P1.isDirectory then
-      begin
-        Result := fLess;
-        Exit;
-      end
-        else
-      begin
-        Result := fGreater;
-        Exit;
-      end;
-    end
-      else
-    begin
-      if P1.Empty then TDirView(I1.ListView).GetDisplayData(I1, False);
-      if P2.Empty then TDirView(I2.ListView).GetDisplayData(I2, False);
-      if P1.IsDirectory then
-      begin
-        Key1 := P1.TypeName + ' ' + P1.DisplayName;
-        Key2 := P2.TypeName + ' ' + P2.DisplayName;
-      end
-        else
-      begin
-        Key1 := P1.TypeName + ' ' + P1.FileExt + ' ' + P1.DisplayName;
-        Key2 := P2.TypeName + ' ' + P2.FileExt + ' ' + P2.DisplayName;
-      end;
-      Result := CompareLogicalText(Key1, Key2);
-      if Result = 0 then
-        // the fallback is probably pointless for directories as they have the same TypeName
-        Result := CompareLogicalText(P1.DisplayName, P2.DisplayName);
-    end;
-  end;
-  if not AOwner.SortAscending then
-    Result := -Result;
-end; {CompareFileType}
+  if Time1 > Time2 then Result := fGreater
+    else Result := fEqual; // fallback
+end;
 
-function CompareFileExt(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
+function CompareFile(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
 var
+  ConsiderDirection: Boolean;
   P1, P2: PFileRec;
 begin
+  ConsiderDirection := True;
   if I1 = I2 then Result := fEqual
     else
   if I1 = nil then Result := fLess
@@ -2097,166 +1969,87 @@ begin
     if P1.isParentDir then
     begin
       Result := fLess;
-      Exit;
+      ConsiderDirection := False;
     end
       else
     if P2.isParentDir then
     begin
       Result := fGreater;
-      Exit;
-    end;
-    {Directories allways should appear "grouped":}
-    if P1.isDirectory <> P2.isDirectory then
-    begin
-      if P1.isDirectory then
-      begin
-        Result := fLess;
-        Exit;
-      end
-        else
-      begin
-        Result := fGreater;
-        Exit;
-      end;
+      ConsiderDirection := False;
     end
       else
-    if P1.isDirectory then
-    begin
-      Result := CompareLogicalText(P1.DisplayName, P2.DisplayName);
-    end
-      else
-    begin
-      Result := CompareLogicalText(
-        P1.FileExt + ' ' + P1.DisplayName, P2.FileExt + ' ' + P2.DisplayName);
-    end;
-  end;
-  if not AOwner.SortAscending then
-    Result := -Result;
-end; {CompareFileExt}
-
-function CompareFileAttr(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
-var
-  P1, P2: PFileRec;
-begin
-  if I1 = I2 then Result := 0
-    else
-  if I1 = nil then Result := -1
-    else
-  if I2 = nil then Result := 1
-    else
-  begin
-    P1 := PFileRec(I1.Data);
-    P2 := PFileRec(I2.Data);
-    if P1.isParentDir then
-    begin
-      Result := fLess;
-      Exit;
-    end
-      else
-    if P2.isParentDir then
-    begin
-      Result := fGreater;
-      Exit;
-    end;
-    {Directories allways should appear "grouped":}
+    {Directories should always appear "grouped":}
     if P1.isDirectory <> P2.isDirectory then
     begin
       if P1.isDirectory then
       begin
         Result := fLess;
-        Exit;
+        ConsiderDirection := False;
       end
         else
       begin
         Result := fGreater;
-        Exit;
+        ConsiderDirection := False;
       end;
     end
       else
     begin
-      if P1.Attr < P2.Attr then Result := fLess
-        else
-      if P1.Attr > P2.Attr then Result := fGreater
-        else
-      Result := CompareLogicalText(P1.DisplayName, P2.DisplayName);
-    end;
-  end;
-  if not AOwner.SortAscending then
-    Result := -Result;
-end; {CompareFileAttr}
+      Result := fEqual;
+
+      case AOwner.DirColProperties.SortDirColumn of
+        dvName:
+          ; // fallback
+
+        dvSize:
+          if P1.Size < P2.Size then Result := fLess
+            else
+          if P1.Size > P2.Size then Result := fGreater
+            else ; // fallback
+
+        dvType:
+          Result := CompareFileType(I1, I2, P1, P2);
+
+        dvChanged:
+          Result := CompareFileTime(P1, P2);
+
+        dvAttr:
+          if P1.Attr < P2.Attr then Result := fLess
+            else
+          if P1.Attr > P2.Attr then Result := fGreater
+            else ; // fallback
+
+        dvExt:
+          if not P1.isDirectory then
+          begin
+            Result := CompareLogicalText(
+              P1.FileExt + ' ' + P1.DisplayName, P2.FileExt + ' ' + P2.DisplayName);
+          end
+            else ; //fallback
 
-function CompareFileTime(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;
-var
-  Time1, Time2: Int64;
-  P1, P2: PFileRec;
-begin
-  if I1 = I2 then Result := fEqual
-    else
-  if I1 = nil then Result := fLess
-    else
-  if I2 = nil then Result := fGreater
-    else
-  begin
-    P1 := PFileRec(I1.Data);
-    P2 := PFileRec(I2.Data);
-    if P1.isParentDir then
-    begin
-      Result := fLess;
-      Exit;
-    end
-      else
-    if P2.isParentDir then
-    begin
-      Result := fGreater;
-      Exit;
-    end;
-    {Directories allways should appear "grouped":}
-    if P1.isDirectory <> P2.isDirectory then
-    begin
-      if P1.isDirectory then
-      begin
-        Result := fLess;
-        Exit;
-      end
         else
+          ; // fallback
+      end;
+
+      if Result = fEqual then
       begin
-        Result := fGreater;
-        Exit;
+        Result := CompareLogicalText(P1.DisplayName, P2.DisplayName)
       end;
-    end
-      else
-    begin
-      Time1 := Int64(P1.FileTime.dwHighDateTime) shl 32 + P1.FileTime.dwLowDateTime;
-      Time2 := Int64(P2.FileTime.dwHighDateTime) shl 32 + P2.FileTime.dwLowDateTime;
-      if Time1 < Time2 then Result := fLess
-        else
-      if Time1 > Time2 then Result := fGreater
-        else
-      Result := CompareFileName(I1, I2, AOwner);
     end;
   end;
-  if not AOwner.SortAscending then
+
+  if ConsiderDirection and (not AOwner.SortAscending) then
+  begin
     Result := -Result;
-end; {CompareFileTime}
+  end;
+end;
 
 procedure TDirView.SortItems;
-var
-  SortProc: TLVCompare;
 begin
   if HandleAllocated then
   begin
     StopIconUpdateThread;
     try
-      case DirColProperties.SortDirColumn of
-        dvName: SortProc := @CompareFilename;
-        dvSize: SortProc := @CompareFileSize;
-        dvType: SortProc := @CompareFileType;
-        dvChanged: SortProc := @CompareFileTime;
-        dvAttr: SortProc := @CompareFileAttr;
-        dvExt: SortProc := @CompareFileExt;
-        else SortProc := @CompareFilename;
-      end;
-      CustomSortItems(Pointer(@SortProc));
+      CustomSortItems(@CompareFile);
     finally
       if (not Loading) and FUseIconUpdateThread then
         StartIconUpdateThread;
@@ -3193,8 +2986,8 @@ begin
   if Assigned(FDriveView) then
   begin
     // When a change is detected while menu is popped up
-    // it loses focus (or somethins similar)
-    // preventing it from handling sussequent click.
+    // it loses focus (or something similar)
+    // preventing it from handling subsequent click.
     // This typically happens when right-dragging from remote to local panel,
     // what causes temp directory being created+deleted.
     // This is HACK, we should implement some uniform watch disabling/enabling
@@ -3223,15 +3016,12 @@ end;
 procedure TDirView.DDDropHandlerSucceeded(Sender: TObject; grfKeyState: Longint;
   Point: TPoint; dwEffect: Longint);
 begin
+  // Not sure why is this here. There's no "disable" counterparty.
   if not WatchThreadActive then
   begin
     FChangeTimer.Interval := FChangeInterval;
     FChangeTimer.Enabled  := True;
   end;
-  if Assigned(FDriveView) then
-  begin
-    TDriveView(FDriveView).ResumeChangeTimer;
-  end;
 
   inherited;
 end;

+ 5 - 0
source/putty/windows/WINNET.C

@@ -990,7 +990,12 @@ static DWORD try_connect(Actual_Socket sock,
 
     if (sndbuf > 0)
     {
+	int rcvbuf = 4 * 1024 * 1024;
 	p_setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf, sizeof(sndbuf));
+
+	// For now we increase receive buffer, whenever send buffer is set.
+	// The size is not configurable. The constant taken from FZ.
+	p_setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*) &rcvbuf, sizeof(rcvbuf));
     }
 
     /*

+ 1 - 1
source/resource/Propagation.rc

@@ -2,7 +2,7 @@ STRINGTABLE
 BEGIN
   DESCRIPTION_45_LETTERS, "Free SFTP, WebDAV and FTP client for Windows"
   DESCRIPTION_80_LETTERS, "WinSCP is a popular free SFTP, SCP, WebDAV and FTP client for Windows."
-  DESCRIPTION_250_LETTERS, "WinSCP, a popular free SFTP and FTP client for Windows, copies files between a local and remote computer. It supports also FTPS, SCP and WebDAV. It offers easy to use GUI for all common file operations and a powerfull automation with .NET assembly."
+  DESCRIPTION_250_LETTERS, "WinSCP, a popular free SFTP and FTP client for Windows, copies files between a local and remote computer. It supports also FTPS, SCP and WebDAV. It offers easy to use GUI for all common file operations and a powerful automation with .NET assembly."
   DESCRIPTION_450_LETTERS, "WinSCP is a popular free SFTP and FTP client for Windows, a powerful tool that will improve your productivity. WinSCP can copy files between a local and remote computer using FTP, FTPS, SCP, SFTP or WebDAV. WinSCP offers an easy to use GUI for all common operations with files and directories. Power users can automate WinSCP using .NET assembly. WinSCP documentation is at http://winscp.net/. WinSCP is available in English and many other languages."
   // "English" shall be replaced by translation language
   DESCRIPTION_2000_LETTERS, "WinSCP is a popular free SFTP and FTP client for Windows. Moreover, WinSCP is a powerful multi-functional tool that will improve your productivity. WinSCP can copy files between a local and remote computer using multiple protocols: FTP, FTPS, SCP, SFTP or WebDAV. On the one hand, WinSCP offers an easy to use graphical user interface; you can choose between Windows Explorer look and tabbed twin-panel interface like Norton commander. On the other hand, advanced users can automate WinSCP functionality using .NET assembly or simple batch file scripting. You will use WinSCP for all common operations with files. You can start editing a file directly from WinSCP, either using WinSCP internal text editor or using integration with your favorite external text editor. WinSCP operations are not limited to individual files; WinSCP offers several ways to synchronize your remote and local directories. After connecting to a site you can choose to store site information for repeated access, WinSCP can even share site settings with another popular open source tool PuTTY. WinSCP integrates also with Pageant (PuTTY authentication agent) for full support of public key authentication with SSH. Admins love WinSCP support for portable operation using a configuration file instead of registry entries, suitable for operation from removable media. A comprehensive WinSCP documentation is freely accessible at http://winscp.net/. This site hosts also a very active user forum for support and feature requests. WinSCP is available in English and many other languages. WinSCP is an open source software distributed free of charge under the terms of the GNU General Public License (GPL)."

+ 2 - 0
source/resource/TextsFileZilla.h

@@ -658,6 +658,8 @@
 #define IDS_OPTIONSCAPTION_FONTNAME     3104
 #endif
 #define IDS_PROXY_CONNECTED             3201
+#define IDS_CONTROL_CONNECTION          3202
+#define IDS_DATA_CONNECTION             3203
 #if 0
 #define ID_CANCELBUTTON                 32768
 #define ID_COPYTOSITEMANAGER            32769

+ 2 - 0
source/resource/TextsFileZilla.rc

@@ -85,4 +85,6 @@ END
 STRINGTABLE
 BEGIN
   IDS_PROXY_CONNECTED, "Connection with proxy established, performing handshake..."
+  IDS_CONTROL_CONNECTION, "control connection"
+  IDS_DATA_CONNECTION, "data connection"
 END

+ 1 - 0
source/resource/TextsWin.h

@@ -224,6 +224,7 @@
 #define MESSAGE_LOADING         1536
 #define NEW_VERSION_CLICK       1537
 #define DIRECTORY_READING_AND_RESOLVING_PROGRESS 1538
+#define MESSAGE_DISPLAY_ERROR   1539
 
 #define WIN_FORMS_STRINGS       1600
 #define LOG_NOLOG               1601

+ 1 - 0
source/resource/TextsWin1.rc

@@ -226,6 +226,7 @@ BEGIN
         MESSAGE_LOADING, "Loading..."
         NEW_VERSION_CLICK, "%s\n\nClick here, to see what is new."
         DIRECTORY_READING_AND_RESOLVING_PROGRESS, "%d Resolving links and Reading directory"
+        MESSAGE_DISPLAY_ERROR, "Cannot display"
 
         WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
         LOG_NOLOG, "No session log."

+ 3 - 3
source/resource/TextsWin2.rc

@@ -25,14 +25,14 @@ BEGIN
 "G:%APP% [mysession] /synchronize [local_dir] [remote_dir] [/defaults]\n"
 "G:%APP% [mysession] /keepuptodate [local_dir] [remote_dir] [/defaults]\n"
 "G:%APP% [mysession] [/privatekey=<key>] [/hostkey=<fingerprint>]\n"
-"G:%APP% [mysession] [/passive[=on|off]] /implicit|explicit\n"
+"G:%APP% [mysession] [/passive[=on|off]] [/implicit|explicit]\n"
 "G:%APP% [mysession] [/timeout=<sec>]\n"
-"G:%APP% [mysession] [/rawsettings setting1=value1 settings2=value2 ...]\n"
+"G:%APP% [mysession] [/rawsettings setting1=value1 setting2=value2 ...]\n"
 "G:%APP% [/console] [/script=file] [/command command1...] [/parameter param1...]\n"
 "C:%APP% [/script=file] [/command command1...] [/parameter param1...]\n"
 "B:%APP% [/ini=<inifile>] [/log=<logfile>] [/xmllog=<logfile> [/xmlgroups]]\n"
 "B:%APP% [/rawconfig config1=value1 config2=value2 ...]\n"
-"B:%APP% [/batchsettings <site_mask> setting1=value1 setting2=value2 ...]\n"
+"B:%APP% /batchsettings <site_mask> setting1=value1 setting2=value2 ...\n"
 "G:%APP% /update\n"
 "B:%APP% /help\n"
 "\n"

+ 2 - 0
source/windows/ConsoleRunner.cpp

@@ -2104,6 +2104,8 @@ int __fastcall Console(TConsoleMode Mode)
       Console = new TNullConsole();
     }
 
+    SetNoGUI();
+
     if (Mode == cmHelp)
     {
       Configuration->Usage->Inc(L"UsageShown");

+ 12 - 7
source/windows/GUITools.cpp

@@ -646,6 +646,17 @@ void __fastcall TBrowserViewer::BeforeNavigate2(
   }
 }
 //---------------------------------------------------------------------------
+TPanel * __fastcall CreateLabelPanel(TPanel * Parent, const UnicodeString & Label)
+{
+  TPanel * Result = new TPanel(Parent);
+  Result->Parent = Parent;
+  Result->BevelOuter = bvNone;
+  Result->BevelInner = bvNone; // default
+  Result->Align = alClient;
+  Result->Caption = Label;
+  return Result;
+}
+//---------------------------------------------------------------------------
 TWebBrowserEx * __fastcall CreateBrowserViewer(TPanel * Parent, const UnicodeString & LoadingLabel)
 {
   TBrowserViewer * Result = new TBrowserViewer(Parent);
@@ -655,13 +666,7 @@ TWebBrowserEx * __fastcall CreateBrowserViewer(TPanel * Parent, const UnicodeStr
   Result->Align = alClient;
   Result->ControlBorder = cbNone;
 
-  TPanel * LoadingPanel = new TPanel(Parent);
-  LoadingPanel->Parent = Parent;
-  LoadingPanel->BevelOuter = bvNone;
-  LoadingPanel->BevelInner = bvNone; // default
-  LoadingPanel->Align = alClient;
-  LoadingPanel->Caption = LoadingLabel;
-  Result->LoadingPanel = LoadingPanel;
+  Result->LoadingPanel = CreateLabelPanel(Parent, LoadingLabel);
 
   return Result;
 }

+ 1 - 0
source/windows/GUITools.h

@@ -42,6 +42,7 @@ typedef int __fastcall (*TCalculateWidth)(UnicodeString Text, void * Arg);
 void __fastcall ApplyTabs(
   UnicodeString & Text, wchar_t Padding,
   TCalculateWidth CalculateWidth, void * CalculateWidthArg);
+TPanel * __fastcall CreateLabelPanel(TPanel * Parent, const UnicodeString & Label);
 namespace Webbrowserex
 {
   class TWebBrowserEx;

+ 5 - 0
source/windows/UserInterface.cpp

@@ -63,6 +63,11 @@ TConfiguration * __fastcall CreateConfiguration()
   return WinConfiguration;
 }
 //---------------------------------------------------------------------------
+TOptions * __fastcall GetGlobalOptions()
+{
+  return TProgramParams::Instance();
+}
+//---------------------------------------------------------------------------
 TCustomScpExplorerForm * __fastcall CreateScpExplorer()
 {
   TCustomScpExplorerForm * ScpExplorer;

+ 1 - 1
source/windows/WinConfiguration.cpp

@@ -61,7 +61,7 @@ bool __fastcall TEditorData::DecideExternalEditorText(UnicodeString ExternalEdit
 {
   bool Result = false;
   // By default we use default transfer mode (binary),
-  // as all reasonable 3rd party editors suppose all EOS styles.
+  // as all reasonable 3rd party editors support all EOL styles.
   // A notable exception is Windows Notepad, so here's an exception for it.
 
   ReformatFileNameCommand(ExternalEditor);

+ 9 - 1
source/windows/WinInterface.cpp

@@ -677,12 +677,20 @@ void __fastcall BusyEnd(void * Token)
 static DWORD MainThread = 0;
 static TDateTime LastGUIUpdate = 0;
 static double GUIUpdateIntervalFrac = static_cast<double>(OneSecond/1000*GUIUpdateInterval);  // 1/5 sec
+static bool NoGUI = false;
+//---------------------------------------------------------------------------
+void __fastcall SetNoGUI()
+{
+  NoGUI = true;
+}
 //---------------------------------------------------------------------------
 bool __fastcall ProcessGUI(bool Force)
 {
   assert(MainThread != 0);
   bool Result = false;
-  if (MainThread == GetCurrentThreadId())
+  // Calling ProcessMessages in Azure WebJob causes access violation in VCL.
+  // As we do not really need to call it in scripting/.NET, just skip it.
+  if ((MainThread == GetCurrentThreadId()) && !NoGUI)
   {
     TDateTime N = Now();
     if (Force ||

+ 27 - 1
source/windows/WinMain.cpp

@@ -502,6 +502,33 @@ int __fastcall Execute()
     Configuration->Usage->Inc(L"ConsoleDotNet");
   }
 
+  UnicodeString SwitchValue;
+  if (Params->FindSwitch(L"loglevel", SwitchValue))
+  {
+    int StarPos = SwitchValue.Pos(L"*");
+    if (StarPos > 0)
+    {
+      bool LogSensitive = true;
+      SwitchValue.Delete(StarPos, 1);
+
+      if ((StarPos <= SwitchValue.Length()) &&
+          (SwitchValue[StarPos] == L'-'))
+      {
+        LogSensitive = false;
+        SwitchValue.Delete(StarPos, 1);
+      }
+
+      SwitchValue = SwitchValue.Trim();
+
+      Configuration->TemporaryLogSensitive(LogSensitive);
+    }
+    int LogProtocol;
+    if (!SwitchValue.IsEmpty() && TryStrToInt(SwitchValue, LogProtocol) && (LogProtocol >= 0))
+    {
+      Configuration->TemporaryLogProtocol(LogProtocol);
+    }
+  }
+
   TConsoleMode Mode = cmNone;
   if (Params->FindSwitch(L"help") || Params->FindSwitch(L"h") || Params->FindSwitch(L"?"))
   {
@@ -559,7 +586,6 @@ int __fastcall Execute()
       }
     }
 
-    UnicodeString SwitchValue;
     if (Params->FindSwitch(L"UninstallCleanup"))
     {
       MaintenanceTask();

+ 2 - 0
source/windows/WinSCP.exe.manifest

@@ -40,6 +40,8 @@
             <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
             <!-- Windows 8.1 -->
             <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+            <!-- Windows 10 -->
+            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
         </application>
     </compatibility>
     <asmv3:application>