Martin Prikryl 12 years ago
parent
commit
11ce636668
47 changed files with 488 additions and 180 deletions
  1. 89 4
      deployment/winscpsetup.iss
  2. 1 1
      dotnet/properties/AssemblyInfo.cs
  3. 1 1
      source/Console.cbproj
  4. 1 1
      source/DragExt.cbproj
  5. 3 3
      source/DragExt64.rc
  6. 2 2
      source/WinSCP.cbproj
  7. 3 3
      source/core/FtpFileSystem.cpp
  8. 2 0
      source/core/ScpFileSystem.cpp
  9. 1 1
      source/core/Script.cpp
  10. 7 4
      source/core/SecureShell.cpp
  11. 1 1
      source/core/SessionData.cpp
  12. 100 13
      source/core/SftpFileSystem.cpp
  13. 1 0
      source/core/SftpFileSystem.h
  14. 63 39
      source/core/Terminal.cpp
  15. 3 1
      source/core/Terminal.h
  16. 3 4
      source/core/WebDAVFileSystem.cpp
  17. 3 1
      source/filezilla/AsyncSocketExLayer.cpp
  18. 5 1
      source/filezilla/FtpControlSocket.cpp
  19. 31 22
      source/forms/Custom.cpp
  20. 8 22
      source/forms/CustomScpExplorer.cpp
  21. 0 2
      source/forms/CustomScpExplorer.h
  22. 0 1
      source/forms/EditMask.dfm
  23. 0 2
      source/forms/FileSystemInfo.dfm
  24. 2 0
      source/forms/LocationProfiles.cpp
  25. 47 8
      source/forms/Login.cpp
  26. 2 0
      source/forms/Login.dfm
  27. 1 0
      source/forms/Login.h
  28. 43 21
      source/forms/MessageDlg.cpp
  29. 1 0
      source/forms/Preferences.dfm
  30. 2 0
      source/forms/Properties.dfm
  31. 1 0
      source/forms/RemoteTransfer.dfm
  32. 6 2
      source/packages/filemng/CustomDirView.pas
  33. 2 0
      source/resource/HelpWin.h
  34. 4 4
      source/resource/TextsCore.h
  35. 4 4
      source/resource/TextsCore1.rc
  36. 1 1
      source/resource/TextsCore2.rc
  37. 1 1
      source/resource/TextsFileZilla.h
  38. 1 1
      source/resource/TextsFileZilla.rc
  39. 2 2
      source/resource/TextsWin.h
  40. 2 2
      source/resource/TextsWin1.rc
  41. 1 1
      source/resource/TextsWin2.rc
  42. 19 0
      source/windows/Tools.cpp
  43. 2 0
      source/windows/Tools.h
  44. 1 1
      source/windows/UserInterface.cpp
  45. 9 0
      source/windows/VCLCommon.cpp
  46. 2 1
      source/windows/WinInterface.h
  47. 4 2
      source/windows/WinMain.cpp

+ 89 - 4
deployment/winscpsetup.iss

@@ -39,8 +39,10 @@
 #define TranslationFileMask "WinSCP.???"
 #define MainFileName "WinSCP.exe"
 #define MainFileSource BinariesDir+"\"+MainFileName
-#define ShellExtFileSource BinariesDir+"\DragExt.dll"
-#define ShellExt64FileSource BinariesDir+"\DragExt64.dll"
+#define ShellExtFileName "DragExt.dll"
+#define ShellExtFileSource BinariesDir+"\"+ShellExtFileName
+#define ShellExt64FileName "DragExt64.dll"
+#define ShellExt64FileSource BinariesDir+"\"+ShellExt64FileName
 #define ConsoleFileSource BinariesDir+"\WinSCP.com"
 #define IconFileSource SourceDir+"\resource\Icon256.ico"
 
@@ -309,11 +311,11 @@ Source: "license.txt"; DestDir: "{app}"; \
 Source: "{#ShellExtFileSource}"; DestDir: "{app}"; \
   Components: shellext; \
   Flags: regserver restartreplace uninsrestartdelete; \
-  Check: not IsWin64
+  Check: not IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExtFileName}'), '{#GetFileVersion(ShellExtFileSource)}')
 Source: "{#ShellExt64FileSource}"; DestDir: "{app}"; \
   Components: shellext; \
   Flags: regserver restartreplace uninsrestartdelete; \
-  Check: IsWin64
+  Check: IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExt64FileName}'), '{#GetFileVersion(ShellExt64FileSource)}')
 Source: "{#PuttySourceDir}\LICENCE"; DestDir: "{app}\PuTTY"; \
   Components: pageant puttygen; Flags: ignoreversion
 Source: "{#PuttySourceDir}\putty.hlp"; DestDir: "{app}\PuTTY"; \
@@ -434,6 +436,8 @@ var
   Upgrade: Boolean;
   MissingTranslations: string;
   PrevVersion: string;
+  ShellExtNewerCacheFileName: string;
+  ShellExtNewerCacheResult: Boolean;
 #ifdef Donations
   DonationPanel: TPanel;
 #endif
@@ -472,6 +476,87 @@ begin
     ((Version.Major = 6) and (Version.Minor >= 2));
 end;
 
+procedure CutVersionPart(var VersionString: string; var VersionPart: Word);
+var
+  P: Integer;
+begin
+  P := Pos('.', VersionString);
+  if P > 0 then
+  begin
+    VersionPart := StrToIntDef(Copy(VersionString, 1, P - 1), 0);
+    Delete(VersionString, 1, P);
+  end
+    else
+  begin
+    VersionPart := StrToIntDef(VersionString, 0);
+    VersionString := '';
+  end;
+end;
+
+function IsShellExtNewer(FileName: string; InstalledVersion: string): Boolean;
+var
+  ExistingMS, ExistingLS: Cardinal;
+  ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild: Cardinal;
+  InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild: Word;
+begin
+  if ShellExtNewerCacheFileName = FileName then
+  begin
+    if ShellExtNewerCacheResult then
+    begin
+      Log(Format('Allowing installation of shell extension %s as already decided', [FileName]));
+      Result := True;
+    end
+      else
+    begin
+      Log(Format('Skipping installation of shell extension %s as already decided', [FileName]));
+      Result := False;
+    end;
+  end
+    else
+  if not FileExists(FileName) then
+  begin
+    Log(Format('Shell extension %s does not exist yet, allowing installation', [FileName]));
+    Result := True;
+  end
+    else
+  if not GetVersionNumbers(FileName, ExistingMS, ExistingLS) then
+  begin
+    Log(Format('Cannot retrieve version of existing shell extension %s, allowing installation', [FileName]));
+    Result := True;
+  end
+    else
+  begin
+    ExistingMajor := ExistingMS shr 16;
+    ExistingMinor := ExistingMS and $FFFF;
+    ExistingRev := ExistingLS shr 16;
+    ExistingBuild := ExistingLS and $FFFF;
+    Log(Format('Existing shell extension %s version: %d.%d.%d[.%d]', [FileName, ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild]));
+
+    Log(Format('Installed extension version string: %s', [InstalledVersion]));
+    CutVersionPart(InstalledVersion, InstalledMajor);
+    CutVersionPart(InstalledVersion, InstalledMinor);
+    CutVersionPart(InstalledVersion, InstalledRev);
+    CutVersionPart(InstalledVersion, InstalledBuild);
+    Log(Format('Installed extension version: %d.%d.%d[.%d]', [InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild]));
+
+    if ((InstalledMajor > ExistingMajor)) or
+       ((InstalledMajor = ExistingMajor) and (InstalledMinor > ExistingMinor)) or
+       ((InstalledMajor = ExistingMajor) and (InstalledMinor = ExistingMinor) and (InstalledRev > ExistingRev)) then
+    begin
+      Log('Installed extension is newer than existing extension, allowing installation');
+      Result := True;
+    end
+      else
+    begin
+      Log('Installed extension is same or older than existing extension, skipping installation');
+      Result := False;
+    end;
+  end;
+
+  ShellExtNewerCacheFileName := FileName;
+  ShellExtNewerCacheResult := Result;
+end;
+
 function UpdatesEnabled: Boolean;
 begin
   Result := AreUpdatesEnabled;

+ 1 - 1
dotnet/properties/AssemblyInfo.cs

@@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
 
 [assembly: AssemblyVersion("1.1.4.0")]
 [assembly: AssemblyFileVersion("1.1.4.0")]
-[assembly: AssemblyInformationalVersionAttribute("5.2.6.0")]
+[assembly: AssemblyInformationalVersionAttribute("5.2.7.0")]
 
 [assembly: CLSCompliant(true)]
 

+ 1 - 1
source/Console.cbproj

@@ -41,7 +41,7 @@
 			<PackageImports>rtl.bpi;$(PackageImports)</PackageImports>
 			<ProjectType>CppConsoleApplication</ProjectType>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.0.2.0;InternalName=console;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.2.6.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.0.2.0;InternalName=console;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.2.7.0;ReleaseType=RC;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>4</VerInfo_MajorVer>
 			<VerInfo_Release>2</VerInfo_Release>

+ 1 - 1
source/DragExt.cbproj

@@ -42,7 +42,7 @@
 			<ProjectType>CppDynamicLibrary</ProjectType>
 			<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-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.2.6.0;ReleaseType=beta;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-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.2.7.0;ReleaseType=RC;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,2,6,0
+PRODUCTVERSION 5,2,7,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.2.6.0\0"
-            VALUE "ReleaseType", "beta\0"
+            VALUE "ProductVersion", "5.2.7.0\0"
+            VALUE "ReleaseType", "RC\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 2 - 2
source/WinSCP.cbproj

@@ -54,11 +54,11 @@
 			<ProjectType>CppVCLApplication</ProjectType>
 			<UsingDelphiRTL>true</UsingDelphiRTL>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.2.6.0;InternalName=winscp;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.2.6.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.2.7.0;InternalName=winscp;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.2.7.0;ReleaseType=RC;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>5</VerInfo_MajorVer>
 			<VerInfo_MinorVer>2</VerInfo_MinorVer>
-			<VerInfo_Release>6</VerInfo_Release>
+			<VerInfo_Release>7</VerInfo_Release>
 		</PropertyGroup>
 		<PropertyGroup Condition="'$(Cfg_1)'!=''">
 			<BCC_DebugLineNumbers>true</BCC_DebugLineNumbers>

+ 3 - 3
source/core/FtpFileSystem.cpp

@@ -1150,7 +1150,7 @@ void __fastcall TFTPFileSystem::Sink(const UnicodeString FileName,
     THROW_SKIP_FILE_NULL;
   }
 
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName)));
+  FTerminal->LogFileDetails(FileName, File->Modification, File->Size);
 
   OperationProgress->SetFile(OnlyFileName);
 
@@ -1405,8 +1405,6 @@ void __fastcall TFTPFileSystem::Source(const UnicodeString FileName,
   TFileOperationProgressType * OperationProgress, unsigned int Flags,
   TUploadSessionAction & Action)
 {
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName)));
-
   Action.FileName(ExpandUNCFileName(FileName));
 
   OperationProgress->SetFile(FileName, false);
@@ -1920,6 +1918,7 @@ void __fastcall TFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
             Repeat = true;
             FListAll = asOff;
             GotNoFilesForAll = true;
+            FTerminal->LogEvent(L"LIST with -a switch returned empty directory listing, will try pure LIST");
           }
           else
           {
@@ -1939,6 +1938,7 @@ void __fastcall TFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
         // further try without "-a" only as the server may not support it
         if (FListAll == asAuto)
         {
+          FTerminal->LogEvent(L"LIST with -a failed, walling back to pure LIST");
           if (!FTerminal->Active)
           {
             FTerminal->Reopen(ropNoReadDirectory);

+ 2 - 0
source/core/ScpFileSystem.cpp

@@ -2317,6 +2317,8 @@ void __fastcall TSCPFileSystem::SCPSink(const UnicodeString TargetDir,
           SCPError(L"", false);
         }
 
+        FTerminal->LogFileDetails(FileName, SourceTimestamp, MaskParams.Size);
+
         UnicodeString DestFileName =
           IncludeTrailingBackslash(TargetDir) +
           CopyParam->ChangeFileName(OperationProgress->FileName, osRemote,

+ 1 - 1
source/core/Script.cpp

@@ -2281,7 +2281,7 @@ void __fastcall TManagementScript::DoChangeLocalDirectory(UnicodeString Director
 {
   if (!SetCurrentDir(Directory))
   {
-    throw Exception(FMTLOAD(CHANGE_DIR_ERROR, (Directory)));
+    throw EOSExtException(FMTLOAD(CHANGE_DIR_ERROR, (Directory)));
   }
 }
 //---------------------------------------------------------------------------

+ 7 - 4
source/core/SecureShell.cpp

@@ -10,6 +10,7 @@
 #include "TextsCore.h"
 #include "HelpCore.h"
 #include "CoreMain.h"
+#include <StrUtils.hpp>
 
 #ifndef AUTO_WINSOCK
 #include <winsock2.h>
@@ -1269,12 +1270,12 @@ int __fastcall TSecureShell::TranslateErrorMessage(
 {
   static const TPuttyTranslation Translation[] = {
     { L"Server unexpectedly closed network connection", UNEXPECTED_CLOSE_ERROR, HELP_UNEXPECTED_CLOSE_ERROR },
-    { L"Network error: Connection refused", NET_TRANSL_REFUSED, HELP_NET_TRANSL_REFUSED },
+    { L"Network error: Connection refused", NET_TRANSL_REFUSED2, HELP_NET_TRANSL_REFUSED },
     { L"Network error: Connection reset by peer", NET_TRANSL_RESET, HELP_NET_TRANSL_RESET },
-    { L"Network error: Connection timed out", NET_TRANSL_TIMEOUT, HELP_NET_TRANSL_TIMEOUT },
-    { L"Network error: No route to host", NET_TRANSL_NO_ROUTE, HELP_NET_TRANSL_NO_ROUTE },
+    { L"Network error: Connection timed out", NET_TRANSL_TIMEOUT2, HELP_NET_TRANSL_TIMEOUT },
+    { L"Network error: No route to host", NET_TRANSL_NO_ROUTE2, HELP_NET_TRANSL_NO_ROUTE },
     { L"Network error: Software caused connection abort", NET_TRANSL_CONN_ABORTED, HELP_NET_TRANSL_CONN_ABORTED },
-    { L"Host does not exist", NET_TRANSL_HOST_NOT_EXIST, HELP_NET_TRANSL_HOST_NOT_EXIST },
+    { L"Host does not exist", NET_TRANSL_HOST_NOT_EXIST2, HELP_NET_TRANSL_HOST_NOT_EXIST },
     { L"Incoming packet was garbled on decryption", NET_TRANSL_PACKET_GARBLED, HELP_NET_TRANSL_PACKET_GARBLED },
   };
 
@@ -1285,6 +1286,8 @@ int __fastcall TSecureShell::TranslateErrorMessage(
     FNoConnectionResponse = true;
   }
 
+  Message = ReplaceStr(Message, L"%HOST%", FSessionData->HostNameExpanded);
+
   return Index;
 }
 //---------------------------------------------------------------------------

+ 1 - 1
source/core/SessionData.cpp

@@ -3100,7 +3100,7 @@ void __fastcall TStoredSessionList::ImportHostKeys(const UnicodeString TargetKey
         Session = Sessions->Sessions[Index];
         if (!OnlySelected || Session->Selected)
         {
-          HostKeyName = PuttyMungeStr(FORMAT(L"@%d:%s", (Session->PortNumber, Session->HostName)));
+          HostKeyName = PuttyMungeStr(FORMAT(L"@%d:%s", (Session->PortNumber, Session->HostNameExpanded)));
           UnicodeString KeyName;
           for (int KeyIndex = 0; KeyIndex < KeyList->Count; KeyIndex++)
           {

+ 100 - 13
source/core/SftpFileSystem.cpp

@@ -12,6 +12,7 @@
 #include "TextsCore.h"
 #include "HelpCore.h"
 #include "SecureShell.h"
+#include <limits>
 
 #include <memory>
 //---------------------------------------------------------------------------
@@ -135,6 +136,10 @@
 #define SFTP_EXT_SPACE_AVAILABLE "space-available"
 #define SFTP_EXT_CHECK_FILE "check-file"
 #define SFTP_EXT_CHECK_FILE_NAME "check-file-name"
+#define SFTP_EXT_STATVFS "[email protected]"
+#define SFTP_EXT_STATVFS_VALUE_V2 L"2"
+#define SFTP_EXT_STATVFS_ST_RDONLY 0x1
+#define SFTP_EXT_STATVFS_ST_NOSUID 0x2
 //---------------------------------------------------------------------------
 #define OGQ_LIST_OWNERS 0x01
 #define OGQ_LIST_GROUPS 0x02
@@ -1925,7 +1930,13 @@ bool __fastcall TSFTPFileSystem::IsCapable(int Capability) const
           (SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_OWNERGROUP)) != 0);
 
     case fcCheckingSpaceAvailable:
-      return SupportsExtension(SFTP_EXT_SPACE_AVAILABLE);
+      return
+        // extension announced in estension list of by
+        // SFTP_EXT_SUPPORTED/SFTP_EXT_SUPPORTED2 extension
+        // (SFTP version 5 and newer only)
+        SupportsExtension(SFTP_EXT_SPACE_AVAILABLE) ||
+        // extension announced by proprietary SFTP_EXT_STATVFS extension
+        FSupportsStatVfsV2;
 
     case fcCalculatingChecksum:
       return SupportsExtension(SFTP_EXT_CHECK_FILE);
@@ -2658,6 +2669,7 @@ void __fastcall TSFTPFileSystem::DoStartup()
   FExtensions->Clear();
   FEOL = "\r\n";
   FSupport->Loaded = false;
+  FSupportsStatVfsV2 = false;
   SAFE_DESTROY(FFixedPaths);
 
   if (FVersion >= 3)
@@ -2805,6 +2817,19 @@ void __fastcall TSFTPFileSystem::DoStartup()
             (AnsiString(ExtensionData.c_str()))));
         }
       }
+      else if (ExtensionName == SFTP_EXT_STATVFS)
+      {
+        UnicodeString StatVfsVersion = AnsiString(ExtensionData.c_str());
+        if (StatVfsVersion == SFTP_EXT_STATVFS_VALUE_V2)
+        {
+          FSupportsStatVfsV2 = true;
+          FTerminal->LogEvent(FORMAT(L"Supports %s extension version %s", (ExtensionName, ExtensionDisplayData)));
+        }
+        else
+        {
+          FTerminal->LogEvent(FORMAT(L"Unsupported %s extension version %s", (ExtensionName, ExtensionDisplayData)));
+        }
+      }
       else
       {
         FTerminal->LogEvent(FORMAT(L"Unknown server extension %s=%s",
@@ -3675,15 +3700,79 @@ TStrings * __fastcall TSFTPFileSystem::GetFixedPaths()
 void __fastcall TSFTPFileSystem::SpaceAvailable(const UnicodeString Path,
   TSpaceAvailable & ASpaceAvailable)
 {
-  TSFTPPacket Packet(SSH_FXP_EXTENDED);
-  Packet.AddString(SFTP_EXT_SPACE_AVAILABLE);
-  Packet.AddPathString(LocalCanonify(Path), FUtfStrings);
-  SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_EXTENDED_REPLY);
-  ASpaceAvailable.BytesOnDevice = Packet.GetInt64();
-  ASpaceAvailable.UnusedBytesOnDevice = Packet.GetInt64();
-  ASpaceAvailable.BytesAvailableToUser = Packet.GetInt64();
-  ASpaceAvailable.UnusedBytesAvailableToUser = Packet.GetInt64();
-  ASpaceAvailable.BytesPerAllocationUnit = Packet.GetCardinal();
+  if (SupportsExtension(SFTP_EXT_SPACE_AVAILABLE))
+  {
+    TSFTPPacket Packet(SSH_FXP_EXTENDED);
+    Packet.AddString(SFTP_EXT_SPACE_AVAILABLE);
+    Packet.AddPathString(LocalCanonify(Path), FUtfStrings);
+
+    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_EXTENDED_REPLY);
+
+    ASpaceAvailable.BytesOnDevice = Packet.GetInt64();
+    ASpaceAvailable.UnusedBytesOnDevice = Packet.GetInt64();
+    ASpaceAvailable.BytesAvailableToUser = Packet.GetInt64();
+    ASpaceAvailable.UnusedBytesAvailableToUser = Packet.GetInt64();
+    ASpaceAvailable.BytesPerAllocationUnit = Packet.GetCardinal();
+  }
+  else if (ALWAYS_TRUE(FSupportsStatVfsV2))
+  {
+    // http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD;content-type=text/plain
+    TSFTPPacket Packet(SSH_FXP_EXTENDED);
+    Packet.AddString(SFTP_EXT_STATVFS);
+    Packet.AddPathString(LocalCanonify(Path), FUtfStrings);
+
+    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_EXTENDED_REPLY);
+
+    __int64 BlockSize = Packet.GetInt64(); // file system block size
+    __int64 FundamentalBlockSize = Packet.GetInt64(); // fundamental fs block size
+    __int64 Blocks = Packet.GetInt64(); // number of blocks (unit f_frsize)
+    __int64 FreeBlocks = Packet.GetInt64(); // free blocks in file system
+    __int64 AvailableBlocks = Packet.GetInt64(); // free blocks for non-root
+    __int64 FileINodes = Packet.GetInt64(); // total file inodes
+    __int64 FreeFileINodes = Packet.GetInt64(); // free file inodes
+    __int64 AvailableFileINodes = Packet.GetInt64(); // free file inodes for to non-root
+    __int64 SID = Packet.GetInt64(); // file system id
+    __int64 Flags = Packet.GetInt64(); // bit mask of f_flag values
+    __int64 NameMax = Packet.GetInt64(); // maximum filename length
+
+    FTerminal->LogEvent(FORMAT(L"Block size: %s", (IntToStr(BlockSize))));
+    FTerminal->LogEvent(FORMAT(L"Fundamental block size: %s", (IntToStr(FundamentalBlockSize))));
+    FTerminal->LogEvent(FORMAT(L"Total blocks: %s", (IntToStr(Blocks))));
+    FTerminal->LogEvent(FORMAT(L"Free blocks: %s", (IntToStr(FreeBlocks))));
+    FTerminal->LogEvent(FORMAT(L"Free blocks for non-root: %s", (IntToStr(AvailableBlocks))));
+    FTerminal->LogEvent(FORMAT(L"Total file inodes: %s", (IntToStr(FileINodes))));
+    FTerminal->LogEvent(FORMAT(L"Free file inodes: %s", (IntToStr(FreeFileINodes))));
+    FTerminal->LogEvent(FORMAT(L"Free file inodes for non-root: %s", (IntToStr(AvailableFileINodes))));
+    FTerminal->LogEvent(FORMAT(L"File system ID: %s", (BytesToHex(reinterpret_cast<const unsigned char *>(&SID), sizeof(SID)))));
+    UnicodeString FlagStr;
+    if (FLAGSET(Flags, SFTP_EXT_STATVFS_ST_RDONLY))
+    {
+      AddToList(FlagStr, L"read-only", L",");
+      Flags -= SFTP_EXT_STATVFS_ST_RDONLY;
+    }
+    if (FLAGSET(Flags, SFTP_EXT_STATVFS_ST_NOSUID))
+    {
+      AddToList(FlagStr, L"no-setuid", L",");
+      Flags -= SFTP_EXT_STATVFS_ST_NOSUID;
+    }
+    if (Flags != 0)
+    {
+      AddToList(FlagStr, UnicodeString(L"0x") + IntToHex(Flags, 2), L",");
+    }
+    if (FlagStr.IsEmpty())
+    {
+      FlagStr = L"none";
+    }
+    FTerminal->LogEvent(FORMAT(L"Flags: %s", (FlagStr)));
+    FTerminal->LogEvent(FORMAT(L"Max name length: %s", (IntToStr(NameMax))));
+
+    ASpaceAvailable.BytesOnDevice = BlockSize * Blocks;
+    ASpaceAvailable.UnusedBytesOnDevice = BlockSize * FreeBlocks;
+    ASpaceAvailable.BytesAvailableToUser = 0;
+    ASpaceAvailable.UnusedBytesAvailableToUser = BlockSize * AvailableBlocks;
+    ASpaceAvailable.BytesPerAllocationUnit =
+      (BlockSize > std::numeric_limits<unsigned long>::max()) ? 0 : static_cast<unsigned long>(BlockSize);
+  }
 }
 //---------------------------------------------------------------------------
 // transfer protocol
@@ -3997,8 +4086,6 @@ void __fastcall TSFTPFileSystem::SFTPSource(const UnicodeString FileName,
   TFileOperationProgressType * OperationProgress, unsigned int Flags,
   TUploadSessionAction & Action, bool & ChildError)
 {
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName)));
-
   Action.FileName(ExpandUNCFileName(FileName));
 
   OperationProgress->SetFile(FileName, false);
@@ -4917,7 +5004,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const UnicodeString FileName,
     THROW_SKIP_FILE_NULL;
   }
 
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName)));
+  FTerminal->LogFileDetails(FileName, File->Modification, File->Size);
 
   OperationProgress->SetFile(OnlyFileName);
 

+ 1 - 0
source/core/SftpFileSystem.h

@@ -103,6 +103,7 @@ protected:
   bool FOpenSSH;
   TStrings * FFixedPaths;
   unsigned long FMaxPacketSize;
+  bool FSupportsStatVfsV2;
 
   void __fastcall SendCustomReadFile(TSFTPPacket * Packet, TSFTPPacket * Response,
     unsigned long Flags);

+ 63 - 39
source/core/Terminal.cpp

@@ -689,7 +689,7 @@ void __fastcall TTerminal::Open()
 
             DoInformation(LoadStr(USING_TUNNEL), false);
             LogEvent(FORMAT(L"Connecting via tunnel interface %s:%d.",
-              (FSessionData->HostName, FSessionData->PortNumber)));
+              (FSessionData->HostNameExpanded, FSessionData->PortNumber)));
           }
           else
           {
@@ -885,7 +885,7 @@ void __fastcall TTerminal::OpenTunnel()
     FTunnelData->Password = FSessionData->TunnelPassword;
     FTunnelData->PublicKeyFile = FSessionData->TunnelPublicKeyFile;
     FTunnelData->TunnelPortFwd = FORMAT(L"L%d\t%s:%d",
-      (FTunnelLocalPortNumber, FSessionData->HostName, FSessionData->PortNumber));
+      (FTunnelLocalPortNumber, FSessionData->HostNameExpanded, FSessionData->PortNumber));
     FTunnelData->HostKey = FSessionData->TunnelHostKey;
     FTunnelData->ProxyMethod = FSessionData->ProxyMethod;
     FTunnelData->ProxyHost = FSessionData->ProxyHost;
@@ -1378,16 +1378,17 @@ bool __fastcall TTerminal::FileOperationLoopQuery(Exception & E,
   bool Result = false;
   Log->AddException(&E);
   unsigned int Answer;
-  bool SkipPossible = AllowSkip && (OperationProgress != NULL);
+  bool SkipToAllPossible = AllowSkip && (OperationProgress != NULL);
 
-  if (SkipPossible && OperationProgress->SkipToAll)
+  if (SkipToAllPossible && OperationProgress->SkipToAll)
   {
     Answer = qaSkip;
   }
   else
   {
     int Answers = qaRetry | qaAbort |
-      FLAGMASK(SkipPossible, (qaSkip | qaAll)) |
+      FLAGMASK(AllowSkip, qaSkip) |
+      FLAGMASK(SkipToAllPossible, qaAll) |
       FLAGMASK(!SpecialRetry.IsEmpty(), qaYes);
     TQueryParams Params(qpAllowContinueOnError | FLAGMASK(!AllowSkip, qpFatalAbort));
     Params.HelpKeyword = HelpKeyword;
@@ -2399,12 +2400,36 @@ void __fastcall TTerminal::ReadDirectory(bool ReloadOnly, bool ForceCache)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TTerminal::LogFile(TRemoteFile * File)
+void __fastcall TTerminal::LogRemoteFile(TRemoteFile * File)
 {
-  LogEvent(FORMAT(L"%s;%s;%d;%s;%s;%s;%s;%d",
-    (File->FileName, File->Type, File->Size, StandardTimestamp(File->Modification),
-     File->Owner.LogText, File->Group.LogText, File->Rights->Text,
-     File->Attr)));
+  // optimization
+  if (Log->Logging)
+  {
+    LogEvent(FORMAT(L"%s;%s;%d;%s;%s;%s;%s;%d",
+      (File->FileName, File->Type, File->Size, StandardTimestamp(File->Modification),
+       File->Owner.LogText, File->Group.LogText, File->Rights->Text,
+       File->Attr)));
+  }
+}
+//---------------------------------------------------------------------------
+UnicodeString __fastcall TTerminal::FormatFileDetailsForLog(const UnicodeString & FileName, TDateTime Modification, __int64 Size)
+{
+  UnicodeString Result;
+    // optimization
+  if (Log->Logging)
+  {
+    Result = FORMAT(L"'%s' [%s] [%s]", (FileName, (Modification != TDateTime() ? StandardTimestamp(Modification) : UnicodeString(L"n/a")), IntToStr(Size)));
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::LogFileDetails(const UnicodeString & FileName, TDateTime Modification, __int64 Size)
+{
+  // optimization
+  if (Log->Logging)
+  {
+    LogEvent(FORMAT("File: %s", (FormatFileDetailsForLog(FileName, Modification, Size))));
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::CustomReadDirectory(TRemoteFileList * FileList)
@@ -2417,7 +2442,7 @@ void __fastcall TTerminal::CustomReadDirectory(TRemoteFileList * FileList)
   {
     for (int Index = 0; Index < FileList->Count; Index++)
     {
-      LogFile(FileList->Files[Index]);
+      LogRemoteFile(FileList->Files[Index]);
     }
   }
 
@@ -2636,7 +2661,7 @@ void __fastcall TTerminal::ReadFile(const UnicodeString FileName,
     LogEvent(FORMAT(L"Listing file \"%s\".", (FileName)));
     FFileSystem->ReadFile(FileName, File);
     ReactOnCommand(fsListFile);
-    LogFile(File);
+    LogRemoteFile(File);
   }
   catch (Exception &E)
   {
@@ -3931,7 +3956,8 @@ bool __fastcall TTerminal::AllowLocalFileTransfer(UnicodeString FileName,
   const TCopyParamType * CopyParam)
 {
   bool Result = true;
-  if (!CopyParam->AllowAnyTransfer())
+  // optimization
+  if (Log->Logging || !CopyParam->AllowAnyTransfer())
   {
     WIN32_FIND_DATA FindData;
     HANDLE Handle;
@@ -3952,6 +3978,10 @@ bool __fastcall TTerminal::AllowLocalFileTransfer(UnicodeString FileName,
       FindData.nFileSizeLow;
     Params.Modification = FileTimeToDateTime(FindData.ftLastWriteTime);
     Result = CopyParam->AllowTransfer(FileName, osLocal, Directory, Params);
+    if (Result)
+    {
+      LogFileDetails(FileName, Params.Modification, Params.Size);
+    }
   }
   return Result;
 }
@@ -4263,13 +4293,13 @@ void __fastcall TTerminal::DoSynchronizeCollectDirectory(const UnicodeString Loc
             FileData->Modified = false;
             Data.LocalFileList->AddObject(FileName,
               reinterpret_cast<TObject*>(FileData));
-            LogEvent(FORMAT(L"Local file '%s' [%s] [%s] included to synchronization",
-              (FullLocalFileName, StandardTimestamp(Modification), IntToStr(Size))));
+            LogEvent(FORMAT(L"Local file %s included to synchronization",
+              (FormatFileDetailsForLog(FullLocalFileName, Modification, Size))));
           }
           else
           {
-            LogEvent(FORMAT(L"Local file '%s' [%s] [%s] excluded from synchronization",
-              (FullLocalFileName, StandardTimestamp(Modification), IntToStr(Size))));
+            LogEvent(FORMAT(L"Local file %s excluded from synchronization",
+              (FormatFileDetailsForLog(FullLocalFileName, Modification, Size))));
           }
 
           FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (LocalDirectory)),
@@ -4310,10 +4340,9 @@ void __fastcall TTerminal::DoSynchronizeCollectDirectory(const UnicodeString Loc
 
         if (New)
         {
-          LogEvent(FORMAT(L"Local file '%s' [%s] [%s] is new",
-            (FileData->Info.Directory + FileData->Info.FileName,
-             StandardTimestamp(FileData->Info.Modification),
-             IntToStr(FileData->Info.Size))));
+          LogEvent(FORMAT(L"Local file %s is new",
+            (FormatFileDetailsForLog(FileData->Info.Directory + FileData->Info.FileName,
+             FileData->Info.Modification, FileData->Info.Size))));
         }
 
         if (Modified || New)
@@ -4503,24 +4532,19 @@ void __fastcall TTerminal::SynchronizeCollectFile(const UnicodeString FileName,
             // we need this for custom commands over checklist only,
             // not for sync itself
             LocalData->MatchingRemoteFileFile = File->Duplicate();
-            LogEvent(FORMAT(L"Local file '%s' [%s] [%s] is modified comparing to remote file '%s' [%s] [%s]",
-              (LocalData->Info.Directory + LocalData->Info.FileName,
-               StandardTimestamp(LocalData->Info.Modification),
-               IntToStr(LocalData->Info.Size),
-               FullRemoteFileName,
-               StandardTimestamp(File->Modification),
-               IntToStr(File->Size))));
+            LogEvent(FORMAT(L"Local file %s is modified comparing to remote file %s",
+              (FormatFileDetailsForLog(LocalData->Info.Directory + LocalData->Info.FileName,
+                 LocalData->Info.Modification, LocalData->Info.Size),
+               FormatFileDetailsForLog(FullRemoteFileName,
+                 File->Modification, File->Size))));
           }
 
           if (Modified)
           {
-            LogEvent(FORMAT(L"Remote file '%s' [%s] [%s] is modified comparing to local file '%s' [%s] [%s]",
-              (FullRemoteFileName,
-               StandardTimestamp(File->Modification),
-               IntToStr(File->Size),
-               LocalData->Info.Directory + LocalData->Info.FileName,
-               StandardTimestamp(LocalData->Info.Modification),
-               IntToStr(LocalData->Info.Size))));
+            LogEvent(FORMAT(L"Remote file %s is modified comparing to local file %s",
+              (FormatFileDetailsForLog(FullRemoteFileName, File->Modification, File->Size),
+               FormatFileDetailsForLog(LocalData->Info.Directory + LocalData->Info.FileName,
+                 LocalData->Info.Modification, LocalData->Info.Size))));
           }
         }
         else if (FLAGCLEAR(Data->Params, spNoRecurse))
@@ -4536,8 +4560,8 @@ void __fastcall TTerminal::SynchronizeCollectFile(const UnicodeString FileName,
       else
       {
         ChecklistItem->Local.Directory = Data->LocalDirectory;
-        LogEvent(FORMAT(L"Remote file '%s' [%s] [%s] is new",
-          (FullRemoteFileName, StandardTimestamp(File->Modification), IntToStr(File->Size))));
+        LogEvent(FORMAT(L"Remote file %s is new",
+          (FormatFileDetailsForLog(FullRemoteFileName, File->Modification, File->Size))));
       }
 
       if (New || Modified)
@@ -4584,8 +4608,8 @@ void __fastcall TTerminal::SynchronizeCollectFile(const UnicodeString FileName,
   }
   else
   {
-    LogEvent(FORMAT(L"Remote file '%s' [%s] [%s] excluded from synchronization",
-      (FullRemoteFileName, StandardTimestamp(File->Modification), IntToStr(File->Size))));
+    LogEvent(FORMAT(L"Remote file %s excluded from synchronization",
+      (FormatFileDetailsForLog(FullRemoteFileName, File->Modification, File->Size))));
   }
 }
 //---------------------------------------------------------------------------

+ 3 - 1
source/core/Terminal.h

@@ -354,7 +354,9 @@ protected:
   TRemoteFileList * __fastcall DoReadDirectoryListing(UnicodeString Directory, bool UseCache);
   RawByteString __fastcall EncryptPassword(const UnicodeString & Password);
   UnicodeString __fastcall DecryptPassword(const RawByteString & Password);
-  void __fastcall LogFile(TRemoteFile * File);
+  void __fastcall LogRemoteFile(TRemoteFile * File);
+  UnicodeString __fastcall FormatFileDetailsForLog(const UnicodeString & FileName, TDateTime Modification, __int64 Size);
+  void __fastcall LogFileDetails(const UnicodeString & FileName, TDateTime Modification, __int64 Size);
 
   __property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
 

+ 3 - 4
source/core/WebDAVFileSystem.cpp

@@ -12198,10 +12198,10 @@ void __fastcall TWebDAVFileSystem::Open()
     FSessionInfo.SecurityProtocolName = LoadStr(FTPS_IMPLICIT);
   }
 
-  UnicodeString HostName = Data->HostName;
+  UnicodeString HostName = Data->HostNameExpanded;
   size_t Port = Data->PortNumber;
   UnicodeString ProtocolName = !Ssl ? L"http" : L"https";
-  UnicodeString UserName = Data->UserName;
+  UnicodeString UserName = Data->UserNameExpanded;
   UnicodeString Path = Data->RemoteDirectory;
   UnicodeString Url = FORMAT(L"%s://%s:%d%s", (ProtocolName.c_str(), HostName.c_str(), Port, Path.c_str()));
 
@@ -12843,7 +12843,6 @@ void __fastcall TWebDAVFileSystem::WebDAVSource(const UnicodeString FileName,
 {
   bool CheckExistence = UnixComparePaths(TargetDir, FTerminal->GetCurrentDirectory()) &&
     (FTerminal->FFiles != NULL) && FTerminal->FFiles->Loaded;
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName.c_str())));
   bool CanProceed = false;
   UnicodeString FileNameOnly =
     CopyParam->ChangeFileName(ExtractFileName(FileName, false), osLocal, true);
@@ -13262,7 +13261,7 @@ void __fastcall TWebDAVFileSystem::Sink(const UnicodeString FileName,
     THROW_SKIP_FILE_NULL;
   }
 
-  FTerminal->LogEvent(FORMAT(L"File: \"%s\"", (FileName.c_str())));
+  FTerminal->LogFileDetails(FileName, TDateTime(), File->Size);
 
   OperationProgress->SetFile(FileNameOnly);
 

+ 3 - 1
source/filezilla/AsyncSocketExLayer.cpp

@@ -928,7 +928,9 @@ BOOL CAsyncSocketExLayer::ShutDownNext(int nHow /*=sends*/)
 		return shutdown(m_pOwnerSocket->GetSocketHandle(), nHow);
 	}
 	else
-		return m_pNextLayer->ShutDownNext(nHow);
+	{
+		return m_pNextLayer->ShutDown(nHow);
+	}
 }
 
 int CAsyncSocketExLayer::GetFamily() const

+ 5 - 1
source/filezilla/FtpControlSocket.cpp

@@ -1298,7 +1298,11 @@ void CFtpControlSocket::OnConnect(int nErrorCode)
 	else
 	{
 		if (nErrorCode == WSAHOST_NOT_FOUND)
-			ShowStatus(IDS_ERRORMSG_CANTRESOLVEHOST, FZ_LOG_ERROR);
+		{
+			CString str;
+			str.Format(IDS_ERRORMSG_CANTRESOLVEHOST2, m_ServerName);
+			ShowStatus(str, FZ_LOG_ERROR);
+		}
 #ifdef MPEXT
 		else
 		{

+ 31 - 22
source/forms/Custom.cpp

@@ -15,6 +15,7 @@
 #include <ProgParams.h>
 #include <Tools.h>
 #include <HistoryComboBox.hpp>
+#include <Math.hpp>
 
 #include "Custom.h"
 //---------------------------------------------------------------------
@@ -205,7 +206,8 @@ class TSaveSessionDialog : public TCustomDialog
 {
 public:
   __fastcall TSaveSessionDialog(TComponent* AOwner);
-  void __fastcall Init(bool CanSavePassword, bool NotRecommendedSavingPassword);
+  void __fastcall Init(bool CanSavePassword, bool NotRecommendedSavingPassword,
+    TStrings * AdditionalFolders);
 
   bool __fastcall Execute(UnicodeString & SessionName, bool & SavePassword,
     bool & CreateShortcut, const UnicodeString & OriginalSessionName);
@@ -231,38 +233,43 @@ __fastcall TSaveSessionDialog::TSaveSessionDialog(TComponent* /*AOwner*/) :
 {
 }
 //---------------------------------------------------------------------------
-void __fastcall TSaveSessionDialog::Init(bool CanSavePassword, bool NotRecommendedSavingPassword)
+void __fastcall TSaveSessionDialog::Init(bool CanSavePassword,
+  bool NotRecommendedSavingPassword, TStrings * AdditionalFolders)
 {
   Caption = LoadStr(SAVE_SESSION_CAPTION);
 
   SessionNameEdit = new TEdit(this);
   AddEdit(SessionNameEdit, CreateLabel(LoadStr(SAVE_SESSION_PROMPT)));
 
-  FolderCombo = new TComboBox(this);
-  AddComboBox(FolderCombo, CreateLabel(LoadStr(SAVE_SESSION_FOLDER)));
-  FolderCombo->Items->BeginUpdate();
-  try
+  FRootFolder = LoadStr(SAVE_SESSION_ROOT_FOLDER);
+  std::auto_ptr<TStringList> Folders(new TStringList());
+
+  if (AdditionalFolders != NULL)
   {
-    FRootFolder = LoadStr(SAVE_SESSION_ROOT_FOLDER);
-    FolderCombo->Items->Add(FRootFolder);
+    Folders->AddStrings(AdditionalFolders);
+  }
 
-    for (int Index = 0; Index < StoredSessions->Count; Index++)
+  for (int Index = 0; Index < StoredSessions->Count; Index++)
+  {
+    TSessionData * Data = StoredSessions->Sessions[Index];
+    if (!Data->Special && !Data->IsWorkspace)
     {
-      TSessionData * Data = StoredSessions->Sessions[Index];
-      if (!Data->Special && !Data->IsWorkspace)
+      UnicodeString Folder = Data->FolderName;
+      if (!Folder.IsEmpty() && Folders->IndexOf(Folder) < 0)
       {
-        UnicodeString Folder = Data->FolderName;
-        if (!Folder.IsEmpty() && FolderCombo->Items->IndexOf(Folder) < 0)
-        {
-          FolderCombo->Items->Add(Folder);
-        }
+        Folders->Add(Folder);
       }
     }
   }
-  __finally
-  {
-    FolderCombo->Items->EndUpdate();
-  }
+
+  assert(!Folders->CaseSensitive);
+  Folders->Sort();
+
+  FolderCombo = new TComboBox(this);
+  AddComboBox(FolderCombo, CreateLabel(LoadStr(SAVE_SESSION_FOLDER)));
+  FolderCombo->DropDownCount = Max(FolderCombo->DropDownCount, 16);
+  FolderCombo->Items->Add(FRootFolder);
+  FolderCombo->Items->AddStrings(Folders.get());
 
   SavePasswordCheck = new TCheckBox(this);
   SavePasswordCheck->Caption = LoadStr(
@@ -341,7 +348,8 @@ void __fastcall TSaveSessionDialog::DoChange(bool & CanSubmit)
 }
 //---------------------------------------------------------------------------
 TSessionData * __fastcall DoSaveSession(TSessionData * SessionData,
-  TSessionData * OriginalSession, bool ForceDialog)
+  TSessionData * OriginalSession, bool ForceDialog,
+  TStrings * AdditionalFolders)
 {
   bool SavePassword = false;
   bool * PSavePassword;
@@ -378,7 +386,7 @@ TSessionData * __fastcall DoSaveSession(TSessionData * SessionData,
     TSaveSessionDialog * Dialog = SafeFormCreate<TSaveSessionDialog>();
     try
     {
-      Dialog->Init((PSavePassword != NULL), NotRecommendedSavingPassword);
+      Dialog->Init((PSavePassword != NULL), NotRecommendedSavingPassword, AdditionalFolders);
       Result = Dialog->Execute(SessionName, SavePassword, CreateShortcut, SessionData->Name);
     }
     __finally
@@ -468,6 +476,7 @@ __fastcall TSaveWorkspaceDialog::TSaveWorkspaceDialog(
   WorkspaceNameCombo = new TComboBox(this);
   WorkspaceNameCombo->AutoComplete = false;
   AddComboBox(WorkspaceNameCombo, CreateLabel(LoadStr(SAVE_WORKSPACE_PROMPT)));
+  WorkspaceNameCombo->DropDownCount = Max(WorkspaceNameCombo->DropDownCount, 16);
 
   std::unique_ptr<TStrings> Workspaces(StoredSessions->GetWorkspaces());
   WorkspaceNameCombo->Items->AddStrings(Workspaces.get());

+ 8 - 22
source/forms/CustomScpExplorer.cpp

@@ -3784,25 +3784,6 @@ void __fastcall TCustomScpExplorerForm::TrayIconClick(TObject * /*Sender*/)
   RestoreApp();
 }
 //---------------------------------------------------------------------------
-bool __fastcall TCustomScpExplorerForm::OpenInNewWindow()
-{
-  return FLAGSET(GetAsyncKeyState(VK_SHIFT), 0x8000);
-}
-//---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::ExecuteNewInstance(const UnicodeString & Param)
-{
-  UnicodeString Arg = Param;
-  if (!Arg.IsEmpty())
-  {
-    Arg = FORMAT(L"\"%s\"", (Arg));
-  }
-
-  if (!ExecuteShell(Application->ExeName, Arg))
-  {
-    throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (Application->ExeName)));
-  }
-}
-//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::NewSession(bool FromSite)
 {
   if (OpenInNewWindow())
@@ -4572,7 +4553,7 @@ void __fastcall TCustomScpExplorerForm::SaveCurrentSession()
   {
     TSessionData * EditingSessionData = StoredSessions->FindSame(SessionData);
 
-    DoSaveSession(SessionData, EditingSessionData, true);
+    DoSaveSession(SessionData, EditingSessionData, true, NULL);
   }
   __finally
   {
@@ -5707,7 +5688,8 @@ void __fastcall TCustomScpExplorerForm::RemoteFileControlDDCreateDragFileList(
   {
     if (!WinConfiguration->DDExtInstalled)
     {
-      throw Exception(LoadStr(DRAGEXT_TARGET_NOT_INSTALLED));
+      Configuration->Usage->Inc(L"DownloadsDragDropExternalExtNotInstalled");
+      throw ExtException(NULL, LoadStr(DRAGEXT_TARGET_NOT_INSTALLED2), HELP_DRAGEXT_TARGET_NOT_INSTALLED);
     }
     DDExtInitDrag(FileList, Created);
   }
@@ -5821,7 +5803,11 @@ void __fastcall TCustomScpExplorerForm::RemoteFileControlDDEnd(TObject * Sender)
             // so we ignore absence of response in this case
             if (DDResult != drInvalid)
             {
-              throw Exception(LoadStr(DRAGEXT_TARGET_UNKNOWN));
+              // here we know that the extension is installed,
+              // as it is checked as soon as drag&drop starts from
+              // RemoteFileControlDDCreateDragFileList
+              Configuration->Usage->Inc(L"DownloadsDragDropExternalExtTargetUnknown");
+              throw ExtException(NULL, LoadStr(DRAGEXT_TARGET_UNKNOWN2), HELP_DRAGEXT_TARGET_UNKNOWN);
             }
           }
           else

+ 0 - 2
source/forms/CustomScpExplorer.h

@@ -488,7 +488,6 @@ protected:
   void __fastcall UpdateNewSessionTab();
   void __fastcall AddFixedSessionImages();
   int __fastcall AddFixedSessionImage(int GlyphsSourceIndex);
-  void __fastcall ExecuteNewInstance(const UnicodeString & Param);
   TObjectList * __fastcall DoCollectWorkspace();
   void __fastcall DoSaveWorkspace(const UnicodeString & Name,
     TObjectList * DataList, bool SavePasswords);
@@ -613,7 +612,6 @@ public:
   void __fastcall UpdateTaskbarList(ITaskbarList3 * TaskbarList);
   virtual void __fastcall DisplaySystemContextMenu();
   bool __fastcall HandleMouseWheel(WPARAM WParam, LPARAM LParam);
-  static bool __fastcall OpenInNewWindow();
 
   __property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };

+ 0 - 1
source/forms/EditMask.dfm

@@ -203,7 +203,6 @@ object EditMaskDialog: TEditMaskDialog
         'MaskMemo')
       ScrollBars = ssVertical
       TabOrder = 0
-      WantReturns = False
     end
   end
 end

+ 0 - 2
source/forms/FileSystemInfo.dfm

@@ -171,10 +171,8 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
           Color = clBtnFace
           Lines.Strings = (
             'InfoMemo')
-          ReadOnly = True
           ScrollBars = ssBoth
           TabOrder = 0
-          WantReturns = False
           WordWrap = False
         end
       end

+ 2 - 0
source/forms/LocationProfiles.cpp

@@ -13,6 +13,7 @@
 #include "LocationProfiles.h"
 #include "WinConfiguration.h"
 #include "Custom.h"
+#include <Math.hpp>
 //---------------------------------------------------------------------
 #pragma link "IEComboBox"
 #pragma link "PngImageList"
@@ -92,6 +93,7 @@ __fastcall TBookmarkNameDialog::TBookmarkNameDialog(TStrings * PeerBookmarks,
 
   NameCombo = new TComboBox(this);
   NameCombo->AutoComplete = false;
+  NameCombo->DropDownCount = Max(NameCombo->DropDownCount, 16);
   AddComboBox(NameCombo, CreateLabel(LoadStr(ADD_BOOKMARK_PROMPT)));
   NameCombo->Items = PeerBookmarks;
 

+ 47 - 8
source/forms/Login.cpp

@@ -667,7 +667,7 @@ void __fastcall TLoginDialog::SessionTreeDblClick(TObject * /*Sender*/)
     {
       if (IsSessionNode(Node) || IsWorkspaceNode(Node))
       {
-        ModalResult = DefaultResult(this);
+        Login();
       }
     }
   }
@@ -789,7 +789,20 @@ void __fastcall TLoginDialog::SaveAsSession(bool ForceDialog)
 
   TSessionData * EditingSessionData = GetEditingSessionData();
 
-  TSessionData * NewSession = DoSaveSession(SessionData.get(), EditingSessionData, ForceDialog);
+  // collect list of empty folders (these are not persistent and known to login dialog only)
+  std::auto_ptr<TStrings> NewFolders(new TStringList());
+  TTreeNode * Node = SessionTree->Items->GetFirstNode();
+  while (Node != NULL)
+  {
+    if (IsFolderNode(Node) && !Node->HasChildren)
+    {
+      NewFolders->Add(SessionNodePath(Node));
+    }
+    Node = Node->GetNext();
+  }
+
+  TSessionData * NewSession =
+    DoSaveSession(SessionData.get(), EditingSessionData, ForceDialog, NewFolders.get());
   if (NewSession != NULL)
   {
     TTreeNode * ParentNode = AddSessionPath(UnixExtractFilePath(NewSession->SessionName), false, false);
@@ -2104,7 +2117,10 @@ void __fastcall TLoginDialog::SessionTreeDragDrop(TObject * Sender,
   TObject * Source, int /*X*/, int /*Y*/)
 {
   TTreeNode * DropTarget = SessionTree->DropTarget;
-  if (ALWAYS_TRUE((Sender == Source) && SessionAllowDrop(DropTarget)))
+  if (ALWAYS_TRUE((Sender == Source) && SessionAllowDrop(DropTarget)) &&
+      // calling EnsureNotEditing only on drop, not on drag start,
+      // to avoid getting popup during unintended micro-dragging
+      EnsureNotEditing())
   {
     TSessionData * Session = SelectedSession;
     UnicodeString Path =
@@ -2673,22 +2689,45 @@ void __fastcall TLoginDialog::CloneToNewSiteActionExecute(TObject * /*Sender*/)
   EditSession();
 }
 //---------------------------------------------------------------------------
+void __fastcall TLoginDialog::Login()
+{
+  if (OpenInNewWindow() && !IsNewSiteNode(SessionTree->Selected))
+  {
+    UnicodeString Path = SessionNodePath(SessionTree->Selected);
+    ExecuteNewInstance(EncodeUrlChars(Path));
+    // prevent closing the window, see below
+    ModalResult = mrNone;
+  }
+  else
+  {
+    // this is not needed when used from LoginButton,
+    // but is needed when used from popup menus
+    ModalResult = LoginButton->ModalResult;
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TLoginDialog::LoginActionExecute(TObject * /*Sender*/)
 {
-  // this is not needed when used from LoginButton,
-  // but is needed when used from popup menus
-  ModalResult = LoginButton->ModalResult;
+  Login();
 }
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::PuttyActionExecute(TObject * /*Sender*/)
 {
+  // following may take some time, so cache the shift key state,
+  // in case user manages to release it before following finishes
+  bool Close = !OpenInNewWindow();
+
   std::unique_ptr<TSessionData> Data(CloneSelectedSession());
   // putty does not support resolving environment variables in session settings
   Data->ExpandEnvironmentVariables();
   OpenSessionInPutty(GUIConfiguration->PuttyPath, Data.get(),
-    Data->UserName,
+    Data->UserNameExpanded,
     GUIConfiguration->PuttyPassword ? Data->Password : UnicodeString());
-  ModalResult = CloseButton->ModalResult;
+
+  if (Close)
+  {
+    ModalResult = CloseButton->ModalResult;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::LoginButtonDropDownClick(TObject * /*Sender*/)

+ 2 - 0
source/forms/Login.dfm

@@ -596,6 +596,8 @@ object LoginDialog: TLoginDialog
       Caption = 'Open in &PuTTY'
       ImageIndex = 1
       ShortCut = 16464
+      SecondaryShortCuts.Strings = (
+        'Shift+Ctrl+P')
       OnExecute = PuttyActionExecute
     end
   end

+ 1 - 0
source/forms/Login.h

@@ -343,6 +343,7 @@ protected:
   void __fastcall Init();
   void __fastcall InitControls();
   void __fastcall EditSession();
+  void __fastcall Login();
   __property TSessionData * SelectedSession  = { read=GetSelectedSession };
 
 public:

+ 43 - 21
source/forms/MessageDlg.cpp

@@ -654,8 +654,45 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
     *TimeoutButton = NULL;
   }
 
+  TColor MainInstructionColor = Graphics::clNone;
+  HFONT MainInstructionFont = 0;
+  HFONT InstructionFont = 0;
+  HTHEME Theme = OpenThemeData(0, L"TEXTSTYLE");
+  if (Theme != NULL)
+  {
+    LOGFONT AFont;
+    COLORREF AColor;
+
+    memset(&AFont, sizeof(AFont), 0);
+    if (GetThemeFont(Theme, NULL, TEXT_MAININSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
+    {
+      MainInstructionFont = CreateFontIndirect(&AFont);
+    }
+    if (GetThemeColor(Theme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &AColor) == S_OK)
+    {
+      MainInstructionColor = (TColor)AColor;
+    }
+
+    memset(&AFont, sizeof(AFont), 0);
+    if (GetThemeFont(Theme, NULL, TEXT_INSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
+    {
+      InstructionFont = CreateFontIndirect(&AFont);
+    }
+
+    CloseThemeData(Theme);
+  }
+
   TMessageForm * Result = SafeFormCreate<TMessageForm>();
-  UseDesktopFont(Result);
+  if (InstructionFont != 0)
+  {
+    Result->Font->Handle = InstructionFont;
+  }
+  else
+  {
+    Result->Font->Assign(Screen->MessageFont);
+  }
+
+  Configuration->Usage->Set(L"ThemeMessageFontSize", Result->Font->Size);
 
   // make sure we consider sizes of the monitor,
   // that is set in DoFormWindowProc(CM_SHOWINGCHANGED) later.
@@ -833,25 +870,6 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
 
   assert(MainMsg.Pos(L"\t") == 0);
 
-  TColor MainInstructionColor = Graphics::clNone;
-  HFONT MainInstructionFont = 0;
-  HTHEME Theme = OpenThemeData(0, L"TEXTSTYLE");
-  if (Theme != NULL)
-  {
-    LOGFONT AMainInstructionFont;
-    memset(&AMainInstructionFont, sizeof(AMainInstructionFont), 0);
-    if (GetThemeFont(Theme, NULL, TEXT_MAININSTRUCTION, 0, TMT_FONT, &AMainInstructionFont) == S_OK)
-    {
-      MainInstructionFont = CreateFontIndirect(&AMainInstructionFont);
-    }
-    COLORREF AMainInstructionColor;
-    if (GetThemeColor(Theme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &AMainInstructionColor) == S_OK)
-    {
-      MainInstructionColor = (TColor)AMainInstructionColor;
-    }
-    CloseThemeData(Theme);
-  }
-
   int IconTextWidth = -1;
   int IconTextHeight = 0;
   int ALeft = IconWidth + HorzMargin;
@@ -895,6 +913,10 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
       if (LabelFont != 0)
       {
         Message->Font->Handle = LabelFont;
+        if (ALWAYS_TRUE(LabelFont == MainInstructionFont))
+        {
+          Configuration->Usage->Set(L"ThemeMainInstructionFontSize", Message->Font->Size);
+        }
       }
       if (LabelColor != Graphics::clNone)
       {
@@ -904,7 +926,7 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
       TRect TextRect;
       SetRect(&TextRect, 0, 0, MaxTextWidth, 0);
       DrawText(Message->Canvas->Handle, LabelMsg.c_str(), LabelMsg.Length() + 1, &TextRect,
-        DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK |
+        DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |
         Result->DrawTextBiDiModeFlagsReadingOnly());
       int MaxWidth = Monitor->Width - HorzMargin * 2 - IconWidth - 30;
       if (TextRect.right > MaxWidth)

+ 1 - 0
source/forms/Preferences.dfm

@@ -2617,6 +2617,7 @@ object PreferencesDialog: TPreferencesDialog
             Width = 332
             Height = 21
             Anchors = [akLeft, akTop, akRight]
+            DropDownCount = 16
             TabOrder = 1
             OnClick = ControlChange
           end

+ 2 - 0
source/forms/Properties.dfm

@@ -220,6 +220,7 @@ object PropertiesDialog: TPropertiesDialog
         Top = 135
         Width = 161
         Height = 21
+        DropDownCount = 16
         MaxLength = 50
         TabOrder = 1
         Text = 'GroupComboBox'
@@ -231,6 +232,7 @@ object PropertiesDialog: TPropertiesDialog
         Top = 163
         Width = 161
         Height = 21
+        DropDownCount = 16
         MaxLength = 50
         TabOrder = 2
         Text = 'OwnerComboBox'

+ 1 - 0
source/forms/RemoteTransfer.dfm

@@ -99,6 +99,7 @@ object RemoteTransferDialog: TRemoteTransferDialog
       Height = 21
       Style = csDropDownList
       Anchors = [akLeft, akTop, akRight]
+      DropDownCount = 16
       MaxLength = 250
       TabOrder = 0
       OnChange = SessionComboChange

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

@@ -1732,8 +1732,12 @@ end;
 
 procedure TCustomDirView.CancelEdit;
 begin
-  // if editing, it has to be focused item
-  if IsEditing and Assigned(ItemFocused) then
+  // - Do nothing when handle is not allocated (we cannot be editing anyway
+  //   without a handle), otherwise this causes handle allocation,
+  //   what is wrong particularly when we are called from ClearItems
+  //   when we are being destroyed
+  // - If editing, it has to be focused item
+  if HandleAllocated and IsEditing and Assigned(ItemFocused) then
   begin
     ItemFocused.CancelEdit;
     FLoadEnabled := True;

+ 2 - 0
source/resource/HelpWin.h

@@ -52,5 +52,7 @@
 #define HELP_IMPORT_CONFIGURATION    "config"
 #define HELP_DELETE_FILE             "task_delete"
 #define HELP_COLOR                   "task_connections#session_color"
+#define HELP_DRAGEXT_TARGET_NOT_INSTALLED "dragext"
+#define HELP_DRAGEXT_TARGET_UNKNOWN  HELP_DRAGEXT_TARGET_NOT_INSTALLED
 
 #endif // TextsWin

+ 4 - 4
source/resource/TextsCore.h

@@ -217,9 +217,9 @@
 #define URL_OPTION_BOOL_VALUE_ERROR 291
 #define FTP_ACCESS_DENIED_EMPTY_PASSWORD 292
 #define CANNOT_OPEN_SESSION_FOLDER 293
-#define NET_TRANSL_NO_ROUTE     294
+#define NET_TRANSL_NO_ROUTE2    294
 #define NET_TRANSL_CONN_ABORTED 295
-#define NET_TRANSL_HOST_NOT_EXIST 296
+#define NET_TRANSL_HOST_NOT_EXIST2 296
 #define NET_TRANSL_PACKET_GARBLED 297
 #define REPORT_ERROR            298
 #define TLS_CERT_DECODE_ERROR   299
@@ -358,9 +358,9 @@
 #define AUTH_TRANSL_KEY_REFUSED 492
 #define PFWD_TRANSL_ADMIN       493
 #define PFWD_TRANSL_CONNECT     494
-#define NET_TRANSL_REFUSED      495
+#define NET_TRANSL_REFUSED2     495
 #define NET_TRANSL_RESET        496
-#define NET_TRANSL_TIMEOUT      497
+#define NET_TRANSL_TIMEOUT2     497
 #define SESSION_INFO_TIP_NO_SSH 498
 #define RESUME_BUTTON           499
 #define FTP_NO_FEATURE_INFO     500

+ 4 - 4
source/resource/TextsCore1.rc

@@ -182,9 +182,9 @@ BEGIN
   URL_OPTION_BOOL_VALUE_ERROR, "Invalid switch value '%s'. Valid values are 'on' and 'off'."
   FTP_ACCESS_DENIED_EMPTY_PASSWORD, "Access without password denied."
   CANNOT_OPEN_SESSION_FOLDER, "Cannot open site folder or workspace."
-  NET_TRANSL_NO_ROUTE, "Network error: No route to host."
+  NET_TRANSL_NO_ROUTE2, "Network error: No route to host \"%HOST%\"."
   NET_TRANSL_CONN_ABORTED, "Network error: Software caused connection abort"
-  NET_TRANSL_HOST_NOT_EXIST, "Host does not exist."
+  NET_TRANSL_HOST_NOT_EXIST2, "Host \"%HOST%\" does not exist."
   NET_TRANSL_PACKET_GARBLED, "Incoming packet was garbled on decryption"
   REPORT_ERROR, "%s\n\nPlease help us improving WinSCP by reporting the error on WinSCP support forum."
   TLS_CERT_DECODE_ERROR, "Error decoding TLS/SSL certificate (%s)."
@@ -323,9 +323,9 @@ BEGIN
   AUTH_TRANSL_KEY_REFUSED, "Server refused our key."
   PFWD_TRANSL_ADMIN, "Administratively prohibited (%s)."
   PFWD_TRANSL_CONNECT, "Connect failed (%s)."
-  NET_TRANSL_REFUSED, "Network error: Connection refused."
+  NET_TRANSL_REFUSED2, "Network error: Connection to \"%HOST%\" refused."
   NET_TRANSL_RESET, "Network error: Connection reset by peer."
-  NET_TRANSL_TIMEOUT, "Network error: Connection timed out."
+  NET_TRANSL_TIMEOUT2, "Network error: Connection to \"%HOST%\" timed out."
   SESSION_INFO_TIP_NO_SSH, "Host: %s\nUser name: %s\nTransfer protocol: %s"
   RESUME_BUTTON, "&Resume"
   FTP_NO_FEATURE_INFO, "The server does not support any additional FTP feature."

+ 1 - 1
source/resource/TextsCore2.rc

@@ -67,7 +67,7 @@ BEGIN
     "switches:\n"
     "  -privatekey=<key>  Private key file\n"
     "  -timeout=<sec>     Server response timeout\n"
-    "  -hostkey=<fingerprint> Fingerprint of server hostkey (SFTP and SCP only).\n"
+    "  -hostkey=<fingerprint> Fingerprint of server host key (SFTP and SCP only).\n"
     "  -certificate=<fingerprint> Fingerprint of TLS/SSL certificate (FTPS only)\n"
     "  -passive=on|off    Passive mode (FTP protocol only)\n"
     "  -implicit          Implicit TLS/SSL (FTPS protocol only)\n"

+ 1 - 1
source/resource/TextsFileZilla.h

@@ -418,7 +418,7 @@
 #if 0
 #define IDS_ERRORMSG_CANTPARSECMDLINE   2113
 #endif
-#define IDS_ERRORMSG_CANTRESOLVEHOST    2114
+#define IDS_ERRORMSG_CANTRESOLVEHOST2   2114
 #define IDS_ERRORMSG_CANTRESUME         2115
 #define IDS_ERRORMSG_CANTRESUME_FINISH  2116
 #define IDS_ERRORMSG_CANTSENDCOMMAND    2117

+ 1 - 1
source/resource/TextsFileZilla.rc

@@ -30,7 +30,7 @@ END
 STRINGTABLE
 BEGIN
   IDS_ERRORMSG_CANTOPENTRANSFERCHANNEL, "Transfer channel can't be opened. Reason: %s"
-  IDS_ERRORMSG_CANTRESOLVEHOST, "Can't resolve host name"
+  IDS_ERRORMSG_CANTRESOLVEHOST2, "Can't resolve host name \"%s\""
   IDS_ERRORMSG_CANTRESUME, "Resume command not supported by server, overwriting file."
   IDS_ERRORMSG_CANTRESUME_FINISH, "Resume command not supported by server, but local and remote filesize are equal."
   IDS_ERRORMSG_CANTSENDCOMMAND, "Unable to send command. Disconnected."

+ 2 - 2
source/resource/TextsWin.h

@@ -26,10 +26,10 @@
 #define REGISTER_URL_ERROR2     1131
 #define MUTEX_RELEASE_TIMEOUT   1132
 #define DRAGEXT_MUTEX_RELEASE_TIMEOUT 1133
-#define DRAGEXT_TARGET_UNKNOWN  1134
+#define DRAGEXT_TARGET_UNKNOWN2 1134
 #define UNKNOWN_TRANSLATION     1135
 #define INCOMPATIBLE_TRANSLATION 1136
-#define DRAGEXT_TARGET_NOT_INSTALLED 1137
+#define DRAGEXT_TARGET_NOT_INSTALLED2 1137
 #define GSSAPI_NOT_INSTALLED2   1138
 #define WATCH_ERROR_GENERAL     1139
 #define WATCH_ERROR_DIRECTORY   1140

+ 2 - 2
source/resource/TextsWin1.rc

@@ -30,10 +30,10 @@ BEGIN
         REGISTER_URL_ERROR2, "Cannot register application to handle URL addresses."
         MUTEX_RELEASE_TIMEOUT, "Mutex was not released in required interval."
         DRAGEXT_MUTEX_RELEASE_TIMEOUT, "Shell drag extension mutex was not released in required interval."
-        DRAGEXT_TARGET_UNKNOWN, "WinSCP was not able to detect folder, where the dragged file(s) was dropped. Either you have not dropped the file(s) to regular folder (e.g. Windows Explorer) or WinSCP shell drag extension is not installed or you have not restarted the computer yet after installation. Install the extension or switch to compatible drag&drop mode (from Preferences window), which uses temporary folder for downloads. It allows you to drop files to any destination."
+        DRAGEXT_TARGET_UNKNOWN2, "**WinSCP was not able to detect folder, where the dragged file(s) was dropped.** Either you have not dropped the file(s) to regular folder (e.g. Windows Explorer) or you have not restarted your computer yet after installation to load drag&drop shell extension. \n\nAlternativelly you can switch to compatible drag&drop mode (from Preferences window), which uses temporary folder for downloads. It allows you to drop files to any destination."
         UNKNOWN_TRANSLATION, "File '%s' does not contain translation for this product version."
         INCOMPATIBLE_TRANSLATION, "File '%s' contains translation for %s version %s."
-        DRAGEXT_TARGET_NOT_INSTALLED, "Shell drag extension support is enabled, but the extension was not loaded. Either it is not installed or you have not restarted the computer yet after installation. Install the extension or switch to compatible drag&drop mode (from Preferences window), which uses temporary folder for downloads."
+        DRAGEXT_TARGET_NOT_INSTALLED2, "**Drag&drop shell extension use is enabled, but the extension is not installed.** Install the extension or switch to compatible drag&drop mode (from Preferences window), which uses temporary folder for downloads."
         GSSAPI_NOT_INSTALLED2, "GSSAPI/SSPI with Kerberos is not supported on this system."
         WATCH_ERROR_GENERAL, "Error watching for changes."
         WATCH_ERROR_DIRECTORY, "Error watching for changes in directory '%s'."

+ 1 - 1
source/resource/TextsWin2.rc

@@ -47,7 +47,7 @@ BEGIN
 "B: /xmllog=      Turns on XML logging to file.\n"
 "G: /privatekey=  Private key file.\n"
 "G: /timeout=     Server response timeout.\n"
-"G: /hostkey=     Fingerprint of server hostkey.\n"
+"G: /hostkey=     Fingerprint of server host key.\n"
 "G: /passive=     Passive mode (FTP protocol only).\n"
 "G: /rawsettings= Configures any session settings using raw format\n"
 "G:               as in an INI file.\n"

+ 19 - 0
source/windows/Tools.cpp

@@ -276,6 +276,25 @@ bool __fastcall ExecuteShellAndWait(const UnicodeString Command)
     &Application->ProcessMessages);
 }
 //---------------------------------------------------------------------------
+bool __fastcall OpenInNewWindow()
+{
+  return FLAGSET(GetAsyncKeyState(VK_SHIFT), 0x8000);
+}
+//---------------------------------------------------------------------------
+void __fastcall ExecuteNewInstance(const UnicodeString & Param)
+{
+  UnicodeString Arg = Param;
+  if (!Arg.IsEmpty())
+  {
+    Arg = FORMAT(L"\"%s\"", (Arg));
+  }
+
+  if (!ExecuteShell(Application->ExeName, Arg))
+  {
+    throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (Application->ExeName)));
+  }
+}
+//---------------------------------------------------------------------------
 IShellLink * __fastcall CreateDesktopShortCut(const UnicodeString & Name,
   const UnicodeString &File, const UnicodeString & Params, const UnicodeString & Description,
   int SpecialFolder, int IconIndex, bool Return)

+ 2 - 0
source/windows/Tools.h

@@ -11,6 +11,8 @@
 void __fastcall CenterFormOn(TForm * Form, TControl * CenterOn);
 bool __fastcall ExecuteShellAndWait(const UnicodeString Path, const UnicodeString Params);
 bool __fastcall ExecuteShellAndWait(const UnicodeString Command);
+bool __fastcall OpenInNewWindow();
+void __fastcall ExecuteNewInstance(const UnicodeString & Param);
 IShellLink * __fastcall CreateDesktopShortCut(const UnicodeString &Name,
   const UnicodeString &File, const UnicodeString & Params, const UnicodeString & Description,
   int SpecialFolder = -1, int IconIndex = 0, bool Return = false);

+ 1 - 1
source/windows/UserInterface.cpp

@@ -956,7 +956,7 @@ void __fastcall InitializeShortCutCombo(TComboBox * ComboBox,
   }
 
   ComboBox->Style = csDropDownList;
-  ComboBox->DropDownCount = 16;
+  ComboBox->DropDownCount = Max(ComboBox->DropDownCount, 16);
 }
 //---------------------------------------------------------------------------
 void __fastcall SetShortCutCombo(TComboBox * ComboBox, TShortCut Value)

+ 9 - 0
source/windows/VCLCommon.cpp

@@ -107,13 +107,22 @@ void __fastcall ReadOnlyControl(TControl * Control, bool ReadOnly)
   if (dynamic_cast<TCustomEdit *>(Control) != NULL)
   {
     ((TEdit*)Control)->ReadOnly = ReadOnly;
+    TMemo * Memo = dynamic_cast<TMemo *>(Control);
     if (ReadOnly)
     {
       SetParentColor(Control);
+      if (Memo != NULL)
+      {
+        // Is true by default and makes the control swallow not only
+        // returns but also escapes
+        Memo->WantReturns = false;
+      }
     }
     else
     {
       ((TEdit*)Control)->Color = clWindow;
+      // not supported atm, we need to persist previous value of WantReturns
+      assert(Memo == NULL);
     }
   }
   else if ((dynamic_cast<TCustomComboBox *>(Control) != NULL) ||

+ 2 - 1
source/windows/WinInterface.h

@@ -101,7 +101,8 @@ unsigned int __fastcall FatalExceptionMessageDialog(Exception * E, TQueryType Ty
 
 // forms\Custom.cpp
 TSessionData * __fastcall DoSaveSession(TSessionData * SessionData,
-  TSessionData * OriginalSession, bool ForceDialog);
+  TSessionData * OriginalSession, bool ForceDialog,
+  TStrings * AdditionalFolders);
 void __fastcall SessionNameValidate(const UnicodeString & Text,
   const UnicodeString & OriginalName);
 bool __fastcall DoSaveWorkspaceDialog(UnicodeString & WorkspaceName,

+ 4 - 2
source/windows/WinMain.cpp

@@ -45,7 +45,7 @@ void __fastcall GetLoginData(UnicodeString SessionName, TOptions * Options,
       if (SessionData->SaveOnly)
       {
         Configuration->Usage->Inc(L"CommandLineSessionSave");
-        TSessionData * SavedSession = DoSaveSession(SessionData, NULL, true);
+        TSessionData * SavedSession = DoSaveSession(SessionData, NULL, true, NULL);
         Close = (SavedSession == NULL);
         if (!Close)
         {
@@ -312,6 +312,8 @@ void __fastcall UpdateStaticUsage()
   Configuration->Usage->Set(L"WorkAreaHeight", Screen->WorkAreaHeight);
   Configuration->Usage->Set(L"MonitorCount", Screen->MonitorCount);
   Configuration->Usage->Set(L"NotUseThemes", !UseThemes());
+  Configuration->Usage->Set(L"ThemeDefaultFontSize", Application->DefaultFont->Size);
+  Configuration->Usage->Set(L"ThemeIconFontSize", Screen->IconFont->Size);
 
   UnicodeString ProgramsFolder;
   ::SpecialFolderLocation(CSIDL_PROGRAM_FILES, ProgramsFolder);
@@ -534,7 +536,7 @@ int __fastcall Execute()
       if (Params->ParamCount > 0)
       {
         if ((ParamCommand == pcNone) &&
-            !TCustomScpExplorerForm::OpenInNewWindow() &&
+            !OpenInNewWindow() &&
             !Params->FindSwitch(L"NewInstance") &&
             SendToAnotherInstance())
         {