Martin Prikryl 12 лет назад
Родитель
Сommit
a9cd1f05f4
61 измененных файлов с 1022 добавлено и 869 удалено
  1. 2 1
      build.bat
  2. 2 2
      deployment/winscp.isl
  3. 18 18
      deployment/winscpsetup.iss
  4. 1 1
      dotnet/properties/AssemblyInfo.cs
  5. 1 1
      source/Console.cbproj
  6. 1 1
      source/DragExt.cbproj
  7. 2 2
      source/DragExt64.rc
  8. 2 2
      source/WinSCP.cbproj
  9. 0 2
      source/components/ThemePageControl.cpp
  10. 3 3
      source/core/Common.cpp
  11. 1 1
      source/core/Common.h
  12. 11 2
      source/core/Configuration.cpp
  13. 1 0
      source/core/Configuration.h
  14. 20 1
      source/core/FtpFileSystem.cpp
  15. 1 1
      source/core/HierarchicalStorage.cpp
  16. 1 0
      source/core/Interface.h
  17. 4 3
      source/core/PuttyIntf.cpp
  18. 1 1
      source/core/ScpFileSystem.cpp
  19. 19 2
      source/core/SecureShell.cpp
  20. 81 26
      source/core/SessionData.cpp
  21. 13 0
      source/core/SessionData.h
  22. 5 0
      source/core/SessionInfo.cpp
  23. 1 1
      source/core/SftpFileSystem.cpp
  24. 17 1
      source/core/Terminal.cpp
  25. 12 6
      source/core/Usage.cpp
  26. 4 2
      source/core/Usage.h
  27. 4 1
      source/filezilla/AsyncSocketEx.cpp
  28. 1 0
      source/filezilla/FileZillaOpt.h
  29. 8 4
      source/filezilla/FtpControlSocket.cpp
  30. 4 484
      source/filezilla/ServerPath.cpp
  31. 0 8
      source/filezilla/ServerPath.h
  32. 2 2
      source/forms/About.dfm
  33. 76 5
      source/forms/Custom.cpp
  34. 3 0
      source/forms/Custom.h
  35. 73 3
      source/forms/Editor.cpp
  36. 1 0
      source/forms/Editor.h
  37. 38 120
      source/forms/Glyphs.dfm
  38. 1 0
      source/forms/Login.dfm
  39. 1 3
      source/forms/MessageDlg.cpp
  40. 0 1
      source/forms/NonVisual.dfm
  41. 33 8
      source/forms/Preferences.cpp
  42. 19 3
      source/forms/Preferences.dfm
  43. 10 2
      source/forms/Preferences.h
  44. 71 24
      source/forms/RemoteTransfer.dfm
  45. 3 0
      source/forms/RemoteTransfer.h
  46. 1 0
      source/forms/ScpCommander.dfm
  47. 5 0
      source/putty/SSH.C
  48. BIN
      source/resource/RemoteMoveFile.png
  49. 6 4
      source/resource/TextsWin.h
  50. 6 4
      source/resource/TextsWin1.rc
  51. 1 0
      source/windows/GUIConfiguration.cpp
  52. 8 4
      source/windows/QueueController.cpp
  53. 377 72
      source/windows/Setup.cpp
  54. 3 1
      source/windows/Setup.h
  55. 8 3
      source/windows/UserInterface.cpp
  56. 8 0
      source/windows/VCLCommon.cpp
  57. 1 0
      source/windows/VCLCommon.h
  58. 4 3
      source/windows/WinConfiguration.cpp
  59. 0 23
      source/windows/WinInterface.cpp
  60. 21 7
      source/windows/WinMain.cpp
  61. 1 0
      source/windows/Windows.rc

+ 2 - 1
build.bat

@@ -1,6 +1,7 @@
 @echo off
 rem See 'readme' file
-set BDS=%ProgramFiles%\Embarcadero\RAD Studio\9.0
+if "%PROCESSOR_ARCHITECTURE%"=="x86" set BDS=%ProgramFiles%\Embarcadero\RAD Studio\9.0
+if "%PROCESSOR_ARCHITECTURE%"=="AMD64" set BDS=%ProgramFiles(x86)%\Embarcadero\RAD Studio\9.0
 set WITH_DRAGEXT64=0
 set WITH_DOTNET=1
 rem set DRAGEXT64CL=<path to x64 cl.exe>

+ 2 - 2
deployment/winscp.isl

@@ -12,12 +12,12 @@ DesktopIconUserTask=For the current user only
 DesktopIconCommonTask=For all users
 QuickLaunchIconTask=Create a &Quick Launch icon
 SendToHookTask=Add &upload shortcut to Explorer's 'Send to' context menu
-RegisterAsUrlHandler=Register to handle sftp:// and scp:// &addresses
+RegisterAsUrlHandlers=Register to handle URL &addresses
 AddSearchPath=Add installation directory to &search path (%PATH%)
 EnableUpdates=Enable automatic &check for application updates (recommended)
 EnableCollectUsage=Enable collecting &anonymous usage statistics
 
-RegisteringAsUrlHandler=Registering to handle sftp:// and scp:// addresses
+RegisteringAsUrlHandlers=Registering to handle URL addresses...
 AddingSearchPath=Adding installation directory to search path (%PATH%)
 RemovingObsoleteTranslations=Removing obsolete translations
 

+ 18 - 18
deployment/winscpsetup.iss

@@ -219,7 +219,7 @@ Name: desktopicon\common; Description: {cm:DesktopIconCommonTask}; \
 Name: quicklaunchicon; Description: {cm:QuickLaunchIconTask}; \
   Flags: unchecked; OnlyBelowVersion: 6.1.7600
 Name: sendtohook; Description: {cm:SendToHookTask}
-Name: urlhandler; Description: {cm:RegisterAsUrlHandler}
+Name: urlhandler; Description: {cm:RegisterAsUrlHandlers}
 Name: searchpath; Description: {cm:AddSearchPath}; \
   Flags: unchecked; Check: IsAdminLoggedOn
 
@@ -266,13 +266,16 @@ Type: filesandordirs; Name: "{commonprograms}\WinSCP"
 Type: filesandordirs; Name: "{userprograms}\WinSCP"
 
 [Run]
-; This is called when urlhandler task is selected
-Filename: "{app}\WinSCP.exe"; Parameters: "/RegisterAsUrlHandler"; \
-  StatusMsg: {cm:RegisteringAsUrlHandler}; Tasks: urlhandler
+Filename: "{app}\WinSCP.exe"; Parameters: "/RegisterForDefaultProtocols"; \
+  StatusMsg: {cm:RegisteringAsUrlHandlers}; Tasks: urlhandler
 Filename: "{app}\WinSCP.exe"; Parameters: "/AddSearchPath"; \
   StatusMsg: {cm:AddingSearchPath}; Tasks: searchpath
 Filename: "{app}\WinSCP.exe"; Parameters: "/ImportSitesIfAny"; \
   StatusMsg: {cm:ImportSites}
+Filename: "{app}\WinSCP.exe"; Parameters: "/Usage=TypicalInstallation:1"; \
+  Check: IsTypicalInstallation
+Filename: "{app}\WinSCP.exe"; Parameters: "/Usage=TypicalInstallation:0"; \
+  Check: not IsTypicalInstallation
 #ifdef Chrome
 Filename: "{tmp}\{#ChromeInstallerFile}"; \
   Parameters: "/r1:{#ChromeBrandCode1} /r2:{#ChromeBrandCode2} /b:1"; \
@@ -358,16 +361,6 @@ Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
   ValueName: "DDExtEnabled"; ValueData: 1; Components: shellext; \
   Flags: uninsdeletevalue
-; This will remove url handler on uninstall
-; (when urlhandler task was selected when installing)
-Root: HKCR; Subkey: "SFTP"; Flags: dontcreatekey uninsdeletekey; \
-  Tasks: urlhandler
-Root: HKCR; Subkey: "SCP"; Flags: dontcreatekey uninsdeletekey; \
-  Tasks: urlhandler
-Root: HKCU; Subkey: "Software\Classes\SFTP"; Flags: dontcreatekey uninsdeletekey; \
-  Tasks: urlhandler
-Root: HKCU; Subkey: "Software\Classes\SCP"; Flags: dontcreatekey uninsdeletekey; \
-  Tasks: urlhandler
 ; Updates
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
   ValueType: dword; ValueName: "Period"; ValueData: 7; \
@@ -418,6 +411,8 @@ Filename: "{app}\WinSCP.exe"; Parameters: "/UninstallCleanup"; \
   RunOnceId: "UninstallCleanup"
 Filename: "{app}\WinSCP.exe"; Parameters: "/RemoveSearchPath"; \
   RunOnceId: "RemoveSearchPath"
+Filename: "{app}\WinSCP.exe"; Parameters: "/UnregisterForProtocols"; \
+  RunOnceId: "UnregisterForProtocols"
 
 [Code]
 const
@@ -641,6 +636,11 @@ begin
   Control.Cursor := crHand;
 end;
 
+function IsTypicalInstallation: Boolean;
+begin
+  Result := TypicalTypeButton.Checked;
+end;
+
 #ifdef Donations
 
 procedure CreateDonateLink(Amount: Integer; Row: Integer; Top: Integer);
@@ -1209,7 +1209,7 @@ procedure RegisterPreviousData(PreviousDataKey: Integer);
 var
   S: string;
 begin
-  if TypicalTypeButton.Checked then S := 'typical'
+  if IsTypicalInstallation then S := 'typical'
     else S := 'custom';
 
   SetPreviousData(PreviousDataKey, '{#SetupTypeData}', S);
@@ -1444,7 +1444,7 @@ begin
     OpenCandyShouldSkipPage(PageID) or
 #endif
     { Hide most pages during typical installation }
-    (TypicalTypeButton.Checked and
+    (IsTypicalInstallation and
      ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
       (PageID = wpSelectTasks) or
       { Hide Interface page for upgrades only, show for fresh installs }
@@ -1463,12 +1463,12 @@ begin
 
   if not Upgrade then
   begin
-    if TypicalTypeButton.Checked then S2 := ExpandConstant('{cm:TypicalType}')
+    if IsTypicalInstallation then S2 := ExpandConstant('{cm:TypicalType}')
       else S2 := ExpandConstant('{cm:CustomType}');
   end
     else
   begin
-    if TypicalTypeButton.Checked then S2 := ExpandConstant('{cm:TypicalUpgradeType}')
+    if IsTypicalInstallation then S2 := ExpandConstant('{cm:TypicalUpgradeType}')
       else S2 := ExpandConstant('{cm:CustomUpgradeType}');
   end;
   StringChange(S2, '&', '');

+ 1 - 1
dotnet/properties/AssemblyInfo.cs

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

+ 1 - 1
source/Console.cbproj

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

+ 1 - 1
source/DragExt.cbproj

@@ -42,7 +42,7 @@
 			<ProjectType>CppDynamicLibrary</ProjectType>
 			<VerInfo_DLL>true</VerInfo_DLL>
 			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
-			<VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Drag&amp;Drop shell extension for WinSCP (32-bit);FileVersion=1.2.1.0;InternalName=dragext32;LegalCopyright=(c) 2000-2013 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.2.5.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.6.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,5,0
+PRODUCTVERSION 5,2,6,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.5.0\0"
+            VALUE "ProductVersion", "5.2.6.0\0"
             VALUE "ReleaseType", "beta\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 2 - 2
source/WinSCP.cbproj

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

+ 0 - 2
source/components/ThemePageControl.cpp

@@ -227,8 +227,6 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect,
     std::unique_ptr<TCanvas> Canvas(new TCanvas());
     Canvas->Handle = DC;
     Images->Draw(Canvas.get(), Left, Y, Pages[Item]->ImageIndex, !Shadowed);
-    //ImageList_Draw((HIMAGELIST)Images->Handle, Pages[Item]->ImageIndex, DC,
-    //  Left, Y, ILD_TRANSPARENT, );
     Rect.Left += Images->Width + 3;
   }
   else

+ 3 - 3
source/core/Common.cpp

@@ -1911,13 +1911,13 @@ void __fastcall AddToList(UnicodeString & List, const UnicodeString & Value, con
   }
 }
 //---------------------------------------------------------------------------
-bool __fastcall IsWinXPOrOlder()
+bool __fastcall IsWinVista()
 {
-  // Win XP is 5.1
   // Vista is 6.0
+  // Win XP is 5.1
   // There also 5.2, what is Windows 2003 or Windows XP 64bit
   // (we consider it WinXP for now)
-  return !CheckWin32Version(6, 0);
+  return CheckWin32Version(6, 0);
 }
 //---------------------------------------------------------------------------
 bool __fastcall IsWin7()

+ 1 - 1
source/core/Common.h

@@ -96,7 +96,7 @@ UnicodeString __fastcall EscapeHotkey(const UnicodeString & Caption);
 bool __fastcall CutToken(UnicodeString & Str, UnicodeString & Token,
   UnicodeString * RawToken = NULL);
 void __fastcall AddToList(UnicodeString & List, const UnicodeString & Value, const UnicodeString & Delimiter);
-bool __fastcall IsWinXPOrOlder();
+bool __fastcall IsWinVista();
 bool __fastcall IsWin7();
 TLibModule * __fastcall FindModule(void * Instance);
 __int64 __fastcall Round(double Number);

+ 11 - 2
source/core/Configuration.cpp

@@ -110,6 +110,15 @@ __fastcall TConfiguration::~TConfiguration()
   delete FUsage;
 }
 //---------------------------------------------------------------------------
+void __fastcall TConfiguration::UpdateStaticUsage()
+{
+  Usage->Set(L"ConfigurationIniFile", (Storage == stIniFile));
+
+  // this is called from here, because we are guarded from calling into
+  // master password handler here, see TWinConfiguration::UpdateStaticUsage
+  StoredSessions->UpdateStaticUsage();
+}
+//---------------------------------------------------------------------------
 THierarchicalStorage * TConfiguration::CreateScpStorage(bool /*SessionList*/)
 {
   if (Storage == stRegistry)
@@ -997,9 +1006,9 @@ void __fastcall TConfiguration::SetStorage(TStorage value)
     {
       // If this fails, do not pretend that storage was switched.
       // For instance:
-      // - When writting to an IFI file fails (unlikely, as we fallsback to user profile)
+      // - When writting to an INI file fails (unlikely, as we fallback to user profile)
       // - When removing INI file fails, when switching to registry
-      //   (possible, when the INI file is in Program files folder)
+      //   (possible, when the INI file is in Program Files folder)
       FStorage = StorageBak;
       throw;
     }

+ 1 - 0
source/core/Configuration.h

@@ -159,6 +159,7 @@ public:
   __fastcall TConfiguration();
   virtual __fastcall ~TConfiguration();
   virtual void __fastcall Default();
+  virtual void __fastcall UpdateStaticUsage();
   void __fastcall Load();
   void __fastcall Save();
   void __fastcall SaveExplicit();

+ 20 - 1
source/core/FtpFileSystem.cpp

@@ -534,16 +534,30 @@ void __fastcall TFTPFileSystem::CollectUsage()
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPMLSD");
   }
+  else
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPLIST");
+  }
+
   if (FFileZillaIntf->UsingUtf8())
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPUTF8");
   }
+  else
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPNonUTF8");
+  }
+
   if (!CurrentDirectory.IsEmpty() && (CurrentDirectory[1] != L'/'))
   {
     if (IsUnixStyleWindowsPath(CurrentDirectory))
     {
       FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPWindowsPath");
     }
+    else if ((CurrentDirectory.Length() >= 3) && IsLetter(CurrentDirectory[1]) && (CurrentDirectory[2] == L':') && (CurrentDirectory[3] == L'/'))
+    {
+      FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPRealWindowsPath");
+    }
     else
     {
       FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOtherPath");
@@ -2088,7 +2102,8 @@ void __fastcall TFTPFileSystem::CopyFile(const UnicodeString FileName,
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TFTPFileSystem::FileUrl(const UnicodeString FileName)
 {
-  return FTerminal->FileUrl(L"ftp", FileName);
+  UnicodeString Protocol = (FTerminal->SessionData->Ftps == ftpsImplicit) ? FtpsProtocol : FtpProtocol;
+  return FTerminal->FileUrl(Protocol, FileName);
 }
 //---------------------------------------------------------------------------
 TStrings * __fastcall TFTPFileSystem::GetFixedPaths()
@@ -2321,6 +2336,10 @@ int __fastcall TFTPFileSystem::GetOptionVal(int OptionID) const
       Result = Data->SendBuf;
       break;
 
+    case OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY:
+      Result = Data->FtpTransferActiveImmediatelly;
+      break;
+
     default:
       assert(false);
       Result = FALSE;

+ 1 - 1
source/core/HierarchicalStorage.cpp

@@ -1068,7 +1068,7 @@ void __fastcall TIniFileStorage::ApplyOverrides()
   }
 }
 //===========================================================================
-#define NOT_IMPLEMENTED throw Exception("Not implemented")
+#define NOT_IMPLEMENTED throw Exception(L"Not implemented")
 //===========================================================================
 class TOptionsIniFile : public TCustomIniFile
 {

+ 1 - 0
source/core/Interface.h

@@ -10,6 +10,7 @@ TConfiguration * __fastcall CreateConfiguration();
 
 void __fastcall ShowExtendedException(Exception * E);
 
+UnicodeString __fastcall GetCompanyRegistryKey();
 UnicodeString __fastcall GetRegistryKey();
 void __fastcall Busy(bool Start);
 UnicodeString __fastcall AppNameString();

+ 4 - 3
source/core/PuttyIntf.cpp

@@ -497,9 +497,10 @@ TKeyType KeyType(UnicodeString FileName)
 {
   assert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
   assert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
-  Filename KeyFile;
-  ASCOPY(KeyFile.path, FileName);
-  return (TKeyType)key_type(&KeyFile);
+  Filename * KeyFile = filename_from_str(AnsiString(FileName).c_str());
+  TKeyType Result = (TKeyType)key_type(KeyFile);
+  filename_free(KeyFile);
+  return Result;
 }
 //---------------------------------------------------------------------------
 UnicodeString KeyTypeName(TKeyType KeyType)

+ 1 - 1
source/core/ScpFileSystem.cpp

@@ -1278,7 +1278,7 @@ void __fastcall TSCPFileSystem::AnyCommand(const UnicodeString Command,
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TSCPFileSystem::FileUrl(const UnicodeString FileName)
 {
-  return FTerminal->FileUrl(L"scp", FileName);
+  return FTerminal->FileUrl(ScpProtocol, FileName);
 }
 //---------------------------------------------------------------------------
 TStrings * __fastcall TSCPFileSystem::GetFixedPaths()

+ 19 - 2
source/core/SecureShell.cpp

@@ -143,7 +143,7 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
   #define CONF_DEF_STR_NONE(KEY) conf_set_str(conf, KEY, "");
   // noop, used only for these and we set the first three explicitly below and latter two are not used in our code
   #define CONF_DEF_INT_INT(KEY) assert((KEY == CONF_ssh_cipherlist) || (KEY == CONF_ssh_kexlist) || (KEY == CONF_ssh_gsslist) || (KEY == CONF_colours) || (KEY == CONF_wordness));
-  // noop, used only for these three and all thay all can handle undef value
+  // noop, used only for these three and they all can handle undef value
   #define CONF_DEF_STR_STR(KEY) assert((KEY == CONF_ttymodes) || (KEY == CONF_portfwd) || (KEY == CONF_environmt));
   // noop, not used in our code
   #define CONF_DEF_FONT_NONE(KEY) assert((KEY == CONF_font) || (KEY == CONF_boldfont) || (KEY == CONF_widefont) || (KEY == CONF_wideboldfont));
@@ -254,7 +254,15 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
   if (!Data->TunnelPortFwd.IsEmpty())
   {
     assert(!Simple);
-    conf_set_str_str(conf, CONF_portfwd, 0, AnsiString(Data->TunnelPortFwd).c_str());
+    UnicodeString TunnelPortFwd = Data->TunnelPortFwd;
+    while (!TunnelPortFwd.IsEmpty())
+    {
+      UnicodeString Buf = CutToChar(TunnelPortFwd, L',', true);
+      AnsiString Key = AnsiString(CutToChar(Buf, L'\t', true));
+      AnsiString Value = AnsiString(Buf);
+      conf_set_str_str(conf, CONF_portfwd, Key.c_str(), Value.c_str());
+    }
+
     // when setting up a tunnel, do not open shell/sftp
     conf_set_int(conf, CONF_ssh_no_shell, TRUE);
   }
@@ -2143,4 +2151,13 @@ void __fastcall TSecureShell::CollectUsage()
   {
     Configuration->Usage->Inc(L"OpenedSessionsPrivateKey2");
   }
+
+  if (FSshVersion == 1)
+  {
+    Configuration->Usage->Inc(L"OpenedSessionsSSH1");
+  }
+  else if (FSshVersion == 2)
+  {
+    Configuration->Usage->Inc(L"OpenedSessionsSSH2");
+  }
 }

+ 81 - 26
source/core/SessionData.cpp

@@ -38,6 +38,14 @@ const UnicodeString AnonymousUserName(L"anonymous");
 const UnicodeString AnonymousPassword(L"[email protected]");
 const UnicodeString PuttySshProtocol(L"ssh");
 const UnicodeString PuttyTelnetProtocol(L"telnet");
+const UnicodeString SftpProtocol(L"sftp");
+const UnicodeString ScpProtocol(L"scp");
+const UnicodeString FtpProtocol(L"ftp");
+const UnicodeString FtpsProtocol(L"ftps");
+const UnicodeString WebDAVProtocol(L"http");
+const UnicodeString WebDAVSProtocol(L"https");
+const UnicodeString ProtocolSeparator(L"://");
+const UnicodeString WinSCPProtocolPrefix(L"winscp-");
 //---------------------------------------------------------------------
 TDateTime __fastcall SecToDateTime(int Sec)
 {
@@ -177,6 +185,7 @@ void __fastcall TSessionData::Default()
   FtpAccount = L"";
   FtpPingInterval = 30;
   FtpPingType = ptDummyCommand;
+  FtpTransferActiveImmediatelly = false;
   Ftps = ftpsNone;
   MinTlsVersion = ssl2;
   MaxTlsVersion = tls12;
@@ -316,6 +325,7 @@ void __fastcall TSessionData::NonPersistant()
   PROPERTY(FtpAccount); \
   PROPERTY(FtpPingInterval); \
   PROPERTY(FtpPingType); \
+  PROPERTY(FtpTransferActiveImmediatelly); \
   PROPERTY(FtpListAll); \
   PROPERTY(SslSessionReuse); \
   \
@@ -397,14 +407,8 @@ bool __fastcall TSessionData::IsInFolderOrWorkspace(UnicodeString AFolder)
 //---------------------------------------------------------------------
 void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & RewritePassword)
 {
-  // In case we are re-loading, reset passwords, to avoid pointless
-  // re-cryption, while loading username/hostname. And moreover, when
-  // the password is wrongly encrypted (using a different master password),
-  // this breaks sites reload and consequently an overal operation,
-  // such as opening Sites menu
-  FPassword = L"";
-  FProxyPassword = L"";
-  FTunnelPassword = L"";
+  // Make sure we only ever use methods supported by TOptionsStorage
+  // (implemented by TOptionsIniFile)
 
   PortNumber = Storage->ReadInteger(L"PortNumber", PortNumber);
   UserName = Storage->ReadString(L"UserName", UserName);
@@ -624,6 +628,7 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & Rewr
   FtpAccount = Storage->ReadString(L"FtpAccount", FtpAccount);
   FtpPingInterval = Storage->ReadInteger(L"FtpPingInterval", FtpPingInterval);
   FtpPingType = static_cast<TPingType>(Storage->ReadInteger(L"FtpPingType", FtpPingType));
+  FtpTransferActiveImmediatelly = Storage->ReadBool(L"FtpTransferActiveImmediatelly", FtpTransferActiveImmediatelly);
   Ftps = static_cast<TFtps>(Storage->ReadInteger(L"Ftps", Ftps));
   FtpListAll = TAutoSwitch(Storage->ReadInteger(L"FtpListAll", FtpListAll));
   SslSessionReuse = Storage->ReadBool(L"SslSessionReuse", SslSessionReuse);
@@ -645,6 +650,15 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
   bool RewritePassword = false;
   if (Storage->OpenSubKey(InternalStorageKey, False))
   {
+    // In case we are re-loading, reset passwords, to avoid pointless
+    // re-cryption, while loading username/hostname. And moreover, when
+    // the password is wrongly encrypted (using a different master password),
+    // this breaks sites reload and consequently an overal operation,
+    // such as opening Sites menu
+    FPassword = L"";
+    FProxyPassword = L"";
+    FTunnelPassword = L"";
+
     DoLoad(Storage, RewritePassword);
 
     Storage->CloseSubKey();
@@ -901,6 +915,7 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
       WRITE_DATA(String, FtpAccount);
       WRITE_DATA(Integer, FtpPingInterval);
       WRITE_DATA(Integer, FtpPingType);
+      WRITE_DATA(Bool, FtpTransferActiveImmediatelly);
       WRITE_DATA(Integer, Ftps);
       WRITE_DATA(Integer, FtpListAll);
       WRITE_DATA(Bool, SslSessionReuse);
@@ -1201,6 +1216,25 @@ inline void __fastcall MoveStr(UnicodeString & Source, UnicodeString * Dest, int
   Source.Delete(1, Count);
 }
 //---------------------------------------------------------------------
+bool __fastcall TSessionData::DoIsProtocolUrl(
+  const UnicodeString & Url, const UnicodeString & Protocol, int & ProtocolLen)
+{
+  bool Result = SameText(Url.SubString(1, Protocol.Length() + 1), Protocol + L":");
+  if (Result)
+  {
+    ProtocolLen = Protocol.Length() + 1;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------
+bool __fastcall TSessionData::IsProtocolUrl(
+  const UnicodeString & Url, const UnicodeString & Protocol, int & ProtocolLen)
+{
+  return
+    DoIsProtocolUrl(Url, Protocol, ProtocolLen) ||
+    DoIsProtocolUrl(Url, WinSCPProtocolPrefix + Protocol, ProtocolLen);
+}
+//---------------------------------------------------------------------
 bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
   TStoredSessionList * StoredSessions, bool & DefaultsOnly, UnicodeString * FileName,
   bool * AProtocolDefined, UnicodeString * MaskedUrl)
@@ -1210,50 +1244,51 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
   TFSProtocol AFSProtocol;
   int APortNumber;
   TFtps AFtps = ftpsNone;
-  if (Url.SubString(1, 4).LowerCase() == L"scp:")
+  int ProtocolLen = 0;
+  if (IsProtocolUrl(Url, ScpProtocol, ProtocolLen))
   {
     AFSProtocol = fsSCPonly;
     APortNumber = SshPortNumber;
-    MoveStr(Url, MaskedUrl, 4);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
-  else if (Url.SubString(1, 5).LowerCase() == L"sftp:")
+  else if (IsProtocolUrl(Url, SftpProtocol, ProtocolLen))
   {
     AFSProtocol = fsSFTPonly;
     APortNumber = SshPortNumber;
-    MoveStr(Url, MaskedUrl, 5);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
-  else if (Url.SubString(1, 4).LowerCase() == L"ftp:")
+  else if (IsProtocolUrl(Url, FtpProtocol, ProtocolLen))
   {
     AFSProtocol = fsFTP;
     Ftps = ftpsNone;
     APortNumber = FtpPortNumber;
-    MoveStr(Url, MaskedUrl, 4);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
-  else if (Url.SubString(1, 5).LowerCase() == L"ftps:")
+  else if (IsProtocolUrl(Url, FtpsProtocol, ProtocolLen))
   {
     AFSProtocol = fsFTP;
     AFtps = ftpsImplicit;
     APortNumber = FtpsImplicitPortNumber;
-    MoveStr(Url, MaskedUrl, 5);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
-  else if (Url.SubString(1, 5).LowerCase() == L"http:")
+  else if (IsProtocolUrl(Url, WebDAVProtocol, ProtocolLen))
   {
     AFSProtocol = fsWebDAV;
     AFtps = ftpsNone;
     APortNumber = HTTPPortNumber;
-    MoveStr(Url, MaskedUrl, 5);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
-  else if (Url.SubString(1, 6).LowerCase() == L"https:")
+  else if (IsProtocolUrl(Url, WebDAVSProtocol, ProtocolLen))
   {
     AFSProtocol = fsWebDAV;
     AFtps = ftpsImplicit;
     APortNumber = HTTPSPortNumber;
-    MoveStr(Url, MaskedUrl, 6);
+    MoveStr(Url, MaskedUrl, ProtocolLen);
     ProtocolDefined = true;
   }
 
@@ -2036,7 +2071,7 @@ UnicodeString __fastcall TSessionData::GetSessionUrl()
     switch (FSProtocol)
     {
       case fsSCPonly:
-        Url = L"scp://";
+        Url = ScpProtocol;
         break;
 
       default:
@@ -2044,20 +2079,33 @@ UnicodeString __fastcall TSessionData::GetSessionUrl()
         // fallback
       case fsSFTP:
       case fsSFTPonly:
-        Url = L"sftp://";
+        Url = SftpProtocol;
         break;
 
       case fsFTP:
-        Url = L"ftp://";
+        if (Ftps == ftpsImplicit)
+        {
+          Url = FtpsProtocol;
+        }
+        else
+        {
+          Url = FtpProtocol;
+        }
         break;
       case fsWebDAV:
-        if (Ftps == ftpsNone)
-          Url = L"http://";
+        if (Ftps == ftpsImplicit)
+        {
+          Url = WebDAVSProtocol;
+        }
         else
-          Url = L"https://";
+        {
+          Url = WebDAVProtocol;
+        }
         break;
     }
 
+    Url += ProtocolSeparator;
+
     if (!HostName.IsEmpty() && !UserName.IsEmpty())
     {
       Url += FORMAT(L"%s@%s", (UserName, HostName));
@@ -2418,6 +2466,11 @@ void __fastcall TSessionData::SetFtpPingType(TPingType value)
   SET_SESSION_PROPERTY(FtpPingType);
 }
 //---------------------------------------------------------------------------
+void __fastcall TSessionData::SetFtpTransferActiveImmediatelly(bool value)
+{
+  SET_SESSION_PROPERTY(FtpTransferActiveImmediatelly);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSessionData::SetFtps(TFtps value)
 {
   SET_SESSION_PROPERTY(Ftps);
@@ -2913,6 +2966,8 @@ void __fastcall TStoredSessionList::UpdateStaticUsage()
         Color++;
       }
 
+      // this effectively does not take passwords (proxy + tunnel) into account,
+      // when master password is set, as master password handler in not set up yet
       if (!Data->IsSame(FactoryDefaults.get(), true, DifferentAdvancedProperties.get()))
       {
         Advanced++;

+ 13 - 0
source/core/SessionData.h

@@ -52,6 +52,14 @@ extern const int HTTPSPortNumber;
 extern const int TelnetPortNumber;
 extern const UnicodeString PuttySshProtocol;
 extern const UnicodeString PuttyTelnetProtocol;
+extern const UnicodeString SftpProtocol;
+extern const UnicodeString ScpProtocol;
+extern const UnicodeString FtpProtocol;
+extern const UnicodeString FtpsProtocol;
+extern const UnicodeString WebDAVProtocol;
+extern const UnicodeString WebDAVSProtocol;
+extern const UnicodeString ProtocolSeparator;
+extern const UnicodeString WinSCPProtocolPrefix;
 //---------------------------------------------------------------------------
 class TStoredSessionList;
 //---------------------------------------------------------------------------
@@ -157,6 +165,7 @@ private:
   UnicodeString FFtpAccount;
   int FFtpPingInterval;
   TPingType FFtpPingType;
+  bool FFtpTransferActiveImmediatelly;
   TFtps FFtps;
   TTlsVersion FMinTlsVersion;
   TTlsVersion FMaxTlsVersion;
@@ -302,6 +311,7 @@ private:
   void __fastcall SetFtpAccount(UnicodeString value);
   void __fastcall SetFtpPingInterval(int value);
   void __fastcall SetFtpPingType(TPingType value);
+  void __fastcall SetFtpTransferActiveImmediatelly(bool value);
   void __fastcall SetFtps(TFtps value);
   void __fastcall SetMinTlsVersion(TTlsVersion value);
   void __fastcall SetMaxTlsVersion(TTlsVersion value);
@@ -322,6 +332,8 @@ private:
   static RawByteString __fastcall EncryptPassword(const UnicodeString & Password, UnicodeString Key);
   static UnicodeString __fastcall DecryptPassword(const RawByteString & Password, UnicodeString Key);
   static RawByteString __fastcall StronglyRecryptPassword(const RawByteString & Password, UnicodeString Key);
+  static bool __fastcall DoIsProtocolUrl(const UnicodeString & Url, const UnicodeString & Protocol, int & ProtocolLen);
+  static bool __fastcall IsProtocolUrl(const UnicodeString & Url, const UnicodeString & Protocol, int & ProtocolLen);
 
   __property UnicodeString InternalStorageKey = { read = GetInternalStorageKey };
 
@@ -473,6 +485,7 @@ public:
   __property int FtpPingInterval  = { read=FFtpPingInterval, write=SetFtpPingInterval };
   __property TDateTime FtpPingIntervalDT  = { read=GetFtpPingIntervalDT };
   __property TPingType FtpPingType = { read = FFtpPingType, write = SetFtpPingType };
+  __property bool FtpTransferActiveImmediatelly = { read = FFtpTransferActiveImmediatelly, write = SetFtpTransferActiveImmediatelly };
   __property TFtps Ftps = { read = FFtps, write = SetFtps };
   __property TTlsVersion MinTlsVersion = { read = FMinTlsVersion, write = SetMinTlsVersion };
   __property TTlsVersion MaxTlsVersion = { read = FMaxTlsVersion, write = SetMaxTlsVersion };

+ 5 - 0
source/core/SessionInfo.cpp

@@ -1054,6 +1054,11 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
           ADF(L"Session reuse: %s", (BooleanToEngStr(Data->SslSessionReuse)));
           ADF(L"TLS/SSL versions: %s-%s", (GetTlsVersionName(Data->MinTlsVersion), GetTlsVersionName(Data->MaxTlsVersion)));
         }
+        // kind of hidden option, so do not reveal it unless it is set
+        if (Data->FtpTransferActiveImmediatelly)
+        {
+          ADF(L"Transfer active immediatelly: %s", (BooleanToEngStr(Data->FtpTransferActiveImmediatelly)));
+        }
       }
       ADF(L"Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
         ((Data->LocalDirectory.IsEmpty() ? UnicodeString(L"default") : Data->LocalDirectory),

+ 1 - 1
source/core/SftpFileSystem.cpp

@@ -3664,7 +3664,7 @@ void __fastcall TSFTPFileSystem::AnyCommand(const UnicodeString /*Command*/,
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TSFTPFileSystem::FileUrl(const UnicodeString FileName)
 {
-  return FTerminal->FileUrl(L"sftp", FileName);
+  return FTerminal->FileUrl(SftpProtocol, FileName);
 }
 //---------------------------------------------------------------------------
 TStrings * __fastcall TSFTPFileSystem::GetFixedPaths()

+ 17 - 1
source/core/Terminal.cpp

@@ -3960,7 +3960,7 @@ UnicodeString __fastcall TTerminal::FileUrl(const UnicodeString Protocol,
   const UnicodeString FileName)
 {
   assert(FileName.Length() > 0);
-  return Protocol + L"://" + EncodeUrlChars(SessionData->SessionName) +
+  return Protocol + ProtocolSeparator + EncodeUrlChars(SessionData->SessionName) +
     (FileName[1] == L'/' ? L"" : L"/") + EncodeUrlChars(FileName, L"/");
 }
 //---------------------------------------------------------------------------
@@ -5004,6 +5004,14 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
     {
       if (CalculatedSize)
       {
+        if (Configuration->Usage->Collect)
+        {
+          int CounterSize = TUsage::CalculateCounterSize(Size);
+          Configuration->Usage->Inc(L"Uploads");
+          Configuration->Usage->Inc(L"UploadedBytes", CounterSize);
+          Configuration->Usage->SetMax(L"MaxUploadSize", CounterSize);
+        }
+
         OperationProgress.SetTotalSize(Size);
       }
 
@@ -5106,6 +5114,14 @@ bool __fastcall TTerminal::CopyToLocal(TStrings * FilesToCopy,
       {
         if (TotalSizeKnown)
         {
+          if (Configuration->Usage->Collect)
+          {
+            int CounterTotalSize = TUsage::CalculateCounterSize(TotalSize);
+            Configuration->Usage->Inc(L"Downloads");
+            Configuration->Usage->Inc(L"DownloadedBytes", CounterTotalSize);
+            Configuration->Usage->SetMax(L"MaxDownloadSize", CounterTotalSize);
+          }
+
           OperationProgress.SetTotalSize(TotalSize);
         }
 

+ 12 - 6
source/core/Usage.cpp

@@ -185,26 +185,26 @@ void __fastcall TUsage::UpdateCurrentVersion()
   Set(L"CurrentVersion", CompoundVersion);
 }
 //---------------------------------------------------------------------------
-void __fastcall TUsage::Inc(const UnicodeString & Key)
+void __fastcall TUsage::Inc(const UnicodeString & Key, int Increment)
 {
   if (Collect)
   {
     TGuard Guard(FCriticalSection);
-    Inc(Key, FPeriodCounters);
-    Inc(Key, FLifetimeCounters);
+    Inc(Key, FPeriodCounters, Increment);
+    Inc(Key, FLifetimeCounters, Increment);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TUsage::Inc(const UnicodeString & Key, TCounters & Counters)
+void __fastcall TUsage::Inc(const UnicodeString & Key, TCounters & Counters, int Increment)
 {
   TCounters::iterator i = Counters.find(Key);
   if (i != Counters.end())
   {
-    i->second++;
+    i->second += Increment;
   }
   else
   {
-    Counters.insert(std::make_pair(Key, 1));
+    Counters.insert(std::make_pair(Key, Increment));
   }
 }
 //---------------------------------------------------------------------------
@@ -277,3 +277,9 @@ void __fastcall TUsage::Serialize(UnicodeString& List,
     i++;
   }
 }
+//---------------------------------------------------------------------------
+int __fastcall TUsage::CalculateCounterSize(__int64 Size)
+{
+  const int SizeCounterFactor = 10240;
+  return (Size <= 0) ? 0 : (Size < SizeCounterFactor ? 1 : Size / SizeCounterFactor);
+}

+ 4 - 2
source/core/Usage.h

@@ -16,7 +16,7 @@ public:
   void __fastcall Set(const UnicodeString & Key, const UnicodeString & Value);
   void __fastcall Set(const UnicodeString & Key, int Value);
   void __fastcall Set(const UnicodeString & Key, bool Value);
-  void __fastcall Inc(const UnicodeString & Key);
+  void __fastcall Inc(const UnicodeString & Key, int Increment = 1);
   void __fastcall SetMax(const UnicodeString & Key, int Value);
 
   void __fastcall UpdateCurrentVersion();
@@ -27,6 +27,8 @@ public:
   void __fastcall Save(THierarchicalStorage * Storage) const;
   UnicodeString __fastcall Serialize() const;
 
+  static int __fastcall CalculateCounterSize(__int64 Size);
+
   __property bool Collect = { read = FCollect, write = SetCollect };
 
 private:
@@ -45,7 +47,7 @@ private:
     const UnicodeString & Name, TCounters & Counters);
   void __fastcall Save(THierarchicalStorage * Storage,
     const UnicodeString & Name, const TCounters & Counters) const;
-  void __fastcall Inc(const UnicodeString & Key, TCounters & Counters);
+  void __fastcall Inc(const UnicodeString & Key, TCounters & Counters, int Increment);
   void __fastcall SetMax(const UnicodeString & Key, int Value, TCounters & Counters);
   void __fastcall Serialize(UnicodeString& List,
     const UnicodeString & Name, const TCounters & Counters) const;

+ 4 - 1
source/filezilla/AsyncSocketEx.cpp

@@ -160,9 +160,12 @@ public:
 		wndclass.lpszClassName=_T("CAsyncSocketEx Helper Window");
 		wndclass.hIconSm=0;
 
+		#ifdef _DEBUG
+		ATOM ClassAtom =
+		#endif
 		RegisterClassEx(&wndclass);
 
-		m_hWnd=CreateWindow(_T("CAsyncSocketEx Helper Window"), _T("CAsyncSocketEx Helper Window"), 0, 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(0));
+		m_hWnd=CreateWindow(_T("CAsyncSocketEx Helper Window"), _T("CAsyncSocketEx Helper Window"), 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(0), 0);
 		ASSERT(m_hWnd);
 		SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG)this);
 	};

+ 1 - 0
source/filezilla/FileZillaOpt.h

@@ -164,5 +164,6 @@
 #define OPTION_MPEXT_SNDBUF 1003
 #define OPTION_MPEXT_MIN_TLS_VERSION 1004
 #define OPTION_MPEXT_MAX_TLS_VERSION 1005
+#define OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY 1006
 //---------------------------------------------------------------------------
 #endif // FileZillaOptH

+ 8 - 4
source/filezilla/FtpControlSocket.cpp

@@ -3970,12 +3970,11 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi
 							}
 						}
 					}
-					if (!nReplyError)
+					if (!nReplyError && !COptions::GetOptionVal(OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY))
 					{
-						m_pTransferSocket->SetActive();
 					}
 				}
-				else if (pData->bPasv)
+				else if (pData->bPasv && !COptions::GetOptionVal(OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY))
 				{
 					m_pTransferSocket->SetActive();
 				}
@@ -4507,8 +4506,13 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi
 			return;
 		}
 		m_pTransferSocket->m_transferdata=pData->transferdata;
-		if ((pData->transferfile.get || !pData->transferdata.bResume) && !pData->bPasv)
+		// not sure what happens when we active transfer socket immediatelly while resuming
+		// it can possibly make transfer socket start reading from a file before a file pointer is advanced
+		if (COptions::GetOptionVal(OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY) ||
+				((pData->transferfile.get || !pData->transferdata.bResume) && !pData->bPasv))
+		{
 			m_pTransferSocket->SetActive();
+		}
 		CString filename;
 
 		filename = pData->transferfile.remotepath.FormatFilename(pData->transferfile.remotefile, !pData->bUseAbsolutePaths);

+ 4 - 484
source/filezilla/ServerPath.cpp

@@ -485,7 +485,10 @@ const bool CServerPath::operator==(const CServerPath &op) const
 		return false;
 	if (m_Prefix != op.m_Prefix)
 		return false;
-	if (m_nServerType != op.m_nServerType)
+	// excluding FZ_SERVERTYPE_LAYERMASK from comparison,
+	// as this part of server type is not set in TFileZillaIntf
+	const int CompareMask = FZ_SERVERTYPE_HIGHMASK | FZ_SERVERTYPE_SUBMASK;
+	if ((m_nServerType & CompareMask) != (op.m_nServerType & CompareMask))
 		return false;
 	tConstIter iter1 = m_Segments.begin();
 	tConstIter iter2 = op.m_Segments.begin();
@@ -549,97 +552,11 @@ BOOL CServerPath::HasParent() const
 		return FALSE;
 }
 
-BOOL CServerPath::IsSubdirOf(const CServerPath &path, BOOL bCompareNoCase /*=FALSE*/) const
-{
-	if (this==&path)
-		return FALSE;
-
-	if (m_bEmpty || path.m_bEmpty)
-		return FALSE;
-	if (m_Prefix != path.m_Prefix)
-		return FALSE;
-	if (m_nServerType!=path.m_nServerType)
-		return FALSE;
-
-	tConstIter iter1 = m_Segments.begin();
-	tConstIter iter2 = path.m_Segments.begin();
-	while (iter1 != m_Segments.end())
-	{
-		if (iter2==path.m_Segments.end())
-			return TRUE;
-		if (bCompareNoCase)
-		{
-			CString Segment1 = *iter1;
-			CString Segment2 = *iter2;
-			Segment1.MakeLower();
-			Segment2.MakeLower();
-			if (Segment1 != Segment2)
-				return FALSE;
-		}
-		else
-			if (*iter1 != *iter2)
-				return FALSE;
-		iter1++;
-		iter2++;
-	}
-	return FALSE;
-}
-
-BOOL CServerPath::IsParentOf(const CServerPath &path, BOOL bCompareNoCase /*=FALSE*/) const
-{
-	if (!this)
-		return FALSE;
-
-	return path.IsSubdirOf(*this, bCompareNoCase);
-}
-
 const BOOL CServerPath::IsEmpty() const
 {
 	return m_bEmpty;
 }
 
-BOOL CServerPath::SetSafePath(CString path)
-{
-	m_bEmpty = TRUE;
-	m_Prefix = _MPT("");
-	m_Segments.clear();
-
-	int pos1 = path.Find( _T(" ") );
-	int pos2 = 0;
-	if (pos1 <= pos2)
-		return FALSE;
-	m_nServerType = _ttoi(path.Mid(pos2,pos1));
-	pos2 = pos1 + 1;
-
-	pos1=path.Find(_T(" "), pos2);
-	if (pos1<=pos2)
-		return FALSE;
-	int len = _ttoi(path.Mid(pos2,pos1));
-	pos2=pos1+1;
-
-	if (len)
-	{
-		m_Prefix=path.Mid(pos2,len);
-		pos2+=len+1;
-	}
-	
-	while (pos2<path.GetLength())
-	{
-		pos1=path.Find(_T(" "), pos2);
-		if (pos1<=pos2)
-			return FALSE;
-		int len=_ttoi(path.Mid(pos2,pos1));
-			pos2=pos1+1;
-		if (len)
-		{
-			m_Segments.push_back(path.Mid(pos2,len));
-			pos2+=len+1;
-		}
-	}
-	m_bEmpty=FALSE;
-	return TRUE;
-}
-
 CString CServerPath::GetSafePath() const
 {
 	if (m_bEmpty)
@@ -663,60 +580,6 @@ CString CServerPath::GetSafePath() const
 	return safepath;
 }
 
-CString CServerPath::GetSubdirsOf(const CServerPath &path) const
-{
-	ASSERT(IsParentOf(path));
-	CServerPath subdirs=path;
-	CString ret;
-	while(IsParentOf(subdirs))
-	{
-		CString tmp;
-		tmp.Format(_T(" %d %s"), subdirs.GetLastSegment().GetLength(),subdirs.GetLastSegment());
-		ret += tmp;
-		subdirs = subdirs.GetParent();
-	}
-	ret.TrimLeft( _T(" ") );
-	return ret;
-}
-
-BOOL CServerPath::AddSubdirs(CString subdirs)
-{
-	// Do nothing if subdirs is empty
-	if (subdirs == _T(""))
-		return TRUE;
-
-	int pos1;
-	int pos2 = 0;
-	if (m_nServerType & (FZ_SERVERTYPE_SUB_FTP_MVS | FZ_SERVERTYPE_SUB_FTP_BS2000) && m_Prefix != _T("."))
-		return FALSE;
-	
-	while (pos2 < subdirs.GetLength())
-	{
-		pos1 = subdirs.Find(_T(" "), pos2);
-		if (pos1 <= pos2)
-			return FALSE;
-		int len = _ttoi(subdirs.Mid(pos2, pos1));
-			pos2 = pos1 + 1;
-		if (len)
-		{
-			m_Segments.push_back(subdirs.Mid(pos2, len));
-			pos2 += len + 1;
-		}
-	}
-	if (m_nServerType & (FZ_SERVERTYPE_SUB_FTP_MVS | FZ_SERVERTYPE_SUB_FTP_BS2000) && !m_Segments.empty())
-	{
-		if (m_Segments.back().Right(1) == _T("."))
-		{
-			m_Segments.back().TrimRight(_MPT('.'));
-			m_Prefix = _T(".");
-		}
-		else
-			m_Prefix = _T("");
-	}
-	
-	return TRUE;
-}
-
 BOOL CServerPath::AddSubdir(CString subdir)
 {
 	subdir.TrimLeft( _T(" ") );
@@ -938,354 +801,11 @@ CServerPath::CServerPath(CString subdir, const CServerPath &parent)
 	}	
 }
 
-
-BOOL CServerPath::ChangePath(CString &subdir, BOOL bIsFile /*=FALSE*/)
-{
-	CServerPath newpath = *this;
-	CString dir = subdir;
-	if (!(newpath.m_nServerType&FZ_SERVERTYPE_HIGHMASK))
-		newpath.m_nServerType = FZ_SERVERTYPE_FTP;
-	
-	dir.TrimLeft(_T(" "));
-	dir.TrimRight(_T(" "));
-
-	if ( dir==_T("") )
-	{
-		if (newpath.IsEmpty() || bIsFile)
-			return FALSE;
-		else
-		{
-			*this=newpath;
-			return TRUE;
-		}
-	}
-
-	switch (newpath.m_nServerType&FZ_SERVERTYPE_HIGHMASK)
-	{
-	case FZ_SERVERTYPE_FTP:
-		switch(newpath.m_nServerType&FZ_SERVERTYPE_SUBMASK)
-		{
-		case FZ_SERVERTYPE_SUB_FTP_MVS:
-		case FZ_SERVERTYPE_SUB_FTP_BS2000:
-			subdir.TrimLeft(FTP_MVS_DOUBLE_QUOTA);
-			subdir.TrimRight(FTP_MVS_DOUBLE_QUOTA);
-
-			if (subdir.Left(1) == _MPT("'"))
-			{
-				if (subdir.Right(1) != _MPT("'"))
-					return FALSE;
-
-				if (!newpath.SetPath(subdir, bIsFile))
-					return FALSE;
-			}
-			else if (subdir.Right(1) == _MPT("'"))
-				return FALSE;
-			else if (!newpath.IsEmpty())
-			{
-				if (m_Prefix != _T("."))
-					return FALSE;
-
-				subdir.TrimLeft(_MPT('.'));
-				while (subdir.Replace(_T(".."), _T(".")));
-
-				int pos = subdir.Find(_MPT('.'));
-				while (pos != -1)
-				{
-					newpath.m_Segments.push_back(subdir.Left(pos));
-					subdir = subdir.Mid(pos + 1);
-				}
-				if (subdir != _T(""))
-				{
-					newpath.m_Segments.push_back(subdir);
-					newpath.m_Prefix = _T("");
-				}
-				else
-					newpath.m_Prefix = _T(".");
-
-				if (bIsFile)
-				{
-					if (newpath.m_Prefix == _T("."))
-						return false;
-
-					if (newpath.m_Segments.empty())
-						return false;
-					subdir = newpath.m_Segments.back();
-					newpath.m_Segments.pop_back();
-
-					int pos = subdir.Find(_MPT('('));
-					int pos2 = subdir.Find(_MPT(')'));
-					if (pos != -1)
-					{
-						if (!pos || pos2 != subdir.GetLength() - 2)
-							return false;
-						newpath.m_Segments.push_back(subdir.Left(pos));
-						subdir = subdir.Mid(pos + 1, pos2 - pos - 1);
-					}
-					else if (pos2 != -1)
-						return false;
-					else
-						newpath.m_Prefix = _T(".");
-				}
-			}
-			else if (!newpath.SetPath(subdir, bIsFile))
-				return FALSE;
-			break;
-		case FZ_SERVERTYPE_SUB_FTP_VMS:
-			{
-				int pos1=dir.Find( _T("[") );
-				if (pos1==-1)
-				{
-					int pos2=dir.ReverseFind(_MPT(']'));
-					if (pos2!=-1)
-						return FALSE;
-					if (bIsFile)
-					{
-						if (newpath.IsEmpty())
-							return FALSE;
-						subdir=dir;
-						*this=newpath;
-						return TRUE;
-					}
-					while ( dir.Replace( _T(".."), _T(".") ) );
-				}
-				else
-				{
-					int pos2=dir.ReverseFind(_MPT(']'));
-					if (pos2==-1)
-						return FALSE;
-					if (bIsFile && pos2==(dir.GetLength()-1))
-						return FALSE;
-					if (!bIsFile && pos2!=(dir.GetLength()-1))
-						return FALSE;
-					if (pos2<=pos1)
-						return FALSE;
-					if (bIsFile)
-						subdir=dir.Mid(pos2+2);
-					dir=dir.Left(pos2);
-					if (pos1)
-						newpath.m_Prefix=dir.Left(pos1);
-					else
-						newpath.m_Prefix=_MPT("");
-					newpath.m_Segments.clear();
-					dir=dir.Mid(pos1+1);
-					pos1=dir.Find( _T("[") );
-					pos2=dir.Find( _T("]") );
-					if (pos1!=-1 || pos2!=-1)
-						return FALSE;
-				}
-				int pos=dir.Find( _T(".") );
-				while(pos!=-1)
-				{
-					newpath.m_Segments.push_back(dir.Left(pos));
-					dir=dir.Mid(pos+1);
-					pos=dir.Find( _T(".") );
-				}
-				if (dir!=_MPT(""))
-					newpath.m_Segments.push_back(dir);
-			}
-			break;
-		case FZ_SERVERTYPE_SUB_FTP_WINDOWS:
-			{
-				dir.Replace( _T("\\"), _T("/") );
-				while(dir.Replace( _T("//"), _T("/") ));
-				if (dir.GetLength() >= 2 && dir[1] == _MPT(':'))
-					newpath.m_Segments.clear();
-				else if (dir[0]==_MPT('/'))
-				{
-					CString firstSegment;
-					if (newpath.m_Segments.empty())
-						firstSegment = _MPT("C:");
-					else
-						firstSegment = newpath.m_Segments.front();
-					newpath.m_Segments.clear();
-					newpath.m_Segments.push_back(firstSegment);
-					dir.TrimLeft( _T("/") );
-				}
-
-				if (newpath.IsEmpty())
-					return FALSE;
-	
-				if (dir.Right(1)==_T("/") && bIsFile)
-					return FALSE;
-				dir.TrimRight( _T("/") );
-
-				int pos=dir.ReverseFind(_MPT('/'));
-				if (bIsFile)
-					if (pos==-1)
-					{
-						subdir=dir;
-						newpath.m_bEmpty=FALSE;
-						*this=newpath;
-						return TRUE;
-					}
-					else
-					{
-						subdir=dir.Mid(pos+1);
-						dir=dir.Left(pos);
-						dir.TrimRight( _T("/") );
-					}
-			
-				pos=dir.Find( _T("/") );
-				while(pos!=-1)
-				{
-					newpath.m_Segments.push_back(dir.Left(pos));
-					dir=dir.Mid(pos+1);
-					pos=dir.Find( _T("/") );
-				}
-				if (dir!=_MPT(""))
-					newpath.m_Segments.push_back(dir);
-				break;
-			}
-		case FZ_SERVERTYPE_SUB_FTP_UNKNOWN:
-			dir.Replace(_MPT('.'), _MPT('/'));
-			dir = _T("/") + dir;
-		default:
-			dir.Replace( _T("\\"), _T("/") );
-			while(dir.Replace( _T("//"), _T("/") ));
-			if (dir[0]==_MPT('/'))
-			{
-				newpath.m_Segments.clear();
-				if (dir!="/")
-					dir.TrimLeft( _T("/") );
-			}
-			else
-				if (newpath.IsEmpty())
-					return FALSE;
-			if (dir.Right(1)==_T("/") && bIsFile)
-				return FALSE;
-			dir.TrimRight( _T("/") );
-
-			int pos=dir.ReverseFind(_MPT('/'));
-			if (bIsFile)
-				if (pos==-1)
-				{
-					subdir=dir;
-					newpath.m_bEmpty=FALSE;
-					*this=newpath;
-					return TRUE;
-				}
-				else
-				{
-					subdir=dir.Mid(pos+1);
-					dir=dir.Left(pos);
-					dir.TrimRight( _T("/") );
-				}
-			
-			pos=dir.Find( _T("/") );
-			while(pos!=-1)
-			{
-				newpath.m_Segments.push_back(dir.Left(pos));
-				dir=dir.Mid(pos+1);
-				pos=dir.Find( _T("/") );
-			}
-			if (dir!=_MPT(""))
-				newpath.m_Segments.push_back(dir);
-			break;
-		}
-		break;
-	case FZ_SERVERTYPE_LOCAL:
-		{
-			if (dir.Right(1)==_T("\\") && bIsFile)
-				return FALSE;
-			dir.TrimRight( _T("\\") );
-			while (dir.Replace( _T("\\\\"), _T("\\") ));
-			if ( dir.Left(1) == _T("\\") )
-				newpath.m_Segments.clear();
-			else
-				if (newpath.IsEmpty())
-					return FALSE;
-			dir.TrimLeft( _T("\\") );
-			if (bIsFile)
-			{
-				int pos=dir.ReverseFind(_MPT('\\'));
-				if (pos==-1)
-				{
-					if (dir.Find( _T(":") )!=-1)
-						return FALSE;
-					subdir=dir;
-					newpath.m_bEmpty=FALSE;
-					*this=newpath;
-					return TRUE;
-				}
-				else
-				{
-					if (dir.Find( _T(":"), pos+1)!=-1)
-						return FALSE;
-					subdir=dir.Mid(pos+1);
-					dir=dir.Left(pos);
-				}
-			}
-			int pos=dir.Find( _T(":") );
-		
-			if (pos==1) //dir is absolute path
-			{
-				newpath.m_Segments.clear();
-				newpath.m_Prefix=dir.Left(pos+1);
-				dir=dir.Mid(pos+1);
-				dir.TrimLeft( _T("\\") );
-				if (dir.Find( _T(":") )!=-1)
-					return FALSE;
-			}
-			if (pos==-1 || pos==1)
-			{
-				pos=dir.Find( _T("\\") );
-				while (pos!=-1)
-				{
-					newpath.m_Segments.push_back(dir.Left(pos));
-					dir=dir.Mid(pos+1);
-					pos=dir.Find( _T("\\") );
-				}
-				if (dir!=_MPT(""))
-					newpath.m_Segments.push_back(dir);
-			}
-			else
-				return FALSE;
-		}
-		break;
-	default:
-		return FALSE;
-	}
-	newpath.m_bEmpty=FALSE;
-	*this=newpath;
-	return TRUE;
-}
-
 BOOL CServerPath::SetPath(CString newpath)
 {
 	return SetPath(newpath, FALSE);
 }
 
-const bool CServerPath::MatchNoCase(const CServerPath &op) const
-{
-	if (this==&op)
-		return TRUE;
-
-	if (m_bEmpty!=op.m_bEmpty)
-		return FALSE;
-	if (m_Prefix!=op.m_Prefix)
-		return FALSE;
-	if (m_nServerType!=op.m_nServerType)
-		return FALSE;
-	tConstIter iter1=m_Segments.begin();
-	tConstIter iter2=op.m_Segments.begin();
-	while (iter1!=m_Segments.end())
-	{
-		if (iter2==op.m_Segments.end())
-			return FALSE;
-		CString Segment1=*iter1;
-		CString Segment2=*iter2;
-		Segment1.MakeLower();
-		Segment2.MakeLower();
-		if (Segment1!=Segment2)
-			return FALSE;
-		iter1++;
-		iter2++;
-	}
-	if (iter2!=op.m_Segments.end())
-		return FALSE;
-	return TRUE;
-}
-
 CString CServerPath::FormatFilename(CString fn, bool omitPath /*=false*/) const
 {
 	if (m_bEmpty)

+ 0 - 8
source/filezilla/ServerPath.h

@@ -31,13 +31,8 @@ class CServerPath
 {
 public:
 	BOOL AddSubdir(CString subdir);
-	BOOL AddSubdirs(CString subdirs);
-	CString GetSubdirsOf(const CServerPath &path) const;
 	CString GetSafePath() const;
-	BOOL SetSafePath(CString path);
 	const BOOL IsEmpty() const;
-	BOOL IsParentOf(const CServerPath &path, BOOL bCompareNoCase = FALSE) const;
-	BOOL IsSubdirOf(const CServerPath &path, BOOL bCompareNoCase = FALSE) const;
 	CServerPath GetParent() const;
 	BOOL HasParent() const;
 	CString GetLastSegment() const;
@@ -53,11 +48,8 @@ public:
 	void SetServer(const t_server &server);
 	BOOL SetPath(CString &newpath, BOOL bIsFile);
 	BOOL SetPath(CString newpath);
-	BOOL ChangePath(CString &subdir, BOOL bIsFile = FALSE);
 	const CString GetPath() const;
 
-	const bool MatchNoCase(const CServerPath &op) const;
-
 	CServerPath& operator=(const CServerPath &op);
 
 	const bool operator == (const CServerPath &op) const;

+ 2 - 2
source/forms/About.dfm

@@ -3611,7 +3611,7 @@ object AboutDialog: TAboutDialog
       Top = 325
       Width = 194
       Height = 13
-      Caption = 'Copyright '#169' 2001-2005 Alex A. Denisov'
+      Caption = 'Copyright '#169' 2001-2005 Alex A. Denisov and contributors'
     end
     object FileZillaVersionLabel: TLabel
       Left = 8
@@ -3679,7 +3679,7 @@ object AboutDialog: TAboutDialog
       Top = 341
       Width = 146
       Height = 17
-      Caption = 'http://indasoftware.com/tbx/'
+      Caption = 'https://github.com/plashenkov/TBX'
       TabOrder = 5
       TabStop = True
     end

+ 76 - 5
source/forms/Custom.cpp

@@ -14,6 +14,7 @@
 #include <PasTools.hpp>
 #include <ProgParams.h>
 #include <Tools.h>
+#include <HistoryComboBox.hpp>
 
 #include "Custom.h"
 //---------------------------------------------------------------------
@@ -28,6 +29,8 @@ __fastcall TCustomDialog::TCustomDialog(UnicodeString AHelpKeyword)
   UseSystemSettings(this);
 
   FPos = ScaleByTextHeight(this, 8);
+  FHorizontalMargin = ScaleByTextHeight(this, 8);
+  FIndent = FHorizontalMargin;
 
   HelpKeyword = AHelpKeyword;
 
@@ -98,6 +101,16 @@ bool __fastcall TCustomDialog::CloseQuery()
   return TForm::CloseQuery();
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomDialog::AddImage(const UnicodeString & ImageName)
+{
+  TImage * Image = new TImage(this);
+  Image->Name = L"Image";
+  Image->Parent = this;
+  LoadResourceImage(Image, ImageName);
+  Image->SetBounds(FIndent, FPos + ScaleByTextHeight(this, 3), 32, 32);
+  FIndent += Image->Width + ScaleByTextHeight(this, 12);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomDialog::AddWinControl(TWinControl * Control)
 {
   Control->TabOrder = FCount;
@@ -115,14 +128,14 @@ void __fastcall TCustomDialog::AddEditLikeControl(TWinControl * Edit, TLabel * L
 {
   int PrePos = FPos;
   Label->Parent = this;
-  Label->Left = ScaleByTextHeight(this, 8);
+  Label->Left = FIndent;
   Label->Top = FPos;
   FPos += Label->Height + ScaleByTextHeight(this, 4);
 
   Edit->Parent = this;
-  Edit->Left = ScaleByTextHeight(this, 8);
+  Edit->Left = FIndent;
   Edit->Top = FPos;
-  Edit->Width = ClientWidth - (Edit->Left * 2);
+  Edit->Width = ClientWidth - Edit->Left - FHorizontalMargin;
   // this updates Height property to real value
   Edit->HandleNeeded();
   FPos += Edit->Height + ScaleByTextHeight(this, 8);
@@ -167,9 +180,9 @@ void __fastcall TCustomDialog::AddButtonControl(TButtonControl * Control)
 {
   int PrePos = FPos;
   Control->Parent = this;
-  Control->Left = ScaleByTextHeight(this, 14);
+  Control->Left = FIndent + ScaleByTextHeight(this, 6);
   Control->Top = FPos;
-  Control->Width = ClientWidth - Control->Left - ScaleByTextHeight(this, 8);
+  Control->Width = ClientWidth - Control->Left - FHorizontalMargin;
   // this updates Height property to real value
   Control->HandleNeeded();
   // buttons do not scale with text on their own
@@ -386,6 +399,10 @@ TSessionData * __fastcall DoSaveSession(TSessionData * SessionData,
       StoredSessions->NewSession(SessionName, SessionData);
     // modified only, explicit
     StoredSessions->Save(false, true);
+    if (!SessionData->HostKey.IsEmpty())
+    {
+      SessionData->CacheHostKeyIfNotCached();
+    }
 
     if (CreateShortcut)
     {
@@ -582,3 +599,57 @@ bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
   }
   return Result;
 }
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TRemoteMoveDialog : public TCustomDialog
+{
+public:
+  __fastcall TRemoteMoveDialog();
+
+  bool __fastcall Execute(UnicodeString & Target, UnicodeString & FileMask);
+
+protected:
+  DYNAMIC void __fastcall DoShow();
+
+private:
+  THistoryComboBox * Combo;
+};
+//---------------------------------------------------------------------------
+__fastcall TRemoteMoveDialog::TRemoteMoveDialog() :
+  TCustomDialog(HELP_REMOTE_MOVE)
+{
+  Caption = LoadStr(REMOTE_MOVE_TITLE);
+
+  AddImage(L"REMOTE_MOVE_FILE");
+
+  Combo = new THistoryComboBox(this);
+  Combo->AutoComplete = false;
+  AddComboBox(Combo, CreateLabel(LoadStr(REMOTE_TRANSFER_PROMPT)));
+}
+//---------------------------------------------------------------------------
+bool __fastcall TRemoteMoveDialog::Execute(UnicodeString & Target, UnicodeString & FileMask)
+{
+  Combo->Items = CustomWinConfiguration->History[L"RemoteTarget"];
+  Combo->Text = UnixIncludeTrailingBackslash(Target) + FileMask;
+  bool Result = TCustomDialog::Execute();
+  if (Result)
+  {
+    Target = UnixExtractFilePath(Combo->Text);
+    FileMask = UnixExtractFileName(Combo->Text);
+    Combo->SaveToHistory();
+    CustomWinConfiguration->History[L"RemoteTarget"] = Combo->Items;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TRemoteMoveDialog::DoShow()
+{
+  TCustomDialog::DoShow();
+  InstallPathWordBreakProc(Combo);
+}
+//---------------------------------------------------------------------------
+bool __fastcall DoRemoteMoveDialog(UnicodeString & Target, UnicodeString & FileMask)
+{
+  std::auto_ptr<TRemoteMoveDialog> Dialog(new TRemoteMoveDialog());
+  return Dialog->Execute(Target, FileMask);
+}

+ 3 - 0
source/forms/Custom.h

@@ -18,6 +18,8 @@ __published:
 
 private:
   int FPos;
+  int FIndent;
+  int FHorizontalMargin;
   short FCount;
 
   void __fastcall Change(TObject * Sender);
@@ -39,6 +41,7 @@ public:
   void __fastcall AddEdit(TCustomEdit * Edit, TLabel * Label);
   void __fastcall AddComboBox(TCustomCombo * Combo, TLabel * Label);
   void __fastcall AddButtonControl(TButtonControl * Control);
+  void __fastcall AddImage(const UnicodeString & ImageName);
 
   bool __fastcall Execute();
 };

+ 73 - 3
source/forms/Editor.cpp

@@ -59,6 +59,59 @@ void __fastcall ReconfigureEditorForm(TForm * Form)
   Editor->ApplyConfiguration();
 }
 //---------------------------------------------------------------------------
+class TPreambleFilteringFileStream : public TFileStream
+{
+public:
+  __fastcall TPreambleFilteringFileStream(const UnicodeString AFileName, System::Word Mode,
+    TEncoding * Encoding, bool AllowPreamble);
+  virtual int __fastcall Write(const void * Buffer, int Count);
+private:
+  TBytes FPreamble;
+  bool FDisallowPreamble;
+};
+//---------------------------------------------------------------------------
+__fastcall TPreambleFilteringFileStream::TPreambleFilteringFileStream(
+    const UnicodeString AFileName, System::Word Mode,
+    TEncoding * Encoding, bool AllowPreamble) :
+  TFileStream(AFileName, Mode)
+{
+  FDisallowPreamble = (Encoding != NULL) && !AllowPreamble;
+  if (FDisallowPreamble)
+  {
+    FPreamble = Encoding->GetPreamble();
+  }
+}
+//---------------------------------------------------------------------------
+int __fastcall TPreambleFilteringFileStream::Write(const void * Buffer, int Count)
+{
+  bool IsDisallowedPreamble = false;
+  if (FDisallowPreamble && (Count > 0) && (FPreamble.Length == Count))
+  {
+    int Index = 0;
+    IsDisallowedPreamble = true;
+    const unsigned char * ByteBuffer = reinterpret_cast<const unsigned char *>(Buffer);
+    while (IsDisallowedPreamble && (Index < Count))
+    {
+      IsDisallowedPreamble = (ByteBuffer[Index] == FPreamble[Index]);
+      Index++;
+    }
+  }
+
+  // only on the first write
+  FDisallowPreamble = false;
+
+  int Result;
+  if (IsDisallowedPreamble)
+  {
+    Result = Count;
+  }
+  else
+  {
+    Result = TFileStream::Write(Buffer, Count);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 class TRichEdit20 : public TRichEdit
 {
 public:
@@ -76,6 +129,7 @@ public:
 
   __property bool SupportsUpSearch = { read = FVersion20 };
   __property bool CanRedo = { read = GetCanRedo };
+  __property bool LoadedWithPreamble = { read = FLoadedWithPreamble };
 
 protected:
   friend unsigned long __stdcall StreamLoad(DWORD_PTR Cookie, unsigned char * Buff, long Read, long * WasRead);
@@ -98,6 +152,7 @@ private:
   bool FInitialized;
   bool FStreamLoadEncodingError;
   bool FStreamLoadError;
+  bool FLoadedWithPreamble;
 };
 //---------------------------------------------------------------------------
 __fastcall TRichEdit20::TRichEdit20(TComponent * AOwner) :
@@ -106,7 +161,8 @@ __fastcall TRichEdit20::TRichEdit20(TComponent * AOwner) :
   FVersion20(false),
   FTabSize(0),
   FWordWrap(true),
-  FInitialized(false)
+  FInitialized(false),
+  FLoadedWithPreamble(false)
 {
 }
 //---------------------------------------------------------------------------
@@ -470,9 +526,14 @@ bool __stdcall TRichEdit20::StreamLoad(
             }
             // If Unicode preamble is present, set StartIndex to skip over it
             TBytes Preamble = TEncoding::Unicode->GetPreamble();
-            if ((WasRead >= 2) && (Buffer[0] == Preamble[0]) && (Buffer[1] == Preamble[1]))
+            if (ALWAYS_TRUE(Preamble.Length == 2) &&
+                (WasRead >= 2) && (Buffer[0] == Preamble[0]) && (Buffer[1] == Preamble[1]))
             {
               StartIndex = 2;
+              // beware that this is also called from CreateWnd with some
+              // dummy contents that always have BOM, so FLoadedWithPreamble
+              // is true, unless overriden by LoadFromStream
+              FLoadedWithPreamble = true;
             }
           }
         }
@@ -501,6 +562,7 @@ bool __fastcall TRichEdit20::LoadFromStream(TStream * Stream, TEncoding * Encodi
 {
   FStreamLoadEncodingError = false;
   FStreamLoadError = false;
+  FLoadedWithPreamble = false;
   Lines->LoadFromStream(Stream, Encoding);
   if (FStreamLoadError)
   {
@@ -716,6 +778,14 @@ void __fastcall TEditorForm::EditorActionsUpdate(TBasicAction *Action,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TEditorForm::SaveToFile()
+{
+  std::auto_ptr<TStream> Stream(
+    new TPreambleFilteringFileStream(
+      FFileName, fmCreate, FEncoding, EditorMemo->LoadedWithPreamble));
+  EditorMemo->Lines->SaveToStream(Stream.get(), FEncoding);
+}
+//---------------------------------------------------------------------------
 void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
       bool &Handled)
 {
@@ -723,7 +793,7 @@ void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
   if (Action == SaveAction)
   {
     assert(!FFileName.IsEmpty());
-    EditorMemo->Lines->SaveToFile(FFileName, FEncoding);
+    SaveToFile();
     if (FOnFileChanged)
     {
       FOnFileChanged(this);

+ 1 - 0
source/forms/Editor.h

@@ -133,6 +133,7 @@ protected:
   void __fastcall ChangeEncoding(TEncoding * Encoding);
   void __fastcall InitCodePage();
   UnicodeString __fastcall GetCodePageName(TEncoding * Encoding);
+  void __fastcall SaveToFile();
   void __fastcall BackupSave();
 };
 //---------------------------------------------------------------------------

+ 38 - 120
source/forms/Glyphs.dfm

@@ -7989,107 +7989,25 @@ object GlyphsModule: TGlyphsModule
         Name = 'Clone remote file to remote directory-name'
         PngImage.Data = {
           89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
-          61000000097048597300000EC400000EC401952B0E1B00000A4F694343505068
-          6F746F73686F70204943432070726F66696C65000078DA9D53675453E9163DF7
-          DEF4424B8880944B6F5215082052428B801491262A2109104A8821A1D91551C1
-          114545041BC8A088038E8E808C15512C0C8A0AD807E421A28E83A3888ACAFBE1
-          7BA36BD6BCF7E6CDFEB5D73EE7ACF39DB3CF07C0080C9648335135800CA9421E
-          11E083C7C4C6E1E42E40810A2470001008B3642173FD230100F87E3C3C2B22C0
-          07BE000178D30B0800C04D9BC0301C87FF0FEA42995C01808401C07491384B08
-          801400407A8E42A600404601809D98265300A0040060CB6362E300502D006027
-          7FE6D300809DF8997B01005B94211501A09100201365884400683B00ACCF568A
-          450058300014664BC43900D82D00304957664800B0B700C0CE100BB200080C00
-          305188852900047B0060C8232378008499001446F2573CF12BAE10E72A000078
-          99B23CB9243945815B082D710757572E1E28CE49172B14366102619A402EC279
-          99193281340FE0F3CC0000A0911511E083F3FD78CE0EAECECE368EB60E5F2DEA
-          BF06FF226262E3FEE5CFAB70400000E1747ED1FE2C2FB31A803B06806DFEA225
-          EE04685E0BA075F78B66B20F40B500A0E9DA57F370F87E3C3C45A190B9D9D9E5
-          E4E4D84AC4425B61CA577DFE67C25FC057FD6CF97E3CFCF7F5E0BEE22481325D
-          814704F8E0C2CCF44CA51CCF92098462DCE68F47FCB70BFFFC1DD322C44962B9
-          582A14E35112718E449A8CF332A52289429229C525D2FF64E2DF2CFB033EDF35
-          00B06A3E017B912DA85D6303F64B27105874C0E2F70000F2BB6FC1D428080380
-          6883E1CF77FFEF3FFD47A02500806649927100005E44242E54CAB33FC7080000
-          44A0812AB0411BF4C1182CC0061CC105DCC10BFC6036844224C4C24210420A64
-          801C726029AC82422886CDB01D2A602FD4401D34C051688693700E2EC255B80E
-          3D700FFA61089EC128BC81090441C808136121DA8801628A58238E08179985F8
-          21C14804128B2420C9881451224B91354831528A542055481DF23D720239875C
-          46BA913BC8003282FC86BC47319481B2513DD40CB543B9A8371A8446A20BD064
-          74319A8F16A09BD072B41A3D8C36A1E7D0AB680FDA8F3E43C730C0E8180733C4
-          6C302EC6C342B1382C099363CBB122AC0CABC61AB056AC03BB89F563CFB17704
-          128145C0093604774220611E4148584C584ED848A8201C243411DA0937090384
-          51C2272293A84BB426BA11F9C4186232318758482C23D6128F132F107B8843C4
-          37241289433227B9900249B1A454D212D246D26E5223E92CA99B34481A2393C9
-          DA646BB20739942C202BC885E49DE4C3E433E41BE421F25B0A9D624071A4F853
-          E22852CA6A4A19E510E534E5066598324155A39A52DDA8A15411358F5A42ADA1
-          B652AF5187A81334759A39CD8316494BA5ADA295D31A681768F769AFE874BA11
-          DD951E4E97D057D2CBE947E897E803F4770C0D861583C7886728199B18071867
-          197718AF984CA619D38B19C754303731EB98E7990F996F55582AB62A7C1591CA
-          0A954A9526951B2A2F54A9AAA6AADEAA0B55F355CB548FA95E537DAE46553353
-          E3A909D496AB55AA9D50EB531B5367A93BA887AA67A86F543FA47E59FD890659
-          C34CC34F43A451A0B15FE3BCC6200B6319B3782C216B0DAB86758135C426B1CD
-          D97C762ABB98FD1DBB8B3DAAA9A13943334A3357B352F394663F07E39871F89C
-          744E09E728A797F37E8ADE14EF29E2291BA6344CB931655C6BAA96979658AB48
-          AB51AB47EBBD36AEEDA79DA6BD45BB59FB810E41C74A275C2747678FCE059DE7
-          53D953DDA70AA7164D3D3AF5AE2EAA6BA51BA1BB4477BF6EA7EE989EBE5E809E
-          4C6FA7DE79BDE7FA1C7D2FFD54FD6DFAA7F5470C5806B30C2406DB0CCE183CC5
-          35716F3C1D2FC7DBF151435DC34043A561956197E18491B9D13CA3D5468D460F
-          8C69C65CE324E36DC66DC6A326062621264B4DEA4DEE9A524DB9A629A63B4C3B
-          4CC7CDCCCDA2CDD699359B3D31D732E79BE79BD79BDFB7605A785A2CB6A8B6B8
-          6549B2E45AA659EEB6BC6E855A3959A558555A5DB346AD9DAD25D6BBADBBA711
-          A7B94E934EAB9ED667C3B0F1B6C9B6A9B719B0E5D806DBAEB66DB67D61676217
-          67B7C5AEC3EE93BD937DBA7D8DFD3D070D87D90EAB1D5A1D7E73B472143A563A
-          DE9ACE9CEE3F7DC5F496E92F6758CF10CFD833E3B613CB29C4699D539BD34767
-          1767B97383F3888B894B82CB2E973E2E9B1BC6DDC8BDE44A74F5715DE17AD2F5
-          9D9BB39BC2EDA8DBAFEE36EE69EE87DC9FCC349F299E593373D0C3C843E051E5
-          D13F0B9F95306BDFAC7E4F434F8167B5E7232F632F9157ADD7B0B7A577AAF761
-          EF173EF63E729FE33EE33C37DE32DE595FCC37C0B7C8B7CB4FC36F9E5F85DF43
-          7F23FF64FF7AFFD100A78025016703898141815B02FBF87A7C21BF8E3F3ADB65
-          F6B2D9ED418CA0B94115418F82AD82E5C1AD2168C8EC90AD21F7E798CE91CE69
-          0E85507EE8D6D00761E6618BC37E0C2785878557863F8E7088581AD131973577
-          D1DC4373DF44FA449644DE9B67314F39AF2D4A352A3EAA2E6A3CDA37BA34BA3F
-          C62E6659CCD5589D58496C4B1C392E2AAE366E6CBEDFFCEDF387E29DE20BE37B
-          17982FC85D7079A1CEC2F485A716A92E122C3A96404C884E3894F041102AA816
-          8C25F21377258E0A79C21DC267222FD136D188D8435C2A1E4EF2482A4D7A92EC
-          91BC357924C533A52CE5B98427A990BC4C0D4CDD9B3A9E169A76206D323D3ABD
-          31839291907142AA214D93B667EA67E66676CBAC6585B2FEC56E8BB72F1E9507
-          C96BB390AC05592D0AB642A6E8545A28D72A07B267655766BFCD89CA3996AB9E
-          2BCDEDCCB3CADB90379CEF9FFFED12C212E192B6A5864B572D1D58E6BDAC6A39
-          B23C7179DB0AE315052B865606AC3CB88AB62A6DD54FABED5797AE7EBD267A4D
-          6B815EC1CA82C1B5016BEB0B550AE5857DEBDCD7ED5D4F582F59DFB561FA869D
-          1B3E15898AAE14DB1797157FD828DC78E51B876FCABF99DC94B4A9ABC4B964CF
-          66D266E9E6DE2D9E5B0E96AA97E6970E6E0DD9DAB40DDF56B4EDF5F645DB2F97
-          CD28DBBB83B643B9A3BF3CB8BC65A7C9CECD3B3F54A454F454FA5436EED2DDB5
-          61D7F86ED1EE1B7BBCF634ECD5DB5BBCF7FD3EC9BEDB5501554DD566D565FB49
-          FBB3F73FAE89AAE9F896FB6D5DAD4E6D71EDC703D203FD07230EB6D7B9D4D51D
-          D23D54528FD62BEB470EC71FBEFE9DEF772D0D360D558D9CC6E223704479E4E9
-          F709DFF71E0D3ADA768C7BACE107D31F761D671D2F6A429AF29A469B539AFB5B
-          625BBA4FCC3ED1D6EADE7AFC47DB1F0F9C343C59794AF354C969DAE982D39367
-          F2CF8C9D959D7D7E2EF9DC60DBA2B67BE763CEDF6A0F6FEFBA1074E1D245FF8B
-          E73BBC3BCE5CF2B874F2B2DBE51357B8579AAF3A5F6DEA74EA3CFE93D34FC7BB
-          9CBB9AAEB95C6BB9EE7ABDB57B66F7E91B9E37CEDDF4BD79F116FFD6D59E393D
-          DDBDF37A6FF7C5F7F5DF16DD7E7227FDCECBBBD97727EEADBC4FBC5FF440ED41
-          D943DD87D53F5BFEDCD8EFDC7F6AC077A0F3D1DC47F7068583CFFE91F58F0F43
-          058F998FCB860D86EB9E383E3939E23F72FDE9FCA743CF64CF269E17FEA2FECB
-          AE17162F7EF8D5EBD7CED198D1A197F29793BF6D7CA5FDEAC0EB19AFDBC6C2C6
-          1EBEC97833315EF456FBEDC177DC771DEFA3DF0F4FE47C207F28FF68F9B1F553
-          D0A7FB93199393FF040398F3FC63332DDB000002144944415478DA85925F4853
-          511CC7BF67732B22427AEB6959CF99B916110A41E5E64348F46FBB62524905BD
-          C50A714A285144F416911B954811D28AA8D646C2A8AEA8DBA2D2B1EDC1285D21
-          5A6B9ADE7BB7DDBBBBD3658475E9DEF5850387DFF9F1391FCEEF9033D79F5268
-          868C1A56E7EC37CF1EE15021E4F4B527F496BB0562F10F87528A91F80CFCE1C9
-          51C31AB122849CBAFA98F65F38809C54521D2C713924A6BF6368F8FD3B184D76
-          AF7B7F4613D071C54FBD9D07E1F6BE5A29F6B63762ED2A23BE2DF2482A107F78
-          2275E35C8BEDF7B1ACAC0221A4AC4C4E5E7EA8000E81CBCB2A72702C05B3C9F8
-          CF8D79519E128ACB3B3B1CBBB265C0894B43D4D77518E77DAF558DB6CDEBE1DC
-          530BB9F4F7DB00D3730B88A5BE4C09B2508690E37D0FA8D773143F05B5C1F078
-          02CEBD5B21C9EA21154409F30B3CA2C9F40C2F9106D2DE7B9FFA3C2E74DE561B
-          6CAFA986735F1D02914F2BB526EB4698AB0CE07222E6B21C62C9F42C3976F11E
-          F57533C8F24515201C49C0A5000A45F574667F2C233DBF54DE67B28B206D3D83
-          D4DBD30ACFDD37AAC67ACB3A304DDB10887ED69CFFA60DD5487EFC0AD2DA3DA0
-          00DA90E1D4066C340E97BD1E7949FBA34A8AD9CBB10408D37547B3A3B9618B02
-          B02218D336D85D67C10B360E029D0C0662947158218825BD1604D9C90A80E711
-          EA72D8107AAB6DD0586B41889DD0070C3C1BA74CF30EF0057D83D0C8077D40FF
-          23969A4D55F85F7E01B224EFDBB2AC2CDA0000000049454E44AE426082}
+          61000000097048597300000EC300000EC301C76FA8640000021A4944415478DA
+          63CCE8DDF49F012B603CC6C4F1DD7D5A76D817063C8031BD7BC3FF1925FE0CBF
+          FE20CCF9FFFF3FC3E14B0F19D6EEBF748C89EB175E4318D33AD7FD9F5916C8F0
+          FDF73F14894F5FBE335C7DF08A61E5EE8B671898593C6795F8BEC16A406AC7DA
+          FF33CB83184A661D800B36C6DB32F0B03333BC7CFF95E1EAFD570C2B769FBF3A
+          BB225807AB01C96DABFFCFAA0861F8F2E32F8A44F9F46DB81C8D12368C492D2B
+          FFCFAE0A65289D7D1045D9D74F1F1988091BC6C4A6E5FF675587337CFC0671C1
+          D575310C7F7E7E62D8FC2E90A1BF3C9560D830C6372EFD3FBB3A92A1622EC405
+          0EAFE319F4CC4419CE9FF9CA60153E83A1730FC205C86173051436BBCE5D658C
+          AB5FF27F764D14C3BBAF7FC08A4E4F5161F0895064F8F0E13FC39943CF18A48C
+          0B19644DD3E1869CBFF382E1F8B5A760F69D7B8F1818E737D9FC17667A84E24C
+          90018C2C1C0C3F7F33319CDEFF90E1D6470D861BBCA90C7F1939E06AECF4E418
+          36ED3DC3C0B8A945EEBF8F071303035003030B3B94E6001B00E2FF636267B87C
+          EC21C3FB8F920CBA616BE0067CFFF987A16EDA067C0640D83003AEDCE5613827
+          508B88E6486B86B2FED52003648F0363D302D30BEC402F3083BDC0C06BC3A0E4
+          D2C5C0C4CA859A56FA573130A22713B08B2214A081F89CE11A6300C3232E6F8C
+          E4541466C550D9B712BB017A66220CE74F7F65300E9EC1F05FD48A0117A8EA5F
+          8ECD00D9FD402FF16DF99E67F4E5BF1003210000D951FF45EB3BE85800000000
+          49454E44AE426082}
       end
       item
         Background = clWindow
@@ -10282,25 +10200,25 @@ object GlyphsModule: TGlyphsModule
         Name = 'Move To'
         PngImage.Data = {
           89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
-          61000000097048597300000EC300000EC301C76FA864000002254944415478DA
-          8D915D48536118C7FFEF3967CD326F82886E625D14845677DD155D045D44F4E5
-          07694B4AD18DAE8A153627611FEAB07657D4961661E85267AEBBC448927913C9
-          4A43616BBAE5CC9D6126DBCEDC876FE72C749D76341F78E085E7797FFC9EE721
-          BA074E0AC5202E264F38FEE84A6904EB04A96D7D4D1F1B4E2191CA7228A518FE
-          328D9E779F5DCC96C4BA10526376D02737CE406F79A3D820B2A21CD8C2878613
-          D38A80EAE61E6AAD3B07FDFD7EAC65F26AD01DE3F2933B944C485553B7082886
-          BEB50F9289905C96352C46048C4FF1B00F8CBAC1AA8E590D27C332C0E5BB766A
-          3396A0D6EC806462B0BE5F2D36561EC656358BB99F518CF942E87AFB69FCE9CD
-          E22219E0D2ED4E6AAD2F83CEDC0BC924124FCB0C463D3F30F27526F3F67CF3A3
-          CD58426480CAC697D4567F1EBA966E4826D76D438ACB3C7260179C831FD16E2A
-          93032EDEEAA036533974CD764826BF62E99CCF35161716A2091430F3E86FF907
-          A06D7841AD0D1522A00B92495D5BAE81637801BBF7EE47D0EF41222EF4321CB9
-          EA7D763A900154989E8B002DF44D9D904C4AEF7D80DB3B9F033978E8285806E0
-          67039809F862CB347D275F485948B9B13D737896E52099ECAB7262FB4E0D54EA
-          CDD824A64A9D0715C789C9FC4996453AB5049F67123C3F37B23ACF8A4961F5FF
-          01A9645C044C201CE6B3801593B150011697B835470805FD087E9FCA8E800D84
-          46DB47357B8A301BF0E62E7143800B8E214AC83686D26BBE8EB3037FD77E0343
-          D207643B8752680000000049454E44AE426082}
+          61000000097048597300000EC300000EC301C76FA8640000022E4944415478DA
+          85D34F68D3501C07F06FD2F5CFCA0485A278B0A8171D8CA2C2864CA727A9870E
+          05715451DB0574C286A088450FA2E069C80AA2D5CBC09B4E1D4A110F1637D1A6
+          3BD8AD5BBBA848B7D942A70EB153DB264DDAC6F4B56666B6DBBB24BFF77EBFCF
+          7BBF47420D0EDE97517350214168B4F7F67665B0C2A0CA00C3B8502C16D54959
+          9631333307961D5B1551014992340BA22821959A4730C84E140A067B4FCFF1EF
+          2B0253533175B2B9791B0C0603B2D91C415896E518C6DD5217E8EE3E85BC286A
+          16CEDF7959EFD021DAC4DB7DD5B654201A9DD6A4DD0DCCE2DEC543100B4B775C
+          BE9B602C812723D1106D16094200B7FB2404412049916106129F863F7D04039E
+          33E0A59206FE95E1C17D5EC0506072023ABD9D002E05988E712421F9DC015B9B
+          0593E12C761FF5A17F84568BAFBB3AD064D4E15B3A0B6E6E010F0311AE02B84E
+          2097CB91A4D7B75AE0706EC1E2A28CF09B796CDC790ED6B6B34AB3152812FF8A
+          B1F729F21E9F4D821AEEEF90F5624273CC32403598909768BC1B4DE0D3CFEDF8
+          B8E6348A9449CDD967B3C2FF2A0CCA7FC32A3B0E2ABA52800663F5692240392E
+          D146C44209FC48AF87CDF95405F87C01577DCFFE05968A2B4025FE0B70F1468C
+          AFBBA6029E637B70C9FB98006F9578EFFF2D18951674A40534B563EB819BA0F5
+          664DAB1EEF2350CB3F137222E7E6EA257EC107AA13497327E465A917BADA7179
+          60A836606BB520329EC1AEC3B7810DFBEBFD47B8E27D500BD834AAECB6F685D0
+          B7E377C982D5C61FBAC105578FDB1D550000000049454E44AE426082}
       end
       item
         Background = clWindow

+ 1 - 0
source/forms/Login.dfm

@@ -595,6 +595,7 @@ object LoginDialog: TLoginDialog
       Category = 'Session'
       Caption = 'Open in &PuTTY'
       ImageIndex = 1
+      ShortCut = 16464
       OnExecute = PuttyActionExecute
     end
   end

+ 1 - 3
source/forms/MessageDlg.cpp

@@ -980,9 +980,7 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
     Image->Parent = Panel;
     if (!ImageName.IsEmpty())
     {
-      std::unique_ptr<TPngImage> Png(new TPngImage());
-      Png->LoadFromResourceName(0, ImageName);
-      Image->Picture->Assign(Png.get());
+      LoadResourceImage(Image, ImageName);
     }
     else
     {

+ 0 - 1
source/forms/NonVisual.dfm

@@ -1341,7 +1341,6 @@ object NonVisualDataModule: TNonVisualDataModule
       Hint = 
         'Exit application|Terminate opened session(s) and close applicati' +
         'on'
-      ImageIndex = 61
     end
     object OpenedSessionsAction: TAction
       Tag = 15

+ 33 - 8
source/forms/Preferences.cpp

@@ -96,6 +96,8 @@ __fastcall TPreferencesDialog::TPreferencesDialog(
 
   PuttyRegistryStorageKeyEdit->Items->Add(OriginalPuttyRegistryStorageKey);
   PuttyRegistryStorageKeyEdit->Items->Add(KittyRegistryStorageKey);
+
+  MenuButton(RegisterAsUrlHandlersButton);
 }
 //---------------------------------------------------------------------------
 __fastcall TPreferencesDialog::~TPreferencesDialog()
@@ -1066,6 +1068,7 @@ void __fastcall TPreferencesDialog::UpdateControls()
     // integration
     // There's no quick launch in Windows 7
     EnableControl(QuickLaunchIconButton, !::IsWin7());
+    MakeDefaultHandlerItem->Visible = IsWinVista();
 
     // languages
     LanguageChangeLabel->Visible =
@@ -1740,16 +1743,39 @@ void __fastcall TPreferencesDialog::Dispatch(void *Message)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TPreferencesDialog::RegisterAsUrlHandlerButtonClick(
+void __fastcall TPreferencesDialog::RegisterAsUrlHandlersButtonClick(
   TObject * /*Sender*/)
 {
-  if (MessageDialog(MainInstructions(LoadStr(CONFIRM_REGISTER_URL)),
-        qtConfirmation, qaYes | qaNo, HELP_REGISTER_URL) == qaYes)
+  MenuPopup(RegisterAsUrlHandlerMenu, RegisterAsUrlHandlersButton);
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::RegisterAsUrlHandlerItemClick(TObject * /*Sender*/)
+{
+  unsigned int Result =
+    MessageDialog(MainInstructions(LoadStr(CONFIRM_REGISTER_URL2)),
+      qtConfirmation, qaYes | qaNo, HELP_REGISTER_URL);
+  if (Result == qaYes)
+  {
+    RegisterForDefaultProtocols();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::UnregisterForDefaultProtocolsItemClick(TObject * /*Sender*/)
+{
+  unsigned int Result =
+    MessageDialog(MainInstructions(LoadStr(CONFIRM_UNREGISTER_URL)),
+      qtConfirmation, qaYes | qaNo, HELP_REGISTER_URL);
+  if (Result == qaYes)
   {
-    RegisterAsUrlHandler();
+    UnregisterForProtocols();
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::MakeDefaultHandlerItemClick(TObject * /*Sender*/)
+{
+  LaunchAdvancedAssociationUI();
+}
+//---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::DDExtLabelClick(TObject * Sender)
 {
   ((Sender == DDExtEnabledLabel) ? DDExtEnabledButton : DDExtDisabledButton)->
@@ -1924,7 +1950,6 @@ void __fastcall TPreferencesDialog::MasterPasswordChanged(
     Message = FMTLOAD(MASTER_PASSWORD_RECRYPT_ERRORS, (Message));
     QueryType = qtWarning;
   }
-  Message = MainInstructions(Message);
   MoreMessageDialog(
     Message, RecryptPasswordErrors, QueryType, qaOK, HELP_MASTER_PASSWORD);
 }
@@ -1951,7 +1976,7 @@ void __fastcall TPreferencesDialog::UseMasterPasswordCheckClick(
       {
         if (UseMasterPasswordCheck->Checked)
         {
-          ChangeMasterPassword(LoadStr(MASTER_PASSWORD_SET));
+          ChangeMasterPassword(LoadStr(MASTER_PASSWORD_SET2));
         }
         else
         {
@@ -1959,7 +1984,7 @@ void __fastcall TPreferencesDialog::UseMasterPasswordCheckClick(
           {
             std::unique_ptr<TStrings> RecryptPasswordErrors(new TStringList());
             WinConfiguration->ClearMasterPassword(RecryptPasswordErrors.get());
-            MasterPasswordChanged(LoadStr(MASTER_PASSWORD_CLEARED), RecryptPasswordErrors.get());
+            MasterPasswordChanged(LoadStr(MASTER_PASSWORD_CLEARED2), RecryptPasswordErrors.get());
           }
         }
       }
@@ -1977,7 +2002,7 @@ void __fastcall TPreferencesDialog::SetMasterPasswordButtonClick(
 {
   if (CanSetMasterPassword())
   {
-    ChangeMasterPassword(LoadStr(MASTER_PASSWORD_CHANGED));
+    ChangeMasterPassword(MainInstructions(LoadStr(MASTER_PASSWORD_CHANGED)));
   }
 }
 //---------------------------------------------------------------------------

+ 19 - 3
source/forms/Preferences.dfm

@@ -1380,15 +1380,15 @@ object PreferencesDialog: TPreferencesDialog
             TabOrder = 2
             OnClick = IconButtonClick
           end
-          object RegisterAsUrlHandlerButton: TButton
+          object RegisterAsUrlHandlersButton: TButton
             Left = 16
             Top = 135
             Width = 357
             Height = 25
             Anchors = [akLeft, akTop, akRight]
-            Caption = 'Register to &handle sftp:// and scp:// addresses'
+            Caption = 'Register to handle &URL addresses'
             TabOrder = 4
-            OnClick = RegisterAsUrlHandlerButtonClick
+            OnClick = RegisterAsUrlHandlersButtonClick
           end
           object AddSearchPathButton: TButton
             Left = 16
@@ -3128,4 +3128,20 @@ object PreferencesDialog: TPreferencesDialog
     TabOrder = 3
     OnClick = HelpButtonClick
   end
+  object RegisterAsUrlHandlerMenu: TPopupMenu
+    Left = 56
+    Top = 384
+    object RegisterAsUrlHandlerItem: TMenuItem
+      Caption = 'Register'
+      OnClick = RegisterAsUrlHandlerItemClick
+    end
+    object MakeDefaultHandlerItem: TMenuItem
+      Caption = 'Make WinSCP &default handler...'
+      OnClick = MakeDefaultHandlerItemClick
+    end
+    object UnregisterForDefaultProtocolsItem: TMenuItem
+      Caption = 'Unregister'
+      OnClick = UnregisterForDefaultProtocolsItemClick
+    end
+  end
 end

+ 10 - 2
source/forms/Preferences.h

@@ -18,6 +18,7 @@
 #include "HistoryComboBox.hpp"
 #include <WinInterface.h>
 #include <Vcl.Imaging.pngimage.hpp>
+#include <Vcl.Menus.hpp>
 //----------------------------------------------------------------------------
 class TCustomCommandList;
 class TEditorList;
@@ -79,7 +80,7 @@ __published:
   TPanel *LeftPanel;
   TTreeView *NavigationTree;
   TCheckBox *DeleteToRecycleBinCheck;
-  TButton *RegisterAsUrlHandlerButton;
+  TButton *RegisterAsUrlHandlersButton;
   TTabSheet *DragDropSheet;
   TGroupBox *DragDropDownloadsGroup;
   TLabel *DDExtEnabledLabel;
@@ -287,6 +288,10 @@ __published:
   TRadioButton *CommanderInterfaceButton2;
   TRadioButton *ExplorerInterfaceButton2;
   TLabel *AutoWorkspaceLabel;
+  TPopupMenu *RegisterAsUrlHandlerMenu;
+  TMenuItem *RegisterAsUrlHandlerItem;
+  TMenuItem *UnregisterForDefaultProtocolsItem;
+  TMenuItem *MakeDefaultHandlerItem;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);
@@ -308,7 +313,7 @@ __published:
           TObject *Source, int X, int Y, TDragState State, bool &Accept);
   void __fastcall NavigationTreeChange(TObject *Sender, TTreeNode *Node);
   void __fastcall PageControlChange(TObject *Sender);
-  void __fastcall RegisterAsUrlHandlerButtonClick(TObject *Sender);
+  void __fastcall RegisterAsUrlHandlersButtonClick(TObject *Sender);
   void __fastcall DDExtLabelClick(TObject *Sender);
   void __fastcall CustomCommandsViewDblClick(TObject *Sender);
   void __fastcall AddSearchPathButtonClick(TObject *Sender);
@@ -360,6 +365,9 @@ __published:
   void __fastcall LanguagesGetMoreButtonClick(TObject *Sender);
   void __fastcall CommanderClick(TObject *Sender);
   void __fastcall ExplorerClick(TObject *Sender);
+  void __fastcall RegisterAsUrlHandlerItemClick(TObject *Sender);
+  void __fastcall UnregisterForDefaultProtocolsItemClick(TObject *Sender);
+  void __fastcall MakeDefaultHandlerItemClick(TObject *Sender);
 
 private:
   TPreferencesMode FPreferencesMode;

+ 71 - 24
source/forms/RemoteTransfer.dfm

@@ -6,8 +6,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'RemoteTransferDialog'
-  ClientHeight = 183
-  ClientWidth = 330
+  ClientHeight = 179
+  ClientWidth = 368
   Color = clBtnFace
   ParentFont = True
   OldCreateOrder = False
@@ -15,39 +15,86 @@ object RemoteTransferDialog: TRemoteTransferDialog
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow
   DesignSize = (
-    330
-    183)
+    368
+    179)
   PixelsPerInch = 96
   TextHeight = 13
   object SymlinkGroup: TGroupBox
     Left = 8
     Top = 6
-    Width = 314
-    Height = 135
+    Width = 352
+    Height = 131
     Anchors = [akLeft, akTop, akRight, akBottom]
     TabOrder = 0
     DesignSize = (
-      314
-      135)
+      352
+      131)
     object SessionLabel: TLabel
-      Left = 11
-      Top = 16
+      Left = 49
+      Top = 12
       Width = 74
       Height = 13
       Caption = 'Target &session:'
       FocusControl = SessionCombo
     end
     object Label2: TLabel
-      Left = 11
-      Top = 64
+      Left = 49
+      Top = 60
       Width = 119
       Height = 13
       Caption = 'Target remote &directory:'
       FocusControl = DirectoryEdit
     end
-    object SessionCombo: TComboBox
+    object Image: TImage
       Left = 11
-      Top = 32
+      Top = 15
+      Width = 32
+      Height = 32
+      AutoSize = True
+      Picture.Data = {
+        0954506E67496D61676589504E470D0A1A0A0000000D49484452000000200000
+        00200806000000737A7AF4000000097048597300000EC300000EC301C76FA864
+        0000046D4944415478DAC5D70B4C53571807F0FF6D8BC68DF8082E668988EE99
+        A823DB32B285CD644CE5E1109C060DC9C6CC00BB8A7320733E06584031BA301E
+        96968A315B266EE8629468CCD4CD17E202F228886C5DE6C6433B68D79652DBD2
+        D7D9EDBD5A297D785B597692F6DED3F47CE777BF7BEE775A0AFF73A35C6F5BE4
+        174990E39A2C76244973E28C930628DFF036080706A1BF74ECCA6F68550E355B
+        EC24E171111E0087935B222E750D40376AC1F55ED563233C00360737406FBF06
+        EA11B31B4181A4960BE3342103F26A7F265F09E360B139B1E3D0C58003F666C5
+        413F6AC6F0888941685D885B83DD3C8AF74E280806902BBD402A444B61B23A39
+        0D9A2A00D4FA71088319CD3D833D951B972D0E09B059F223A9CA898771CC819D
+        753F051C5096BD147F0D8DE00ABD0E0CA631F7E72AB51ED59B12A890009BAACE
+        92039B1361303B380F74D24F431F0D19501BE0A417EFF91625249F268506D858
+        718648725760C4644741DDB947DF82694F043B8FDFBAC10044E58DA4266F2574
+        34804BDB53DF84C9AA1B0C40F8E54922CD4F85F69E1D85F2B37E83BD3E560D01
+        2CF875D627D82D5A3B2975830164EF3B41649FAD82C61838032DD5CFE1A598D9
+        E8ECB0E0AD750731635E2C27C0C4BA41F896951559895A3720ABEC38917EBE86
+        0188E5A7FD064AB6E622397D01FE19B6A2BD5983F96F6E87AC232AE0E413EB86
+        0BD13CAE6E30808F763710D9B634A88DB680C1DA25CF33008AC787C944D07AE9
+        0EC22353F1E2F252F0F8617EC779D70D1762A0BB52B42C9A01AC2FF98EC8B6AF
+        857AD48E6279A3DF40A98E7C37009400762785CE6B77D17BE729743E99031B15
+        EE35C65537FE54E971F5E6A0CFBAC1003E141F21B21DE9181E0D9C812ED90B1E
+        00FAB241283E940A35FA6F132C4A3988F039BE8BA1BFBAC1000E972C21B3A93E
+        4E0B6A22807D09A0EA33A0EDEA203AF8191812BCE2777C71E67218CD56ECAFBF
+        0C69DEBB2CA0B1741E494EA44F2836983BB0579F3DF706B07D83CE8296734A44
+        2CCC44546C1EFD19CF2FA4807EDC65F9290100D4C3AB9B88A17CE2D8BE95DED0
+        6E9C57A267280A8A29197060AAC7C4A5C22416507B06F2ADAB8205B0FD4000D7
+        B993F0D0DDF407B4BA39589876DC67060A6B4FA36EDB6A5F80FB8128BEC73D0E
+        1670F3DA6D286EF1D114B6C56362B130993916494FE1D0CE347F00EF45E60108
+        901DD72D68BBA0846DCA6B58105F0E5E98EF8DABA8E6240E17AC1B0748203E17
+        5C300083DE8A567A112A8CB1E8E5AD0081F7EEBC4B98721F70025F17A53F0044
+        5EA7A3BCC1F931F4717B54FD067435FF8DA7979461C6B3498F8CB34BF203BE11
+        BF4F05F50382C954FA7C1AF0303B047CFCDEAD81A2D3845F480674981B3046E1
+        C7EFB18003C7F06D4946A800F6CAD952AC82DEF20CE6C6D740302D82732C7175
+        03EAF7AC0F1D603613B45CBE0B8566313A9CA970D299E0D2BE10AD618EC55547
+        71746F6668002DBD1DB7D1DBF1F497B762E6A20F8209E16EC595F5F87E5F76F0
+        80E89808B4DF30237AB51CD69931214DCE028EA061FF86600191F4BF166AFAA9
+        7B39AF1A9DB3429EFC410B1AF05FB47F019CF95B3FF06E46AF0000000049454E
+        44AE426082}
+    end
+    object SessionCombo: TComboBox
+      Left = 49
+      Top = 28
       Width = 292
       Height = 21
       Style = csDropDownList
@@ -57,8 +104,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
       OnChange = SessionComboChange
     end
     object DirectoryEdit: THistoryComboBox
-      Left = 11
-      Top = 80
+      Left = 49
+      Top = 76
       Width = 292
       Height = 21
       AutoComplete = False
@@ -68,8 +115,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
       OnChange = ControlChange
     end
     object NotDirectCopyCheck: TCheckBox
-      Left = 17
-      Top = 109
+      Left = 55
+      Top = 105
       Width = 288
       Height = 17
       Anchors = [akLeft, akTop, akRight]
@@ -79,8 +126,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
     end
   end
   object OkButton: TButton
-    Left = 78
-    Top = 150
+    Left = 116
+    Top = 146
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -90,8 +137,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
     TabOrder = 1
   end
   object CancelButton: TButton
-    Left = 162
-    Top = 150
+    Left = 200
+    Top = 146
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -101,8 +148,8 @@ object RemoteTransferDialog: TRemoteTransferDialog
     TabOrder = 2
   end
   object HelpButton: TButton
-    Left = 246
-    Top = 150
+    Left = 284
+    Top = 146
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]

+ 3 - 0
source/forms/RemoteTransfer.h

@@ -7,6 +7,8 @@
 #include <StdCtrls.hpp>
 #include <Forms.hpp>
 #include "HistoryComboBox.hpp"
+#include <Vcl.ExtCtrls.hpp>
+#include <Vcl.Imaging.pngimage.hpp>
 //---------------------------------------------------------------------------
 class TRemoteTransferDialog : public TForm
 {
@@ -20,6 +22,7 @@ __published:
   TButton *CancelButton;
   TButton *HelpButton;
   TCheckBox *NotDirectCopyCheck;
+  TImage *Image;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);

+ 1 - 0
source/forms/ScpCommander.dfm

@@ -1544,6 +1544,7 @@ inherited ScpCommanderForm: TScpCommanderForm
       object TBXItem178: TTBXItem
         Action = NonVisualDataModule.CloseApplicationAction
         DisplayMode = nbdmImageAndText
+        ImageIndex = 61
         Stretch = True
       end
     end

+ 5 - 0
source/putty/SSH.C

@@ -9716,7 +9716,12 @@ static void ssh2_timer(void *ctx, unsigned long now)
 	return;
 
     if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 &&
+#ifdef MPEXT
+// our call from call_ssh_timer() does not guarantee the `now` to be exactly as scheduled
+	(now >= ssh->next_rekey)) {
+#else
 	now == ssh->next_rekey) {
+#endif
 	do_ssh2_transport(ssh, "timeout", -1, NULL);
     }
 }

BIN
source/resource/RemoteMoveFile.png


+ 6 - 4
source/resource/TextsWin.h

@@ -23,7 +23,7 @@
 #define CUSTOM_COMMAND_DUPLICATE 1124
 #define CHECK_FOR_UPDATES_HTTP  1125
 #define CHECK_FOR_UPDATES_ERROR 1126
-#define REGISTER_URL_ERROR      1131
+#define REGISTER_URL_ERROR2     1131
 #define MUTEX_RELEASE_TIMEOUT   1132
 #define DRAGEXT_MUTEX_RELEASE_TIMEOUT 1133
 #define DRAGEXT_TARGET_UNKNOWN  1134
@@ -87,7 +87,7 @@
 #define DELETE_BOOKMARK_FOLDER  1320
 #define PREV_BUTTON             1321
 #define NEXT_BUTTON             1322
-#define CONFIRM_REGISTER_URL    1324
+#define CONFIRM_REGISTER_URL2   1324
 #define UNINSTALL_CLEANUP       1325
 #define EXIT_ON_COMPLETION      1326
 #define DISCONNECT_ON_COMPLETION 1327
@@ -125,6 +125,7 @@
 #define REPORT_BUTTON           1361
 #define MASTER_PASSWORD_OTHER_INSTANCE 1362
 #define EDIT_SESSION_REATTACH   1363
+#define CONFIRM_UNREGISTER_URL  1364
 
 #define WIN_INFORMATION_STRINGS 1400
 #define APP_CAPTION             1401
@@ -190,9 +191,9 @@
 #define AUTO_SWITCH_ON          1508
 #define AUTO_SWITCH_OFF         1509
 #define AUTO_SWITCH_AUTO        1510
-#define MASTER_PASSWORD_SET     1511
+#define MASTER_PASSWORD_SET2     1511
 #define MASTER_PASSWORD_CHANGED 1512
-#define MASTER_PASSWORD_CLEARED 1513
+#define MASTER_PASSWORD_CLEARED2 1513
 #define CONSOLE_MASTER_PASSWORD_PROMPT 1514
 #define UPDATE_LAST             1515
 #define UPDATE_NEXT             1516
@@ -214,6 +215,7 @@
 #define COPY_PARAM_PRESET_HEADER 1532
 #define QUEUE_DONE              1533
 #define MASTER_PASSWORD_RECRYPT_ERRORS 1534
+#define REGISTERED_APP_DESC     1535
 
 #define WIN_FORMS_STRINGS       1600
 #define LOG_NOLOG               1601

+ 6 - 4
source/resource/TextsWin1.rc

@@ -27,7 +27,7 @@ BEGIN
         CUSTOM_COMMAND_DUPLICATE, "Custom command with description '%s' already exists."
         CHECK_FOR_UPDATES_ERROR, "Error checking for application updates."
         CHECK_FOR_UPDATES_HTTP, "Cannot query application homepage for updates information."
-        REGISTER_URL_ERROR, "Cannot register application to handle sftp:// and scp:// addresses."
+        REGISTER_URL_ERROR2, "Cannot register application to handle URL addresses."
         MUTEX_RELEASE_TIMEOUT, "Mutex was not released in required interval."
         DRAGEXT_MUTEX_RELEASE_TIMEOUT, "Shell drag extension mutex was not released in required interval."
         DRAGEXT_TARGET_UNKNOWN, "WinSCP was not able to detect folder, where the dragged file(s) was dropped. Either you have not dropped the file(s) to regular folder (e.g. Windows Explorer) or WinSCP shell drag extension is not installed or you have not restarted the computer yet after installation. Install the extension or switch to compatible drag&drop mode (from Preferences window), which uses temporary folder for downloads. It allows you to drop files to any destination."
@@ -89,7 +89,7 @@ BEGIN
         DELETE_BOOKMARK_FOLDER, "Delete selected Location Profile folder?"
         PREV_BUTTON, "&Previous"
         NEXT_BUTTON, "&Next"
-        CONFIRM_REGISTER_URL, "Do you want to register application to handle sftp:// and scp:// addresses?"
+        CONFIRM_REGISTER_URL2, "Do you want to register WinSCP to handle URL addresses?"
         UNINSTALL_CLEANUP, "Do you want to cleanup data stored by the application on this computer?"
         EXIT_ON_COMPLETION, "%s\n\nDo you want to close application?"
         DISCONNECT_ON_COMPLETION, "%%s\n\nDo you want to terminate %d remaining session(s) and close the application?"
@@ -126,6 +126,7 @@ BEGIN
         REPORT_BUTTON, "Re&port"
         MASTER_PASSWORD_OTHER_INSTANCE, "There is other instance of WinSCP running.\n\nSetting or clearing master password, while another instance of WinSCP is running, can cause loss of your stored passwords.\n\nPlease quit other instances of WinSCP before continuing."
         EDIT_SESSION_REATTACH, "**Do you want to attach edited file '%s' to session '%s'?**\n\nOriginal session used to download file '%s' to editor was closed already."
+        CONFIRM_UNREGISTER_URL, "Do you want to unregister WinSCP from handling all URL addresses?"
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         APP_CAPTION, "%s - %s"
@@ -192,9 +193,9 @@ BEGIN
         AUTO_SWITCH_ON, "On"
         AUTO_SWITCH_OFF, "Off"
         AUTO_SWITCH_AUTO, "Auto"
-        MASTER_PASSWORD_SET, "Master password successfully set. Your stored passwords are secured by AES cipher now."
+        MASTER_PASSWORD_SET2, "**Master password successfully set.**\n\nYour stored passwords are secured by AES cipher now."
         MASTER_PASSWORD_CHANGED, "Master password successfully changed."
-        MASTER_PASSWORD_CLEARED, "You have deleted your master password. Your stored passwords are not secured now."
+        MASTER_PASSWORD_CLEARED2, "**You have deleted your master password.**\n\nYour stored passwords are not secured now."
         UPDATE_LAST, "Last checked on: %s"
         UPDATE_NEXT, "Will check again on: %s"
         JUMPLIST_RECENT, "Recent Sites"
@@ -216,6 +217,7 @@ BEGIN
         QUEUE_DONE, "Completed"
         EDITOR_AUTO_CONFIG, "Custom text editor '%s' was detected.\n\nDo you want to use '%s' instead of default internal editor?"
         MASTER_PASSWORD_RECRYPT_ERRORS, "%s\n\nThere were some errors when encrypting passwords using new master password or decrypting passwords."
+        REGISTERED_APP_DESC, "WinSCP is an open source free SFTP client and FTP client for Windows. Its main function is the secure file transfer between local and remote computer. Beyond this, WinSCP offers basic file manager functionality."
 
         WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
         LOG_NOLOG, "No session log."

+ 1 - 0
source/windows/GUIConfiguration.cpp

@@ -616,6 +616,7 @@ void __fastcall TGUIConfiguration::DefaultLocalized()
 //---------------------------------------------------------------------------
 void __fastcall TGUIConfiguration::UpdateStaticUsage()
 {
+  TConfiguration::UpdateStaticUsage();
   Usage->Set(L"CopyParamsCount", (FCopyParamListDefaults ? 0 : FCopyParamList->Count));
   Usage->Set(L"Putty", ExtractProgramName(PuttyPath));
 }

+ 8 - 4
source/windows/QueueController.cpp

@@ -163,16 +163,20 @@ bool __fastcall TQueueController::AllowOperation(
         TQueueItem::TStatus Status =
           (Operation == qoPauseAll) ? TQueueItem::qsProcessing : TQueueItem::qsPaused;
         bool Result = false;
-        for (int i = FQueueStatus->DoneCount; !Result && (i < FQueueStatus->DoneAndActiveCount); i++)
+        // can be NULL when action update is triggered while disconnecting
+        if (FQueueStatus != NULL)
         {
-          QueueItem = FQueueStatus->Items[i];
-          Result = (QueueItem->Status == Status);
+          for (int i = FQueueStatus->DoneCount; !Result && (i < FQueueStatus->DoneAndActiveCount); i++)
+          {
+            QueueItem = FQueueStatus->Items[i];
+            Result = (QueueItem->Status == Status);
+          }
         }
         return Result;
       }
 
     case qoDeleteAllDone:
-      return (FQueueStatus->DoneCount > 0);
+      return (FQueueStatus != NULL) && (FQueueStatus->DoneCount > 0);
 
     default:
       assert(false);

+ 377 - 72
source/windows/Setup.cpp

@@ -262,92 +262,397 @@ void __fastcall RemoveSearchPath(const UnicodeString Path)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall RegisterAsUrlHandler()
+static const UnicodeString SoftwareClassesBaseKey = L"Software\\Classes\\";
+//---------------------------------------------------------------------------
+static void __fastcall DeleteKeyIfEmpty(TRegistry * Registry, const UnicodeString & Key, bool AllowRootValues)
 {
-  try
+  if (Registry->OpenKey(Key, false))
   {
-    bool Success;
-    bool User = true;
-    TRegistry * Registry = new TRegistry();
-    try
+    std::auto_ptr<TStrings> List(new TStringList());
+
+    Registry->GetValueNames(List.get());
+    bool CanDelete = true;
+    for (int Index = 0; CanDelete && (Index < List->Count); Index++)
     {
-      do
+      UnicodeString ValueName = List->Strings[Index];
+      if (!AllowRootValues)
       {
-        Success = true;
-        User = !User;
+        CanDelete = false;
+      }
+      if ((ValueName != L"") &&
+          (ValueName != L"URL Protocol") &&
+          (ValueName != L"EditFlags") &&
+          (ValueName != L"BrowserFlags"))
+      {
+        CanDelete = false;
+      }
+    }
 
-        try
-        {
-          assert(Configuration != NULL);
-          UnicodeString FileName = Application->ExeName;
-          UnicodeString BaseKey;
+    List->Clear();
+    Registry->GetKeyNames(List.get());
 
-          Registry->Access = KEY_WRITE;
-          if (User)
-          {
-            Registry->RootKey = HKEY_CURRENT_USER;
-            BaseKey = _T("Software\\Classes\\");
-          }
-          else
-          {
-            Registry->RootKey = HKEY_CLASSES_ROOT;
-            BaseKey = _T("");
-          }
+    Registry->CloseKey();
 
-          UnicodeString Protocol;
-          for (int Index = 0; Index <= 1; Index++)
-          {
-            Protocol = (Index == 0) ? L"SCP" : L"SFTP";
-            if (Registry->OpenKey(BaseKey + Protocol, true))
-            {
-              Registry->WriteString(L"", FMTLOAD(PROTOCOL_URL_DESC, (Protocol)));
-              Registry->WriteString(L"URL Protocol", L"");
-              Registry->WriteInteger(L"EditFlags", 0x02);
-              Registry->WriteInteger(L"BrowserFlags", 0x08);
-              if (Registry->OpenKey(L"DefaultIcon", true))
-              {
-                Registry->WriteString(L"", FORMAT(L"\"%s\",0", (FileName)));
-                Registry->CloseKey();
-              }
-              else
-              {
-                Abort();
-              }
-            }
-            else
-            {
-              Abort();
-            }
+    if (CanDelete)
+    {
+      for (int Index = 0; Index < List->Count; Index++)
+      {
+        DeleteKeyIfEmpty(Registry, IncludeTrailingBackslash(Key) + List->Strings[Index], false);
+      }
 
-            if (Registry->OpenKey(BaseKey + Protocol, false) &&
-                Registry->OpenKey(L"shell", true) &&
-                Registry->OpenKey(L"open", true) &&
-                Registry->OpenKey(L"command", true))
-            {
-              Registry->WriteString(L"", FORMAT(L"\"%s\" /unsafe \"%%1\"", (FileName)));
-              Registry->CloseKey();
-            }
-            else
-            {
-              Abort();
-            }
-          }
-        }
-        catch(...)
-        {
-          Success = false;
-        }
+      // will fail, if not all subkeys got removed
+      Registry->DeleteKey(Key);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterProtocol(TRegistry * Registry,
+  const UnicodeString & Protocol, UnicodeString Description, bool Force)
+{
+
+  if (Description.IsEmpty())
+  {
+    Description = FMTLOAD(PROTOCOL_URL_DESC, (Protocol));
+  }
+
+  UnicodeString ProtocolKey = SoftwareClassesBaseKey + Protocol;
+  if (Force || !Registry->KeyExists(ProtocolKey))
+  {
+    if (Registry->OpenKey(SoftwareClassesBaseKey + Protocol, true))
+    {
+      Registry->WriteString(L"", Description);
+      Registry->WriteString(L"URL Protocol", L"");
+      Registry->WriteInteger(L"EditFlags", 0x02);
+      Registry->WriteInteger(L"BrowserFlags", 0x08);
+      if (Registry->OpenKey(L"DefaultIcon", true))
+      {
+        Registry->WriteString(L"", FORMAT(L"\"%s\",0", (Application->ExeName)));
+        Registry->CloseKey();
+      }
+      else
+      {
+        Abort();
       }
-      while (!Success && !User);
     }
-    __finally
+    else
     {
-      delete Registry;
+      Abort();
     }
   }
-  catch(Exception & E)
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterProtocol(TRegistry * Registry,
+  const UnicodeString & Protocol)
+{
+  DeleteKeyIfEmpty(Registry, SoftwareClassesBaseKey + Protocol, true);
+}
+//---------------------------------------------------------------------------
+static TRegistry * __fastcall CreateRegistry(HKEY RootKey)
+{
+  std::auto_ptr<TRegistry> Registry(new TRegistry());
+
+  Registry->Access = KEY_WRITE | KEY_READ;
+  Registry->RootKey = RootKey;
+
+  return Registry.release();
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterAsUrlHandler(HKEY RootKey,
+  const UnicodeString & Protocol, UnicodeString Description = "")
+{
+  std::auto_ptr<TRegistry> Registry(CreateRegistry(RootKey));
+
+  RegisterProtocol(Registry.get(), Protocol, Description, true);
+
+  if (Registry->OpenKey(SoftwareClassesBaseKey + Protocol, false) &&
+      Registry->OpenKey(L"shell", true) &&
+      Registry->OpenKey(L"open", true) &&
+      Registry->OpenKey(L"command", true))
+  {
+    Registry->WriteString(L"", FORMAT(L"\"%s\" /unsafe \"%%1\"", (Application->ExeName)));
+    Registry->CloseKey();
+  }
+  else
+  {
+    Abort();
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterAsUrlHandler(const UnicodeString & Protocol, UnicodeString Description = L"")
+{
+
+  try
+  {
+    RegisterAsUrlHandler(HKEY_LOCAL_MACHINE, Protocol, Description);
+
+    // get rid of any HKCU registraction that would overrite the HKLM one
+    std::auto_ptr<TRegistry> Registry(CreateRegistry(HKEY_CURRENT_USER));
+    if (Registry->KeyExists(SoftwareClassesBaseKey + Protocol))
+    {
+      Registry->DeleteKey(SoftwareClassesBaseKey + Protocol);
+    }
+  }
+  catch (Exception & E)
+  {
+    try
+    {
+      RegisterAsUrlHandler(HKEY_CURRENT_USER, Protocol, Description);
+    }
+    catch(Exception & E)
+    {
+      throw ExtException(&E, LoadStr(REGISTER_URL_ERROR2));
+    }
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterAsUrlHandler(HKEY RootKey,
+  const UnicodeString & Protocol, bool UnregisterProtocol, bool ForceHandlerUnregistration)
+{
+  std::auto_ptr<TRegistry> Registry(CreateRegistry(RootKey));
+
+  UnicodeString DefaultIconKey = SoftwareClassesBaseKey + Protocol + L"\\DefaultIcon";
+  if (Registry->OpenKey(DefaultIconKey, false))
+  {
+    UnicodeString Value = Registry->ReadString(L"");
+    UnicodeString ExeBaseName = ExtractFileBaseName(Application->ExeName);
+    if (ForceHandlerUnregistration || ContainsText(Value, ExeBaseName))
+    {
+      Registry->DeleteValue(L"");
+    }
+    Registry->CloseKey();
+
+    DeleteKeyIfEmpty(Registry.get(), DefaultIconKey, false);
+  }
+
+  UnicodeString ShellKey = SoftwareClassesBaseKey + Protocol + L"\\shell";
+  if (Registry->OpenKey(ShellKey + L"\\open\\command", false))
+  {
+    UnicodeString Value = Registry->ReadString(L"");
+    UnicodeString ExeBaseName = ExtractFileBaseName(Application->ExeName);
+    if (ForceHandlerUnregistration || ContainsText(Value, ExeBaseName))
+    {
+      Registry->DeleteValue(L"");
+    }
+
+    Registry->CloseKey();
+
+    DeleteKeyIfEmpty(Registry.get(), ShellKey, false);
+  }
+
+  if (UnregisterProtocol)
+  {
+    ::UnregisterProtocol(Registry.get(), Protocol);
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterAsUrlHandler(const UnicodeString & Protocol, bool UnregisterProtocol)
+{
+  UnregisterAsUrlHandler(HKEY_LOCAL_MACHINE, Protocol, UnregisterProtocol, false);
+  UnregisterAsUrlHandler(HKEY_CURRENT_USER, Protocol, UnregisterProtocol, false);
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterAsNonBrowserUrlHandler(const UnicodeString & Prefix)
+{
+  RegisterAsUrlHandler(Prefix + SftpProtocol.UpperCase());
+  RegisterAsUrlHandler(Prefix + ScpProtocol.UpperCase());
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterAsUrlHandlers(const UnicodeString & Prefix, bool UnregisterProtocol)
+{
+  UnregisterAsUrlHandler(Prefix + SftpProtocol, UnregisterProtocol);
+  UnregisterAsUrlHandler(Prefix + ScpProtocol, UnregisterProtocol);
+  // add WebDAV
+}
+//---------------------------------------------------------------------------
+static const UnicodeString GenericUrlHandler(L"WinSCP.Url");
+//---------------------------------------------------------------------------
+static void __fastcall RegisterProtocolForDefaultPrograms(HKEY RootKey, const UnicodeString & Protocol)
+{
+  // Register protocol, if it does not exist yet.
+  // Prior to Windows 8, we need to register ourselves as legacy handler to
+  // become the default handler. On Windows 8, it's automatic as long as no other
+  // application is registered for the protocol (i.e. RegisterProtocol would be enough)
+  RegisterAsUrlHandler(RootKey, Protocol);
+
+  // see http://msdn.microsoft.com/en-us/library/windows/desktop/cc144154.aspx#registration
+  std::auto_ptr<TRegistry> Registry(CreateRegistry(RootKey));
+
+  // create capabilities record
+
+  // this has to be a separate branch from WinSCP one, as by its presence we
+  // enforce registry storage usage, and the capabilities branch may exist
+  // even if we are using INI file
+  UnicodeString CapabilitiesKey = IncludeTrailingBackslash(GetCompanyRegistryKey()) + L"WinSCPCapabilities";
+  if (!Registry->OpenKey(CapabilitiesKey, true))
+  {
+    Abort();
+  }
+
+  UnicodeString Description = LoadStr(REGISTERED_APP_DESC);
+  Registry->WriteString(L"ApplicationDescription", Description);
+
+  if (!Registry->OpenKey(L"UrlAssociations", true))
+  {
+    Abort();
+  }
+
+  Registry->WriteString(Protocol, GenericUrlHandler);
+  Registry->CloseKey();
+
+  // register application
+
+  if (!Registry->OpenKey(L"Software\\RegisteredApplications", true))
+  {
+    Abort();
+  }
+
+  Registry->WriteString(AppNameString(), CapabilitiesKey);
+  Registry->CloseKey();
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterProtocolForDefaultPrograms(HKEY RootKey,
+  const UnicodeString & Protocol, bool ForceHandlerUnregistration)
+{
+  std::auto_ptr<TRegistry> Registry(CreateRegistry(RootKey));
+
+  // unregister the protocol
+  UnregisterAsUrlHandler(RootKey, Protocol, false, ForceHandlerUnregistration);
+
+  // remove capabilities record
+
+  UnicodeString CapabilitiesKey = IncludeTrailingBackslash(GetCompanyRegistryKey()) + L"WinSCPCapabilities";
+  UnicodeString UrlAssociationsKey = CapabilitiesKey + L"\\UrlAssociations";
+  if (Registry->OpenKey(UrlAssociationsKey, false))
+  {
+    Registry->DeleteValue(Protocol);
+    Registry->CloseKey();
+
+    DeleteKeyIfEmpty(Registry.get(), UrlAssociationsKey, false);
+  }
+
+  if (Registry->OpenKey(CapabilitiesKey, false))
+  {
+    if (!Registry->HasSubKeys())
+    {
+      Registry->DeleteValue(L"ApplicationDescription");
+    }
+
+    Registry->CloseKey();
+
+    DeleteKeyIfEmpty(Registry.get(), CapabilitiesKey, false);
+  }
+
+  if (!Registry->KeyExists(CapabilitiesKey))
+  {
+    // unregister application
+
+    if (Registry->OpenKey(L"Software\\RegisteredApplications", false))
+    {
+      Registry->DeleteValue(AppNameString());
+      Registry->CloseKey();
+    }
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterProtocolsForDefaultPrograms(HKEY RootKey)
+{
+  // register URL handler, if it does not exist yet
+  RegisterAsUrlHandler(RootKey, GenericUrlHandler, L"WinSCP URL");
+
+  RegisterProtocolForDefaultPrograms(RootKey, FtpProtocol);
+  RegisterProtocolForDefaultPrograms(RootKey, FtpsProtocol);
+  RegisterProtocolForDefaultPrograms(RootKey, SftpProtocol);
+  RegisterProtocolForDefaultPrograms(RootKey, ScpProtocol);
+}
+//---------------------------------------------------------------------------
+static void __fastcall UnregisterProtocolsForDefaultPrograms(HKEY RootKey, bool ForceHandlerUnregistration)
+{
+  UnregisterProtocolForDefaultPrograms(RootKey, FtpProtocol, ForceHandlerUnregistration);
+  UnregisterProtocolForDefaultPrograms(RootKey, FtpsProtocol, ForceHandlerUnregistration);
+  UnregisterProtocolForDefaultPrograms(RootKey, SftpProtocol, ForceHandlerUnregistration);
+  UnregisterProtocolForDefaultPrograms(RootKey, ScpProtocol, ForceHandlerUnregistration);
+
+  // we should not really need the "force" flag here, but why not
+  UnregisterAsUrlHandler(RootKey, GenericUrlHandler, true, true);
+}
+//---------------------------------------------------------------------------
+static void __fastcall RegisterForDefaultPrograms()
+{
+  try
+  {
+    RegisterProtocolsForDefaultPrograms(HKEY_LOCAL_MACHINE);
+    // make sure we unregister any legacy protocol handler for CU,
+    // this is needed for Windows Vista+7
+    UnregisterProtocolsForDefaultPrograms(HKEY_CURRENT_USER, true);
+  }
+  catch (Exception & E)
+  {
+    try
+    {
+      RegisterProtocolsForDefaultPrograms(HKEY_CURRENT_USER);
+    }
+    catch (Exception & E)
+    {
+      throw ExtException(&E, LoadStr(REGISTER_URL_ERROR2));
+    }
+  }
+}
+//---------------------------------------------------------------------------
+static void __fastcall NotifyChangedAssociations()
+{
+  SHChangeNotify(SHCNE_ASSOCCHANGED, 0, 0, 0);
+}
+//---------------------------------------------------------------------------
+void __fastcall RegisterForDefaultProtocols()
+{
+  if (IsWinVista())
+  {
+    RegisterForDefaultPrograms();
+  }
+  else
+  {
+    RegisterAsNonBrowserUrlHandler(UnicodeString());
+  }
+
+  RegisterAsNonBrowserUrlHandler(WinSCPProtocolPrefix);
+  RegisterAsUrlHandler(WinSCPProtocolPrefix + FtpProtocol.UpperCase());
+  RegisterAsUrlHandler(WinSCPProtocolPrefix + FtpsProtocol.UpperCase());
+  // add WebDAV
+
+  NotifyChangedAssociations();
+}
+//---------------------------------------------------------------------------
+void __fastcall UnregisterForProtocols()
+{
+  UnregisterAsUrlHandlers(UnicodeString(), false);
+  UnregisterAsUrlHandlers(WinSCPProtocolPrefix, true);
+  UnregisterAsUrlHandler(WinSCPProtocolPrefix + FtpProtocol.UpperCase(), true);
+  UnregisterAsUrlHandler(WinSCPProtocolPrefix + FtpsProtocol.UpperCase(), true);
+
+  UnregisterProtocolsForDefaultPrograms(HKEY_CURRENT_USER, false);
+  UnregisterProtocolsForDefaultPrograms(HKEY_LOCAL_MACHINE, false);
+
+  NotifyChangedAssociations();
+}
+//---------------------------------------------------------------------------
+void __fastcall LaunchAdvancedAssociationUI()
+{
+  assert(IsWinVista());
+
+  RegisterForDefaultPrograms();
+  NotifyChangedAssociations();
+  // sleep recommended by http://msdn.microsoft.com/en-us/library/windows/desktop/cc144154.aspx#browser
+  Sleep(1000);
+
+  IApplicationAssociationRegistrationUI * AppAssocRegUI;
+
+  HRESULT Result =
+    CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
+      NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI), (void**)&AppAssocRegUI);
+  if (SUCCEEDED(Result))
   {
-    throw ExtException(&E, LoadStr(REGISTER_URL_ERROR));
+    AppAssocRegUI->LaunchAdvancedAssociationUI(AppNameString().c_str());
+    AppAssocRegUI->Release();
   }
 }
 //---------------------------------------------------------------------------

+ 3 - 1
source/windows/Setup.h

@@ -10,7 +10,9 @@ void __fastcall RemoveSearchPath(const UnicodeString Path);
 void __fastcall GetUpdatesMessage(UnicodeString & Message, bool & New, TQueryType & Type, bool Force);
 void __fastcall CheckForUpdates(bool CachedResults);
 UnicodeString __fastcall GetUsageData();
-void __fastcall RegisterAsUrlHandler();
+void __fastcall RegisterForDefaultProtocols();
+void __fastcall UnregisterForProtocols();
+void __fastcall LaunchAdvancedAssociationUI();
 void __fastcall TemporaryDirectoryCleanup();
 void __fastcall StartUpdateThread(TThreadMethod OnUpdatesChecked);
 void __fastcall StopUpdateThread();

+ 8 - 3
source/windows/UserInterface.cpp

@@ -74,9 +74,14 @@ UnicodeString __fastcall AppNameString()
   return L"WinSCP";
 }
 //---------------------------------------------------------------------------
+UnicodeString __fastcall GetCompanyRegistryKey()
+{
+  return L"Software\\Martin Prikryl";
+}
+//---------------------------------------------------------------------------
 UnicodeString __fastcall GetRegistryKey()
 {
-  return L"Software\\Martin Prikryl\\WinSCP 2";
+  return GetCompanyRegistryKey() + L"\\WinSCP 2";
 }
 //---------------------------------------------------------------------------
 static bool ForcedOnForeground = false;
@@ -1074,7 +1079,7 @@ void __fastcall TMasterPasswordDialog::DoValidate()
   {
     CurrentEdit->SetFocus();
     CurrentEdit->SelectAll();
-    throw Exception(LoadStr(MASTER_PASSWORD_INCORRECT));
+    throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_INCORRECT)));
   }
 
   if (NewEdit != NULL)
@@ -1083,7 +1088,7 @@ void __fastcall TMasterPasswordDialog::DoValidate()
     {
       ConfirmEdit->SetFocus();
       ConfirmEdit->SelectAll();
-      throw Exception(LoadStr(MASTER_PASSWORD_DIFFERENT));
+      throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_DIFFERENT)));
     }
 
     int Valid = IsValidPassword(NewEdit->Text);

+ 8 - 0
source/windows/VCLCommon.cpp

@@ -14,6 +14,7 @@
 #include <FileCtrl.hpp>
 #include <PathLabel.hpp>
 #include <PasTools.hpp>
+#include <Vcl.Imaging.pngimage.hpp>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -1648,3 +1649,10 @@ void __fastcall UseDesktopFont(TControl * Control)
 
   reinterpret_cast<TPublicControl *>(Control)->DesktopFont = true;
 }
+//---------------------------------------------------------------------------
+void __fastcall LoadResourceImage(TImage * Image, const UnicodeString & ImageName)
+{
+  std::unique_ptr<TPngImage> Png(new TPngImage());
+  Png->LoadFromResourceName(0, ImageName);
+  Image->Picture->Assign(Png.get());
+}

+ 1 - 0
source/windows/VCLCommon.h

@@ -65,6 +65,7 @@ FormType * __fastcall SafeFormCreate(TComponent * Owner = NULL)
 bool __fastcall SupportsSplitButton();
 TModalResult __fastcall DefaultResult(TCustomForm * Form);
 void __fastcall UseDesktopFont(TControl * Control);
+void __fastcall LoadResourceImage(TImage * Image, const UnicodeString & ImageName);
 //---------------------------------------------------------------------------
 // When exception is left to be handled by Application->OnException
 // memory error occurs when clearing the exception for unknown reason.

+ 4 - 3
source/windows/WinConfiguration.cpp

@@ -1377,9 +1377,6 @@ void __fastcall TWinConfiguration::AskForMasterPassword()
     FMasterPasswordSessionAsked = true;
   }
 
-  // this can happen from TStoredSessionList::UpdateStaticUsage(),
-  // when the default session settings have password set (and no other basic property,
-  // like hostname/username), and master password is enabled
   if (FOnMasterPasswordPrompt == NULL)
   {
     throw Exception(L"Master password handler not set");
@@ -2250,6 +2247,10 @@ void __fastcall TWinConfiguration::UpdateJumpList()
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::UpdateStaticUsage()
 {
+  // This is here because of TStoredSessionList::UpdateStaticUsage() call
+  // from TConfiguration::UpdateStaticUsage()
+  TAutoNestingCounter DontDecryptPasswordsAutoCounter(FDontDecryptPasswords);
+
   TCustomWinConfiguration::UpdateStaticUsage();
   Usage->Set(L"Beta", IsBeta);
 

+ 0 - 23
source/windows/WinInterface.cpp

@@ -579,29 +579,6 @@ void __fastcall Busy(bool Start)
   }
 }
 //---------------------------------------------------------------------------
-static void __fastcall CopyParamListBoolToggleClick(TMenuItem * Item)
-{
-  bool * BoolToggle = reinterpret_cast<bool *>(Item->Tag);
-  *BoolToggle = !*BoolToggle;
-  Item->Checked = *BoolToggle;
-}
-//---------------------------------------------------------------------------
-bool __fastcall DoRemoteMoveDialog(UnicodeString & Target, UnicodeString & FileMask)
-{
-  UnicodeString Value = UnixIncludeTrailingBackslash(Target) + FileMask;
-  TStrings * History = CustomWinConfiguration->History[L"RemoteTarget"];
-  bool Result = InputDialog(
-    LoadStr(REMOTE_MOVE_TITLE), LoadStr(REMOTE_TRANSFER_PROMPT),
-    Value, HELP_REMOTE_MOVE, History, true);
-  if (Result)
-  {
-    CustomWinConfiguration->History[L"RemoteTarget"] = History;
-    Target = UnixExtractFilePath(Value);
-    FileMask = UnixExtractFileName(Value);
-  }
-  return Result;
-}
-//---------------------------------------------------------------------------
 void __fastcall CopyParamListButton(TButton * Button)
 {
   if (!SupportsSplitButton())

+ 21 - 7
source/windows/WinMain.cpp

@@ -47,10 +47,9 @@ void __fastcall GetLoginData(UnicodeString SessionName, TOptions * Options,
         Configuration->Usage->Inc(L"CommandLineSessionSave");
         TSessionData * SavedSession = DoSaveSession(SessionData, NULL, true);
         Close = (SavedSession == NULL);
-        if (!Close && !SessionData->HostKey.IsEmpty())
+        if (!Close)
         {
           WinConfiguration->LastStoredSession = SavedSession->Name;
-          SessionData->CacheHostKeyIfNotCached();
         }
         DataList->Clear();
       }
@@ -238,6 +237,12 @@ void __fastcall ImportSitesIfAny()
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall Usage(UnicodeString Param)
+{
+  UnicodeString Key = ::CutToChar(Param, L':', true);
+  Configuration->Usage->Set(Key, Param);
+}
+//---------------------------------------------------------------------------
 void __fastcall RecordWrapperVersions(UnicodeString ConsoleVersion, UnicodeString DotNetVersion)
 {
   TUpdatesConfiguration Updates = WinConfiguration->Updates;
@@ -315,7 +320,6 @@ void __fastcall UpdateStaticUsage()
   bool InProgramFiles = AnsiSameText(ExeName.SubString(1, ProgramsFolder.Length()), ProgramsFolder);
   Configuration->Usage->Set(L"InProgramFiles", InProgramFiles);
 
-  StoredSessions->UpdateStaticUsage();
   WinConfiguration->UpdateStaticUsage();
 
 }
@@ -406,8 +410,6 @@ int __fastcall Execute()
 
     Application->HintHidePause = 3000;
 
-    UnicodeString Value;
-
     UnicodeString IniFileName = Params->SwitchValue(L"ini");
     if (!IniFileName.IsEmpty())
     {
@@ -420,6 +422,7 @@ int __fastcall Execute()
       }
     }
 
+    UnicodeString SwitchValue;
     if (Params->FindSwitch(L"UninstallCleanup"))
     {
       MaintenanceTask();
@@ -433,10 +436,16 @@ int __fastcall Execute()
         DoCleanupDialog(StoredSessions, Configuration);
       }
     }
-    else if (Params->FindSwitch(L"RegisterAsUrlHandler"))
+    else if (Params->FindSwitch(L"RegisterForDefaultProtocols") ||
+             Params->FindSwitch(L"RegisterAsUrlHandler")) // BACKWARD COMPATIBILITY
+    {
+      MaintenanceTask();
+      RegisterForDefaultProtocols();
+    }
+    else if (Params->FindSwitch(L"UnregisterForProtocols"))
     {
       MaintenanceTask();
-      RegisterAsUrlHandler();
+      UnregisterForProtocols();
     }
     else if (Params->FindSwitch(L"AddSearchPath"))
     {
@@ -463,6 +472,11 @@ int __fastcall Execute()
       MaintenanceTask();
       ImportSitesIfAny();
     }
+    else if (Params->FindSwitch(L"Usage", SwitchValue))
+    {
+      MaintenanceTask();
+      Usage(SwitchValue);
+    }
     else if (Params->FindSwitch(L"Update"))
     {
       MaintenanceTask();

+ 1 - 0
source/windows/Windows.rc

@@ -17,3 +17,4 @@ Z_ICON_EDITOR_BIG ICON "..\\resource\\Editor32.ico"
 LICENSE RCDATA "License.txt"
 
 DELETE_FILE RCDATA "..\\resource\\DeleteFile.png"
+REMOTE_MOVE_FILE RCDATA "..\\resource\\RemoteMoveFile.png"