Martin Prikryl 12 年之前
父節點
當前提交
2908ab65ca
共有 70 個文件被更改,包括 1293 次插入399 次删除
  1. 2 2
      build.bat
  2. 3 3
      deployment/winscpsetup.iss
  3. 4 0
      dotnet/GlobalSuppressions.cs
  4. 14 26
      dotnet/Session.cs
  5. 1 1
      dotnet/WinSCPnet.csproj
  6. 1 1
      dotnet/internal/ConsoleCommStruct.cs
  7. 34 13
      dotnet/internal/ExeSessionProcess.cs
  8. 1 0
      dotnet/internal/SessionLogReader.cs
  9. 28 2
      dotnet/internal/UnsafeNativeMethods.cs
  10. 3 3
      dotnet/properties/AssemblyInfo.cs
  11. 1 1
      source/Console.cbproj
  12. 1 1
      source/DragExt.cbproj
  13. 2 2
      source/DragExt64.rc
  14. 2 1
      source/WinSCP.cbproj
  15. 1 1
      source/components/UnixDirView.cpp
  16. 11 0
      source/core/Common.cpp
  17. 5 3
      source/core/Common.h
  18. 14 9
      source/core/Configuration.cpp
  19. 1 0
      source/core/Configuration.h
  20. 1 0
      source/core/CoreMain.cpp
  21. 13 1
      source/core/FtpFileSystem.cpp
  22. 4 0
      source/core/Interface.h
  23. 1 1
      source/core/PuttyIntf.cpp
  24. 35 27
      source/core/ScpFileSystem.cpp
  25. 6 0
      source/core/ScpFileSystem.h
  26. 12 3
      source/core/SecureShell.cpp
  27. 2 0
      source/core/SecureShell.h
  28. 57 27
      source/core/SessionData.cpp
  29. 8 7
      source/core/SessionData.h
  30. 10 6
      source/core/SessionInfo.cpp
  31. 17 3
      source/core/SftpFileSystem.cpp
  32. 112 16
      source/core/Terminal.cpp
  33. 6 2
      source/core/Terminal.h
  34. 1 2
      source/filezilla/FileZillaIntf.cpp
  35. 2 2
      source/filezilla/FtpControlSocket.cpp
  36. 2 1
      source/filezilla/FtpListResult.cpp
  37. 24 5
      source/forms/Authenticate.cpp
  38. 28 9
      source/forms/Authenticate.dfm
  39. 2 0
      source/forms/Authenticate.h
  40. 11 11
      source/forms/CopyParams.dfm
  41. 22 9
      source/forms/CustomScpExplorer.cpp
  42. 1 0
      source/forms/CustomScpExplorer.dfm
  43. 4 1
      source/forms/FileFind.cpp
  44. 1 1
      source/forms/ImportSessions.cpp
  45. 25 25
      source/forms/LocationProfiles.dfm
  46. 8 2
      source/forms/Login.cpp
  47. 11 8
      source/forms/Login.dfm
  48. 1 1
      source/forms/Login.h
  49. 294 40
      source/forms/MessageDlg.cpp
  50. 4 2
      source/forms/Preferences.cpp
  51. 22 13
      source/forms/Preferences.dfm
  52. 2 1
      source/forms/Preferences.h
  53. 10 5
      source/forms/SynchronizeChecklist.cpp
  54. 19 8
      source/packages/my/NortonLikeListView.pas
  55. 247 13
      source/packages/my/PathLabel.pas
  56. 3 3
      source/resource/TextsCore.h
  57. 4 4
      source/resource/TextsCore1.rc
  58. 2 0
      source/resource/TextsWin.h
  59. 2 0
      source/resource/TextsWin1.rc
  60. 2 1
      source/windows/ConsoleRunner.cpp
  61. 8 14
      source/windows/GUIConfiguration.cpp
  62. 2 3
      source/windows/GUIConfiguration.h
  63. 4 4
      source/windows/GUITools.cpp
  64. 27 8
      source/windows/TerminalManager.cpp
  65. 2 0
      source/windows/TerminalManager.h
  66. 31 24
      source/windows/Tools.cpp
  67. 1 1
      source/windows/UserInterface.cpp
  68. 22 4
      source/windows/VCLCommon.cpp
  69. 9 9
      source/windows/WinConfiguration.cpp
  70. 20 3
      source/windows/WinInterface.cpp

+ 2 - 2
build.bat

@@ -1,6 +1,6 @@
 @echo off
 rem See 'readme' file
-set BDS=c:\program files\Embarcadero\RAD Studio\9.0
+set BDS=%ProgramFiles%\Embarcadero\RAD Studio\9.0
 set WITH_DRAGEXT64=0
 rem set DRAGEXT64CL=<path to x64 cl.exe>
 rem set DRAGEXT64INCL=<path to x64 includes>
@@ -12,4 +12,4 @@ cd libs
 call buildlibs.bat
 
 cd ..\source
-C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe WinSCP.groupproj /t:clean,%BUILD_TARGET% /p:RELEASE_TYPE=%RELEASE_TYPE%;CONFIG=%BUILD_CONFIG%;INTERM_PATH=.;FINAL_PATH=.
+C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe WinSCP.groupproj /t:%BUILD_TARGET% /p:RELEASE_TYPE=%RELEASE_TYPE%;CONFIG=%BUILD_CONFIG%;INTERM_PATH=.;FINAL_PATH=.

+ 3 - 3
deployment/winscpsetup.iss

@@ -114,6 +114,7 @@ WizardSmallImageFile=compiler:WizModernSmallImage-IS.bmp
 ShowTasksTreeLines=yes
 PrivilegesRequired=none
 UsePreviousLanguage=yes
+DisableProgramGroupPage=yes
 MinVersion=0,5.1
 #ifdef Sign
 SignTool=sign $f "WinSCP Installer" http://winscp.net/eng/docs/installation
@@ -1467,10 +1468,9 @@ begin
     { Hide most pages during typical installation }
     (TypicalTypeButton.Checked and
      ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
-      (PageID = wpSelectProgramGroup) or (PageID = wpSelectTasks) or
+      (PageID = wpSelectTasks) or
       { Hide Interface page for upgrades only, show for fresh installs }
-      ((PageID = wpInterface) and Upgrade))) or
-    (IsWin8 and (PageID = wpSelectProgramGroup));
+      ((PageID = wpInterface) and Upgrade)));
 end;
 
 function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,

+ 4 - 0
dotnet/GlobalSuppressions.cs

@@ -139,3 +139,7 @@ using System.Diagnostics.CodeAnalysis;
 [assembly: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "WinSCP.Logger.#GetProcessStartTime(System.Diagnostics.Process)")]
 [assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.Logger.#GetTotalProcessorTime(System.Diagnostics.Process)")]
 [assembly: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "WinSCP.Logger.#GetTotalProcessorTime(System.Diagnostics.Process)")]
+[assembly: SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Scope = "type", Target = "WinSCP.FileMapAccess")]
+[assembly: SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Scope = "member", Target = "WinSCP.UnsafeNativeMethods.#CreateFileMapping(Microsoft.Win32.SafeHandles.SafeFileHandle,System.IntPtr,WinSCP.FileMapProtection,System.Int32,System.Int32,System.String)")]
+[assembly: SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Scope = "member", Target = "WinSCP.UnsafeNativeMethods.#MapViewOfFile(Microsoft.Win32.SafeHandles.SafeFileHandle,WinSCP.FileMapAccess,System.UInt32,System.UInt32,System.UIntPtr)")]
+[assembly: SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke", Scope = "member", Target = "WinSCP.ExeSessionProcess.#InitializeConsole()")]

+ 14 - 26
dotnet/Session.cs

@@ -51,6 +51,7 @@ namespace WinSCP
         public string DebugLogPath { get { CheckNotDisposed(); return Logger.LogPath; } set { CheckNotDisposed(); Logger.LogPath = value; } }
         public string SessionLogPath { get { return _sessionLogPath; } set { CheckNotOpened(); _sessionLogPath = value; } }
         public string XmlLogPath { get { return _xmlLogPath; } set { CheckNotOpened(); _xmlLogPath = value; } }
+        public bool GuardProcessWithJob { get { return _guardProcessWithJob; } set { CheckNotOpened(); _guardProcessWithJob = value; } }
 
         public TimeSpan Timeout { get; set; }
 
@@ -97,14 +98,7 @@ namespace WinSCP
                 _disposed = false;
                 _defaultConfiguration = true;
                 _logUnique = 0;
-            }
-        }
-
-        ~Session()
-        {
-            using (Logger.CreateCallstack())
-            {
-                DoDispose();
+                _guardProcessWithJob = true;
             }
         }
 
@@ -112,7 +106,17 @@ namespace WinSCP
         {
             using (Logger.CreateCallstackAndLock())
             {
-                DoDispose();
+                _disposed = true;
+
+                Cleanup();
+                Logger.Dispose();
+
+                if (_eventsEvent != null)
+                {
+                    _eventsEvent.Close();
+                    _eventsEvent = null;
+                }
+
                 GC.SuppressFinalize(this);
             }
         }
@@ -733,23 +737,6 @@ namespace WinSCP
             return path;
         }
 
-        private void DoDispose()
-        {
-            using (Logger.CreateCallstack())
-            {
-                _disposed = true;
-
-                Cleanup();
-                Logger.Dispose();
-
-                if (_eventsEvent != null)
-                {
-                    _eventsEvent.Close();
-                    _eventsEvent = null;
-                }
-            }
-        }
-
         private void Cleanup()
         {
             using (Logger.CreateCallstack())
@@ -1468,5 +1455,6 @@ namespace WinSCP
         private string _xmlLogPath;
         private FileTransferProgressEventHandler _fileTransferProgress;
         private int _progressHandling;
+        private bool _guardProcessWithJob;
     }
 }

+ 1 - 1
dotnet/WinSCPnet.csproj

@@ -9,7 +9,7 @@
     <OutputType>Library</OutputType>
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>WinSCP</RootNamespace>
-    <AssemblyName>WinSCP</AssemblyName>
+    <AssemblyName>WinSCPnet</AssemblyName>
     <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <OutputPath>$(FINAL_PATH)\$(Configuration)</OutputPath>

+ 1 - 1
dotnet/internal/ConsoleCommStruct.cs

@@ -95,7 +95,7 @@ namespace WinSCP
         {
             _session = session;
             _fileMapping = fileMapping;
-            _ptr = UnsafeNativeMethods.MapViewOfFile(_fileMapping, 0x1F /*FILE_MAP_ALL_ACCESS*/, 0, 0, UIntPtr.Zero);
+            _ptr = UnsafeNativeMethods.MapViewOfFile(_fileMapping, FileMapAccess.FileMapAllAccess, 0, 0, UIntPtr.Zero);
             _payloadPtr = new IntPtr(_ptr.ToInt64() + 12);
             _header = (ConsoleCommHeader)Marshal.PtrToStructure(_ptr, typeof(ConsoleCommHeader));
         }

+ 34 - 13
dotnet/internal/ExeSessionProcess.cs

@@ -6,6 +6,7 @@ using System.IO;
 using System.Threading;
 using Microsoft.Win32;
 using Microsoft.Win32.SafeHandles;
+using System.Runtime.InteropServices;
 
 namespace WinSCP
 {
@@ -127,7 +128,11 @@ namespace WinSCP
 
                 _logger.WriteLine("Started process {0}", _process.Id);
 
-                _job.AddProcess(_process.Handle);
+                if (_session.GuardProcessWithJob)
+                {
+                    _job = new Job();
+                    _job.AddProcess(_process.Handle);
+                }
 
                 _thread = new Thread(ProcessEvents);
                 _thread.IsBackground = true;
@@ -375,8 +380,6 @@ namespace WinSCP
                 Random random = new Random();
                 int process = Process.GetCurrentProcess().Id;
 
-                bool uniqEvent;
-
                 do
                 {
                     if (attempts > MaxAttempts)
@@ -387,20 +390,24 @@ namespace WinSCP
                     int instanceNumber = random.Next(1000);
 
                     _instanceName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", process, instanceNumber);
-                    _requestEvent = new EventWaitHandle(false, EventResetMode.AutoReset, ConsoleEventRequest + _instanceName, out uniqEvent);
-                    if (!uniqEvent)
+                    _logger.WriteLine("Trying event {0}", _instanceName);
+                    if (!TryCreateEvent(ConsoleEventRequest + _instanceName, out _requestEvent))
                     {
+                        _logger.WriteLine("Event {0} is not unique", _instanceName);
                         _requestEvent.Close();
                         _requestEvent = null;
                     }
                     else
                     {
+                        _logger.WriteLine("Event {0} is unique", _instanceName);
                         _responseEvent = CreateEvent(ConsoleEventResponse + _instanceName);
                         _cancelEvent = CreateEvent(ConsoleEventCancel + _instanceName);
                         string fileMappingName = ConsoleMapping + _instanceName;
-                        _fileMapping = UnsafeNativeMethods.CreateFileMapping(
-                            new SafeFileHandle(new IntPtr(-1), true), IntPtr.Zero, 0x04 /*readwrite*/, 0,
-                            ConsoleCommStruct.Size, fileMappingName);
+                        _fileMapping = CreateFileMapping(fileMappingName);
+                        if (Marshal.GetLastWin32Error() == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)
+                        {
+                            throw new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "File mapping {0} already exists", fileMappingName));
+                        }
                         if (_fileMapping.IsInvalid)
                         {
                             throw new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "Cannot create file mapping {0}", fileMappingName));
@@ -408,7 +415,7 @@ namespace WinSCP
                     }
                     ++attempts;
                 }
-                while (!uniqEvent);
+                while (_requestEvent == null);
 
                 using (ConsoleCommStruct commStruct = AcquireCommStruct())
                 {
@@ -417,16 +424,30 @@ namespace WinSCP
             }
         }
 
+        private static SafeFileHandle CreateFileMapping(string fileMappingName)
+        {
+            return
+                UnsafeNativeMethods.CreateFileMapping(
+                    new SafeFileHandle(new IntPtr(-1), true), IntPtr.Zero, FileMapProtection.PageReadWrite, 0,
+                    ConsoleCommStruct.Size, fileMappingName);
+        }
+
         private ConsoleCommStruct AcquireCommStruct()
         {
             return new ConsoleCommStruct(_session, _fileMapping);
         }
 
-        private EventWaitHandle CreateEvent(string name)
+        private static bool TryCreateEvent(string name, out EventWaitHandle ev)
         {
             bool createdNew;
-            EventWaitHandle ev = new EventWaitHandle(false, EventResetMode.AutoReset, name, out createdNew);
-            if (!createdNew)
+            ev = new EventWaitHandle(false, EventResetMode.AutoReset, name, out createdNew);
+            return createdNew;
+        }
+
+        private EventWaitHandle CreateEvent(string name)
+        {
+            EventWaitHandle ev;
+            if (!TryCreateEvent(name, out ev))
             {
                 throw new SessionLocalException(_session, string.Format(CultureInfo.InvariantCulture, "Event {0} already exists", name));
             }
@@ -637,6 +658,6 @@ namespace WinSCP
         private string _incompleteLine;
         private readonly List<string> _input = new List<string>();
         private AutoResetEvent _inputEvent = new AutoResetEvent(false);
-        private Job _job = new Job();
+        private Job _job;
     }
 }

+ 1 - 0
dotnet/internal/SessionLogReader.cs

@@ -32,6 +32,7 @@ namespace WinSCP
                 _stream = null;
             }
 
+            ((IDisposable)_reader).Dispose();
             _reader = null;
         }
 

+ 28 - 2
dotnet/internal/UnsafeNativeMethods.cs

@@ -59,13 +59,39 @@ namespace WinSCP
         public UInt32 PeakJobMemoryUsed;
     }
 
+    [Flags]
+    internal enum FileMapProtection : uint
+    {
+        PageReadonly = 0x02,
+        PageReadWrite = 0x04,
+        PageWriteCopy = 0x08,
+        PageExecuteRead = 0x20,
+        PageExecuteReadWrite = 0x40,
+        SectionCommit = 0x8000000,
+        SectionImage = 0x1000000,
+        SectionNoCache = 0x10000000,
+        SectionReserve = 0x4000000,
+    }
+
+    [Flags]
+    public enum FileMapAccess : int
+    {
+        FileMapCopy = 0x0001,
+        FileMapWrite = 0x0002,
+        FileMapRead = 0x0004,
+        FileMapAllAccess = 0x001f,
+        FileMapExecute = 0x0020,
+    }
+    
     internal static class UnsafeNativeMethods
     {
+        public const int ERROR_ALREADY_EXISTS = 183;
+        
         [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
-        public static extern SafeFileHandle CreateFileMapping(SafeFileHandle hFile, IntPtr lpAttributes, int fProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);
+        public static extern SafeFileHandle CreateFileMapping(SafeFileHandle hFile, IntPtr lpAttributes, FileMapProtection fProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);
 
         [DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
-        public static extern IntPtr MapViewOfFile(SafeFileHandle handle, int dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
+        public static extern IntPtr MapViewOfFile(SafeFileHandle handle, FileMapAccess dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
 
         [DllImport("kernel32", ExactSpelling = true)]
         [return: MarshalAs(UnmanagedType.Bool)]

+ 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.1.0.0")]
-[assembly: AssemblyFileVersion("1.1.0.0")]
-[assembly: AssemblyInformationalVersionAttribute("5.2.0.0")]
+[assembly: AssemblyVersion("1.1.1.0")]
+[assembly: AssemblyFileVersion("1.1.1.0")]
+[assembly: AssemblyInformationalVersionAttribute("5.2.1.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.0.0;InternalName=console;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.2.0.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.0.0.0;InternalName=console;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.2.1.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>4</VerInfo_MajorVer>
 		</PropertyGroup>

+ 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.0.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.1.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MinorVer>2</VerInfo_MinorVer>
 			<VerInfo_Release>1</VerInfo_Release>

+ 2 - 2
source/DragExt64.rc

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

+ 2 - 1
source/WinSCP.cbproj

@@ -51,10 +51,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.0.0;InternalName=winscp;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.2.0.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
+			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.2.1.0;InternalName=winscp;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.2.1.0;ReleaseType=beta;WWW=http://winscp.net/</VerInfo_Keys>
 			<VerInfo_Locale>1033</VerInfo_Locale>
 			<VerInfo_MajorVer>5</VerInfo_MajorVer>
 			<VerInfo_MinorVer>2</VerInfo_MinorVer>
+			<VerInfo_Release>1</VerInfo_Release>
 		</PropertyGroup>
 		<PropertyGroup Condition="'$(Cfg_1)'!=''">
 			<BCC_DebugLineNumbers>true</BCC_DebugLineNumbers>

+ 1 - 1
source/components/UnixDirView.cpp

@@ -398,7 +398,7 @@ void __fastcall TUnixDirView::LoadFiles()
           // this is out of date
           // (missing columns and does not update then file properties are loaded)
           Item->ImageIndex = File->IconIndex;
-          Item->SubItems->Add((!File->IsDirectory ? FormatFloat(L"#,##0", File->Size) : UnicodeString()));
+          Item->SubItems->Add(!File->IsDirectory ? FormatBytes(File->Size, FormatSizeBytes, FormatSizeBytes) : UnicodeString());
           Item->SubItems->Add(File->UserModificationStr);
           Item->SubItems->Add(File->RightsStr);
           Item->SubItems->Add(File->Owner.DisplayText);

+ 11 - 0
source/core/Common.cpp

@@ -1884,5 +1884,16 @@ bool __fastcall IsDirectoryWriteable(const UnicodeString & Path)
   return Result;
 }
 //---------------------------------------------------------------------------
+UnicodeString __fastcall FormatNumber(__int64 Number)
+{
+  return FormatFloat(L"#,##0", Number);
+}
+//---------------------------------------------------------------------------
+// simple alternative to FormatBytes
+UnicodeString __fastcall FormatSize(__int64 Size)
+{
+  return FormatNumber(Size);
+}
+//---------------------------------------------------------------------------
 // Suppress warning about unused constants in DateUtils.hpp
 #pragma warn -8080

+ 5 - 3
source/core/Common.h

@@ -95,6 +95,8 @@ LCID __fastcall GetDefaultLCID();
 UnicodeString __fastcall DefaultEncodingName();
 UnicodeString __fastcall WindowsProductName();
 bool __fastcall IsDirectoryWriteable(const UnicodeString & Path);
+UnicodeString __fastcall FormatNumber(__int64 Size);
+UnicodeString __fastcall FormatSize(__int64 Size);
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure* TProcessLocalFileEvent)
   (const UnicodeString FileName, const TSearchRec Rec, void * Param);
@@ -175,9 +177,9 @@ private:
 #else
 #define CHECK(p) { bool __CHECK_RESULT__ = (p); assert(__CHECK_RESULT__); }
 #define FAIL assert(false)
-#define ALWAYS_TRUE(p) DoAlwaysTrue(p, TEXT(#p), TEXT(__FILE__), __LINE__)
-#define ALWAYS_FALSE(p) DoAlwaysFalse(p, TEXT(#p), TEXT(__FILE__), __LINE__)
-#define NOT_NULL(P) DoCheckNotNull(P, TEXT(#P), TEXT(__FILE__), __LINE__)
+#define ALWAYS_TRUE(p) p
+#define ALWAYS_FALSE(p) p
+#define NOT_NULL(P) P
 #endif
 #define USEDPARAM(p) ((&p) == (&p))
 //---------------------------------------------------------------------------

+ 14 - 9
source/core/Configuration.cpp

@@ -126,11 +126,16 @@ THierarchicalStorage * TConfiguration::CreateScpStorage(bool /*SessionList*/)
   }
 }
 //---------------------------------------------------------------------------
-#define LASTELEM(ELEM) \
-  ELEM.SubString(ELEM.LastDelimiter(L".>")+1, ELEM.Length() - ELEM.LastDelimiter(L".>"))
+UnicodeString __fastcall TConfiguration::PropertyToKey(const UnicodeString & Property)
+{
+  // no longer useful
+  int P = Property.LastDelimiter(L".>");
+  return Property.SubString(P + 1, Property.Length() - P);
+}
+//---------------------------------------------------------------------------
 #define BLOCK(KEY, CANCREATE, BLOCK) \
   if (Storage->OpenSubKey(KEY, CANCREATE, true)) try { BLOCK } __finally { Storage->CloseSubKey(); }
-#define KEY(TYPE, VAR) KEYEX(TYPE, VAR, VAR)
+#define KEY(TYPE, VAR) KEYEX(TYPE, VAR, PropertyToKey(TEXT(#VAR)))
 #define REGCONFIG(CANCREATE) \
   BLOCK(L"Interface", CANCREATE, \
     KEY(String,   RandomSeedFile); \
@@ -151,18 +156,18 @@ THierarchicalStorage * TConfiguration::CreateScpStorage(bool /*SessionList*/)
     KEY(Bool,     CollectUsage); \
   ); \
   BLOCK(L"Logging", CANCREATE, \
-    KEYEX(Bool,  PermanentLogging, Logging); \
-    KEYEX(String,PermanentLogFileName, LogFileName); \
+    KEYEX(Bool,  PermanentLogging, L"Logging"); \
+    KEYEX(String,PermanentLogFileName, L"LogFileName"); \
     KEY(Bool,    LogFileAppend); \
     KEY(Integer, LogWindowLines); \
     KEY(Integer, LogProtocol); \
-    KEYEX(Bool,  PermanentLogActions, LogActions); \
-    KEYEX(String,PermanentActionsLogFileName, ActionsLogFileName); \
+    KEYEX(Bool,  PermanentLogActions, L"LogActions"); \
+    KEYEX(String,PermanentActionsLogFileName, L"ActionsLogFileName"); \
   );
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::SaveData(THierarchicalStorage * Storage, bool /*All*/)
 {
-  #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(LASTELEM(UnicodeString(TEXT(#NAME))), VAR)
+  #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(NAME, VAR)
   REGCONFIG(true);
   #undef KEYEX
 
@@ -272,7 +277,7 @@ void __fastcall TConfiguration::Import(const UnicodeString & FileName)
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::LoadData(THierarchicalStorage * Storage)
 {
-  #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(LASTELEM(UnicodeString(TEXT(#NAME))), VAR)
+  #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(NAME, VAR)
   #pragma warn -eas
   REGCONFIG(false);
   #pragma warn +eas

+ 1 - 0
source/core/Configuration.h

@@ -131,6 +131,7 @@ protected:
   virtual void __fastcall Saved();
   void __fastcall CleanupRegistry(UnicodeString CleanupSubKey);
   UnicodeString __fastcall BannerHash(const UnicodeString & Banner);
+  static UnicodeString __fastcall PropertyToKey(const UnicodeString & Property);
 
   virtual bool __fastcall GetConfirmOverwriting();
   virtual void __fastcall SetConfirmOverwriting(bool value);

+ 1 - 0
source/core/CoreMain.cpp

@@ -21,6 +21,7 @@ TStoredSessionList * StoredSessions = NULL;
 TQueryButtonAlias::TQueryButtonAlias()
 {
   OnClick = NULL;
+  GroupWith = -1;
 }
 //---------------------------------------------------------------------------
 TQueryParams::TQueryParams(unsigned int AParams, UnicodeString AHelpKeyword)

+ 13 - 1
source/core/FtpFileSystem.cpp

@@ -804,13 +804,25 @@ bool __fastcall TFTPFileSystem::ConfirmOverwrite(UnicodeString & FileName,
     {
       Answers |= qaRetry;
     }
-    TQueryButtonAlias Aliases[3];
+    TQueryButtonAlias Aliases[5];
     Aliases[0].Button = qaRetry;
     Aliases[0].Alias = LoadStr(RESUME_BUTTON);
+    Aliases[0].GroupWith = qaNo;
+    Aliases[0].GrouppedShiftState = TShiftState() << ssAlt;
     Aliases[1].Button = qaAll;
     Aliases[1].Alias = LoadStr(YES_TO_NEWER_BUTTON);
+    Aliases[1].GroupWith = qaYes;
+    Aliases[1].GrouppedShiftState = TShiftState() << ssCtrl;
     Aliases[2].Button = qaIgnore;
     Aliases[2].Alias = LoadStr(RENAME_BUTTON);
+    Aliases[2].GroupWith = qaNo;
+    Aliases[2].GrouppedShiftState = TShiftState() << ssCtrl;
+    Aliases[3].Button = qaYesToAll;
+    Aliases[3].GroupWith = qaYes;
+    Aliases[3].GrouppedShiftState = TShiftState() << ssShift;
+    Aliases[4].Button = qaNoToAll;
+    Aliases[4].GroupWith = qaNo;
+    Aliases[4].GrouppedShiftState = TShiftState() << ssShift;
     TQueryParams QueryParams(qpNeverAskAgainCheck);
     QueryParams.Aliases = Aliases;
     QueryParams.AliasesCount = LENOF(Aliases);

+ 4 - 0
source/core/Interface.h

@@ -46,6 +46,8 @@ struct TQueryButtonAlias
   unsigned int Button;
   UnicodeString Alias;
   TNotifyEvent OnClick;
+  int GroupWith;
+  TShiftState GrouppedShiftState;
 };
 
 typedef void __fastcall (__closure *TQueryParamsTimerEvent)(unsigned int & Result);
@@ -82,6 +84,8 @@ enum TPromptKind
   pkNewPassword
 };
 
+enum TPromptUserParam { pupEcho = 0x01, pupRemember = 0x02 };
+
 bool __fastcall IsAuthenticationPrompt(TPromptKind Kind);
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TFileFoundEvent)

+ 1 - 1
source/core/PuttyIntf.cpp

@@ -128,7 +128,7 @@ int get_userpass_input(prompts_t * p, unsigned char * /*in*/, int /*inlen*/)
     for (int Index = 0; Index < int(p->n_prompts); Index++)
     {
       prompt_t * Prompt = p->prompts[Index];
-      Prompts->AddObject(Prompt->prompt, (TObject *)Prompt->echo);
+      Prompts->AddObject(Prompt->prompt, (TObject *)(FLAGMASK(Prompt->echo, pupEcho)));
       Results->AddObject(L"", (TObject *)Prompt->result_len);
     }
 

+ 35 - 27
source/core/ScpFileSystem.cpp

@@ -1276,6 +1276,36 @@ void __fastcall TSCPFileSystem::SpaceAvailable(const UnicodeString Path,
 //---------------------------------------------------------------------------
 // transfer protocol
 //---------------------------------------------------------------------------
+unsigned int __fastcall TSCPFileSystem::ConfirmOverwrite(
+  UnicodeString & FileName, TOperationSide Side,
+  const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,
+  int Params, TFileOperationProgressType * OperationProgress)
+{
+  TQueryButtonAlias Aliases[3];
+  Aliases[0].Button = qaAll;
+  Aliases[0].Alias = LoadStr(YES_TO_NEWER_BUTTON);
+  Aliases[0].GroupWith = qaYes;
+  Aliases[0].GrouppedShiftState = TShiftState() << ssCtrl;
+  Aliases[1].Button = qaYesToAll;
+  Aliases[1].GroupWith = qaYes;
+  Aliases[1].GrouppedShiftState = TShiftState() << ssShift;
+  Aliases[2].Button = qaNoToAll;
+  Aliases[2].GroupWith = qaNo;
+  Aliases[2].GrouppedShiftState = TShiftState() << ssShift;
+  TQueryParams QueryParams(qpNeverAskAgainCheck);
+  QueryParams.Aliases = Aliases;
+  QueryParams.AliasesCount = LENOF(Aliases);
+  unsigned int Answer;
+  SUSPEND_OPERATION
+  (
+    Answer = FTerminal->ConfirmFileOverwrite(
+      FileName, FileParams,
+      qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll,
+      &QueryParams, Side, CopyParam, Params, OperationProgress);
+  );
+  return Answer;
+}
+//---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::SCPResponse(bool * GotLastLine)
 {
   // Taken from scp.c response() and modified
@@ -1434,19 +1464,8 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
             FileParams.DestSize = File->Size;
             FileParams.DestTimestamp = File->Modification;
 
-            TQueryButtonAlias Aliases[1];
-            Aliases[0].Button = qaAll;
-            Aliases[0].Alias = LoadStr(YES_TO_NEWER_BUTTON);
-            TQueryParams QueryParams(qpNeverAskAgainCheck);
-            QueryParams.Aliases = Aliases;
-            QueryParams.AliasesCount = LENOF(Aliases);
-            SUSPEND_OPERATION
-            (
-              Answer = FTerminal->ConfirmFileOverwrite(
-                FileNameOnly, &FileParams,
-                qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll,
-                &QueryParams, osRemote, CopyParam, Params, OperationProgress);
-            );
+            Answer = ConfirmOverwrite(FileNameOnly, osRemote,
+              &FileParams, CopyParam, Params, OperationProgress);
           }
 
           switch (Answer)
@@ -2331,20 +2350,9 @@ void __fastcall TSCPFileSystem::SCPSink(const UnicodeString TargetDir,
                   FileParams.DestTimestamp = UnixToDateTime(MTime,
                     FTerminal->SessionData->DSTMode);
 
-                  TQueryButtonAlias Aliases[1];
-                  Aliases[0].Button = qaAll;
-                  Aliases[0].Alias = LoadStr(YES_TO_NEWER_BUTTON);
-                  TQueryParams QueryParams(qpNeverAskAgainCheck);
-                  QueryParams.Aliases = Aliases;
-                  QueryParams.AliasesCount = LENOF(Aliases);
-
-                  unsigned int Answer;
-                  SUSPEND_OPERATION (
-                    Answer = FTerminal->ConfirmFileOverwrite(
-                      OperationProgress->FileName, &FileParams,
-                      qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll,
-                      &QueryParams, osLocal, CopyParam, Params, OperationProgress);
-                  );
+                  unsigned int Answer =
+                    ConfirmOverwrite(OperationProgress->FileName, osLocal,
+                      &FileParams, CopyParam, Params, OperationProgress);
 
                   switch (Answer)
                   {

+ 6 - 0
source/core/ScpFileSystem.h

@@ -3,9 +3,11 @@
 #define ScpFileSystemH
 
 #include <FileSystems.h>
+#include <CopyParam.h>
 //---------------------------------------------------------------------------
 class TCommandSet;
 class TSecureShell;
+struct TOverwriteFileParams;
 //---------------------------------------------------------------------------
 class TSCPFileSystem : public TCustomFileSystem
 {
@@ -122,6 +124,10 @@ private:
   void __fastcall CaptureOutput(const UnicodeString & AddedLine, bool StdError);
   void __fastcall ChangeFileToken(const UnicodeString & DelimitedName,
     const TRemoteToken & Token, TFSCommand Cmd, const UnicodeString & RecursiveStr);
+  unsigned int __fastcall ConfirmOverwrite(
+    UnicodeString & FileName, TOperationSide Side,
+    const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,
+    int Params, TFileOperationProgressType * OperationProgress);
 
   static bool __fastcall RemoveLastLine(UnicodeString & Line,
     int & ReturnCode, UnicodeString LastLine = L"");

+ 12 - 3
source/core/SecureShell.cpp

@@ -60,6 +60,7 @@ __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
   FSocketEvent = CreateEvent(NULL, false, false, NULL);
   FFrozen = false;
   FSimple = false;
+  FCollectPrivateKeyUsage = false;
 }
 //---------------------------------------------------------------------------
 __fastcall TSecureShell::~TSecureShell()
@@ -664,7 +665,7 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
   {
     if (FSessionData->AuthKIPassword && !FSessionData->Password.IsEmpty() &&
         !FStoredPasswordTriedForKI && (Prompts->Count == 1) &&
-        !bool(Prompts->Objects[0]))
+        FLAGCLEAR(int(Prompts->Objects[0]), pupEcho))
     {
       LogEvent(L"Using stored password.");
       FUI->Information(LoadStr(AUTH_PASSWORD), false);
@@ -1152,9 +1153,12 @@ int __fastcall TSecureShell::TranslateAuthenticationMessage(UnicodeString & Mess
 
   int Result = TranslatePuttyMessage(Translation, LENOF(Translation), Message);
 
-  if ((Result == 2) || (Result == 3) || (Result == 4))
+  if (FCollectPrivateKeyUsage &&
+      ((Result == 2) || (Result == 3) || (Result == 4)))
   {
-    Configuration->Usage->Inc(L"OpenedSessionsPrivateKey");
+    Configuration->Usage->Inc(L"OpenedSessionsPrivateKey2");
+    // once only
+    FCollectPrivateKeyUsage = false;
   }
 
   return Result;
@@ -2032,3 +2036,8 @@ bool __fastcall TSecureShell::GetReady()
 {
   return FOpened && (FWaiting == 0);
 }
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::EnableUsage()
+{
+  FCollectPrivateKeyUsage = true;
+}

+ 2 - 0
source/core/SecureShell.h

@@ -46,6 +46,7 @@ private:
   int FWaiting;
   bool FSimple;
   bool FNoConnectionResponse;
+  bool FCollectPrivateKeyUsage;
 
   unsigned PendLen;
   unsigned PendSize;
@@ -128,6 +129,7 @@ public:
   unsigned long __fastcall MaxPacketSize();
   void __fastcall ClearStdError();
   bool __fastcall GetStoredCredentialsTried();
+  void __fastcall EnableUsage();
 
   void __fastcall RegisterReceiveHandler(TNotifyEvent Handler);
   void __fastcall UnregisterReceiveHandler(TNotifyEvent Handler);

+ 57 - 27
source/core/SessionData.cpp

@@ -32,10 +32,12 @@ const int FtpPortNumber = 21;
 const int FtpsImplicitPortNumber = 990;
 const int HTTPPortNumber = 80;
 const int HTTPSPortNumber = 443;
+const int TelnetPortNumber = 23;
 const int DefaultSendBuf = 262144;
-const UnicodeString AnonymousUserName("anonymous");
-const UnicodeString AnonymousPassword("[email protected]");
-
+const UnicodeString AnonymousUserName(L"anonymous");
+const UnicodeString AnonymousPassword(L"[email protected]");
+const UnicodeString PuttySshProtocol(L"ssh");
+const UnicodeString PuttyTelnetProtocol(L"telnet");
 //---------------------------------------------------------------------
 TDateTime __fastcall SecToDateTime(int Sec)
 {
@@ -86,7 +88,7 @@ void __fastcall TSessionData::Default()
     Kex[Index] = DefaultKexList[Index];
   }
   PublicKeyFile = L"";
-  FProtocol = ptSSH;
+  FPuttyProtocol = PuttySshProtocol;
   TcpNoDelay = true;
   SendBuf = DefaultSendBuf;
   SshSimple = true;
@@ -547,6 +549,8 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & Rewr
 
   Color = Storage->ReadInteger(L"Color", Color);
 
+  PuttyProtocol = Storage->ReadString(L"Protocol", PuttyProtocol);
+
   Tunnel = Storage->ReadBool(L"Tunnel", Tunnel);
   TunnelPortNumber = Storage->ReadInteger(L"TunnelPortNumber", TunnelPortNumber);
   TunnelUserName = Storage->ReadString(L"TunnelUserName", TunnelUserName);
@@ -818,6 +822,11 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
     Storage->DeleteValue(L"BuggyMAC");
     Storage->DeleteValue(L"AliasGroupList");
 
+    if (PuttyExport)
+    {
+      WRITE_DATA_EX(String, L"Protocol", PuttyProtocol, );
+    }
+
     if (!PuttyExport)
     {
       WRITE_DATA(String, SftpServer);
@@ -897,9 +906,9 @@ int __fastcall TSessionData::ReadXmlNode(_di_IXMLNode Node, const UnicodeString
   return Result;
 }
 //---------------------------------------------------------------------
-void __fastcall TSessionData::ImportFromFilezilla(_di_IXMLNode Node)
+void __fastcall TSessionData::ImportFromFilezilla(_di_IXMLNode Node, const UnicodeString & Path)
 {
-  Name = ReadXmlNode(Node, L"Name", Name);
+  Name = UnixIncludeTrailingBackslash(Path) + ReadXmlNode(Node, L"Name", Name);
   HostName = ReadXmlNode(Node, L"Host", HostName);
   PortNumber = ReadXmlNode(Node, L"Port", PortNumber);
 
@@ -928,7 +937,7 @@ void __fastcall TSessionData::ImportFromFilezilla(_di_IXMLNode Node)
   }
 
   // LogonType enum
-  int LogonType = ReadXmlNode(Node, L"LogonType", 0);
+  int LogonType = ReadXmlNode(Node, L"Logontype", 0);
   if (LogonType == 0) // ANONYMOUS
   {
     UserName = AnonymousUserName;
@@ -1059,13 +1068,13 @@ UnicodeString __fastcall TSessionData::GetSource()
   switch (FSource)
   {
     case ::ssNone:
-      return L"Ad-Hoc session";
+      return L"Ad-Hoc site";
 
     case ssStored:
-      return L"Stored session";
+      return L"Site";
 
     case ssStoredModified:
-      return L"Modified stored session";
+      return L"Modified site";
 
     default:
       assert(false);
@@ -1789,11 +1798,6 @@ void __fastcall TSessionData::SetTimeout(int value)
   SET_SESSION_PROPERTY(Timeout);
 }
 //---------------------------------------------------------------------------
-void __fastcall TSessionData::SetProtocol(TProtocol value)
-{
-  SET_SESSION_PROPERTY(Protocol);
-}
-//---------------------------------------------------------------------------
 void __fastcall TSessionData::SetFSProtocol(TFSProtocol value)
 {
   SET_SESSION_PROPERTY(FSProtocol);
@@ -1830,6 +1834,11 @@ bool __fastcall TSessionData::GetDefaultShell()
 {
   return Shell.IsEmpty();
 }
+//---------------------------------------------------------------------------
+void __fastcall TSessionData::SetPuttyProtocol(UnicodeString value)
+{
+  SET_SESSION_PROPERTY(PuttyProtocol);
+}
 //---------------------------------------------------------------------
 void __fastcall TSessionData::SetPingIntervalDT(TDateTime value)
 {
@@ -2551,6 +2560,37 @@ void __fastcall TStoredSessionList::Saved()
   }
 }
 //---------------------------------------------------------------------
+void __fastcall TStoredSessionList::ImportLevelFromFilezilla(_di_IXMLNode Node, const UnicodeString & Path)
+{
+  for (int Index = 0; Index < Node->ChildNodes->Count; Index++)
+  {
+    _di_IXMLNode ChildNode = Node->ChildNodes->Get(Index);
+    if (ChildNode->NodeName == L"Server")
+    {
+      std::auto_ptr<TSessionData> SessionData(new TSessionData(L""));
+      SessionData->Assign(DefaultSettings);
+      SessionData->ImportFromFilezilla(ChildNode, Path);
+      Add(SessionData.release());
+    }
+    else if (ChildNode->NodeName == L"Folder")
+    {
+      UnicodeString Name;
+
+      for (int Index = 0; Index < ChildNode->ChildNodes->Count; Index++)
+      {
+        _di_IXMLNode PossibleTextMode = ChildNode->ChildNodes->Get(Index);
+        if (PossibleTextMode->NodeType == ntText)
+        {
+          UnicodeString NodeValue = PossibleTextMode->NodeValue;
+          AddToList(Name, NodeValue.Trim(), L" ");
+        }
+      }
+
+      ImportLevelFromFilezilla(ChildNode, UnixIncludeTrailingBackslash(Path) + Name.Trim());
+    }
+  }
+}
+//---------------------------------------------------------------------
 void __fastcall TStoredSessionList::ImportFromFilezilla(const UnicodeString FileName)
 {
   const _di_IXMLDocument Document = interface_cast<Xmlintf::IXMLDocument>(new TXMLDocument(NULL));
@@ -2561,17 +2601,7 @@ void __fastcall TStoredSessionList::ImportFromFilezilla(const UnicodeString File
     _di_IXMLNode ServersNode = FileZilla3Node->ChildNodes->FindNode(L"Servers");
     if (ServersNode != NULL)
     {
-      for (int Index = 0; Index < ServersNode->ChildNodes->Count; Index++)
-      {
-        _di_IXMLNode ChildNode = ServersNode->ChildNodes->Get(Index);
-        if (ChildNode->NodeName == L"Server")
-        {
-          std::auto_ptr<TSessionData> SessionData(new TSessionData(L""));
-          SessionData->Assign(DefaultSettings);
-          SessionData->ImportFromFilezilla(ChildNode);
-          Add(SessionData.release());
-        }
-      }
+      ImportLevelFromFilezilla(ServersNode, L"");
     }
   }
 }
@@ -2623,7 +2653,7 @@ void __fastcall TStoredSessionList::SelectSessionsToImport
   for (int Index = 0; Index < Count; Index++)
   {
     Sessions[Index]->Selected =
-      (!SSHOnly || (Sessions[Index]->Protocol == ptSSH)) &&
+      (!SSHOnly || (Sessions[Index]->PuttyProtocol == PuttySshProtocol)) &&
       !Dest->FindByName(Sessions[Index]->Name);
   }
 }

+ 8 - 7
source/core/SessionData.h

@@ -15,8 +15,6 @@
 //---------------------------------------------------------------------------
 enum TCipher { cipWarn, cip3DES, cipBlowfish, cipAES, cipDES, cipArcfour };
 #define CIPHER_COUNT (cipArcfour+1)
-enum TProtocol { ptRaw, ptTelnet, ptRLogin, ptSSH };
-#define PROTOCOL_COUNT (ptSSH+1)
 // explicit values to skip obsoleted fsExternalSSH, fsExternalSFTP
 enum TFSProtocol { fsSCPonly = 0, fsSFTP = 1, fsSFTPonly = 2, fsFTP = 5, fsWebDAV = 6 };
 #define FSPROTOCOL_COUNT (fsWebDAV+1)
@@ -36,7 +34,6 @@ enum TSessionSource { ssNone, ssStored, ssStoredModified };
 //---------------------------------------------------------------------------
 extern const wchar_t CipherNames[CIPHER_COUNT][10];
 extern const wchar_t KexNames[KEX_COUNT][20];
-extern const wchar_t ProtocolNames[PROTOCOL_COUNT][10];
 extern const wchar_t SshProtList[][10];
 extern const wchar_t ProxyMethodList[][10];
 extern const TCipher DefaultCipherList[CIPHER_COUNT];
@@ -50,6 +47,9 @@ extern const int FtpPortNumber;
 extern const int FtpsImplicitPortNumber;
 extern const int HTTPPortNumber;
 extern const int HTTPSPortNumber;
+extern const int TelnetPortNumber;
+extern const UnicodeString PuttySshProtocol;
+extern const UnicodeString PuttyTelnetProtocol;
 //---------------------------------------------------------------------------
 class TStoredSessionList;
 //---------------------------------------------------------------------------
@@ -83,7 +83,7 @@ private:
   bool FClearAliases;
   TEOLType FEOLType;
   UnicodeString FPublicKeyFile;
-  TProtocol FProtocol;
+  UnicodeString FPuttyProtocol;
   TFSProtocol FFSProtocol;
   bool FModified;
   UnicodeString FLocalDirectory;
@@ -194,6 +194,7 @@ private:
   TKex __fastcall GetKex(int Index) const;
   void __fastcall SetPublicKeyFile(UnicodeString value);
 
+  void __fastcall SetPuttyProtocol(UnicodeString value);
   bool __fastcall GetCanLogin();
   void __fastcall SetPingIntervalDT(TDateTime value);
   TDateTime __fastcall GetPingIntervalDT();
@@ -204,7 +205,6 @@ private:
   bool __fastcall HasSessionName();
   UnicodeString __fastcall GetDefaultSessionName();
   UnicodeString __fastcall GetSessionUrl();
-  void __fastcall SetProtocol(TProtocol value);
   void __fastcall SetFSProtocol(TFSProtocol value);
   UnicodeString __fastcall GetFSProtocolStr();
   void __fastcall SetLocalDirectory(UnicodeString value);
@@ -322,7 +322,7 @@ public:
   void __fastcall Default();
   void __fastcall NonPersistant();
   void __fastcall Load(THierarchicalStorage * Storage);
-  void __fastcall ImportFromFilezilla(_di_IXMLNode Node);
+  void __fastcall ImportFromFilezilla(_di_IXMLNode Node, const UnicodeString & Path);
   void __fastcall Save(THierarchicalStorage * Storage, bool PuttyExport,
     const TSessionData * Default = NULL);
   void __fastcall SaveRecryptedPasswords(THierarchicalStorage * Storage);
@@ -367,7 +367,7 @@ public:
   __property TCipher Cipher[int Index] = { read=GetCipher, write=SetCipher };
   __property TKex Kex[int Index] = { read=GetKex, write=SetKex };
   __property UnicodeString PublicKeyFile  = { read=FPublicKeyFile, write=SetPublicKeyFile };
-  __property TProtocol Protocol  = { read=FProtocol, write=SetProtocol };
+  __property UnicodeString PuttyProtocol  = { read=FPuttyProtocol, write=SetPuttyProtocol };
   __property TFSProtocol FSProtocol  = { read=FFSProtocol, write=SetFSProtocol  };
   __property UnicodeString FSProtocolStr  = { read=GetFSProtocolStr };
   __property bool Modified  = { read=FModified, write=FModified };
@@ -525,6 +525,7 @@ private:
   bool __fastcall IsFolderOrWorkspace(const UnicodeString & Name, bool Workspace);
   TSessionData * __fastcall CheckIsInFolderOrWorkspaceAndResolve(
     TSessionData * Data, const UnicodeString & Name);
+  void __fastcall ImportLevelFromFilezilla(_di_IXMLNode Node, const UnicodeString & Path);
 };
 //---------------------------------------------------------------------------
 UnicodeString GetExpandedLogFileName(UnicodeString LogFileName, TSessionData * SessionData);

+ 10 - 6
source/core/SessionInfo.cpp

@@ -994,12 +994,6 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
           Bugs += UnicodeString(BugFlags[Data->Bug[(TSshBug)Index]])+(Index<BUG_COUNT-1?L",":L"");
         }
         ADF(L"SSH Bugs: %s", (Bugs));
-        Bugs = L"";
-        for (int Index = 0; Index < SFTP_BUG_COUNT; Index++)
-        {
-          Bugs += UnicodeString(BugFlags[Data->SFTPBug[(TSftpBug)Index]])+(Index<SFTP_BUG_COUNT-1?L",":L"");
-        }
-        ADF(L"SFTP Bugs: %s", (Bugs));
         ADF(L"Return code variable: %s; Lookup user groups: %s",
           ((Data->DetectReturnVar ? UnicodeString(L"Autodetect") : Data->ReturnVar),
            BugFlags[Data->LookupUserGroups]));
@@ -1013,6 +1007,16 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
            BooleanToEngStr(Data->IgnoreLsWarnings),
            BooleanToEngStr(Data->Scp1Compatibility)));
       }
+      if (Data->FSProtocol == fsSFTP)
+      {
+        UnicodeString Bugs;
+        for (int Index = 0; Index < SFTP_BUG_COUNT; Index++)
+        {
+          Bugs += UnicodeString(BugFlags[Data->SFTPBug[(TSftpBug)Index]])+(Index<SFTP_BUG_COUNT-1?L",":L"");
+        }
+        ADF(L"SFTP Bugs: %s", (Bugs));
+        ADF(L"SFTP Server: %s", ((Data->SftpServer.IsEmpty()? UnicodeString(L"default") : Data->SftpServer)));
+      }
       if (Data->FSProtocol == fsFTP)
       {
         UnicodeString Ftps;

+ 17 - 3
source/core/SftpFileSystem.cpp

@@ -2136,8 +2136,8 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     {
       LanguageTag = FORMAT(L" (%s)", (LanguageTag));
     }
-    UnicodeString Error = FMTLOAD(SFTP_ERROR_FORMAT2, (MessageStr,
-      int(Code), LanguageTag, ServerMessage, int(Packet->RequestType)));
+    UnicodeString Error = FMTLOAD(SFTP_ERROR_FORMAT3, (MessageStr,
+      int(Code), LanguageTag, ServerMessage));
     FTerminal->TerminalError(NULL, Error);
     return 0;
   }
@@ -3715,13 +3715,25 @@ void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(UnicodeString & FileName,
     {
       Answers |= qaRetry;
     }
-    TQueryButtonAlias Aliases[3];
+    TQueryButtonAlias Aliases[5];
     Aliases[0].Button = qaRetry;
     Aliases[0].Alias = LoadStr(APPEND_BUTTON);
+    Aliases[0].GroupWith = qaNo;
+    Aliases[0].GrouppedShiftState = TShiftState() << ssAlt;
     Aliases[1].Button = qaAll;
     Aliases[1].Alias = LoadStr(YES_TO_NEWER_BUTTON);
+    Aliases[1].GroupWith = qaYes;
+    Aliases[1].GrouppedShiftState = TShiftState() << ssCtrl;
     Aliases[2].Button = qaIgnore;
     Aliases[2].Alias = LoadStr(RENAME_BUTTON);
+    Aliases[2].GroupWith = qaNo;
+    Aliases[2].GrouppedShiftState = TShiftState() << ssCtrl;
+    Aliases[3].Button = qaYesToAll;
+    Aliases[3].GroupWith = qaYes;
+    Aliases[3].GrouppedShiftState = TShiftState() << ssShift;
+    Aliases[4].Button = qaNoToAll;
+    Aliases[4].GroupWith = qaNo;
+    Aliases[4].GrouppedShiftState = TShiftState() << ssShift;
     TQueryParams QueryParams(qpNeverAskAgainCheck);
     QueryParams.NoBatchAnswers = qaIgnore | qaRetry | qaAll;
     QueryParams.Aliases = Aliases;
@@ -4476,6 +4488,8 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
     {
       if (!OpenParams->Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
       {
+        FTerminal->LogEvent(FORMAT(L"Cannot create new file \"%s\", checking if it exists already", (OpenParams->RemoteFileName)));
+
         bool ThrowOriginal = false;
 
         // When exclusive opening of file fails, try to detect if file exists.

+ 112 - 16
source/core/Terminal.cpp

@@ -508,6 +508,7 @@ __fastcall TTerminal::TTerminal(TSessionData * SessionData,
   FTunnelUI = NULL;
   FTunnelOpening = false;
   FCallbackGuard = NULL;
+  FEnableSecureShellUsage = false;
 }
 //---------------------------------------------------------------------------
 __fastcall TTerminal::~TTerminal()
@@ -731,6 +732,14 @@ void __fastcall TTerminal::Open()
                 FSecureShell = new TSecureShell(this, FSessionData, Log, Configuration);
                 try
                 {
+                  if (FEnableSecureShellUsage)
+                  {
+                    // only on the first connect,
+                    // this is not ideal as it may prevent usage from being collected,
+                    // e.g. when connection fails on the first try
+                    FSecureShell->EnableUsage();
+                    FEnableSecureShellUsage = false;
+                  }
                   // there will be only one channel in this session
                   FSecureShell->Simple = true;
                   FSecureShell->Open();
@@ -1015,7 +1024,7 @@ bool __fastcall TTerminal::PromptUser(TSessionData * Data, TPromptKind Kind,
   TStrings * Results = new TStringList;
   try
   {
-    Prompts->AddObject(Prompt, (TObject *)Echo);
+    Prompts->AddObject(Prompt, (TObject *)(FLAGMASK(Echo, pupEcho)));
     Results->AddObject(Result, (TObject *)MaxLen);
 
     AResult = PromptUser(Data, Kind, Name, Instructions, Prompts, Results);
@@ -1045,6 +1054,15 @@ bool __fastcall TTerminal::DoPromptUser(TSessionData * /*Data*/, TPromptKind Kin
 {
   bool AResult = false;
 
+  bool PasswordPrompt =
+    (Prompts->Count == 1) && FLAGCLEAR(int(Prompts->Objects[0]), pupEcho) &&
+    ((Kind == pkPassword) || (Kind == pkPassphrase) || (Kind == pkKeybInteractive) ||
+     (Kind == pkTIS) || (Kind == pkCryptoCard));
+  if (PasswordPrompt && !Configuration->RememberPassword)
+  {
+    Prompts->Objects[0] = (TObject*)(int(Prompts->Objects[0]) | pupRemember);
+  }
+
   if (OnPromptUser != NULL)
   {
     TCallbackGuard Guard(this);
@@ -1052,10 +1070,8 @@ bool __fastcall TTerminal::DoPromptUser(TSessionData * /*Data*/, TPromptKind Kin
     Guard.Verify();
   }
 
-  if (AResult && (Configuration->RememberPassword) &&
-      (Prompts->Count == 1) && !bool(Prompts->Objects[0]) &&
-      ((Kind == pkPassword) || (Kind == pkPassphrase) || (Kind == pkKeybInteractive) ||
-       (Kind == pkTIS) || (Kind == pkCryptoCard)))
+  if (AResult && PasswordPrompt &&
+      (Configuration->RememberPassword || FLAGSET(int(Prompts->Objects[0]), pupRemember)))
   {
     RawByteString EncryptedPassword = EncryptPassword(Results->Strings[0]);
     if (FTunnelOpening)
@@ -1469,6 +1485,10 @@ void __fastcall TTerminal::ClearCaches()
   {
     FDirectoryChangesCache->Clear();
   }
+  if (FCommandSession != NULL)
+  {
+    FCommandSession->ClearCaches();
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::ClearCachedFileList(const UnicodeString Path,
@@ -1932,7 +1952,7 @@ bool __fastcall TTerminal::CheckRemoteFile(
 }
 //---------------------------------------------------------------------------
 unsigned int __fastcall TTerminal::ConfirmFileOverwrite(const UnicodeString FileName,
-  const TOverwriteFileParams * FileParams, unsigned int Answers, const TQueryParams * QueryParams,
+  const TOverwriteFileParams * FileParams, unsigned int Answers, TQueryParams * QueryParams,
   TOperationSide Side, const TCopyParamType * CopyParam, int Params, TFileOperationProgressType * OperationProgress,
   UnicodeString Message)
 {
@@ -1970,17 +1990,21 @@ unsigned int __fastcall TTerminal::ConfirmFileOverwrite(const UnicodeString File
   {
     if (Message.IsEmpty())
     {
-      Message = FMTLOAD((Side == osLocal ? LOCAL_FILE_OVERWRITE :
-        REMOTE_FILE_OVERWRITE), (FileName));
+      Message = FMTLOAD((Side == osLocal ? LOCAL_FILE_OVERWRITE2 :
+        REMOTE_FILE_OVERWRITE2), (FileName, FileName));
     }
     if (FileParams != NULL)
     {
       Message = FMTLOAD(FILE_OVERWRITE_DETAILS, (Message,
-        IntToStr(FileParams->SourceSize),
+        FormatSize(FileParams->SourceSize),
         UserModificationStr(FileParams->SourceTimestamp, FileParams->SourcePrecision),
-        IntToStr(FileParams->DestSize),
+        FormatSize(FileParams->DestSize),
         UserModificationStr(FileParams->DestTimestamp, FileParams->DestPrecision)));
     }
+    if (ALWAYS_TRUE(QueryParams->HelpKeyword.IsEmpty()))
+    {
+      QueryParams->HelpKeyword = L"ui_overwrite";
+    }
     Result = QueryUser(Message, NULL, Answers, QueryParams);
     switch (Result)
     {
@@ -2378,7 +2402,7 @@ void __fastcall TTerminal::CustomReadDirectory(TRemoteFileList * FileList)
   assert(FFileSystem);
   FFileSystem->ReadDirectory(FileList);
 
-  if (Configuration->ActualLogProtocol >= 1)
+  if (Log->Logging)
   {
     for (int Index = 0; Index < FileList->Count; Index++)
     {
@@ -2945,7 +2969,21 @@ void __fastcall TTerminal::DoCustomCommandOnFile(UnicodeString FileName,
       assert(FCommandSession->FSProtocol == cfsSCP);
       LogEvent(L"Executing custom command on command session.");
 
-      FCommandSession->CurrentDirectory = CurrentDirectory;
+      if (FCommandSession->CurrentDirectory != CurrentDirectory)
+      {
+        FCommandSession->CurrentDirectory = CurrentDirectory;
+        // We are likely in transaction, so ReadCurrentDirectory won't get called
+        // until transaction ends. But we need to know CurrentDirectory to
+        // expand !/ pattern.
+        // Doing this only, when current directory of the main and secondary shell differs,
+        // what would be the case before the first file in transation.
+        // Otherwise we would be reading pwd before every time as the
+        // CustomCommandOnFile on its own sets FReadCurrentDirectoryPending
+        if (FCommandSession->FReadCurrentDirectoryPending)
+        {
+          FCommandSession->ReadCurrentDirectory();
+        }
+      }
       FCommandSession->FFileSystem->CustomCommandOnFile(FileName, File, Command,
         Params, OutputEvent);
     }
@@ -4048,6 +4086,58 @@ TSynchronizeChecklist * __fastcall TTerminal::SynchronizeCollect(const UnicodeSt
   return Checklist;
 }
 //---------------------------------------------------------------------------
+static void __fastcall AddFlagName(UnicodeString & ParamsStr, int & Params, int Param, const UnicodeString & Name)
+{
+  if (FLAGSET(Params, Param))
+  {
+    AddToList(ParamsStr, Name, ", ");
+  }
+  Params &= ~Param;
+}
+//---------------------------------------------------------------------------
+UnicodeString __fastcall TTerminal::SynchronizeModeStr(TSynchronizeMode Mode)
+{
+  UnicodeString ModeStr;
+  switch (Mode)
+  {
+    case smRemote:
+      ModeStr = L"Remote";
+      break;
+    case smLocal:
+      ModeStr = L"Local";
+      break;
+    case smBoth:
+      ModeStr = L"Both";
+      break;
+    default:
+      ModeStr = L"Unknown";
+      break;
+  }
+  return ModeStr;
+}
+//---------------------------------------------------------------------------
+UnicodeString __fastcall TTerminal::SynchronizeParamsStr(int Params)
+{
+  UnicodeString ParamsStr;
+  AddFlagName(ParamsStr, Params, spDelete, L"Delete");
+  AddFlagName(ParamsStr, Params, spNoConfirmation, L"NoConfirmation");
+  AddFlagName(ParamsStr, Params, spExistingOnly, L"ExistingOnly");
+  AddFlagName(ParamsStr, Params, spNoRecurse, L"NoRecurse");
+  AddFlagName(ParamsStr, Params, spUseCache, L"UseCache");
+  AddFlagName(ParamsStr, Params, spDelayProgress, L"DelayProgress");
+  AddFlagName(ParamsStr, Params, spPreviewChanges, L"*PreviewChanges"); // GUI only
+  AddFlagName(ParamsStr, Params, spTimestamp, L"Timestamp");
+  AddFlagName(ParamsStr, Params, spNotByTime, L"NotByTime");
+  AddFlagName(ParamsStr, Params, spBySize, L"BySize");
+  AddFlagName(ParamsStr, Params, spSelectedOnly, L"*SelectedOnly"); // GUI only
+  AddFlagName(ParamsStr, Params, spMirror, L"Mirror");
+  if (Params > 0)
+  {
+    AddToList(ParamsStr, FORMAT(L"0x%x", (int(Params))), L", ");
+  }
+  return ParamsStr;
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminal::DoSynchronizeCollectDirectory(const UnicodeString LocalDirectory,
   const UnicodeString RemoteDirectory, TSynchronizeMode Mode,
   const TCopyParamType * CopyParam, int Params,
@@ -4068,8 +4158,8 @@ void __fastcall TTerminal::DoSynchronizeCollectDirectory(const UnicodeString Loc
   Data.Checklist = Checklist;
 
   LogEvent(FORMAT(L"Collecting synchronization list for local directory '%s' and remote directory '%s', "
-    "mode = %d, params = %d", (LocalDirectory, RemoteDirectory,
-    int(Mode), int(Params))));
+    "mode = %s, params = 0x%x (%s)", (LocalDirectory, RemoteDirectory,
+    SynchronizeModeStr(Mode), int(Params), SynchronizeParamsStr(Params))));
 
   if (FLAGCLEAR(Params, spDelayProgress))
   {
@@ -4504,7 +4594,8 @@ void __fastcall TTerminal::SynchronizeApply(TSynchronizeChecklist * Checklist,
       UnicodeString CurrentRemoteDirectory = ChecklistItem->Remote.Directory;
 
       LogEvent(FORMAT(L"Synchronizing local directory '%s' with remote directory '%s', "
-        "params = %d", (CurrentLocalDirectory, CurrentRemoteDirectory, int(Params))));
+        "params = 0x%x (%s)", (CurrentLocalDirectory, CurrentRemoteDirectory,
+        int(Params), SynchronizeParamsStr(Params))));
 
       int Count = 0;
 
@@ -5040,6 +5131,11 @@ void __fastcall TTerminal::ReflectSettings()
   // also FTunnelLog ?
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::EnableUsage()
+{
+  FEnableSecureShellUsage = true;
+}
+//---------------------------------------------------------------------------
 __fastcall TSecondaryTerminal::TSecondaryTerminal(TTerminal * MainTerminal,
   TSessionData * ASessionData, TConfiguration * Configuration, const UnicodeString & Name) :
   TTerminal(ASessionData, Configuration),
@@ -5076,7 +5172,7 @@ bool __fastcall TSecondaryTerminal::DoPromptUser(TSessionData * Data,
 {
   bool AResult = false;
 
-  if ((Prompts->Count == 1) && !bool(Prompts->Objects[0]) &&
+  if ((Prompts->Count == 1) && FLAGCLEAR(int(Prompts->Objects[0]), pupEcho) &&
       ((Kind == pkPassword) || (Kind == pkPassphrase) || (Kind == pkKeybInteractive) ||
        (Kind == pkTIS) || (Kind == pkCryptoCard)))
   {

+ 6 - 2
source/core/Terminal.h

@@ -133,7 +133,7 @@ public:
   static const int spTimestamp = 0x100;
   static const int spNotByTime = 0x200; // cannot be combined with spTimestamp and smBoth
   static const int spBySize = 0x400; // cannot be combined with smBoth, has opposite meaning for spTimestamp
-  // 0x800 is reserved for GUI (spSelectedOnly)
+  static const int spSelectedOnly = 0x800; // not used by core
   static const int spMirror = 0x1000;
 
 // for TranslateLockedPath()
@@ -199,6 +199,7 @@ private:
   TNotifyEvent FOnClose;
   TCallbackGuard * FCallbackGuard;
   TFindingFileEvent FOnFindingFile;
+  bool FEnableSecureShellUsage;
 
   void __fastcall CommandError(Exception * E, const UnicodeString Msg);
   unsigned int __fastcall CommandError(Exception * E, const UnicodeString Msg, unsigned int Answers);
@@ -222,6 +223,8 @@ private:
   UnicodeString __fastcall GetTunnelPassword();
   bool __fastcall GetStoredCredentialsTried();
   inline bool __fastcall InTransaction();
+  static UnicodeString __fastcall SynchronizeModeStr(TSynchronizeMode Mode);
+  static UnicodeString __fastcall SynchronizeParamsStr(int Params);
 
 protected:
   bool FReadCurrentDirectoryPending;
@@ -286,7 +289,7 @@ protected:
   bool __fastcall CheckRemoteFile(
     const TCopyParamType * CopyParam, int Params, TFileOperationProgressType * OperationProgress);
   unsigned int __fastcall ConfirmFileOverwrite(const UnicodeString FileName,
-    const TOverwriteFileParams * FileParams, unsigned int Answers, const TQueryParams * QueryParams,
+    const TOverwriteFileParams * FileParams, unsigned int Answers, TQueryParams * QueryParams,
     TOperationSide Side, const TCopyParamType * CopyParam, int Params,
     TFileOperationProgressType * OperationProgress, UnicodeString Message = L"");
   void __fastcall DoSynchronizeCollectDirectory(const UnicodeString LocalDirectory,
@@ -444,6 +447,7 @@ public:
   UnicodeString __fastcall PeekCurrentDirectory();
   void __fastcall FatalAbort();
   void __fastcall ReflectSettings();
+  void __fastcall EnableUsage();
 
   const TSessionInfo & __fastcall GetSessionInfo();
   const TFileSystemInfo & __fastcall GetFileSystemInfo(bool Retrieve = false);

+ 1 - 2
source/filezilla/FileZillaIntf.cpp

@@ -402,11 +402,10 @@ bool __fastcall TFileZillaIntf::HandleMessage(WPARAM wParam, LPARAM lParam)
         try
         {
             TNeedPassRequestData Data;
-            Data.Password = NULL;
             Data.Password = AData->Password.GetBuffer(AData->Password.GetLength());
             Result = HandleAsynchRequestNeedPass(Data, RequestResult);
             AData->Password.ReleaseBuffer(AData->Password.GetLength());
-            if (Result)
+            if (Result && (RequestResult == TFileZillaIntf::REPLY_OK))
             {
               AData->Password = Data.Password;
               free(Data.Password);

+ 2 - 2
source/filezilla/FtpControlSocket.cpp

@@ -6219,8 +6219,8 @@ bool CFtpControlSocket::IsRoutableAddress(const CString & host)
 	else if (host.Left(3) == _T("172"))
 	{
 		CString middle = host.Mid(4);
-		int pos = host.Find(_T("."));
-		long part = atol(T2CA(host.Left(pos)));
+		int pos = middle.Find(_T("."));
+		long part = atol(T2CA(middle.Left(pos)));
 		if ((part >= 16) && (part <= 31))
 		{
 			return false;

+ 2 - 1
source/filezilla/FtpListResult.cpp

@@ -760,7 +760,8 @@ char * CFtpListResult::GetLine()
 
 void CFtpListResult::AddLine(t_directory::t_direntry &direntry)
 {
-	if (m_server.nTimeZoneOffset && direntry.date.hasdate && direntry.date.hastime)
+	if (m_server.nTimeZoneOffset &&
+		direntry.date.hasdate && direntry.date.hastime && !direntry.date.utc)
 	{
 		SYSTEMTIME st = {0};
 		st.wYear = direntry.date.year;

+ 24 - 5
source/forms/Authenticate.cpp

@@ -202,7 +202,8 @@ TList * __fastcall TAuthenticateForm::GeneratePrompt(UnicodeString Instructions,
     TLabel * Label = GenerateLabel(Current, Prompts->Strings[Index]);
     Current += Label->Height + FPromptEditGap;
 
-    TCustomEdit * Edit = GenerateEdit(Current, bool(Prompts->Objects[Index]),
+    bool Echo = FLAGSET(int(Prompts->Objects[Index]), pupEcho);
+    TCustomEdit * Edit = GenerateEdit(Current, Echo,
       int(Results->Objects[Index]));
     Result->Add(Edit);
     Label->FocusControl = Edit;
@@ -224,19 +225,32 @@ bool __fastcall TAuthenticateForm::PromptUser(TPromptKind Kind, UnicodeString Na
 
   try
   {
+    bool ShowSessionRememberPasswordPanel = false;
     bool ShowSavePasswordPanel = false;
     TSessionData * Data = NULL;
-    if (((Kind == pkPassword) || (Kind == pkTIS) || (Kind == pkCryptoCard) ||
-         (Kind == pkKeybInteractive)) &&
-        (Prompts->Count == 1) && !bool(Prompts->Objects[0]) &&
-        StoredCredentialsTried)
+    bool PasswordPrompt =
+      ((Kind == pkPassword) || (Kind == pkTIS) || (Kind == pkCryptoCard) ||
+       (Kind == pkKeybInteractive)) &&
+      (Prompts->Count == 1) && FLAGCLEAR(int(Prompts->Objects[0]), pupEcho);
+    if (PasswordPrompt && StoredCredentialsTried)
     {
       Data = StoredSessions->FindSame(FSessionData);
       ShowSavePasswordPanel = (Data != NULL) && !Data->Password.IsEmpty();
     }
+    // do not offer to rememeber password,
+    // if we are offering to save the password to stored session
+    if (!ShowSavePasswordPanel &&
+        (Prompts->Count == 1) &&
+        FLAGSET(int(Prompts->Objects[0]), pupRemember) &&
+        ALWAYS_TRUE(PasswordPrompt))
+    {
+      ShowSessionRememberPasswordPanel = true;
+    }
 
     SavePasswordCheck->Checked = false;
     SavePasswordPanel->Visible = ShowSavePasswordPanel;
+    SessionRememberPasswordCheck->Checked = false;
+    SessionRememberPasswordPanel->Visible = ShowSessionRememberPasswordPanel;
 
     if (PasswordPanel->AutoSize)
     {
@@ -263,12 +277,17 @@ bool __fastcall TAuthenticateForm::PromptUser(TPromptKind Kind, UnicodeString Na
       {
         TCustomEdit * Edit = reinterpret_cast<TCustomEdit *>(Edits->Items[Index]);
         Results->Strings[Index] = Edit->Text;
+
+        Prompts->Objects[Index] = (TObject *)
+          ((int(Prompts->Objects[Index]) & ~pupRemember) |
+           FLAGMASK(((Index == 0) && SessionRememberPasswordCheck->Checked), pupRemember));
       }
 
       if (SavePasswordCheck->Checked)
       {
         assert(Data != NULL);
         assert(Results->Count >= 1);
+        FSessionData->Password = Results->Strings[0];
         Data->Password = Results->Strings[0];
         // modified only, explicit
         StoredSessions->Save(false, true);

+ 28 - 9
source/forms/Authenticate.dfm

@@ -6,7 +6,7 @@ object AuthenticateForm: TAuthenticateForm
   BorderIcons = [biSystemMenu]
   BorderStyle = bsDialog
   Caption = 'AuthenticateForm'
-  ClientHeight = 316
+  ClientHeight = 359
   ClientWidth = 375
   Color = clBtnFace
   Constraints.MinHeight = 200
@@ -26,7 +26,7 @@ object AuthenticateForm: TAuthenticateForm
     Left = 0
     Top = 0
     Width = 375
-    Height = 26
+    Height = 44
     Align = alClient
     Columns = <
       item
@@ -46,9 +46,9 @@ object AuthenticateForm: TAuthenticateForm
   end
   object PasswordPanel: TPanel
     Left = 0
-    Top = 26
+    Top = 44
     Width = 375
-    Height = 208
+    Height = 233
     Align = alBottom
     AutoSize = True
     BevelOuter = bvNone
@@ -122,12 +122,12 @@ object AuthenticateForm: TAuthenticateForm
     end
     object SavePasswordPanel: TPanel
       Left = 0
-      Top = 139
+      Top = 164
       Width = 375
       Height = 25
       Align = alTop
       BevelOuter = bvNone
-      TabOrder = 1
+      TabOrder = 2
       object SavePasswordCheck: TCheckBox
         Left = 14
         Top = 6
@@ -141,12 +141,12 @@ object AuthenticateForm: TAuthenticateForm
     end
     object ButtonsPanel: TPanel
       Left = 0
-      Top = 164
+      Top = 189
       Width = 375
       Height = 44
       Align = alTop
       BevelOuter = bvNone
-      TabOrder = 2
+      TabOrder = 3
       DesignSize = (
         375
         44)
@@ -181,10 +181,29 @@ object AuthenticateForm: TAuthenticateForm
         OnClick = HelpButtonClick
       end
     end
+    object SessionRememberPasswordPanel: TPanel
+      Left = 0
+      Top = 139
+      Width = 375
+      Height = 25
+      Align = alTop
+      BevelOuter = bvNone
+      TabOrder = 1
+      object SessionRememberPasswordCheck: TCheckBox
+        Left = 14
+        Top = 6
+        Width = 275
+        Height = 17
+        Caption = '&Remember password for this session'
+        Checked = True
+        State = cbChecked
+        TabOrder = 0
+      end
+    end
   end
   object BannerPanel: TPanel
     Left = 0
-    Top = 234
+    Top = 277
     Width = 375
     Height = 82
     Align = alBottom

+ 2 - 0
source/forms/Authenticate.h

@@ -33,6 +33,8 @@ __published:
   TLabel *InstructionsLabel;
   TLabel *PromptLabel2;
   TPasswordEdit *PromptEdit2;
+  TPanel *SessionRememberPasswordPanel;
+  TCheckBox *SessionRememberPasswordCheck;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
   void __fastcall FormResize(TObject *Sender);

+ 11 - 11
source/forms/CopyParams.dfm

@@ -308,18 +308,18 @@ object CopyParamsFrame: TCopyParamsFrame
       Caption = '&New and updated files only'
       ParentShowHint = False
       ShowHint = True
-      TabOrder = 2
+      TabOrder = 3
       OnClick = ControlChange
     end
-  end
-  object IncludeFileMaskHintText: TStaticText
-    Left = 271
-    Top = 380
-    Width = 54
-    Height = 17
-    Alignment = taCenter
-    Caption = 'mask hints'
-    TabOrder = 6
-    TabStop = True
+    object IncludeFileMaskHintText: TStaticText
+      Left = 263
+      Top = 58
+      Width = 54
+      Height = 17
+      Alignment = taCenter
+      Caption = 'mask hints'
+      TabOrder = 2
+      TabStop = True
+    end
   end
 end

+ 22 - 9
source/forms/CustomScpExplorer.cpp

@@ -2748,7 +2748,7 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(const UnicodeString
          (Data.SessionName, ExtractFileName(FileName)));
       if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel) == qaOK)
       {
-        TTerminalManager::Instance()->ActiveTerminal = Data.Terminal;
+        TTerminalManager::Instance()->SetActiveTerminalWithAutoReconnect(Data.Terminal);
       }
       else
       {
@@ -3585,7 +3585,7 @@ void __fastcall TCustomScpExplorerForm::Idle()
     {
       if (FRefreshRemoteDirectory)
       {
-        if ((Terminal != NULL) && Terminal->Active)
+        if ((Terminal != NULL) && (Terminal->Status == ssOpened))
         {
           Terminal->RefreshDirectory();
         }
@@ -3601,7 +3601,7 @@ void __fastcall TCustomScpExplorerForm::Idle()
       {
         TManagedTerminal * ManagedTerminal =
           dynamic_cast<TManagedTerminal *>(Terminal);
-        if ((ManagedTerminal != NULL) && Terminal->Active &&
+        if ((ManagedTerminal != NULL) && (Terminal->Status == ssOpened) &&
             (Now() - ManagedTerminal->DirectoryLoaded >
                WinConfiguration->RefreshRemotePanelInterval))
         {
@@ -5491,19 +5491,28 @@ void __fastcall TCustomScpExplorerForm::ShowExtendedException(
     PopupTrayBalloon(Terminal, L"", qtError, E);
   }
 
+  // particularly prevent opening new session from jump list,
+  // while exception is shown
+  NonVisualDataModule->StartBusy();
   try
   {
     ShowExtendedExceptionEx(Terminal, E);
   }
   __finally
   {
+    NonVisualDataModule->EndBusy();
     FTrayIcon->CancelBalloon();
   }
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::TerminalReady()
 {
-  UpdateSessionTab(SessionsPageControl->ActivePage);
+  // cannot rely on active page being page for active terminal,
+  // as it can happen that active page is the "new session" page
+  // (e.g. when reconnecting active terminal, while login dialog
+  // invoked from "new session" page is modal)
+  int ActiveTerminalIndex = TTerminalManager::Instance()->ActiveTerminalIndex;
+  UpdateSessionTab(SessionsPageControl->Pages[ActiveTerminalIndex]);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::InactiveTerminalException(
@@ -6558,8 +6567,8 @@ UnicodeString __fastcall TCustomScpExplorerForm::FileStatusBarText(
     FMTLOAD(FILE_INFO_FORMAT,
       (FormatBytes(FileInfo.SelectedSize),
        FormatBytes(FileInfo.FilesSize),
-       FormatFloat(L"#,##0", FileInfo.SelectedCount),
-       FormatFloat(L"#,##0", FileInfo.FilesCount)));
+       FormatNumber(FileInfo.SelectedCount),
+       FormatNumber(FileInfo.FilesCount)));
 
   if ((FileInfo.HiddenCount > 0) || (FileInfo.FilteredCount > 0))
   {
@@ -7739,11 +7748,15 @@ bool __fastcall TCustomScpExplorerForm::HandleMouseWheel(WPARAM WParam, LPARAM L
   {
     TPoint Point(LOWORD(LParam), HIWORD(LParam));
     TWinControl * Control = FindVCLWindow(Point);
-    TCustomForm * Form = ValidParentForm(Control);
-    Result = (Form == this);
+    Result = (Control != NULL);
     if (Result)
     {
-      SendMessage(Control->Handle, WM_MOUSEWHEEL, WParam, LParam);
+      TCustomForm * Form = ValidParentForm(Control);
+      Result = (Form == this);
+      if (Result)
+      {
+        SendMessage(Control->Handle, WM_MOUSEWHEEL, WParam, LParam);
+      }
     }
   }
   return Result;

+ 1 - 0
source/forms/CustomScpExplorer.dfm

@@ -106,6 +106,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnHistoryChange = DirViewHistoryChange
       OnDisplayProperties = RemoteDirViewDisplayProperties
       OnRead = RemoteDirViewRead
+      Items.ItemData = {}
     end
     object RemoteDriveView: TUnixDriveView
       Left = 0

+ 4 - 1
source/forms/FileFind.cpp

@@ -9,6 +9,7 @@
 #include <WinConfiguration.h>
 #include <CoreMain.h>
 #include <Tools.h>
+#include <BaseUtils.hpp>
 #include "FileFind.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
@@ -243,7 +244,9 @@ void __fastcall TFileFindDialog::FileFound(TTerminal * /*Terminal*/,
   }
   else
   {
-    Item->SubItems->Add(FormatFloat(L"#,##0", File->Size));
+    Item->SubItems->Add(
+      FormatBytes(File->Size,
+        WinConfiguration->FormatSizeBytes, WinConfiguration->FormatSizeBytes));
   }
   Item->SubItems->Add(UserModificationStr(File->Modification, File->ModificationFmt));
 

+ 1 - 1
source/forms/ImportSessions.cpp

@@ -29,7 +29,7 @@ bool __fastcall DoImportSessionsDialog()
   SessionListsList->Add(PuttyImportSessionList.get());
   SessionListsList->Add(FilezillaImportSessionList.get());
 
-  bool ImportKeys = false;
+  bool ImportKeys = true;
   std::auto_ptr<TImportSessionsDialog> ImportSessionsDialog(
     new TImportSessionsDialog(Application, SessionListsList.get()));
 

+ 25 - 25
source/forms/LocationProfiles.dfm

@@ -461,7 +461,7 @@ object LocationProfilesDialog: TLocationProfilesDialog
       end
       item
         Background = clWindow
-        Name = 'Closed bookmark folder-stored session folder'
+        Name = 'Opened bookmark folder-stored session folder'
         PngImage.Data = {
           89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
           61000000097048597300000EC400000EC401952B0E1B00000A4F694343505068
@@ -547,20 +547,23 @@ object LocationProfilesDialog: TLocationProfilesDialog
           058F998FCB860D86EB9E383E3939E23F72FDE9FCA743CF64CF269E17FEA2FECB
           AE17162F7EF8D5EBD7CED198D1A197F29793BF6D7CA5FDEAC0EB19AFDBC6C2C6
           1EBEC97833315EF456FBEDC177DC771DEFA3DF0F4FE47C207F28FF68F9B1F553
-          D0A7FB93199393FF040398F3FC63332DDB0000010E4944415478DA63FCFFFF3F
-          032580B1A18181C988456E3F906D872677E4DC9F47F640F97FD834C22C66DCDC
-          2C172228C1B3DADA471345C1D12DD719DEBDFC1CE057F378235E0336B5C85DB6
-          F650D21112E34251F0EED53786A33BEE61773603C3319FEA87D63003FEBBFAC8
-          92E4EFDD5B1E33F8563F64841B404EE0A118E05B798624CD9BDB4DD00CC84E65
-          60F87D1D3988C0084A2068A8D8E63927D00CC88C6060F8751FA706748337CF3F
-          8B6640AA1703C39FE7981AFEA3190815DBBCE8329A0189C018F9FB114903B22B
-          B0B860E90D5403EC9C151818FEFDC4125CFF517D00153B74E025AA013EA9E998
-          4EC56A0884B165CE1C14033E01695E52A2F13FC3FF2F7ED58FC07A1829CE8D94
-          1A000054D89AE168C695320000000049454E44AE426082}
+          D0A7FB93199393FF040398F3FC63332DDB000001694944415478DA63FCFFFF3F
+          032580B1A18181C988456E3F906D872677E8DC9F478E40F97F780DD8DC2C1722
+          28C9BFDA3AC00045E2E8860B0CEF5E7C08F0AB79BC11AF019B5AE4AE5AFBEA68
+          0949F0A148BC7BF189E1E8E62BB8F41DF1AB79640B33E0BF6B883649FEDEBDE6
+          2A03D00046B801BE09862419B079C1793403E274182E9F7CC6F0F0D63B061223
+          E515C40B41CA0CA70E7F63B04BD94CB4CEFFFFBE336CEDB67F063640DF4C94E1
+          F3571E066D730506863F6F80EE02B98E114223B391E847B7DF305C3A74771ED8
+          0051090E06157D35061151A0FBFFFFC2A909993EB9E71EC3EBC79FBDC106B0B2
+          3133B8046A3130337E81C42D4C210ECDFFFE3230EC587DF3C7BF1FBF84C10648
+          4A7232A8A8F2026DFF0355C8806400030A0DA2BE7EFFCB70F6E4DB1D7E350F3D
+          C1069839DB3088C92921D904338001C14712BF75E618C3ED0B57337D6B1ECF60
+          DCD42CF71A28264252E431307C67FBF34FC6A3E1C93B468A7323A5060000367C
+          916AA60EF9940000000049454E44AE426082}
       end
       item
         Background = clWindow
-        Name = 'Opened bookmark folder-stored session folder'
+        Name = 'Closed bookmark folder-stored session folder'
         PngImage.Data = {
           89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
           61000000097048597300000EC400000EC401952B0E1B00000A4F694343505068
@@ -646,19 +649,16 @@ object LocationProfilesDialog: TLocationProfilesDialog
           058F998FCB860D86EB9E383E3939E23F72FDE9FCA743CF64CF269E17FEA2FECB
           AE17162F7EF8D5EBD7CED198D1A197F29793BF6D7CA5FDEAC0EB19AFDBC6C2C6
           1EBEC97833315EF456FBEDC177DC771DEFA3DF0F4FE47C207F28FF68F9B1F553
-          D0A7FB93199393FF040398F3FC63332DDB000001694944415478DA63FCFFFF3F
-          032580B1A18181C988456E3F906D872677E8DC9F478E40F97F780DD8DC2C1722
-          28C9BFDA3AC00045E2E8860B0CEF5E7C08F0AB79BC11AF019B5AE4AE5AFBEA68
-          0949F0A148BC7BF189E1E8E62BB8F41DF1AB79640B33E0BF6B883649FEDEBDE6
-          2A03D00046B801BE09862419B079C1793403E274182E9F7CC6F0F0D63B061223
-          E515C40B41CA0CA70E7F63B04BD94CB4CEFFFFBE336CEDB67F063640DF4C94E1
-          F3571E066D730506863F6F80EE02B98E114223B391E847B7DF305C3A74771ED8
-          0051090E06157D35061151A0FBFFFFC2A909993EB9E71EC3EBC79FBDC106B0B2
-          3133B8046A3130337E81C42D4C210ECDFFFE3230EC587DF3C7BF1FBF84C10648
-          4A7232A8A8F2026DFF0355C8806400030A0DA2BE7EFFCB70F6E4DB1D7E350F3D
-          C1069839DB3088C92921D904338001C14712BF75E618C3ED0B57337D6B1ECF60
-          DCD42CF71A28264252E431307C67FBF34FC6A3E1C93B468A7323A5060000367C
-          916AA60EF9940000000049454E44AE426082}
+          D0A7FB93199393FF040398F3FC63332DDB0000010E4944415478DA63FCFFFF3F
+          032580B1A18181C988456E3F906D872677E4DC9F47F640F97FD834C22C66DCDC
+          2C172228C1B3DADA471345C1D12DD719DEBDFC1CE057F378235E0336B5C85DB6
+          F650D21112E34251F0EED53786A33BEE61773603C3319FEA87D63003FEBBFAC8
+          92E4EFDD5B1E33F8563F64841B404EE0A118E05B798624CD9BDB4DD00CC84E65
+          60F87D1D3988C0084A2068A8D8E63927D00CC88C6060F8751FA706748337CF3F
+          8B6640AA1703C39FE7981AFEA3190815DBBCE8329A0189C018F9FB114903B22B
+          B0B860E90D5403EC9C151818FEFDC4125CFF517D00153B74E025AA013EA9E998
+          4EC56A0884B165CE1C14033E01695E52A2F13FC3FF2F7ED58FC07A1829CE8D94
+          1A000054D89AE168C695320000000049454E44AE426082}
       end>
     Left = 232
     Top = 400

+ 8 - 2
source/forms/Login.cpp

@@ -1063,7 +1063,7 @@ void __fastcall TLoginDialog::UpdateControls()
       EnableControl(IPvGroup, SshProtocol || FtpProtocol);
       EnableControl(IPAutoButton, IPvGroup->Enabled && SshProtocol);
 
-      // stored sessions sheet
+      // sites sheet
       EnableControl(SessionTree, SessionTree->Items->Count > 0);
       if (SitesIncrementalSearchLabel->Visible != !FSitesIncrementalSearch.IsEmpty())
       {
@@ -1234,7 +1234,10 @@ void __fastcall TLoginDialog::UpdateControls()
       EnableControl(UtfCombo, (SftpProtocol || FtpProtocol) && EnvironmentSheet->Enabled);
       EnableControl(UtfLabel, UtfCombo->Enabled);
       // should be enabled for fsSFTP (SCP fallback) too, but it would cause confusion
-      EnableControl(TimeDifferenceEdit, ((FtpProtocol || ScpProtocol) && EnvironmentSheet->Enabled));
+      EnableControl(TimeDifferenceEdit,
+        ((FtpProtocol && (ComboAutoSwitchSave(FtpUseMlsdCombo) == asOff)) ||
+         ScpProtocol) &&
+        EnvironmentSheet->Enabled);
       EnableControl(TimeDifferenceLabel, TimeDifferenceEdit->Enabled);
       EnableControl(TimeDifferenceHoursLabel, TimeDifferenceEdit->Enabled);
       EnableControl(TimeDifferenceMinutesEdit, TimeDifferenceEdit->Enabled);
@@ -1263,6 +1266,9 @@ void __fastcall TLoginDialog::UpdateControls()
 
       // environment/ftp
       FtpSheet->Enabled = Advanced && FtpProtocol;
+      EnableControl(FtpListAllCombo,
+        (ComboAutoSwitchSave(FtpUseMlsdCombo) == asOff));
+      EnableControl(FtpListAllLabel, FtpListAllCombo->Enabled);
       EnableControl(FtpForcePasvIpCombo,
         FtpPasvModeCheck->Checked &&
         (IPAutoButton->Checked || IPv4Button->Checked));

+ 11 - 8
source/forms/Login.dfm

@@ -355,7 +355,7 @@ object LoginDialog: TLoginDialog
       object SessionListSheet: TTabSheet
         Tag = 2
         HelpType = htKeyword
-        HelpKeyword = 'ui_login_stored_sessions'
+        HelpKeyword = 'ui_login_sites'
         Caption = 'Sites'
         TabVisible = False
         DesignSize = (
@@ -1173,9 +1173,9 @@ object LoginDialog: TLoginDialog
             Caption = 'Post login &commands:'
             FocusControl = PostLoginCommandsMemo
           end
-          object Label5: TLabel
+          object FtpListAllLabel: TLabel
             Left = 12
-            Top = 102
+            Top = 126
             Width = 159
             Height = 13
             Caption = '&Support for listing of hidden files:'
@@ -1183,7 +1183,7 @@ object LoginDialog: TLoginDialog
           end
           object Label24: TLabel
             Left = 12
-            Top = 126
+            Top = 102
             Width = 188
             Height = 13
             Caption = 'Use &MLSD command for directory listing'
@@ -1208,12 +1208,13 @@ object LoginDialog: TLoginDialog
           end
           object FtpListAllCombo: TComboBox
             Left = 272
-            Top = 97
+            Top = 121
             Width = 61
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            TabOrder = 1
+            TabOrder = 2
+            OnChange = DataChange
           end
           object FtpForcePasvIpCombo: TComboBox
             Left = 272
@@ -1223,15 +1224,17 @@ object LoginDialog: TLoginDialog
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
             TabOrder = 3
+            OnChange = DataChange
           end
           object FtpUseMlsdCombo: TComboBox
             Left = 272
-            Top = 121
+            Top = 97
             Width = 61
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            TabOrder = 2
+            TabOrder = 1
+            OnChange = DataChange
           end
         end
       end

+ 1 - 1
source/forms/Login.h

@@ -279,7 +279,7 @@ __published:
   TMemo *PostLoginCommandsMemo;
   TLabel *BugMaxPkt2Label;
   TComboBox *BugMaxPkt2Combo;
-  TLabel *Label5;
+  TLabel *FtpListAllLabel;
   TComboBox *FtpListAllCombo;
   TComboBox *FtpForcePasvIpCombo;
   TLabel *Label22;

+ 294 - 40
source/forms/MessageDlg.cpp

@@ -23,25 +23,34 @@ public:
 
 protected:
   __fastcall TMessageForm(TComponent * AOwner);
+  virtual __fastcall ~TMessageForm();
 
   DYNAMIC void __fastcall KeyDown(Word & Key, TShiftState Shift);
+  DYNAMIC void __fastcall KeyUp(Word & Key, TShiftState Shift);
   UnicodeString __fastcall GetFormText();
   virtual void __fastcall CreateParams(TCreateParams & Params);
   DYNAMIC void __fastcall DoShow();
   virtual void __fastcall Dispatch(void * Message);
+  void __fastcall MenuItemClick(TObject * Sender);
+  void __fastcall ButtonDropDownClick(TObject * Sender);
+  void __fastcall UpdateForShiftStateTimer(TObject * Sender);
 
 private:
   TLabel * Message;
   TMemo * MessageMemo;
+  TShiftState FShiftState;
+  TTimer * FUpdateForShiftStateTimer;
 
   void __fastcall HelpButtonClick(TObject * Sender);
   void __fastcall CMDialogKey(TWMKeyDown & Message);
+  void __fastcall UpdateForShiftState();
 };
 //---------------------------------------------------------------------------
 __fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner, 0)
 {
   Message = NULL;
   MessageMemo = NULL;
+  FUpdateForShiftStateTimer = NULL;
   TNonClientMetrics NonClientMetrics;
   NonClientMetrics.cbSize = sizeof(NonClientMetrics);
   if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NonClientMetrics, 0))
@@ -52,6 +61,11 @@ __fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner, 0)
   UseSystemSettingsPre(this);
 }
 //---------------------------------------------------------------------------
+__fastcall TMessageForm::~TMessageForm()
+{
+  SAFE_DESTROY(FUpdateForShiftStateTimer);
+}
+//---------------------------------------------------------------------------
 void __fastcall TMessageForm::HelpButtonClick(TObject * /*Sender*/)
 {
   if (HelpKeyword != HELP_NONE)
@@ -69,12 +83,91 @@ void __fastcall TMessageForm::HelpButtonClick(TObject * /*Sender*/)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TMessageForm::UpdateForShiftState()
+{
+
+  TShiftState ShiftState =
+    KeyboardStateToShiftState() *
+    (TShiftState() << ssShift << ssCtrl << ssAlt);
+
+  if (FShiftState != ShiftState)
+  {
+    FShiftState = ShiftState;
+
+    for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
+    {
+      TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
+      if ((Button != NULL) && (Button->DropDownMenu != NULL))
+      {
+        TMenuItem * MenuItems = Button->DropDownMenu->Items;
+        for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)
+        {
+          TMenuItem * Item = MenuItems->Items[ItemIndex];
+          TShiftState GrouppedShiftState(Item->Tag >> 16);
+          if (Item->Enabled &&
+              ((ShiftState.Empty() && Item->Default) ||
+               (!ShiftState.Empty() && (ShiftState == GrouppedShiftState))))
+          {
+            int From = 1;
+            Button->Caption = CopyToChars(Item->Caption, From, L"\t", false);
+            Button->ModalResult = Item->Tag & 0xFFFF;
+            assert(Button->OnClick == NULL);
+            assert(Item->OnClick == MenuItemClick);
+            break;
+          }
+        }
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TMessageForm::KeyUp(Word & Key, TShiftState Shift)
+{
+
+  UpdateForShiftState();
+
+  TForm::KeyUp(Key, Shift);
+}
+//---------------------------------------------------------------------------
 void __fastcall TMessageForm::KeyDown(Word & Key, TShiftState Shift)
 {
   if (Shift.Contains(ssCtrl) && (Key == L'C'))
   {
     CopyToClipboard(GetFormText());
   }
+  else
+  {
+    if (!Shift.Contains(ssCtrl))
+    {
+      for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
+      {
+        TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
+        if ((Button != NULL) && (Button->DropDownMenu != NULL))
+        {
+          TMenuItem * MenuItems = Button->DropDownMenu->Items;
+          for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)
+          {
+            TMenuItem * Item = MenuItems->Items[ItemIndex];
+            if (IsAccel(Key, MenuItems->Items[ItemIndex]->Caption))
+            {
+              Item->OnClick(Item);
+              Key = 0;
+              break;
+            }
+          }
+        }
+
+        if (Key == 0)
+        {
+          break;
+        }
+      }
+    }
+
+    UpdateForShiftState();
+
+    TForm::KeyDown(Key, Shift);
+  }
 }
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TMessageForm::GetFormText()
@@ -113,7 +206,38 @@ void __fastcall TMessageForm::CMDialogKey(TWMKeyDown & Message)
   {
     OnKeyDown(this, Message.CharCode, KeyDataToShiftState(Message.KeyData));
   }
-  TForm::Dispatch(&Message);
+
+  if (Message.CharCode == VK_MENU)
+  {
+    bool AnyButtonWithGrouppedCommandsWithShiftState = false;
+    for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
+    {
+      TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
+      if ((Button != NULL) && (Button->DropDownMenu != NULL))
+      {
+        // we should check if there are any commands with shift state,
+        // but it's bit overkill
+        AnyButtonWithGrouppedCommandsWithShiftState = true;
+        break;
+      }
+    }
+
+    // this is to make Alt only alter button meaning (if there is any
+    // alternable button) and not popup system menu
+    if (AnyButtonWithGrouppedCommandsWithShiftState)
+    {
+      Message.Result = 1;
+      UpdateForShiftState();
+    }
+    else
+    {
+      TForm::Dispatch(&Message);
+    }
+  }
+  else
+  {
+    TForm::Dispatch(&Message);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TMessageForm::Dispatch(void * Message)
@@ -146,6 +270,32 @@ void __fastcall TMessageForm::DoShow()
   TForm::DoShow();
 }
 //---------------------------------------------------------------------------
+void __fastcall TMessageForm::MenuItemClick(TObject * Sender)
+{
+  TMenuItem * Item = NOT_NULL(dynamic_cast<TMenuItem *>(Sender));
+  ModalResult = (Item->Tag & 0xFFFF);
+}
+//---------------------------------------------------------------------------
+void __fastcall TMessageForm::UpdateForShiftStateTimer(TObject * /*Sender*/)
+{
+  // this is needed to reflect shift state, even when we do not have a keyboard
+  // focus, what happens when drop down menu is popped up
+  UpdateForShiftState();
+}
+//---------------------------------------------------------------------------
+void __fastcall TMessageForm::ButtonDropDownClick(TObject * /*Sender*/)
+{
+  // as optimization, do not waste time running timer, unless
+  // user pops up drop down menu. we do not have a way to stop timer, once
+  // it closes, but functionaly is does not matter
+  if (FUpdateForShiftStateTimer == NULL)
+  {
+    FUpdateForShiftStateTimer = new TTimer(this);
+    FUpdateForShiftStateTimer->Interval = 50;
+    FUpdateForShiftStateTimer->OnTimer = UpdateForShiftStateTimer;
+  }
+}
+//---------------------------------------------------------------------------
 const ResourceString * Captions[] = { &_SMsgDlgWarning, &_SMsgDlgError, &_SMsgDlgInformation,
   &_SMsgDlgConfirm, NULL };
 const wchar_t * IconIDs[] = { IDI_EXCLAMATION, IDI_HAND, IDI_ASTERISK,
@@ -154,9 +304,13 @@ const int ButtonCount = 11;
 const UnicodeString ButtonNames[ButtonCount] = {
   L"Yes", L"No", L"OK", L"Cancel", L"Abort", L"Retry", L"Ignore", L"All", L"NoToAll",
   L"YesToAll", L"Help" };
+// Own variant to avoid accelerator conflict with "Abort" button.
+// Note that as of now, ALL_BUTTON is never actually used, because it's always aliased
+ResourceString MsgDlgAll = { NULL, ALL_BUTTON };
+ResourceString MsgDlgYesToAll = { NULL, YES_TO_ALL_BUTTON };
 const ResourceString * ButtonCaptions[ButtonCount] = {
   &_SMsgDlgYes, &_SMsgDlgNo, &_SMsgDlgOK, &_SMsgDlgCancel, &_SMsgDlgAbort,
-  &_SMsgDlgRetry, &_SMsgDlgIgnore, &_SMsgDlgAll, &_SMsgDlgNoToAll, &_SMsgDlgYesToAll,
+  &_SMsgDlgRetry, &_SMsgDlgIgnore, &MsgDlgAll, &_SMsgDlgNoToAll, &MsgDlgYesToAll,
   &_SMsgDlgHelp };
 extern const int ModalResults[ButtonCount] = {
   mrYes, mrNo, mrOk, mrCancel, mrAbort, mrRetry, mrIgnore, mrAll, mrNoToAll,
@@ -171,6 +325,22 @@ const int mcButtonSpacing = 4;
 const int mcMoreMessageWidth = 320;
 const int mcMoreMessageHeight = 80;
 //---------------------------------------------------------------------------
+static UnicodeString __fastcall GetKeyNameStr(int Key)
+{
+  wchar_t Buf[MAX_PATH];
+  LONG VirtualKey = MapVirtualKey(Key, MAPVK_VK_TO_VSC);
+  VirtualKey <<= 16;
+  if (GetKeyNameText(VirtualKey, Buf, LENOF(Buf)) > 0)
+  {
+    Buf[LENOF(Buf) - 1] = L'\0';
+  }
+  else
+  {
+    Buf[0] = L'\0';
+  }
+  return Buf;
+}
+//---------------------------------------------------------------------------
 TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
   TStrings * MoreMessages, TMsgDlgType DlgType, TMsgDlgButtons Buttons,
   TQueryButtonAlias * Aliases, unsigned int AliasesCount,
@@ -228,46 +398,44 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
   int ButtonWidth = MulDiv(mcButtonWidth, DialogUnits.x, 4);
   TButton * ButtonControls[ButtonCount + 1];
   int ButtonControlsCount = 0;
+  typedef std::map<unsigned int, TButton *> TAnswerButtons;
+  TAnswerButtons AnswerButtons;
   for (unsigned int B = mbYes; B <= mbHelp; B++)
   {
     assert(B < ButtonCount);
     if (Buttons.Contains(TMsgDlgBtn(B)))
     {
       TextRect = Rect(0,0,0,0);
-      UnicodeString Caption = LoadResourceString(ButtonCaptions[B]);
-
-      // temporary fix of accelerators (&Abort vs. &All/Yes to &All)
-      // must be removed
-      if (Caption == L"&All")
-      {
-        Caption = L"A&ll";
-      }
-      else if (Caption == L"Yes to &All")
-      {
-        Caption = L"Yes to A&ll";
-      }
+      const ResourceString * CaptionResource = ButtonCaptions[B];
+      UnicodeString Caption = LoadStr(CaptionResource->Identifier);
 
       TNotifyEvent OnClick = NULL;
+      int GroupWith = -1;
+      TShiftState GrouppedShiftState;
       if (Aliases != NULL)
       {
         for (unsigned int i = 0; i < AliasesCount; i++)
         {
           if (B == Aliases[i].Button)
           {
-            Caption = Aliases[i].Alias;
+            if (!Aliases[i].Alias.IsEmpty())
+            {
+              Caption = Aliases[i].Alias;
+            }
             OnClick = Aliases[i].OnClick;
+            GroupWith = Aliases[i].GroupWith;
+            GrouppedShiftState = Aliases[i].GrouppedShiftState;
+            assert((OnClick == NULL) || (GrouppedShiftState == TShiftState()));
             break;
           }
         }
       }
 
-      TButton * Button = new TButton(Result);
-
       UnicodeString MeasureCaption = Caption;
-      if ((TimeoutButton != NULL) && (B == static_cast<unsigned int>(TimeoutResult)))
+      bool IsTimeoutButton = (TimeoutButton != NULL) && (B == static_cast<unsigned int>(TimeoutResult));
+      if (IsTimeoutButton)
       {
         MeasureCaption = FMTLOAD(TIMEOUT_BUTTON, (MeasureCaption, 99));
-        *TimeoutButton = Button;
       }
 
       DrawText(Result->Canvas->Handle,
@@ -275,35 +443,121 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
         &TextRect, DT_CALCRECT | DT_LEFT | DT_SINGLELINE |
         Result->DrawTextBiDiModeFlagsReadingOnly());
       int CurButtonWidth = TextRect.Right - TextRect.Left + 8;
-      if (CurButtonWidth > ButtonWidth)
-      {
-        ButtonWidth = CurButtonWidth;
-      }
 
-      Button->Name = ButtonNames[TMsgDlgBtn(B)];
-      Button->Parent = Result;
-      Button->Caption = Caption;
-      if (OnClick != NULL)
+      int ModalResult = ModalResults[B];
+
+      // we hope that all grouped-with buttons are for asnwer with greater
+      // value that the answer to be grouped with
+      if (SupportsSplitButton() &&
+          (GroupWith >= 0) && ALWAYS_TRUE(GroupWith < static_cast<int>(B)) &&
+          ALWAYS_TRUE(AnswerButtons.find(GroupWith) != AnswerButtons.end()) &&
+          ALWAYS_TRUE(B != static_cast<unsigned int>(TimeoutResult)) &&
+          ALWAYS_TRUE(B != static_cast<unsigned int>(DefaultButton)) &&
+          ALWAYS_TRUE(B != static_cast<unsigned int>(CancelButton)))
       {
-        Button->OnClick = OnClick;
+        TButton * GroupWithButton = AnswerButtons[GroupWith];
+
+        if (GroupWithButton->DropDownMenu == NULL)
+        {
+          GroupWithButton->Style = TCustomButton::bsSplitButton;
+          GroupWithButton->DropDownMenu = new TPopupMenu(Result);
+          // cannot handle subitems with shift state,
+          // if the button has its own handler
+          // (though it may not be the case still here)
+          assert(GroupWithButton->OnClick == NULL);
+
+          TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);
+          GroupWithButton->DropDownMenu->Items->Add(Item);
+          GroupWithButton->OnDropDownClick = Result->ButtonDropDownClick;
+
+          Item->Caption = GroupWithButton->Caption;
+          Item->OnClick = Result->MenuItemClick;
+          assert(GroupWithButton->ModalResult <= 0xFFFF);
+          Item->Tag = GroupWithButton->ModalResult;
+          Item->Default = true;
+        }
+
+        TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);
+        GroupWithButton->DropDownMenu->Items->Add(Item);
+
+        // See ShortCutToText in Vcl.Menus.pas
+        if (GrouppedShiftState == (TShiftState() << ssAlt))
+        {
+          Caption = Caption + L"\t" + GetKeyNameStr(VK_MENU);
+        }
+        else if (GrouppedShiftState == (TShiftState() << ssCtrl))
+        {
+          Caption = Caption + L"\t" + GetKeyNameStr(VK_CONTROL);
+        }
+        else if (GrouppedShiftState == (TShiftState() << ssShift))
+        {
+          Caption = Caption + L"\t" + GetKeyNameStr(VK_SHIFT);
+        }
+        else
+        {
+          // do not support combined shift states yet
+          assert(GrouppedShiftState == TShiftState());
+        }
+
+        Item->Caption = Caption;
+        if (OnClick != NULL)
+        {
+          Item->OnClick = OnClick;
+        }
+        else
+        {
+          Item->OnClick = Result->MenuItemClick;
+          assert((ModalResult <= 0xFFFF) && (GrouppedShiftState.ToInt() <= 0xFFFF));
+          Item->Tag = ModalResult + (GrouppedShiftState.ToInt() << 16);
+        }
+
+        // Hard-coded drop down button width (do not know how to ask for system width).
+        // Also we do not update the max button width for the default groupped
+        // button caption. We just blindly hope that captions of advanced commands
+        // are always longer than the caption of simple default command
+        CurButtonWidth += 15;
       }
       else
       {
-        Button->ModalResult = ModalResults[B];
-        Button->Default = (B == static_cast<unsigned int>(DefaultButton));
-        Button->Cancel = (B == static_cast<unsigned int>(CancelButton));
-      }
-      if (MoreMessages != NULL)
-      {
-        Button->Anchors = TAnchors() << akBottom << akLeft;
+        TButton * Button = new TButton(Result);
+
+        if (IsTimeoutButton)
+        {
+          *TimeoutButton = Button;
+        }
+
+        Button->Name = ButtonNames[TMsgDlgBtn(B)];
+        Button->Parent = Result;
+        Button->Caption = Caption;
+        if (OnClick != NULL)
+        {
+          Button->OnClick = OnClick;
+        }
+        else
+        {
+          Button->ModalResult = ModalResult;
+          Button->Default = (B == static_cast<unsigned int>(DefaultButton));
+          Button->Cancel = (B == static_cast<unsigned int>(CancelButton));
+        }
+        if (MoreMessages != NULL)
+        {
+          Button->Anchors = TAnchors() << akBottom << akLeft;
+        }
+        if (B == mbHelp)
+        {
+          Button->OnClick = Result->HelpButtonClick;
+        }
+
+        ButtonControls[ButtonControlsCount] = Button;
+        ButtonControlsCount++;
+
+        AnswerButtons.insert(TAnswerButtons::value_type(B, Button));
       }
-      if (B == mbHelp)
+
+      if (CurButtonWidth > ButtonWidth)
       {
-        Button->OnClick = Result->HelpButtonClick;
+        ButtonWidth = CurButtonWidth;
       }
-
-      ButtonControls[ButtonControlsCount] = Button;
-      ButtonControlsCount++;
     }
   }
 

+ 4 - 2
source/forms/Preferences.cpp

@@ -315,7 +315,6 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
     QueueCheck->Checked = GUIConfiguration->DefaultCopyParam.Queue;
     QueueIndividuallyCheck->Checked = GUIConfiguration->DefaultCopyParam.QueueIndividually;
     QueueNoConfirmationCheck->Checked = GUIConfiguration->DefaultCopyParam.QueueNoConfirmation;
-    RememberPasswordCheck->Checked = GUIConfiguration->QueueRememberPassword;
     if (!GUIConfiguration->QueueKeepDoneItems)
     {
       QueueKeepDoneItemsForCombo->ItemIndex = 0;
@@ -444,6 +443,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
 
     // security
     UseMasterPasswordCheck->Checked = WinConfiguration->UseMasterPassword;
+    SessionRememberPasswordCheck->Checked = GUIConfiguration->SessionRememberPassword;
 
     // network
     RetrieveExternalIpAddressButton->Checked = Configuration->ExternalIpAddress.IsEmpty();
@@ -606,7 +606,6 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
     CopyParam.Queue = QueueCheck->Checked;
     CopyParam.QueueIndividually = QueueIndividuallyCheck->Checked;
     CopyParam.QueueNoConfirmation = QueueNoConfirmationCheck->Checked;
-    GUIConfiguration->QueueRememberPassword = RememberPasswordCheck->Checked;
     GUIConfiguration->QueueKeepDoneItems = (QueueKeepDoneItemsForCombo->ItemIndex != 0);
     switch (QueueKeepDoneItemsForCombo->ItemIndex)
     {
@@ -744,6 +743,9 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
       (CustomExternalIpAddressButton->Checked ? CustomExternalIpAddressEdit->Text : UnicodeString());
     Configuration->TryFtpWhenSshFails = TryFtpWhenSshFailsCheck->Checked;
 
+    // security
+    GUIConfiguration->SessionRememberPassword = SessionRememberPasswordCheck->Checked;
+
     #undef BOOLPROP
   }
   __finally

+ 22 - 13
source/forms/Preferences.dfm

@@ -1186,7 +1186,7 @@ object PreferencesDialog: TPreferencesDialog
           Left = 8
           Top = 8
           Width = 357
-          Height = 222
+          Height = 200
           Anchors = [akLeft, akTop, akRight]
           Caption = 'Background transfers'
           TabOrder = 0
@@ -1200,7 +1200,7 @@ object PreferencesDialog: TPreferencesDialog
           end
           object QueueKeepDoneItemsCheck: TLabel
             Left = 16
-            Top = 194
+            Top = 172
             Width = 198
             Height = 13
             Caption = 'Display completed transfers in queue for:'
@@ -1234,14 +1234,6 @@ object PreferencesDialog: TPreferencesDialog
             Caption = 'Transfer on &background by default'
             TabOrder = 2
           end
-          object RememberPasswordCheck: TCheckBox
-            Left = 16
-            Top = 170
-            Width = 337
-            Height = 17
-            Caption = 'Remember &password of main session for background transfers'
-            TabOrder = 6
-          end
           object QueueNoConfirmationCheck: TCheckBox
             Left = 16
             Top = 122
@@ -1268,12 +1260,12 @@ object PreferencesDialog: TPreferencesDialog
           end
           object QueueKeepDoneItemsForCombo: TComboBox
             Left = 248
-            Top = 191
+            Top = 169
             Width = 97
             Height = 21
             Style = csDropDownList
             MaxLength = 1
-            TabOrder = 7
+            TabOrder = 6
             OnChange = ControlChange
             Items.Strings = (
               'Never'
@@ -1286,7 +1278,7 @@ object PreferencesDialog: TPreferencesDialog
         end
         object QueueViewGroup: TGroupBox
           Left = 8
-          Top = 236
+          Top = 214
           Width = 357
           Height = 99
           Anchors = [akLeft, akTop, akRight]
@@ -2163,6 +2155,23 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = UseMasterPasswordCheckClick
           end
         end
+        object PasswordGroupBox: TGroupBox
+          Left = 8
+          Top = 106
+          Width = 357
+          Height = 56
+          Anchors = [akLeft, akTop, akRight]
+          Caption = 'Session password'
+          TabOrder = 1
+          object SessionRememberPasswordCheck: TCheckBox
+            Left = 17
+            Top = 24
+            Width = 333
+            Height = 17
+            Caption = 'Remember &password for duration of session'
+            TabOrder = 0
+          end
+        end
       end
       object IntegrationAppSheet: TTabSheet
         Tag = 18

+ 2 - 1
source/forms/Preferences.h

@@ -104,7 +104,6 @@ __published:
   TCheckBox *QueueAutoPopupCheck;
   TCheckBox *QueueCheck;
   TCheckBox *DDAllowMoveInitCheck;
-  TCheckBox *RememberPasswordCheck;
   TCheckBox *ConfirmResumeCheck;
   TTabSheet *StorageSheet;
   TGroupBox *StorageGroup;
@@ -256,6 +255,8 @@ __published:
   TTabSheet *PanelLocalSheet;
   TGroupBox *LocalPanelGroup;
   TCheckBox *SystemContextMenuCheck;
+  TGroupBox *PasswordGroupBox;
+  TCheckBox *SessionRememberPasswordCheck;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);

+ 10 - 5
source/forms/SynchronizeChecklist.cpp

@@ -13,7 +13,8 @@
 
 #include <VCLCommon.h>
 #include <Tools.h>
-#include <CustomWinConfiguration.h>
+#include <BaseUtils.hpp>
+#include <WinConfiguration.h>
 //---------------------------------------------------------------------
 #pragma link "IEListView"
 #pragma link "NortonLikeListView"
@@ -242,7 +243,9 @@ void __fastcall TSynchronizeChecklistDialog::LoadItem(TListItem * Item)
       }
       else
       {
-        Item->SubItems->Add(FormatFloat(L"#,##0", ChecklistItem->Local.Size));
+        Item->SubItems->Add(
+          FormatBytes(ChecklistItem->Local.Size,
+            WinConfiguration->FormatSizeBytes, WinConfiguration->FormatSizeBytes));
       }
       Item->SubItems->Add(UserModificationStr(ChecklistItem->Local.Modification,
         ChecklistItem->Local.ModificationFmt));
@@ -283,7 +286,9 @@ void __fastcall TSynchronizeChecklistDialog::LoadItem(TListItem * Item)
       }
       else
       {
-        Item->SubItems->Add(FormatFloat(L"#,##0", ChecklistItem->Remote.Size));
+        Item->SubItems->Add(
+          FormatBytes(ChecklistItem->Remote.Size,
+            WinConfiguration->FormatSizeBytes, WinConfiguration->FormatSizeBytes));
       }
       Item->SubItems->Add(UserModificationStr(ChecklistItem->Remote.Modification,
         ChecklistItem->Remote.ModificationFmt));
@@ -438,8 +443,8 @@ void __fastcall TSynchronizeChecklistDialog::StatusBarDrawPanel(
   if (Possible)
   {
     PanelText = FORMAT(LoadStrPart(SYNCHRONIZE_SELECTED_ACTIONS, 1),
-      (FormatFloat(L"#,##0", FChecked[Panel->Index]),
-       FormatFloat(L"#,##0", FTotals[Panel->Index])));
+      (FormatNumber(FChecked[Panel->Index]),
+       FormatNumber(FTotals[Panel->Index])));
   }
   else
   {

+ 19 - 8
source/packages/my/NortonLikeListView.pas

@@ -643,8 +643,13 @@ var
   PLastSelectMethod: TSelectMethod;
   PDontUnSelectItem: Boolean;
   PDontSelectItem: Boolean;
-  AParent: TWinControl;
+  WParam: UINT_PTR;
+  LParam: INT_PTR;
 begin
+  // This whole is replacement for mere ItemFocused := Item
+  // because that does not reset some internal focused pointer,
+  // causing subsequent Shift-Click selects range from the first item,
+  // not from focused item.
   Item.MakeVisible(False);
   if Focused then
   begin
@@ -657,13 +662,19 @@ begin
     FDontUnSelectItem := True;
     FFocusingItem := True;
     try
-      AParent := Parent;
-      P := ClientToScreen(P);
-      while AParent.Parent <> nil do
-        AParent := AParent.Parent;
-      P := AParent.ScreenToClient(P);
-      SendMessage(AParent.Handle, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(P.X, P.Y));
-      SendMessage(AParent.Handle, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(P.X, P.Y));
+      // HACK
+      // WM_LBUTTONDOWN enters loop, waiting for WM_LBUTTONUP,
+      // so we have to post it in advance to break the loop immediately
+
+      // Without MK_CONTROL, if there are more items selected,
+      // they won't get unselected on subsequent focus change
+      // (with explorer-style selection).
+      // And it also makes the click the least obtrusive, affecting the focused
+      // file only.
+      WParam := MK_LBUTTON or MK_CONTROL;
+      LParam := MAKELPARAM(P.X, P.Y);
+      PostMessage(Handle, WM_LBUTTONUP, WParam, LParam);
+      SendMessage(Handle, WM_LBUTTONDOWN, WParam, LParam);
     finally
       FFocusingItem := False;
       FLastSelectMethod := PLastSelectMethod;

+ 247 - 13
source/packages/my/PathLabel.pas

@@ -29,6 +29,7 @@ type
     FIsActive: Boolean;
     FMask: string;
     FAutoSizeVertical: Boolean;
+    FAutoHotTrackColors: Boolean;
     procedure CMHintShow(var Message: TMessage); message CM_HINTSHOW;
     procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
     procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
@@ -39,6 +40,13 @@ type
     procedure SetUnixPath(AUnixPath: Boolean);
     procedure SetMask(Value: string);
     procedure SetAutoSizeVertical(Value: Boolean);
+    procedure SetFocusControl(Value: TWinControl);
+    function GetFocusControl: TWinControl;
+    function HotTrackColorsStored(Index: Integer): Boolean;
+    procedure SetAutoHotTrackColors(Value: Boolean);
+    function CalculateAutoHotTrackColor(C: TColor): TColor;
+    procedure CalculateAutoHotTrackColors;
+    function CalculateAutoHotTrackColorComponent(C: Byte; Bright: Boolean): Byte;
   protected
     procedure AdjustBounds; override;
     procedure Click; override;
@@ -47,6 +55,7 @@ type
       Operation: TOperation); override;
     procedure Paint; override;
     function IsActive: Boolean;
+    function TrackingActive: Boolean;
     function HotTrackPath(Path: string): string;
     procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
     procedure DoPathClick(Path: string); virtual;
@@ -60,7 +69,7 @@ type
     property ActiveTextColor: TColor index 3 read GetColors write SetColors
       default clCaptionText;
     property ActiveHotTrackColor: TColor index 5 read GetColors write SetColors
-      default clGradientActiveCaption;
+      stored HotTrackColorsStored;
     property UnixPath: Boolean read FUnixPath write SetUnixPath default False;
     property IndentHorizontal: Integer read FIndentHorizontal
       write SetIndentHorizontal default 5;
@@ -71,14 +80,15 @@ type
     property InactiveTextColor: TColor index 2 read GetColors write SetColors
       default clInactiveCaptionText;
     property InactiveHotTrackColor: TColor index 4 read GetColors write SetColors
-      default clGradientInactiveCaption;
+      stored HotTrackColorsStored;
     property OnGetStatus: TPathLabelGetStatusEvent read FOnGetStatus write FOnGetStatus;
     property OnPathClick: TPathLabelPathClickEvent read FOnPathClick write FOnPathClick;
     property HotTrack: Boolean read FHotTrack write FHotTrack default False;
     property Mask: string read FMask write SetMask;
     property AutoSizeVertical: Boolean read FAutoSizeVertical write SetAutoSizeVertical default False;
+    property AutoHotTrackColors: Boolean read FAutoHotTrackColors write SetAutoHotTrackColors default True;
 
-    property FocusControl;
+    property FocusControl: TWinControl read GetFocusControl write SetFocusControl;
     property Caption;
     property Hint stored False;
     property Align default alTop;
@@ -100,6 +110,7 @@ type
     property HotTrack;
     property OnGetStatus;
     property OnPathClick;
+    property AutoHotTrackColors;
 
     property Align;
     property Alignment;
@@ -155,13 +166,12 @@ begin
   FIndentVertical := 1;
   FUnixPath := False;
   FHotTrack := False;
+  FAutoHotTrackColors := True;
   FColors[0] := clInactiveCaption;
   FColors[1] := clActiveCaption;
   FColors[2] := clInactiveCaptionText;
   FColors[3] := clCaptionText;
-  FColors[4] := clGradientInactiveCaption;
-  FColors[5] := clGradientActiveCaption;
-  UpdateStatus;
+  CalculateAutoHotTrackColors;
 end;
 
 procedure TCustomPathLabel.CMHintShow(var Message: TMessage);
@@ -234,10 +244,211 @@ begin
   if FColors[Index] <> Value then
   begin
     FColors[Index] := Value;
+
+    if (Index = 4) or (Index = 5) then
+      FAutoHotTrackColors := False
+    else
+      CalculateAutoHotTrackColors;
+
     UpdateStatus;
   end;
 end; { SetColors }
 
+function TCustomPathLabel.HotTrackColorsStored(Index: Integer): Boolean;
+begin
+  Result := not AutoHotTrackColors;
+end;
+
+procedure TCustomPathLabel.SetAutoHotTrackColors(Value: Boolean);
+begin
+  if AutoHotTrackColors <> Value then
+  begin
+    FAutoHotTrackColors := Value;
+    CalculateAutoHotTrackColors;
+    UpdateStatus;
+  end;
+end;
+
+// taken from PngImageListEditor
+
+const
+  WeightR: single = 0.764706;
+  WeightG: single = 1.52941;
+  WeightB: single = 0.254902;
+
+function ColorDistance(C1, C2: Integer): Single;
+var
+  DR, DG, DB: Integer;
+begin
+  DR := (C1 and $FF) - (C2 and $FF);
+  Result := Sqr(DR * WeightR);
+  DG := (C1 shr 8 and $FF) - (C2 shr 8 and $FF);
+  Result := Result + Sqr(DG * WeightG);
+  DB := (C1 shr 16) - (C2 shr 16);
+  Result := Result + Sqr(DB * WeightB);
+  Result := Sqrt(Result);
+end;
+
+function GetAdjustedThreshold(BkgndIntensity, Threshold: Single): Single;
+begin
+  if BkgndIntensity < 220 then
+    Result := (2 - BkgndIntensity / 220) * Threshold
+  else
+    Result := Threshold;
+end;
+
+function IsContrastEnough(AColor, ABkgndColor: Integer; DoAdjustThreshold: Boolean; Threshold: Single): Boolean;
+begin
+  if DoAdjustThreshold then
+    Threshold := GetAdjustedThreshold(ColorDistance(ABkgndColor, $000000),
+      Threshold);
+  Result := ColorDistance(ABkgndColor, AColor) > Threshold;
+end;
+
+procedure AdjustContrast(var AColor: Integer; ABkgndColor: Integer; Threshold: Single);
+var
+  X, Y, Z: Single;
+  R, G, B: Single;
+  RR, GG, BB: Integer;
+  I1, I2, S, Q, W: Single;
+  DoInvert: Boolean;
+begin
+  I1 := ColorDistance(AColor, $000000);
+  I2 := ColorDistance(ABkgndColor, $000000);
+  Threshold := GetAdjustedThreshold(I2, Threshold);
+
+  if I1 > I2 then
+    DoInvert := I2 < 442 - Threshold
+  else
+    DoInvert := I2 < Threshold;
+
+  X := (ABkgndColor and $FF) * WeightR;
+  Y := (ABkgndColor shr 8 and $FF) * WeightG;
+  Z := (ABkgndColor shr 16) * WeightB;
+
+  R := (AColor and $FF) * WeightR;
+  G := (AColor shr 8 and $FF) * WeightG;
+  B := (AColor shr 16) * WeightB;
+
+  if DoInvert then begin
+    R := 195 - R;
+    G := 390 - G;
+    B := 65 - B;
+    X := 195 - X;
+    Y := 390 - Y;
+    Z := 65 - Z;
+  end;
+
+  S := Sqrt(Sqr(B) + Sqr(G) + Sqr(R));
+  if S < 0.01 then
+    S := 0.01;
+
+  Q := (R * X + G * Y + B * Z) / S;
+
+  X := Q / S * R - X;
+  Y := Q / S * G - Y;
+  Z := Q / S * B - Z;
+
+  W := Sqrt(Sqr(Threshold) - Sqr(X) - Sqr(Y) - Sqr(Z));
+
+  R := (Q - W) * R / S;
+  G := (Q - W) * G / S;
+  B := (Q - W) * B / S;
+
+  if DoInvert then begin
+    R := 195 - R;
+    G := 390 - G;
+    B := 65 - B;
+  end;
+
+  if R < 0 then
+    R := 0
+  else if R > 195 then
+    R := 195;
+  if G < 0 then
+    G := 0
+  else if G > 390 then
+    G := 390;
+  if B < 0 then
+    B := 0
+  else if B > 65 then
+    B := 65;
+
+  RR := Trunc(R * (1 / WeightR) + 0.5);
+  GG := Trunc(G * (1 / WeightG) + 0.5);
+  BB := Trunc(B * (1 / WeightB) + 0.5);
+
+  if RR > $FF then
+    RR := $FF
+  else if RR < 0 then
+    RR := 0;
+  if GG > $FF then
+    GG := $FF
+  else if GG < 0 then
+    GG := 0;
+  if BB > $FF then
+    BB := $FF
+  else if BB < 0 then
+    BB := 0;
+
+  AColor := (BB and $FF) shl 16 or (GG and $FF) shl 8 or (RR and $FF);
+end;
+
+procedure SetContrast(var Color: TColor; BkgndColor: TColor; Threshold: Integer);
+var
+  T: Single;
+begin
+  if Color < 0 then
+    Color := GetSysColor(Color and $FF);
+  if BkgndColor < 0 then
+    BkgndColor := GetSysColor(BkgndColor and $FF);
+  T := Threshold;
+  if not IsContrastEnough(Color, BkgndColor, True, T) then
+    AdjustContrast(Integer(Color), BkgndColor, T);
+end;
+
+function TCustomPathLabel.CalculateAutoHotTrackColorComponent(C: Byte; Bright: Boolean): Byte;
+var
+  Delta: Byte;
+begin
+  Delta := Max(Round(C * 0.3), 80);
+  if Bright then
+    Result := Byte(Max(Integer(C) - Delta, 0))
+  else
+    Result := Byte(Min(C + Delta, 255));
+end;
+
+function TCustomPathLabel.CalculateAutoHotTrackColor(C: TColor): TColor;
+var
+  R, G, B: Byte;
+  Bright: Boolean;
+begin
+  C := ColorToRGB(C);
+
+  R := GetRValue(C);
+  G := GetGValue(C);
+  B := GetBValue(C);
+
+  Bright := (R + G + B) > (256 / 2 * 3);
+
+  R := CalculateAutoHotTrackColorComponent(R, Bright);
+  G := CalculateAutoHotTrackColorComponent(G, Bright);
+  B := CalculateAutoHotTrackColorComponent(B, Bright);
+
+  Result := RGB(R, G, B);
+end;
+
+procedure TCustomPathLabel.CalculateAutoHotTrackColors;
+begin
+  if AutoHotTrackColors then
+  begin
+    FColors[4] := CalculateAutoHotTrackColor(FColors[2]);
+    SetContrast(FColors[4], FColors[0], 50);
+    FColors[5] := CalculateAutoHotTrackColor(FColors[3]);
+    SetContrast(FColors[5], FColors[1], 50);
+  end;
+end;
+
 procedure TCustomPathLabel.SetIndentHorizontal(AIndent: Integer);
 begin
   if FIndentHorizontal <> AIndent then
@@ -268,6 +479,20 @@ begin
   end;
 end;
 
+function TCustomPathLabel.GetFocusControl: TWinControl;
+begin
+  Result := inherited FocusControl;
+end;
+
+procedure TCustomPathLabel.SetFocusControl(Value: TWinControl);
+begin
+  if FocusControl <> Value then
+  begin
+    inherited FocusControl := Value;
+    UpdateStatus;
+  end;
+end;
+
 procedure TCustomPathLabel.DoDrawText(var Rect: TRect; Flags: Longint);
 var
   i: Integer;
@@ -375,7 +600,8 @@ begin
     if FDisplayHotTrack <> '' then
     begin
       StandardColor := Canvas.Font.Color;
-      Canvas.Font.Color := FColors[4 + Integer(FIsActive)];
+      if TrackingActive then
+        Canvas.Font.Color := FColors[4 + Integer(FIsActive)];
       DrawText(Canvas.Handle, PChar(FDisplayHotTrack), Length(FDisplayHotTrack), Rect, Flags);
       Canvas.Font.Color := StandardColor;
       HotTrackOffset := Canvas.TextWidth(FDisplayHotTrack);
@@ -523,14 +749,22 @@ begin
   end;
 end;
 
+function TCustomPathLabel.TrackingActive: Boolean;
+begin
+  Result := Assigned(FocusControl) or Assigned(OnGetStatus);
+end;
+
 procedure TCustomPathLabel.UpdateStatus;
 begin
-  FIsActive := IsActive;
-  Color := FColors[Integer(FIsActive)];
-  // We don't want to store Font properties in DFM
-  // which would be if Font.Color is set to something else than clWindowText
-  if not (csDesigning in ComponentState) then
-    Font.Color := FColors[2 + Integer(FIsActive)];
+  if TrackingActive then
+  begin
+    FIsActive := IsActive;
+    Color := FColors[Integer(FIsActive)];
+    // We don't want to store Font properties in DFM
+    // which would be if Font.Color is set to something else than clWindowText
+    if not (csDesigning in ComponentState) then
+      Font.Color := FColors[2 + Integer(FIsActive)];
+  end;
 end; { UpdateStatus }
 
 procedure TCustomPathLabel.Notification(AComponent: TComponent;

+ 3 - 3
source/resource/TextsCore.h

@@ -98,7 +98,7 @@
 #define SFTP_STATUS_NO_CONNECTION 168
 #define SFTP_STATUS_CONNECTION_LOST 169
 #define SFTP_STATUS_OP_UNSUPPORTED 170
-#define SFTP_ERROR_FORMAT2      171
+#define SFTP_ERROR_FORMAT3      171
 #define SFTP_STATUS_UNKNOWN     172
 #define READ_SYMLINK_ERROR      173
 #define EMPTY_DIRECTORY         174
@@ -232,8 +232,8 @@
 #define APPEND_OR_RESUME        312
 #define FILE_OVERWRITE_DETAILS  313
 #define READ_ONLY_OVERWRITE     314
-#define LOCAL_FILE_OVERWRITE    315
-#define REMOTE_FILE_OVERWRITE   316
+#define LOCAL_FILE_OVERWRITE2   315
+#define REMOTE_FILE_OVERWRITE2  316
 #define TIMEOUT_STILL_WAITING3  321
 #define KEX_BELOW_TRESHOLD      322
 #define RECONNECT_BUTTON        323

+ 4 - 4
source/resource/TextsCore1.rc

@@ -69,7 +69,7 @@ BEGIN
   SFTP_STATUS_NO_CONNECTION, "No connection."
   SFTP_STATUS_CONNECTION_LOST, "Connection lost."
   SFTP_STATUS_OP_UNSUPPORTED, "The server does not support the operation."
-  SFTP_ERROR_FORMAT2, "%s\nError code: %d\nError message from server%s: %s\nRequest code: %d"
+  SFTP_ERROR_FORMAT3, "%s\nError code: %d\nError message from server%s: %s"
   SFTP_STATUS_UNKNOWN, "Unknown status code."
   READ_SYMLINK_ERROR, "Error reading symlink '%s'."
   EMPTY_DIRECTORY, "Server returned empty listing for directory '%s'."
@@ -103,7 +103,7 @@ BEGIN
   SCP_INIT_ERROR, "Cannot execute SCP to start transfer. Please make sure that SCP is installed on the server and path to it is included in PATH. You may also try SFTP instead of SCP."
   DUPLICATE_BOOKMARK, "Location Profile with name '%s' already exists."
   MOVE_FILE_ERROR, "Error moving file '%s' to '%s'."
-  SFTP_PACKET_TOO_BIG_INIT_EXPLAIN, "%s\n \nThe error is typically caused by message printed from startup script (like .profile). The message may start with \"%s\"."
+  SFTP_PACKET_TOO_BIG_INIT_EXPLAIN, "%s\n \nThe error is typically caused by message printed from startup script (like .profile). The message may start with %s."
   PRESERVE_TIME_PERM_ERROR, "Upload of file '%s' was successful, but error occurred while setting the permissions and/or timestamp. If the problem persists, turn on 'Ignore permission errors' option."
   ACCESS_VIOLATION_ERROR2, "Invalid access to memory. Please report the error on WinSCP support forum."
   SFTP_STATUS_NO_SPACE_ON_FILESYSTEM, "There is no free space on the filesystem."
@@ -197,8 +197,8 @@ BEGIN
   APPEND_OR_RESUME, "Do you want to append file '%s' at the end of existing file? Press 'No' to resume file transfer instead."
   FILE_OVERWRITE_DETAILS, "%s\n \nNew:      \t%s bytes, %s\nExisting: \t%s bytes, %s"
   READ_ONLY_OVERWRITE, "File '%s' is read-only. Overwrite?"
-  LOCAL_FILE_OVERWRITE, "Local file '%s' already exists. Overwrite?"
-  REMOTE_FILE_OVERWRITE, "Remote file '%s' already exists. Overwrite?"
+  LOCAL_FILE_OVERWRITE2, "Overwrite local file '%s'?\n\nDestination directory already contains file '%s'.\nChoose, if you want to overwrite the file or skip this transfer and keep existing file."
+  REMOTE_FILE_OVERWRITE2, "Overwrite remote file '%s'?\n\nDestination directory already contains file '%s'.\nChoose, if you want to overwrite the file or skip this transfer and keep existing file."
   TIMEOUT_STILL_WAITING3, "Host is not communicating for more than %d seconds. Still waiting...\n\nNote: If the problem repeats, try turning off 'Optimize connection buffer size'."
   KEX_BELOW_TRESHOLD, "The first key-exchange algorithm supported by the server is %s, which is below the configured warning threshold.\n\nDo you want to continue with this connection?"
   RECONNECT_BUTTON, "&Reconnect"

+ 2 - 0
source/resource/TextsWin.h

@@ -118,6 +118,8 @@
 #define EDITOR_AUTO_CONFIG      1356
 #define IMPORT_SESSIONS         1357
 #define IMPORT_CONFIGURATION    1358
+#define ALL_BUTTON              1359
+#define YES_TO_ALL_BUTTON       1360
 
 #define WIN_INFORMATION_STRINGS 1400
 #define APP_CAPTION             1401

+ 2 - 0
source/resource/TextsWin1.rc

@@ -119,6 +119,8 @@ BEGIN
         CLOSE_SESSIONS_WORKSPACE, "Terminate all sessions and close application without saving a workspace?\n\nPress 'No' to enable automatic saving of the workspace."
         IMPORT_SESSIONS, "You have stored sessions/sites in %s.\n\nDo you want to import them into WinSCP?\n\n(You can import them anytime later from Login dialog)|PuTTY SSH client|Filezilla FTP client|%s and %s"
         IMPORT_CONFIGURATION, "Importing configuration will overwrite all your preferences and sites.\n\nDo you want to continue?"
+        ALL_BUTTON, "A&ll"
+        YES_TO_ALL_BUTTON, "Yes to A&ll"
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         APP_CAPTION, "%s - %s"

+ 2 - 1
source/windows/ConsoleRunner.cpp

@@ -1222,7 +1222,8 @@ void __fastcall TConsoleRunner::ScriptTerminalPromptUser(TTerminal * /*Terminal*
     Print(Prompt);
 
     UnicodeString AResult = Results->Strings[Index]; // useless
-    Result = DoInput(AResult, bool(Prompts->Objects[Index]), InputTimeout(), true);
+    bool Echo = FLAGSET(int(Prompts->Objects[Index]), pupEcho);
+    Result = DoInput(AResult, Echo, InputTimeout(), true);
     Results->Strings[Index] = AResult;
   }
 }

+ 8 - 14
source/windows/GUIConfiguration.cpp

@@ -554,7 +554,7 @@ void __fastcall TGUIConfiguration::Default()
   FQueueKeepDoneItems = true;
   FQueueKeepDoneItemsFor = 15;
   FQueueAutoPopup = true;
-  FQueueRememberPassword = false;
+  FSessionRememberPassword = false;
   UnicodeString ProgramsFolder;
   SpecialFolderLocation(CSIDL_PROGRAM_FILES, ProgramsFolder);
   FDefaultPuttyPathOnly = IncludeTrailingBackslash(ProgramsFolder) + L"PuTTY\\putty.exe";
@@ -610,16 +610,10 @@ void __fastcall TGUIConfiguration::UpdateStaticUsage()
   Usage->Set(L"CopyParamsCount", (FCopyParamListDefaults ? 0 : FCopyParamList->Count));
 }
 //---------------------------------------------------------------------------
-UnicodeString __fastcall TGUIConfiguration::PropertyToKey(const UnicodeString Property)
-{
-  // no longer useful
-  int P = Property.LastDelimiter(L".>");
-  return Property.SubString(P + 1, Property.Length() - P);
-}
-//---------------------------------------------------------------------------
 // duplicated from core\configuration.cpp
 #define BLOCK(KEY, CANCREATE, BLOCK) \
   if (Storage->OpenSubKey(KEY, CANCREATE, true)) try { BLOCK } __finally { Storage->CloseSubKey(); }
+#define KEY(TYPE, VAR) KEYEX(TYPE, VAR, PropertyToKey(TEXT(#VAR)))
 #define REGCONFIG(CANCREATE) \
   BLOCK(L"Interface", CANCREATE, \
     KEY(Bool,     ContinueOnError); \
@@ -633,7 +627,7 @@ UnicodeString __fastcall TGUIConfiguration::PropertyToKey(const UnicodeString Pr
     KEY(Integer,  QueueKeepDoneItems); \
     KEY(Integer,  QueueKeepDoneItemsFor); \
     KEY(Bool,     QueueAutoPopup); \
-    KEY(Bool,     QueueRememberPassword); \
+    KEYEX(Bool,   SessionRememberPassword, L"QueueRememberPassword"); \
     KEY(String,   PuttySession); \
     KEY(String,   PuttyPath); \
     KEY(Bool,     PuttyPassword); \
@@ -651,9 +645,9 @@ void __fastcall TGUIConfiguration::SaveData(THierarchicalStorage * Storage, bool
   TConfiguration::SaveData(Storage, All);
 
   // duplicated from core\configuration.cpp
-  #define KEY(TYPE, VAR) Storage->Write ## TYPE(PropertyToKey(TEXT(#VAR)), VAR)
+  #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(NAME, VAR)
   REGCONFIG(true);
-  #undef KEY
+  #undef KEYEX
 
   if (Storage->OpenSubKey(L"Interface\\CopyParam", true, true))
   try
@@ -692,11 +686,11 @@ void __fastcall TGUIConfiguration::LoadData(THierarchicalStorage * Storage)
   TConfiguration::LoadData(Storage);
 
   // duplicated from core\configuration.cpp
-  #define KEY(TYPE, VAR) VAR = Storage->Read ## TYPE(PropertyToKey(TEXT(#VAR)), VAR)
+  #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(NAME, VAR)
   #pragma warn -eas
   REGCONFIG(false);
   #pragma warn +eas
-  #undef KEY
+  #undef KEYEX
 
   if (Storage->OpenSubKey(L"Interface\\CopyParam", false, true))
   try
@@ -1048,7 +1042,7 @@ void __fastcall TGUIConfiguration::SetDefaultCopyParam(const TGUICopyParamType &
 //---------------------------------------------------------------------------
 bool __fastcall TGUIConfiguration::GetRememberPassword()
 {
-  return QueueRememberPassword || PuttyPassword;
+  return SessionRememberPassword || PuttyPassword;
 }
 //---------------------------------------------------------------------------
 const TCopyParamList * __fastcall TGUIConfiguration::GetCopyParamList()

+ 2 - 3
source/windows/GUIConfiguration.h

@@ -160,7 +160,7 @@ private:
   int FMaxWatchDirectories;
   TDateTime FIgnoreCancelBeforeFinish;
   bool FQueueAutoPopup;
-  bool FQueueRememberPassword;
+  bool FSessionRememberPassword;
   int FQueueTransfersLimit;
   bool FQueueKeepDoneItems;
   int FQueueKeepDoneItemsFor;
@@ -196,7 +196,6 @@ protected:
   virtual bool __fastcall GetRememberPassword();
   const TCopyParamList * __fastcall GetCopyParamList();
   void __fastcall SetCopyParamList(const TCopyParamList * value);
-  static UnicodeString __fastcall PropertyToKey(const UnicodeString Property);
   virtual void __fastcall DefaultLocalized();
   int __fastcall GetCopyParamIndex();
   TGUICopyParamType __fastcall GetCurrentCopyParam();
@@ -233,7 +232,7 @@ public:
   __property bool QueueKeepDoneItems = { read = FQueueKeepDoneItems, write = SetQueueKeepDoneItems };
   __property int QueueKeepDoneItemsFor = { read = FQueueKeepDoneItemsFor, write = SetQueueKeepDoneItemsFor };
   __property bool QueueAutoPopup = { read = FQueueAutoPopup, write = FQueueAutoPopup };
-  __property bool QueueRememberPassword = { read = FQueueRememberPassword, write = FQueueRememberPassword };
+  __property bool SessionRememberPassword = { read = FSessionRememberPassword, write = FSessionRememberPassword };
   __property LCID Locale = { read = GetLocale, write = SetLocale };
   __property LCID LocaleSafe = { read = GetLocale, write = SetLocaleSafe };
   __property TStrings * Locales = { read = GetLocales };

+ 4 - 4
source/windows/GUITools.cpp

@@ -92,15 +92,15 @@ void __fastcall OpenSessionInPutty(const UnicodeString PuttyPath,
           {
             if (GUIConfiguration->TelnetForFtpInPutty)
             {
-              ExportData->Protocol = ptTelnet;
-              ExportData->PortNumber = 23;
+              ExportData->PuttyProtocol = PuttyTelnetProtocol;
+              ExportData->PortNumber = TelnetPortNumber;
               // PuTTY  does not allow -pw for telnet
               Password = L"";
             }
             else
             {
-              ExportData->Protocol = ptSSH;
-              ExportData->PortNumber = 22;
+              ExportData->PuttyProtocol = PuttySshProtocol;
+              ExportData->PortNumber = SshPortNumber;
             }
           }
 

+ 27 - 8
source/windows/TerminalManager.cpp

@@ -403,6 +403,8 @@ bool __fastcall TTerminalManager::ConnectActiveTerminal()
     Configuration->Usage->Inc(L"OpenedSessionsAdvanced");
   }
 
+  ActiveTerminal->EnableUsage();
+
   // add only stored sessions to the jump list,
   // ad-hoc session cannot be reproduced from just a session name
   if (StoredSessions->FindSame(ActiveTerminal->SessionData) != NULL)
@@ -588,6 +590,16 @@ void __fastcall TTerminalManager::SetScpExplorer(TCustomScpExplorerForm * value)
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value)
+{
+  DoSetActiveTerminal(value, false);
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminalManager::SetActiveTerminalWithAutoReconnect(TTerminal * value)
+{
+  DoSetActiveTerminal(value, true);
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminalManager::DoSetActiveTerminal(TTerminal * value, bool AutoReconnect)
 {
   if (ActiveTerminal != value)
   {
@@ -631,16 +643,23 @@ void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value)
       {
         UnicodeString Message = FTerminationMessages->Strings[Index];
         FTerminationMessages->Strings[Index] = L"";
-        Exception * E = new ESshFatal(NULL, Message);
-        try
+        if (AutoReconnect)
         {
-          // finally show pending terminal message,
-          // this gives user also possibility to reconnect
-          ActiveTerminal->ShowExtendedException(E);
+          ReconnectActiveTerminal();
         }
-        __finally
+        else
         {
-          delete E;
+          Exception * E = new ESshFatal(NULL, Message);
+          try
+          {
+            // finally show pending terminal message,
+            // this gives user also possibility to reconnect
+            ActiveTerminal->ShowExtendedException(E);
+          }
+          __finally
+          {
+            delete E;
+          }
         }
       }
     }
@@ -947,7 +966,7 @@ void __fastcall TTerminalManager::TerminalPromptUser(
   {
     assert(Instructions.IsEmpty());
     assert(Prompts->Count == 1);
-    assert(bool(Prompts->Objects[0]));
+    assert(FLAGSET(int(Prompts->Objects[0]), pupEcho));
     UnicodeString AResult = Results->Strings[0];
 
     TInputDialogInitialize InputDialogInitialize = NULL;

+ 2 - 0
source/windows/TerminalManager.h

@@ -52,6 +52,7 @@ public:
   void __fastcall FreeActiveTerminal();
   void __fastcall CycleTerminals(bool Forward);
   static void ConnectTerminal(TTerminal * Terminal, bool Reopen);
+  void __fastcall SetActiveTerminalWithAutoReconnect(TTerminal * value);
   void __fastcall UpdateAppTitle();
   bool __fastcall CanOpenInPutty();
   void __fastcall OpenInPutty();
@@ -103,6 +104,7 @@ private:
   void __fastcall CreateLogMemo();
   void __fastcall FreeLogMemo();
   void __fastcall SetScpExplorer(TCustomScpExplorerForm * value);
+  void __fastcall DoSetActiveTerminal(TTerminal * value, bool AutoReconnect);
   void __fastcall SetActiveTerminal(TTerminal * value);
   void __fastcall SetLogMemo(TLogMemo * value);
   void __fastcall UpdateAll();

+ 31 - 24
source/windows/Tools.cpp

@@ -843,13 +843,39 @@ typedef struct {
 
 typedef BOOL (*WINHTTPGETDEFAULTPROXYCONFIGURATION)(WINHTTP_PROXY_INFO * pProxyInfo);
 typedef BOOL (*WINHTTPGETIEPROXYCONFIGFORCURRENTUSER)(
-  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig);
+  IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig);
+  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyInfo;
+static HMODULE WinHTTP = NULL;
+static WINHTTPGETDEFAULTPROXYCONFIGURATION WinHttpGetDefaultProxyConfiguration = NULL;
+static WINHTTPGETIEPROXYCONFIGFORCURRENTUSER WinHttpGetIEProxyConfigForCurrentUser = NULL;
+//---------------------------------------------------------------------------
+static bool __fastcall GetProxyUrlFromIE(UnicodeString & Proxy)
+{
+  bool Result = false;
+  memset(&IEProxyInfo, 0, sizeof(IEProxyInfo));
+  if ((WinHttpGetIEProxyConfigForCurrentUser != NULL) &&
+      WinHttpGetIEProxyConfigForCurrentUser(&IEProxyInfo))
+  {
+    if (IEProxyInfo.lpszProxy != NULL)
+    {
+      Proxy = IEProxyInfo.lpszProxy;
+      GlobalFree(IEProxyInfo.lpszProxy);
+      Result = true;
+    }
+    if (IEProxyInfo.lpszAutoConfigUrl != NULL)
+    {
+      GlobalFree(IEProxyInfo.lpszAutoConfigUrl);
+    }
+    if (IEProxyInfo.lpszProxyBypass != NULL)
+    {
+      GlobalFree(IEProxyInfo.lpszProxyBypass);
+    }
+  }
+  return Result;
+}
 //---------------------------------------------------------------------------
 bool __fastcall AutodetectProxyUrl(UnicodeString & Proxy)
 {
-  static HMODULE WinHTTP = NULL;
-  static WINHTTPGETDEFAULTPROXYCONFIGURATION WinHttpGetDefaultProxyConfiguration = NULL;
-  static WINHTTPGETIEPROXYCONFIGFORCURRENTUSER WinHttpGetIEProxyConfigForCurrentUser = NULL;
 
   bool Result = true;
 
@@ -921,26 +947,7 @@ bool __fastcall AutodetectProxyUrl(UnicodeString & Proxy)
        WinHttpGetProxyForUrl() */
     if (!Result)
     {
-      WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyInfo;
-      memset(&IEProxyInfo, 0, sizeof(IEProxyInfo));
-      if ((WinHttpGetIEProxyConfigForCurrentUser != NULL) &&
-          WinHttpGetIEProxyConfigForCurrentUser(&IEProxyInfo))
-      {
-        if (IEProxyInfo.lpszProxy != NULL)
-        {
-          Proxy = IEProxyInfo.lpszProxy;
-          GlobalFree(IEProxyInfo.lpszProxy);
-          Result = true;
-        }
-        if (IEProxyInfo.lpszAutoConfigUrl != NULL)
-        {
-          GlobalFree(IEProxyInfo.lpszAutoConfigUrl);
-        }
-        if (IEProxyInfo.lpszProxyBypass != NULL)
-        {
-          GlobalFree(IEProxyInfo.lpszProxyBypass);
-        }
-      }
+      Result = GetProxyUrlFromIE(Proxy);
     }
 
     // We can also use WinHttpGetProxyForUrl, but it is lengthy

+ 1 - 1
source/windows/UserInterface.cpp

@@ -89,7 +89,7 @@ void __fastcall FlashOnBackground()
   assert(Application);
   if (!ForcedOnForeground && !ForegroundTask())
   {
-    FlashWindow(Application->Handle, true);
+    FlashWindow(Application->MainFormHandle, true);
   }
 }
 //---------------------------------------------------------------------------

+ 22 - 4
source/windows/VCLCommon.cpp

@@ -572,6 +572,13 @@ TAutoSwitch __fastcall CheckBoxAutoSwitchSave(TCheckBox * CheckBox)
   }
 }
 //---------------------------------------------------------------------------
+static const wchar_t PathWordDelimiters[] = L"\\/ ;,.";
+//---------------------------------------------------------------------------
+static bool IsPathWordDelimiter(wchar_t Ch)
+{
+  return (wcschr(PathWordDelimiters, Ch) != NULL);
+}
+//---------------------------------------------------------------------------
 // Windows algorithm is as follows (tested on W2k):
 // right:
 //   is_delimiter(current)
@@ -583,17 +590,22 @@ TAutoSwitch __fastcall CheckBoxAutoSwitchSave(TCheckBox * CheckBox)
 //   right(left(current) + 1)
 int CALLBACK PathWordBreakProc(wchar_t * Ch, int Current, int Len, int Code)
 {
-  wchar_t Delimiters[] = L"\\/ ;,.";
   int Result;
   UnicodeString ACh(Ch, Len);
   if (Code == WB_ISDELIMITER)
   {
     // we return negacy of what WinAPI docs says
-    Result = (wcschr(Delimiters, ACh[Current + 1]) == NULL);
+    Result = !IsPathWordDelimiter(ACh[Current + 1]);
   }
   else if (Code == WB_LEFT)
   {
-    Result = ACh.SubString(1, Current - 1).LastDelimiter(Delimiters);
+    // skip consecutive delimiters
+    while ((Current > 0) &&
+           IsPathWordDelimiter(ACh[Current]))
+    {
+      Current--;
+    }
+    Result = ACh.SubString(1, Current - 1).LastDelimiter(PathWordDelimiters);
   }
   else if (Code == WB_RIGHT)
   {
@@ -604,7 +616,7 @@ int CALLBACK PathWordBreakProc(wchar_t * Ch, int Current, int Len, int Code)
     }
     else
     {
-      const wchar_t * P = wcspbrk(ACh.c_str() + Current - 1, Delimiters);
+      const wchar_t * P = wcspbrk(ACh.c_str() + Current - 1, PathWordDelimiters);
       if (P == NULL)
       {
         Result = Len;
@@ -612,6 +624,12 @@ int CALLBACK PathWordBreakProc(wchar_t * Ch, int Current, int Len, int Code)
       else
       {
         Result = P - ACh.c_str() + 1;
+        // skip consecutive delimiters
+        while ((Result < Len) &&
+               IsPathWordDelimiter(ACh[Result + 1]))
+        {
+          Result++;
+        }
       }
     }
   }

+ 9 - 9
source/windows/WinConfiguration.cpp

@@ -491,7 +491,7 @@ void __fastcall TWinConfiguration::Default()
   FEditor.FindMatchCase = false;
   FEditor.FindWholeWord = false;
   FEditor.FindDown = true;
-  FEditor.TabSize = 7;
+  FEditor.TabSize = 8;
   FEditor.MaxEditors = 500;
   FEditor.EarlyClose = 2; // seconds
   FEditor.SDIShellEditor = false;
@@ -759,14 +759,14 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
   ELEM.SubString(ELEM.LastDelimiter(L".>")+1, ELEM.Length() - ELEM.LastDelimiter(L".>"))
 #define BLOCK(KEY, CANCREATE, BLOCK) \
   if (Storage->OpenSubKey(KEY, CANCREATE, true)) try { BLOCK } __finally { Storage->CloseSubKey(); }
-#define KEY(TYPE, VAR) KEYEX(TYPE, VAR, VAR)
+#define KEY(TYPE, VAR) KEYEX(TYPE, VAR, PropertyToKey(TEXT(#VAR)))
 #define REGCONFIG(CANCREATE) \
   BLOCK(L"Interface", CANCREATE, \
-    KEYEX(Integer,DoubleClickAction, CopyOnDoubleClick); \
+    KEYEX(Integer,DoubleClickAction, L"CopyOnDoubleClick"); \
     KEY(Bool,     CopyOnDoubleClickConfirmation); \
     KEY(Bool,     DDAllowMove); \
     KEY(Bool,     DDAllowMoveInit); \
-    KEYEX(Integer, DDTransferConfirmation, DDTransferConfirmation2); \
+    KEYEX(Integer, DDTransferConfirmation, L"DDTransferConfirmation2"); \
     KEY(String,   DDTemporaryDirectory); \
     KEY(Bool,     DDWarnLackOfTempSpace); \
     KEY(Float,    DDWarnLackOfTempSpaceRatio); \
@@ -885,7 +885,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(Bool,    ScpCommander.SessionsTabs); \
     KEY(Bool,    ScpCommander.StatusBar); \
     KEY(String,  ScpCommander.WindowParams); \
-    KEYEX(Integer, ScpCommander.NortonLikeMode, ExplorerStyleSelection); \
+    KEYEX(Integer, ScpCommander.NortonLikeMode, L"ExplorerStyleSelection"); \
     KEY(Bool,    ScpCommander.PreserveLocalDirectory); \
     KEY(Bool,    ScpCommander.CompareByTime); \
     KEY(Bool,    ScpCommander.CompareBySize); \
@@ -912,8 +912,8 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(String,  LogWindowParams); \
   ); \
   BLOCK(L"Security", CANCREATE, \
-    KEYEX(Bool,  FUseMasterPassword, UseMasterPassword); \
-    KEYEX(String,FMasterPasswordVerifier, MasterPasswordVerifier); \
+    KEYEX(Bool,  FUseMasterPassword, L"UseMasterPassword"); \
+    KEYEX(String,FMasterPasswordVerifier, L"MasterPasswordVerifier"); \
   );
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SaveData(THierarchicalStorage * Storage, bool All)
@@ -921,7 +921,7 @@ void __fastcall TWinConfiguration::SaveData(THierarchicalStorage * Storage, bool
   TCustomWinConfiguration::SaveData(Storage, All);
 
   // duplicated from core\configuration.cpp
-  #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(LASTELEM(UnicodeString(TEXT(#NAME))), VAR)
+  #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(NAME, VAR)
   REGCONFIG(true);
   #undef KEYEX
 
@@ -1019,7 +1019,7 @@ void __fastcall TWinConfiguration::LoadData(THierarchicalStorage * Storage)
   TCustomWinConfiguration::LoadData(Storage);
 
   // duplicated from core\configuration.cpp
-  #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(LASTELEM(UnicodeString(TEXT(#NAME))), VAR)
+  #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(NAME, VAR)
   #pragma warn -eas
   REGCONFIG(false);
   #pragma warn +eas

+ 20 - 3
source/windows/WinInterface.cpp

@@ -147,10 +147,21 @@ static void __fastcall NeverAskAgainCheckClick(void * /*Data*/, TObject * Sender
   for (int ii = 0; ii < Dialog->ControlCount; ii++)
   {
     TButton * Button = dynamic_cast<TButton *>(Dialog->Controls[ii]);
-    if ((Button != NULL) && (Button->ModalResult != mrNone) &&
-        (Button->ModalResult != mrCancel))
+    if (Button != NULL)
     {
-      Button->Enabled = !CheckBox->Checked || (Button->ModalResult == PositiveAnswer);
+      if ((Button->ModalResult != mrNone) && (Button->ModalResult != mrCancel))
+      {
+        Button->Enabled = !CheckBox->Checked || (Button->ModalResult == PositiveAnswer);
+      }
+
+      if (Button->DropDownMenu != NULL)
+      {
+        for (int iii = 0; iii < Button->DropDownMenu->Items->Count; iii++)
+        {
+          TMenuItem * Item = Button->DropDownMenu->Items->Items[iii];
+          Item->Enabled = Item->Default || !CheckBox->Checked;
+        }
+      }
     }
   }
 }
@@ -268,6 +279,12 @@ TForm * __fastcall CreateMessageDialogEx(const UnicodeString Msg,
         Aliases[i].Button = Button;
         Aliases[i].Alias = Params->Aliases[i].Alias;
         Aliases[i].OnClick = Params->Aliases[i].OnClick;
+        if (Params->Aliases[i].GroupWith >= 0)
+        {
+          CHECK(MapButton(Params->Aliases[i].GroupWith, Button));
+          Aliases[i].GroupWith = Button;
+        }
+        Aliases[i].GrouppedShiftState = Params->Aliases[i].GrouppedShiftState;
       }
       Dialog = CreateMoreMessageDialog(Msg, MoreMessages, DlgType, Buttons,
         Aliases, Params->AliasesCount, TimeoutResult, &TimeoutButton, ImageName);