Martin Prikryl 16 years ago
parent
commit
7dd6058929
94 changed files with 3473 additions and 859 deletions
  1. 4 4
      Console.rc
  2. 4 4
      DragExt.rc
  3. 4 4
      DragExt64.rc
  4. 4 4
      Putty.bpr
  5. 8 7
      ScpCore.bpr
  6. 1 0
      ScpForms.bpf
  7. 15 14
      ScpForms.bpr
  8. 5 5
      WinSCP.rc
  9. 1 2
      components/UnixDirView.cpp
  10. 116 34
      console/Main.cpp
  11. 32 13
      core/Common.cpp
  12. 16 0
      core/Configuration.cpp
  13. 3 0
      core/Configuration.h
  14. 2 2
      core/CopyParam.cpp
  15. 6 2
      core/CoreMain.cpp
  16. 627 0
      core/Cryptography.cpp
  17. 22 0
      core/Cryptography.h
  18. 40 9
      core/FtpFileSystem.cpp
  19. 2 0
      core/FtpFileSystem.h
  20. 6 0
      core/Interface.h
  21. 3 0
      core/PuttyTools.h
  22. 2 1
      core/RemoteFiles.cpp
  23. 5 1
      core/ScpFileSystem.cpp
  24. 66 61
      core/Script.cpp
  25. 0 2
      core/Script.h
  26. 26 1
      core/Security.cpp
  27. 2 0
      core/Security.h
  28. 158 45
      core/SessionData.cpp
  29. 12 0
      core/SessionData.h
  30. 2 2
      core/SftpFileSystem.cpp
  31. 156 7
      core/Terminal.cpp
  32. 12 1
      core/Terminal.h
  33. 69 34
      filezilla/AsyncSslSocketLayer.cpp
  34. 3 5
      forms/Console.cpp
  35. 1 0
      forms/Copy.cpp
  36. 269 194
      forms/Custom.cpp
  37. 6 36
      forms/Custom.dfm
  38. 24 15
      forms/Custom.h
  39. 34 13
      forms/CustomScpExplorer.cpp
  40. 4 0
      forms/CustomScpExplorer.h
  41. 373 0
      forms/FileFind.cpp
  42. 194 0
      forms/FileFind.dfm
  43. 78 0
      forms/FileFind.h
  44. 31 31
      forms/Glyphs.dfm
  45. 218 126
      forms/LocationProfiles.cpp
  46. 0 4
      forms/LocationProfiles.h
  47. 21 5
      forms/Login.cpp
  48. 1 1
      forms/Login.dfm
  49. 1 0
      forms/Login.h
  50. 4 10
      forms/MessageDlg.cpp
  51. 2 0
      forms/NonVisual.cpp
  52. 9 0
      forms/NonVisual.dfm
  53. 1 0
      forms/NonVisual.h
  54. 43 3
      forms/Preferences.cpp
  55. 54 9
      forms/Preferences.dfm
  56. 6 3
      forms/Preferences.h
  57. 1 5
      forms/Properties.cpp
  58. 16 2
      forms/ScpCommander.cpp
  59. 6 0
      forms/ScpCommander.dfm
  60. 3 0
      forms/ScpCommander.h
  61. 6 0
      forms/ScpExplorer.dfm
  62. 2 0
      forms/ScpExplorer.h
  63. 1 1
      forms/SelectMask.dfm
  64. 2 6
      forms/SynchronizeChecklist.cpp
  65. 1 1
      packages/ThemeManagerC6D.bpk
  66. 0 3
      packages/filemng/DriveView.pas
  67. 12 2
      packages/my/PathLabel.pas
  68. 6 2
      putty/LOGGING.C
  69. 11 4
      putty/SSH.C
  70. 29 0
      putty/SSHAES_.C
  71. 8 0
      putty/SSHSHA_.C
  72. 12 0
      putty/puttyexp.h
  73. 8 5
      putty/windows/WINNET.C
  74. 1 1
      putty/windows/WINPROXY.C
  75. 1 1
      release/winscp.u3i
  76. 4 0
      resource/HelpWin.h
  77. 1 1
      resource/TextsCore2.rc
  78. 21 0
      resource/TextsWin.h
  79. 20 0
      resource/TextsWin1.rc
  80. 25 20
      windows/ConsoleRunner.cpp
  81. 38 0
      windows/CustomWinConfiguration.cpp
  82. 13 0
      windows/CustomWinConfiguration.h
  83. 28 6
      windows/TerminalManager.cpp
  84. 1 0
      windows/TerminalManager.h
  85. 12 0
      windows/Tools.cpp
  86. 2 0
      windows/Tools.h
  87. 151 0
      windows/UserInterface.cpp
  88. 51 0
      windows/VCLCommon.cpp
  89. 2 0
      windows/VCLCommon.h
  90. 137 7
      windows/WinConfiguration.cpp
  91. 17 3
      windows/WinConfiguration.h
  92. 0 20
      windows/WinInterface.cpp
  93. 14 53
      windows/WinInterface.h
  94. 2 2
      windows/WinMain.cpp

+ 4 - 4
Console.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 2,2,0,107
-PRODUCTVERSION 2,2,0,107
+FILEVERSION 2,3,0,111
+PRODUCTVERSION 2,3,0,111
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Console interface for WinSCP\0"
-            VALUE "FileVersion", "2.2.0.107\0"
+            VALUE "FileVersion", "2.3.0.111\0"
             VALUE "InternalName", "console\0"
             VALUE "LegalCopyright", "(c) 2000-2009 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "winscp.com\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.3.0\0"
+            VALUE "ProductVersion", "4.2.4.0\0"
             VALUE "ReleaseType", "beta\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 4 - 4
DragExt.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,9,102
-PRODUCTVERSION 1,1,9,102
+FILEVERSION 1,1,9,105
+PRODUCTVERSION 1,1,9,105
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP (32-bit)\0"
-            VALUE "FileVersion", "1.1.9.102\0"
+            VALUE "FileVersion", "1.1.9.105\0"
             VALUE "InternalName", "dragext32\0"
             VALUE "LegalCopyright", "(c) 2000-2009 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.3.0\0"
+            VALUE "ProductVersion", "4.2.4.0\0"
             VALUE "ReleaseType", "beta\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 4 - 4
DragExt64.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,9,102
-PRODUCTVERSION 1,1,9,102
+FILEVERSION 1,1,9,105
+PRODUCTVERSION 1,1,9,105
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP (64-bit)\0"
-            VALUE "FileVersion", "1.1.9.102\0"
+            VALUE "FileVersion", "1.1.9.105\0"
             VALUE "InternalName", "dragext64\0"
             VALUE "LegalCopyright", "(c) 2000-2009 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext64.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.3.0\0"
+            VALUE "ProductVersion", "4.2.4.0\0"
             VALUE "ReleaseType", "beta\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 4 - 4
Putty.bpr

@@ -6,10 +6,10 @@
     <PROJECT value="lib\Putty.lib"/>
     <OBJFILES value="putty\CPROXY.obj putty\INT64.obj putty\LOGGING.obj
       putty\MISC.obj putty\PORTFWD_.obj putty\PROXY.obj putty\SSH_.obj
-      putty\SSHAES.obj putty\SSHARCF.obj putty\SSHBLOWF.obj putty\SSHBN.obj
+      putty\SSHAES_.obj putty\SSHARCF.obj putty\SSHBLOWF.obj putty\SSHBN.obj
       putty\SSHCRC.obj putty\SSHCRCDA.obj putty\SSHDES.obj putty\SSHDH.obj
       putty\SSHDSS.obj putty\SSHMD5.obj putty\SSHPUBK.obj putty\SSHRAND.obj
-      putty\SSHRSA.obj putty\SSHSH256.obj putty\SSHSH512.obj putty\SSHSHA.obj
+      putty\SSHRSA.obj putty\SSHSH256.obj putty\SSHSH512.obj putty\SSHSHA_.obj
       putty\SSHZLIB.obj putty\TREE234.obj putty\CHARSET\UTF8.obj
       putty\WILDCARD.obj putty\WINDOWS\WINGSS.obj putty\WINDOWS\WINHANDL.obj
       putty\WINDOWS\WINMISC.obj putty\WINDOWS\WINNET.obj
@@ -59,7 +59,7 @@
       <FILE FILENAME="putty\PORTFWD_.C" FORMNAME="" UNITNAME="PORTFWD_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\PROXY.C" FORMNAME="" UNITNAME="PROXY.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSH_.C" FORMNAME="" UNITNAME="SSH_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
-      <FILE FILENAME="putty\SSHAES.C" FORMNAME="" UNITNAME="SSHAES.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="putty\SSHAES_.C" FORMNAME="" UNITNAME="SSHAES_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHARCF.C" FORMNAME="" UNITNAME="SSHARCF" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHBLOWF.C" FORMNAME="" UNITNAME="SSHBLOWF.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHBN.C" FORMNAME="" UNITNAME="SSHBN.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
@@ -74,7 +74,7 @@
       <FILE FILENAME="putty\SSHRSA.C" FORMNAME="" UNITNAME="SSHRSA.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHSH256.C" FORMNAME="" UNITNAME="SSHSH256.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHSH512.C" FORMNAME="" UNITNAME="SSHSH512.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
-      <FILE FILENAME="putty\SSHSHA.C" FORMNAME="" UNITNAME="SSHSHA.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="putty\SSHSHA_.C" FORMNAME="" UNITNAME="SSHSHA_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHZLIB.C" FORMNAME="" UNITNAME="SSHZLIB.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\TREE234.C" FORMNAME="" UNITNAME="TREE234.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\CHARSET\UTF8.C" FORMNAME="" UNITNAME="UTF8.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

+ 8 - 7
ScpCore.bpr

@@ -6,13 +6,13 @@
     <PROJECT value="lib\ScpCore.lib"/>
     <OBJFILES value="core\Bookmarks.obj core\Common.obj
       core\Configuration.obj core\CopyParam.obj core\CoreMain.obj
-      core\Exceptions.obj core\FileBuffer.obj core\FileInfo.obj
-      core\FileMasks.obj core\FileOperationProgress.obj core\FileSystems.obj
-      core\FtpFileSystem.obj core\HierarchicalStorage.obj core\NamedObjs.obj
-      core\Option.obj core\PuttyIntf.obj core\Queue.obj core\RemoteFiles.obj
-      core\ScpFileSystem.obj core\Script.obj core\SecureShell.obj
-      core\Security.obj core\SessionData.obj core\SessionInfo.obj
-      core\SftpFileSystem.obj core\Terminal.obj"/>
+      core\Cryptography.obj core\Exceptions.obj core\FileBuffer.obj
+      core\FileInfo.obj core\FileMasks.obj core\FileOperationProgress.obj
+      core\FileSystems.obj core\FtpFileSystem.obj core\HierarchicalStorage.obj
+      core\NamedObjs.obj core\Option.obj core\PuttyIntf.obj core\Queue.obj
+      core\RemoteFiles.obj core\ScpFileSystem.obj core\Script.obj
+      core\SecureShell.obj core\Security.obj core\SessionData.obj
+      core\SessionInfo.obj core\SftpFileSystem.obj core\Terminal.obj"/>
     <RESFILES value=""/>
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
@@ -53,6 +53,7 @@
       <FILE FILENAME="core\Configuration.cpp" FORMNAME="" UNITNAME="Configuration.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="core\CopyParam.cpp" FORMNAME="" UNITNAME="CopyParam.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="core\CoreMain.cpp" FORMNAME="" UNITNAME="CoreMain.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="core\Cryptography.cpp" FORMNAME="" UNITNAME="Cryptography" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="core\Exceptions.cpp" FORMNAME="" UNITNAME="Exceptions" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="core\FileBuffer.cpp" FORMNAME="" UNITNAME="FileBuffer.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="core\FileInfo.cpp" FORMNAME="" UNITNAME="FileInfo" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

+ 1 - 0
ScpForms.bpf

@@ -16,6 +16,7 @@ USEFORM("forms\Custom.cpp", CustomDialog);
 USEFORM("forms\Editor.cpp", EditorForm);
 USEFORM("forms\EditorPreferences.cpp", EditorPreferencesDialog);
 USEFORM("forms\FileSystemInfo.cpp", FileSystemInfoDialog);
+USEFORM("forms\FileFind.cpp", FileFindDialog);
 USEFORM("forms\FullSynchronize.cpp", FullSynchronizeDialog);
 USEFORM("forms\GeneralSettings.cpp", GeneralSettingsFrame); /* TFrame: File Type */
 USEFORM("forms\Glyphs.cpp", GlyphsModule); /* TDataModule: File Type */

+ 15 - 14
ScpForms.bpr

@@ -10,13 +10,13 @@
       forms\CopyParamCustom.obj forms\CopyParamPreset.obj
       forms\CreateDirectory.obj forms\CustomCommand.obj forms\Custom.obj
       forms\Editor.obj forms\EditorPreferences.obj forms\FileSystemInfo.obj
-      forms\FullSynchronize.obj forms\GeneralSettings.obj forms\Glyphs.obj
-      forms\ImportSessions.obj forms\License.obj forms\LocationProfiles.obj
-      forms\Log.obj forms\Login.obj forms\LogSettings.obj
-      forms\OpenDirectory.obj forms\Preferences.obj forms\Progress.obj
-      forms\Properties.obj forms\RemoteTransfer.obj forms\Rights.obj
-      forms\RightsExt.obj forms\SelectMask.obj forms\Symlink.obj
-      forms\Synchronize.obj forms\SynchronizeChecklist.obj
+      forms\FileFind.obj forms\FullSynchronize.obj forms\GeneralSettings.obj
+      forms\Glyphs.obj forms\ImportSessions.obj forms\License.obj
+      forms\LocationProfiles.obj forms\Log.obj forms\Login.obj
+      forms\LogSettings.obj forms\OpenDirectory.obj forms\Preferences.obj
+      forms\Progress.obj forms\Properties.obj forms\RemoteTransfer.obj
+      forms\Rights.obj forms\RightsExt.obj forms\SelectMask.obj
+      forms\Symlink.obj forms\Synchronize.obj forms\SynchronizeChecklist.obj
       forms\SynchronizeProgress.obj"/>
     <RESFILES value=""/>
     <DEFFILE value=""/>
@@ -25,13 +25,13 @@
       forms\CopyParamCustom.dfm forms\CopyParamPreset.dfm
       forms\CreateDirectory.dfm forms\CustomCommand.dfm forms\Custom.dfm
       forms\Editor.dfm forms\EditorPreferences.dfm forms\FileSystemInfo.dfm
-      forms\FullSynchronize.dfm forms\GeneralSettings.dfm forms\Glyphs.dfm
-      forms\ImportSessions.dfm forms\License.dfm forms\LocationProfiles.dfm
-      forms\Log.dfm forms\Login.dfm forms\LogSettings.dfm
-      forms\OpenDirectory.dfm forms\Preferences.dfm forms\Progress.dfm
-      forms\Properties.dfm forms\RemoteTransfer.dfm forms\Rights.dfm
-      forms\RightsExt.dfm forms\SelectMask.dfm forms\Symlink.dfm
-      forms\Synchronize.dfm forms\SynchronizeChecklist.dfm
+      forms\FileFind.dfm forms\FullSynchronize.dfm forms\GeneralSettings.dfm
+      forms\Glyphs.dfm forms\ImportSessions.dfm forms\License.dfm
+      forms\LocationProfiles.dfm forms\Log.dfm forms\Login.dfm
+      forms\LogSettings.dfm forms\OpenDirectory.dfm forms\Preferences.dfm
+      forms\Progress.dfm forms\Properties.dfm forms\RemoteTransfer.dfm
+      forms\Rights.dfm forms\RightsExt.dfm forms\SelectMask.dfm
+      forms\Symlink.dfm forms\Synchronize.dfm forms\SynchronizeChecklist.dfm
       forms\SynchronizeProgress.dfm"/>
     <LIBFILES value=""/>
     <LIBRARIES value=""/>
@@ -84,6 +84,7 @@
       <FILE FILENAME="forms\Editor.cpp" FORMNAME="EditorForm" UNITNAME="Editor" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\EditorPreferences.cpp" FORMNAME="EditorPreferencesDialog" UNITNAME="EditorPreferences" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\FileSystemInfo.cpp" FORMNAME="FileSystemInfoDialog" UNITNAME="FileSystemInfo" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="forms\FileFind.cpp" FORMNAME="FileFindDialog" UNITNAME="FileFind" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\FullSynchronize.cpp" FORMNAME="FullSynchronizeDialog" UNITNAME="FullSynchronize" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\GeneralSettings.cpp" FORMNAME="GeneralSettingsFrame" UNITNAME="GeneralSettings" CONTAINERID="CCompiler" DESIGNCLASS="TFrame" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Glyphs.cpp" FORMNAME="GlyphsModule" UNITNAME="Glyphs" CONTAINERID="CCompiler" DESIGNCLASS="TDataModule" LOCALCOMMAND=""/>

+ 5 - 5
WinSCP.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 4,2,3,494
-PRODUCTVERSION 4,2,3,494
+FILEVERSION 4,2,4,610
+PRODUCTVERSION 4,2,4,610
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "WinSCP: SFTP, FTP and SCP client\0"
-            VALUE "FileVersion", "4.2.3.494\0"
+            VALUE "FileVersion", "4.2.4.610\0"
             VALUE "InternalName", "winscp\0"
             VALUE "LegalCopyright", "(c) 2000-2009 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
-            VALUE "OriginalFilename", "winscp423.exe\0"
+            VALUE "OriginalFilename", "winscp424.exe\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.3.0\0"
+            VALUE "ProductVersion", "4.2.4.0\0"
             VALUE "ReleaseType", "beta\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 1 - 2
components/UnixDirView.cpp

@@ -627,8 +627,7 @@ AnsiString __fastcall TUnixDirView::GetPath()
 void __fastcall TUnixDirView::SetPath(AnsiString Value)
 {
 #ifndef DESIGN_ONLY
-  Value = UnixExcludeTrailingBackslash(
-    StringReplace(Value, '\\', '/', TReplaceFlags() << rfReplaceAll));
+  Value = UnixExcludeTrailingBackslash(Value);
 
   if (Active && (Terminal->CurrentDirectory != Value))
   {

+ 116 - 34
console/Main.cpp

@@ -134,38 +134,109 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
   FreeCommStruct(CommStruct);
 }
 //---------------------------------------------------------------------------
-void InitializeChild(int argc, char* argv[], int InstanceNumber, HANDLE& Child)
+// duplicated in Common.cpp
+bool __fastcall CutToken(const char *& Str, char * Token)
+{
+  bool Result;
+
+  // inspired by Putty's sftp_getcmd() from PSFTP.C
+  int Length = strlen(Str);
+  int Index = 0;
+  while ((Index < Length) &&
+    ((Str[Index] == ' ') || (Str[Index] == '\t')))
+  {
+    Index++;
+  }
+
+  if (Index < Length)
+  {
+    bool Quoting = false;
+
+    while (Index < Length)
+    {
+      if (!Quoting && ((Str[Index] == ' ') || (Str[Index] == '\t')))
+      {
+        break;
+      }
+      else if ((Str[Index] == '"') && (Index + 1 < Length) &&
+        (Str[Index + 1] == '"'))
+      {
+        Index += 2;
+        *Token = '"';
+        Token++;
+      }
+      else if (Str[Index] == '"')
+      {
+        Index++;
+        Quoting = !Quoting;
+      }
+      else
+      {
+        *Token = Str[Index];
+        Token++;
+        Index++;
+      }
+    }
+
+    if (Index < Length)
+    {
+      Index++;
+    }
+
+    Str += Index;
+
+    Result = true;
+  }
+  else
+  {
+    Result = false;
+    Str += Length;
+  }
+
+  *Token = '\0';
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void InitializeChild(const char* CommandLine, int InstanceNumber, HANDLE& Child)
 {
   int SkipParam = 0;
   char ChildPath[MAX_PATH] = "";
 
-  for (int i = 1; i < argc; i++)
+  size_t CommandLineLen = strlen(CommandLine);
+  char* Buffer = new char[CommandLineLen + 1];
+
+  int Count = 0;
+  const char * P = CommandLine;
+  while (CutToken(P, Buffer))
   {
-    if ((strchr("-/", argv[i][0]) != NULL) &&
-        (strncmpi(argv[i] + 1, CONSOLE_CHILD_PARAM, strlen(CONSOLE_CHILD_PARAM)) == 0) &&
-        (argv[i][strlen(CONSOLE_CHILD_PARAM) + 1] == '='))
+    if ((strchr("-/", Buffer[0]) != NULL) &&
+        (strncmpi(Buffer + 1, CONSOLE_CHILD_PARAM, strlen(CONSOLE_CHILD_PARAM)) == 0) &&
+        (Buffer[strlen(CONSOLE_CHILD_PARAM) + 1] == '='))
     {
-      SkipParam = i;
-      strcpy(ChildPath, argv[i] + 1 + strlen(CONSOLE_CHILD_PARAM) + 1);
-      break;
+      SkipParam = Count;
+      strcpy(ChildPath, Buffer + 1 + strlen(CONSOLE_CHILD_PARAM) + 1);
     }
+    ++Count;
   }
 
   if (strlen(ChildPath) == 0)
   {
-    const char* AppPath = argv[0];
-    const char* LastDelimiter = strrchr(AppPath, '\\');
+    P = CommandLine;
+    // skip executable path
+    CutToken(P, Buffer);
+    const char* LastDelimiter = strrchr(Buffer, '\\');
     const char* AppFileName;
     if (LastDelimiter != NULL)
     {
-      strncpy(ChildPath, AppPath, LastDelimiter - AppPath + 1);
-      ChildPath[LastDelimiter - AppPath + 1] = '\0';
+      strncpy(ChildPath, Buffer, LastDelimiter - Buffer + 1);
+      ChildPath[LastDelimiter - Buffer + 1] = '\0';
       AppFileName = LastDelimiter + 1;
     }
     else
     {
       ChildPath[0] = '\0';
-      AppFileName = AppPath;
+      AppFileName = Buffer;
     }
 
     const char* ExtensionStart = strrchr(AppFileName, '.');
@@ -182,27 +253,49 @@ void InitializeChild(int argc, char* argv[], int InstanceNumber, HANDLE& Child)
     strcat(ChildPath, ".exe");
   }
 
-  char Parameters[10240];
+  char* Parameters = new char[(CommandLineLen * 2) + 100 + (Count * 3) + 1];
   sprintf(Parameters, "\"%s\" /console /consoleinstance=%d ", ChildPath, InstanceNumber);
-  for (int i = 1; i < argc; i++)
+  P = CommandLine;
+  // skip executable path
+  CutToken(P, Buffer);
+  int i = 1;
+  while (CutToken(P, Buffer))
   {
     if (i != SkipParam)
     {
-      if (strlen(Parameters) + strlen(argv[i]) + 4 > sizeof(Parameters))
+      strcat(Parameters, "\"");
+      char* P2 = Parameters + strlen(Parameters);
+      const char* P3 = Buffer;
+      const char* BufferEnd = Buffer + strlen(Buffer) + 1;
+      while (P3 != BufferEnd)
       {
-        throw length_error("Too many parameters");
+        *P2 = *P3;
+        ++P2;
+        if (*P3 == '"')
+        {
+          *P2 = '"';
+          ++P2;
+        }
+        ++P3;
       }
-      strcat(Parameters, "\"");
-      strcat(Parameters, argv[i]);
+
       strcat(Parameters, "\" ");
     }
+    ++i;
   }
 
+  delete[] Buffer;
+
   STARTUPINFO StartupInfo = { sizeof(STARTUPINFO) };
   PROCESS_INFORMATION ProcessInfomation;
 
-  if (CreateProcess(ChildPath, Parameters, NULL, NULL, false, 0, NULL, NULL,
-        &StartupInfo, &ProcessInfomation) != 0)
+  BOOL Result =
+    CreateProcess(ChildPath, Parameters, NULL, NULL, false, 0, NULL, NULL,
+      &StartupInfo, &ProcessInfomation);
+
+  delete[] Parameters;
+
+  if (Result)
   {
     Child = ProcessInfomation.hProcess;
   }
@@ -286,17 +379,6 @@ inline void ProcessPrintEvent(TConsoleCommStruct::TPrintEvent& Event)
 //---------------------------------------------------------------------------
 void BreakInput()
 {
-  FlushConsoleInputBuffer(ConsoleInput);
-  INPUT_RECORD InputRecord;
-  memset(&InputRecord, 0, sizeof(InputRecord));
-  InputRecord.EventType = KEY_EVENT;
-  InputRecord.Event.KeyEvent.bKeyDown = true;
-  InputRecord.Event.KeyEvent.wRepeatCount = 1;
-  InputRecord.Event.KeyEvent.uChar.AsciiChar = '\r';
-
-  unsigned long Written;
-  WriteConsoleInput(ConsoleInput, &InputRecord, 1, &Written);
-
   SetEvent(CancelEvent);
 }
 //---------------------------------------------------------------------------
@@ -557,7 +639,7 @@ BOOL WINAPI HandlerRoutine(DWORD CtrlType)
 }
 //---------------------------------------------------------------------------
 #pragma argsused
-int main(int argc, char* argv[])
+int main(int /*argc*/, char* /*argv*/[])
 {
   unsigned long Result = RESULT_UNKNOWN_ERROR;
 
@@ -583,7 +665,7 @@ int main(int argc, char* argv[])
     try
     {
       #ifndef CONSOLE_TEST
-      InitializeChild(argc, argv, InstanceNumber, Child);
+      InitializeChild(GetCommandLine(), InstanceNumber, Child);
       #endif
 
       try

+ 32 - 13
core/Common.cpp

@@ -125,12 +125,26 @@ AnsiString RootKeyToStr(HKEY RootKey)
 //---------------------------------------------------------------------------
 AnsiString BooleanToEngStr(bool B)
 {
-  return B ? "Yes" : "No";
+  if (B)
+  {
+    return "Yes";
+  }
+  else
+  {
+    return "No";
+  }
 }
 //---------------------------------------------------------------------------
 AnsiString BooleanToStr(bool B)
 {
-  return B ? LoadStr(YES_STR) : LoadStr(NO_STR);
+  if (B)
+  {
+    return LoadStr(YES_STR);
+  }
+  else
+  {
+    return LoadStr(NO_STR);
+  }
 }
 //---------------------------------------------------------------------------
 AnsiString DefaultStr(const AnsiString & Str, const AnsiString & Default)
@@ -443,19 +457,23 @@ bool __fastcall ComparePaths(const AnsiString & Path1, const AnsiString & Path2)
 bool __fastcall IsReservedName(AnsiString FileName)
 {
   int P = FileName.Pos(".");
-  if (P > 0)
-  {
-    FileName.SetLength(P - 1);
-  }
-  static AnsiString Reserved[] = {
-    "CON", "PRN", "AUX", "NUL",
-    "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
-    "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
-  for (int Index = 0; Index < LENOF(Reserved); Index++)
+  int Len = (P > 0) ? P - 1 : FileName.Length();
+  if ((Len == 3) || (Len == 4))
   {
-    if (SameText(FileName, Reserved[Index]))
+    if (P > 0)
     {
-      return true;
+      FileName.SetLength(P - 1);
+    }
+    static AnsiString Reserved[] = {
+      "CON", "PRN", "AUX", "NUL",
+      "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+      "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
+    for (int Index = 0; Index < LENOF(Reserved); Index++)
+    {
+      if (SameText(FileName, Reserved[Index]))
+      {
+        return true;
+      }
     }
   }
   return false;
@@ -1338,6 +1356,7 @@ AnsiString __fastcall EscapeHotkey(const AnsiString & Caption)
   return StringReplace(Caption, "&", "&&", TReplaceFlags() << rfReplaceAll);
 }
 //---------------------------------------------------------------------------
+// duplicated in console's Main.cpp
 bool __fastcall CutToken(AnsiString & Str, AnsiString & Token)
 {
   bool Result;

+ 16 - 0
core/Configuration.cpp

@@ -11,6 +11,7 @@
 #include "TextsCore.h"
 #include "Interface.h"
 #include "CoreMain.h"
+#include "Security.h"
 #include <shfolder.h>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
@@ -527,6 +528,21 @@ void __fastcall TConfiguration::CleanupIniFile()
   }
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::EncryptPassword(AnsiString Password, AnsiString Key)
+{
+  return ::EncryptPassword(Password, Key);
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::DecryptPassword(AnsiString Password, AnsiString Key)
+{
+  return ::DecryptPassword(Password, Key);
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::StronglyRecryptPassword(AnsiString Password, AnsiString /*Key*/)
+{
+  return Password;
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::GetOSVersionStr()
 {
   AnsiString Result;

+ 3 - 0
core/Configuration.h

@@ -156,6 +156,9 @@ public:
   void __fastcall NeverShowBanner(const AnsiString SessionKey, const AnsiString & Banner);
   virtual THierarchicalStorage * CreateScpStorage(bool SessionList);
   void __fastcall TemporaryLogging(const AnsiString ALogFileName);
+  virtual AnsiString __fastcall EncryptPassword(AnsiString Password, AnsiString Key);
+  virtual AnsiString __fastcall DecryptPassword(AnsiString Password, AnsiString Key);
+  virtual AnsiString __fastcall StronglyRecryptPassword(AnsiString Password, AnsiString Key);
 
   __property TVSFixedFileInfo *FixedApplicationInfo  = { read=GetFixedApplicationInfo };
   __property void * ApplicationInfo  = { read=GetApplicationInfo };

+ 2 - 2
core/CopyParam.cpp

@@ -290,8 +290,8 @@ AnsiString __fastcall TCopyParamType::ValidLocalFileName(AnsiString FileName) co
 
     // Windows trim trailing space or dot, hence we must encode it to preserve it
     if (!FileName.IsEmpty() &&
-        ((FileName[FileName.Length()] == ' ')/* ||
-         (FileName[FileName.Length()] == '.')*/))
+        ((FileName[FileName.Length()] == ' ') ||
+         (FileName[FileName.Length()] == '.')))
     {
       ReplaceChar(FileName, FileName.c_str() + FileName.Length() - 1);
     }

+ 6 - 2
core/CoreMain.cpp

@@ -8,6 +8,7 @@
 #include "Interface.h"
 #include "Configuration.h"
 #include "PuttyIntf.h"
+#include "Cryptography.h"
 #ifndef NO_FILEZILLA
 #include "FileZillaIntf.h"
 #endif
@@ -47,12 +48,13 @@ bool __fastcall IsAuthenticationPrompt(TPromptKind Kind)
 //---------------------------------------------------------------------------
 void CoreInitialize()
 {
+  Randomize();
+  CryptographyInitialize();
+
   // configuration needs to be created and loaded before putty is initialized,
   // so that random seed path is known
   Configuration = CreateConfiguration();
 
-  Randomize();
-
   try
   {
     Configuration->Load();
@@ -100,6 +102,8 @@ void CoreFinalize()
   StoredSessions = NULL;
   delete Configuration;
   Configuration = NULL;
+
+  CryptographyFinalize();
 }
 //---------------------------------------------------------------------------
 void CoreSetResourceModule(void * ResourceHandle)

+ 627 - 0
core/Cryptography.cpp

@@ -0,0 +1,627 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include "Common.h"
+#include "PuttyIntf.h"
+#include "Cryptography.h"
+#include <process.h>
+
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <[email protected]>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ -------------------------------------------------------------------------
+
+ This file implements password based file encryption and authentication
+ using AES in CTR mode, HMAC-SHA1 authentication and RFC2898 password
+ based key derivation.
+
+ This is an implementation of HMAC, the FIPS standard keyed hash function
+
+*/
+
+#include <memory.h>
+
+#define sha1_ctx                  SHA_State
+#define sha1_begin(ctx)           putty_SHA_Init(ctx)
+#define sha1_hash(buf, len, ctx)  SHA_Bytes(ctx, buf, len)
+#define sha1_end(dig, ctx)        putty_SHA_Final(ctx, dig)
+
+#define IN_BLOCK_LENGTH     64
+#define OUT_BLOCK_LENGTH    20
+#define HMAC_IN_DATA        0xffffffff
+
+typedef struct
+{   unsigned char   key[IN_BLOCK_LENGTH];
+    sha1_ctx        ctx[1];
+    unsigned int    klen;
+} hmac_ctx;
+
+/* initialise the HMAC context to zero */
+static void hmac_sha1_begin(hmac_ctx cx[1])
+{
+    memset(cx, 0, sizeof(hmac_ctx));
+}
+
+/* input the HMAC key (can be called multiple times)    */
+static void hmac_sha1_key(const unsigned char key[], unsigned long key_len, hmac_ctx cx[1])
+{
+    if(cx->klen + key_len > IN_BLOCK_LENGTH)    /* if the key has to be hashed  */
+    {
+        if(cx->klen <= IN_BLOCK_LENGTH)         /* if the hash has not yet been */
+        {                                       /* started, initialise it and   */
+            sha1_begin(cx->ctx);                /* hash stored key characters   */
+            sha1_hash(cx->key, cx->klen, cx->ctx);
+        }
+
+        sha1_hash(const_cast<unsigned char *>(key), key_len, cx->ctx);       /* hash long key data into hash */
+    }
+    else                                        /* otherwise store key data     */
+        memcpy(cx->key + cx->klen, key, key_len);
+
+    cx->klen += key_len;                        /* update the key length count  */
+}
+
+/* input the HMAC data (can be called multiple times) - */
+/* note that this call terminates the key input phase   */
+static void hmac_sha1_data(const unsigned char data[], unsigned long data_len, hmac_ctx cx[1])
+{   unsigned int i;
+
+    if(cx->klen != HMAC_IN_DATA)                /* if not yet in data phase */
+    {
+        if(cx->klen > IN_BLOCK_LENGTH)          /* if key is being hashed   */
+        {                                       /* complete the hash and    */
+            sha1_end(cx->key, cx->ctx);         /* store the result as the  */
+            cx->klen = OUT_BLOCK_LENGTH;        /* key and set new length   */
+        }
+
+        /* pad the key if necessary */
+        memset(cx->key + cx->klen, 0, IN_BLOCK_LENGTH - cx->klen);
+
+        /* xor ipad into key value  */
+        for(i = 0; i < (IN_BLOCK_LENGTH >> 2); ++i)
+            ((unsigned long*)cx->key)[i] ^= 0x36363636;
+
+        /* and start hash operation */
+        sha1_begin(cx->ctx);
+        sha1_hash(cx->key, IN_BLOCK_LENGTH, cx->ctx);
+
+        /* mark as now in data mode */
+        cx->klen = HMAC_IN_DATA;
+    }
+
+    /* hash the data (if any)       */
+    if(data_len)
+        sha1_hash(const_cast<unsigned char *>(data), data_len, cx->ctx);
+}
+
+/* compute and output the MAC value */
+static void hmac_sha1_end(unsigned char mac[], unsigned long mac_len, hmac_ctx cx[1])
+{   unsigned char dig[OUT_BLOCK_LENGTH];
+    unsigned int i;
+
+    /* if no data has been entered perform a null data phase        */
+    if(cx->klen != HMAC_IN_DATA)
+        hmac_sha1_data((const unsigned char*)0, 0, cx);
+
+    sha1_end(dig, cx->ctx);         /* complete the inner hash      */
+
+    /* set outer key value using opad and removing ipad */
+    for(i = 0; i < (IN_BLOCK_LENGTH >> 2); ++i)
+        ((unsigned long*)cx->key)[i] ^= 0x36363636 ^ 0x5c5c5c5c;
+
+    /* perform the outer hash operation */
+    sha1_begin(cx->ctx);
+    sha1_hash(cx->key, IN_BLOCK_LENGTH, cx->ctx);
+    sha1_hash(dig, OUT_BLOCK_LENGTH, cx->ctx);
+    sha1_end(dig, cx->ctx);
+
+    /* output the hash value            */
+    for(i = 0; i < mac_len; ++i)
+        mac[i] = dig[i];
+}
+
+#define BLOCK_SIZE  16
+
+void aes_set_encrypt_key(const unsigned char in_key[], unsigned int klen, void * cx)
+{
+  call_aes_setup(cx, BLOCK_SIZE, const_cast<unsigned char *>(in_key), klen);
+}
+
+void aes_encrypt_block(const unsigned char in_blk[], unsigned char out_blk[], void * cx)
+{
+  int Index;
+  memcpy(out_blk, in_blk, BLOCK_SIZE);
+  for (Index = 0; Index < 4; Index++)
+  {
+    unsigned char t;
+    t = out_blk[Index * 4 + 0];
+    out_blk[Index * 4 + 0] = out_blk[Index * 4 + 3];
+    out_blk[Index * 4 + 3] = t;
+    t = out_blk[Index * 4 + 1];
+    out_blk[Index * 4 + 1] = out_blk[Index * 4 + 2];
+    out_blk[Index * 4 + 2] = t;
+  }
+  call_aes_encrypt(cx, reinterpret_cast<unsigned int*>(out_blk));
+  for (Index = 0; Index < 4; Index++)
+  {
+    unsigned char t;
+    t = out_blk[Index * 4 + 0];
+    out_blk[Index * 4 + 0] = out_blk[Index * 4 + 3];
+    out_blk[Index * 4 + 3] = t;
+    t = out_blk[Index * 4 + 1];
+    out_blk[Index * 4 + 1] = out_blk[Index * 4 + 2];
+    out_blk[Index * 4 + 2] = t;
+  }
+}
+
+typedef struct
+{   unsigned char   nonce[BLOCK_SIZE];          /* the CTR nonce          */
+    unsigned char   encr_bfr[BLOCK_SIZE];       /* encrypt buffer         */
+    void *          encr_ctx;                   /* encryption context     */
+    hmac_ctx        auth_ctx;                   /* authentication context */
+    unsigned int    encr_pos;                   /* block position (enc)   */
+    unsigned int    pwd_len;                    /* password length        */
+    unsigned int    mode;                       /* File encryption mode   */
+} fcrypt_ctx;
+
+#define MAX_KEY_LENGTH        32
+#define KEYING_ITERATIONS   1000
+#define PWD_VER_LENGTH         2
+
+/*
+    Field lengths (in bytes) versus File Encryption Mode (0 < mode < 4)
+
+    Mode Key Salt  MAC Overhead
+       1  16    8   10       18
+       2  24   12   10       22
+       3  32   16   10       26
+
+   The following macros assume that the mode value is correct.
+*/
+
+#define KEY_LENGTH(mode)        (8 * (mode & 3) + 8)
+#define SALT_LENGTH(mode)       (4 * (mode & 3) + 4)
+#define MAC_LENGTH(mode)        (10)
+
+/* subroutine for data encryption/decryption    */
+/* this could be speeded up a lot by aligning   */
+/* buffers and using 32 bit operations          */
+
+static void derive_key(const unsigned char pwd[],  /* the PASSWORD     */
+               unsigned int pwd_len,        /* and its length   */
+               const unsigned char salt[],  /* the SALT and its */
+               unsigned int salt_len,       /* length           */
+               unsigned int iter,   /* the number of iterations */
+               unsigned char key[], /* space for the output key */
+               unsigned int key_len)/* and its required length  */
+{
+    unsigned int    i, j, k, n_blk;
+    unsigned char uu[OUT_BLOCK_LENGTH], ux[OUT_BLOCK_LENGTH];
+    hmac_ctx c1[1], c2[1], c3[1];
+
+    /* set HMAC context (c1) for password               */
+    hmac_sha1_begin(c1);
+    hmac_sha1_key(pwd, pwd_len, c1);
+
+    /* set HMAC context (c2) for password and salt      */
+    memcpy(c2, c1, sizeof(hmac_ctx));
+    hmac_sha1_data(salt, salt_len, c2);
+
+    /* find the number of SHA blocks in the key         */
+    n_blk = 1 + (key_len - 1) / OUT_BLOCK_LENGTH;
+
+    for(i = 0; i < n_blk; ++i) /* for each block in key */
+    {
+        /* ux[] holds the running xor value             */
+        memset(ux, 0, OUT_BLOCK_LENGTH);
+
+        /* set HMAC context (c3) for password and salt  */
+        memcpy(c3, c2, sizeof(hmac_ctx));
+
+        /* enter additional data for 1st block into uu  */
+        uu[0] = (unsigned char)((i + 1) >> 24);
+        uu[1] = (unsigned char)((i + 1) >> 16);
+        uu[2] = (unsigned char)((i + 1) >> 8);
+        uu[3] = (unsigned char)(i + 1);
+
+        /* this is the key mixing iteration         */
+        for(j = 0, k = 4; j < iter; ++j)
+        {
+            /* add previous round data to HMAC      */
+            hmac_sha1_data(uu, k, c3);
+
+            /* obtain HMAC for uu[]                 */
+            hmac_sha1_end(uu, OUT_BLOCK_LENGTH, c3);
+
+            /* xor into the running xor block       */
+            for(k = 0; k < OUT_BLOCK_LENGTH; ++k)
+                ux[k] ^= uu[k];
+
+            /* set HMAC context (c3) for password   */
+            memcpy(c3, c1, sizeof(hmac_ctx));
+        }
+
+        /* compile key blocks into the key output   */
+        j = 0; k = i * OUT_BLOCK_LENGTH;
+        while(j < OUT_BLOCK_LENGTH && k < key_len)
+            key[k++] = ux[j++];
+    }
+}
+
+static void encr_data(unsigned char data[], unsigned long d_len, fcrypt_ctx cx[1])
+{
+    unsigned long i = 0, pos = cx->encr_pos;
+
+    while(i < d_len)
+    {
+        if(pos == BLOCK_SIZE)
+        {   unsigned int j = 0;
+            /* increment encryption nonce   */
+            while(j < 8 && !++cx->nonce[j])
+                ++j;
+            /* encrypt the nonce to form next xor buffer    */
+            aes_encrypt_block(cx->nonce, cx->encr_bfr, cx->encr_ctx);
+            pos = 0;
+        }
+
+        data[i++] ^= cx->encr_bfr[pos++];
+    }
+
+    cx->encr_pos = pos;
+}
+
+static void fcrypt_init(
+    int mode,                               /* the mode to be used (input)          */
+    const unsigned char pwd[],              /* the user specified password (input)  */
+    unsigned int pwd_len,                   /* the length of the password (input)   */
+    const unsigned char salt[],             /* the salt (input)                     */
+    unsigned char pwd_ver[PWD_VER_LENGTH],  /* 2 byte password verifier (output)    */
+    fcrypt_ctx      cx[1])                  /* the file encryption context (output) */
+{
+    unsigned char kbuf[2 * MAX_KEY_LENGTH + PWD_VER_LENGTH];
+
+    cx->mode = mode;
+    cx->pwd_len = pwd_len;
+
+    /* derive the encryption and authetication keys and the password verifier   */
+    derive_key(pwd, pwd_len, salt, SALT_LENGTH(mode), KEYING_ITERATIONS,
+                        kbuf, 2 * KEY_LENGTH(mode) + PWD_VER_LENGTH);
+
+    /* initialise the encryption nonce and buffer pos   */
+    cx->encr_pos = BLOCK_SIZE;
+    /* if we need a random component in the encryption  */
+    /* nonce, this is where it would have to be set     */
+    memset(cx->nonce, 0, BLOCK_SIZE * sizeof(unsigned char));
+
+    /* initialise for encryption using key 1            */
+    cx->encr_ctx = call_aes_make_context();
+    aes_set_encrypt_key(kbuf, KEY_LENGTH(mode), cx->encr_ctx);
+
+    /* initialise for authentication using key 2        */
+    hmac_sha1_begin(&cx->auth_ctx);
+    hmac_sha1_key(kbuf + KEY_LENGTH(mode), KEY_LENGTH(mode), &cx->auth_ctx);
+
+    if (pwd_ver != NULL)
+    {
+      memcpy(pwd_ver, kbuf + 2 * KEY_LENGTH(mode), PWD_VER_LENGTH);
+    }
+}
+
+/* perform 'in place' encryption and authentication */
+
+static void fcrypt_encrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1])
+{
+    encr_data(data, data_len, cx);
+    hmac_sha1_data(data, data_len, &cx->auth_ctx);
+}
+
+/* perform 'in place' authentication and decryption */
+
+static void fcrypt_decrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1])
+{
+    hmac_sha1_data(data, data_len, &cx->auth_ctx);
+    encr_data(data, data_len, cx);
+}
+
+/* close encryption/decryption and return the MAC value */
+
+static int fcrypt_end(unsigned char mac[], fcrypt_ctx cx[1])
+{
+    hmac_sha1_end(mac, MAC_LENGTH(cx->mode), &cx->auth_ctx);
+    call_aes_free_context(cx->encr_ctx);
+    return MAC_LENGTH(cx->mode);    /* return MAC length in bytes   */
+}
+//---------------------------------------------------------------------------
+#define PASSWORD_MANAGER_AES_MODE 3
+//---------------------------------------------------------------------------
+static void __fastcall FillBufferWithRandomData(char * Buf, int Len)
+{
+  while (Len > 0)
+  {
+    *Buf = static_cast<char>((rand() >> 7) & 0xFF);
+    Buf++;
+    Len--;
+  }
+}
+//---------------------------------------------------------------------------
+static AnsiString __fastcall AES256Salt()
+{
+  AnsiString Result;
+  Result.SetLength(SALT_LENGTH(PASSWORD_MANAGER_AES_MODE));
+  FillBufferWithRandomData(Result.c_str(), Result.Length());
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall AES256EncyptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Salt, AnsiString & Output, AnsiString & Mac)
+{
+  fcrypt_ctx aes;
+  if (Salt.IsEmpty())
+  {
+    Salt = AES256Salt();
+  }
+  assert(Salt.Length() == SALT_LENGTH(PASSWORD_MANAGER_AES_MODE));
+  fcrypt_init(PASSWORD_MANAGER_AES_MODE,
+    reinterpret_cast<const unsigned char *>(Password.c_str()), Password.Length(),
+    reinterpret_cast<const unsigned char *>(Salt.c_str()), NULL, &aes);
+  Output = Input;
+  Output.Unique();
+  fcrypt_encrypt(reinterpret_cast<unsigned char *>(Output.c_str()), Output.Length(), &aes);
+  Mac.SetLength(MAC_LENGTH(PASSWORD_MANAGER_AES_MODE));
+  fcrypt_end(reinterpret_cast<unsigned char *>(Mac.c_str()), &aes);
+}
+//---------------------------------------------------------------------------
+void __fastcall AES256EncyptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Output)
+{
+  AnsiString Salt;
+  AnsiString Encrypted;
+  AnsiString Mac;
+  AES256EncyptWithMAC(Input, Password, Salt, Encrypted, Mac);
+  Output = Salt + Encrypted + Mac;
+}
+//---------------------------------------------------------------------------
+bool __fastcall AES256DecryptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString Salt, AnsiString & Output, AnsiString Mac)
+{
+  fcrypt_ctx aes;
+  assert(Salt.Length() == SALT_LENGTH(PASSWORD_MANAGER_AES_MODE));
+  fcrypt_init(PASSWORD_MANAGER_AES_MODE,
+    reinterpret_cast<const unsigned char *>(Password.c_str()), Password.Length(),
+    reinterpret_cast<const unsigned char *>(Salt.c_str()), NULL, &aes);
+  Output = Input;
+  Output.Unique();
+  fcrypt_decrypt(reinterpret_cast<unsigned char *>(Output.c_str()), Output.Length(), &aes);
+  AnsiString Mac2;
+  Mac2.SetLength(MAC_LENGTH(PASSWORD_MANAGER_AES_MODE));
+  assert(Mac.Length() == Mac2.Length());
+  fcrypt_end(reinterpret_cast<unsigned char *>(Mac2.c_str()), &aes);
+  return (Mac2 == Mac);
+}
+//---------------------------------------------------------------------------
+bool __fastcall AES256DecryptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Output)
+{
+  bool Result =
+    Input.Length() > SALT_LENGTH(PASSWORD_MANAGER_AES_MODE) + MAC_LENGTH(PASSWORD_MANAGER_AES_MODE);
+  if (Result)
+  {
+    AnsiString Salt = Input.SubString(1, SALT_LENGTH(PASSWORD_MANAGER_AES_MODE));
+    AnsiString Encrypted =
+      Input.SubString(SALT_LENGTH(PASSWORD_MANAGER_AES_MODE) + 1,
+        Input.Length() - SALT_LENGTH(PASSWORD_MANAGER_AES_MODE) - MAC_LENGTH(PASSWORD_MANAGER_AES_MODE));
+    AnsiString Mac =
+      Input.SubString(Input.Length() - MAC_LENGTH(PASSWORD_MANAGER_AES_MODE) + 1,
+        MAC_LENGTH(PASSWORD_MANAGER_AES_MODE));
+    Result = AES256DecryptWithMAC(Encrypted, Password, Salt, Output, Mac);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall AES256CreateVerifier(AnsiString Input, AnsiString & Verifier)
+{
+  AnsiString Salt = AES256Salt();
+  AnsiString Dummy = AES256Salt();
+
+  AnsiString Encrypted;
+  AnsiString Mac;
+  AES256EncyptWithMAC(Dummy, Input, Salt, Encrypted, Mac);
+
+  Verifier = Salt + Dummy + Mac;
+}
+//---------------------------------------------------------------------------
+bool __fastcall AES256Verify(AnsiString Input, AnsiString Verifier)
+{
+  int SaltLength = SALT_LENGTH(PASSWORD_MANAGER_AES_MODE);
+  AnsiString Salt = Verifier.SubString(1, SaltLength);
+  AnsiString Dummy = Verifier.SubString(SaltLength + 1, SaltLength);
+  AnsiString Mac = Verifier.SubString(SaltLength + SaltLength + 1, MAC_LENGTH(PASSWORD_MANAGER_AES_MODE));
+
+  AnsiString Encrypted;
+  AnsiString Mac2;
+  AES256EncyptWithMAC(Dummy, Input, Salt, Encrypted, Mac2);
+
+  assert(Mac2.Length() == Mac.Length());
+
+  return (Mac == Mac2);
+}
+//---------------------------------------------------------------------------
+unsigned char SScrambleTable[256] =
+{
+    0, 223, 235, 233, 240, 185,  88, 102,  22, 130,  27,  53,  79, 125,  66, 201,
+   90,  71,  51,  60, 134, 104, 172, 244, 139,  84,  91,  12, 123, 155, 237, 151,
+  192,   6,  87,  32, 211,  38, 149,  75, 164, 145,  52, 200, 224, 226, 156,  50,
+  136, 190, 232,  63, 129, 209, 181, 120,  28,  99, 168,  94, 198,  40, 238, 112,
+   55, 217, 124,  62, 227,  30,  36, 242, 208, 138, 174, 231,  26,  54, 214, 148,
+   37, 157,  19, 137, 187, 111, 228,  39, 110,  17, 197, 229, 118, 246, 153,  80,
+   21, 128,  69, 117, 234,  35,  58,  67,  92,   7, 132, 189,   5, 103,  10,  15,
+  252, 195,  70, 147, 241, 202, 107,  49,  20, 251, 133,  76, 204,  73, 203, 135,
+  184,  78, 194, 183,   1, 121, 109,  11, 143, 144, 171, 161,  48, 205, 245,  46,
+   31,  72, 169, 131, 239, 160,  25, 207, 218, 146,  43, 140, 127, 255,  81,  98,
+   42, 115, 173, 142, 114,  13,   2, 219,  57,  56,  24, 126,   3, 230,  47, 215,
+    9,  44, 159,  33, 249,  18,  93,  95,  29, 113, 220,  89,  97, 182, 248,  64,
+   68,  34,   4,  82,  74, 196, 213, 165, 179, 250, 108, 254,  59,  14, 236, 175,
+   85, 199,  83, 106,  77, 178, 167, 225,  45, 247, 163, 158,   8, 221,  61, 191,
+  119,  16, 253, 105, 186,  23, 170, 100, 216,  65, 162, 122, 150, 176, 154, 193,
+  206, 222, 188, 152, 210, 243,  96,  41,  86, 180, 101, 177, 166, 141, 212, 116
+};
+//---------------------------------------------------------------------------
+unsigned char * ScrambleTable;
+unsigned char * UnscrambleTable;
+//---------------------------------------------------------------------------
+void __fastcall ScramblePassword(AnsiString & Password)
+{
+  #define SCRAMBLE_LENGTH_EXTENSION 50
+  int Len = Password.Length();
+  char * Buf = new char[Len + SCRAMBLE_LENGTH_EXTENSION];
+  int Padding = (((Len + 3) / 17) * 17 + 17) - 3 - Len;
+  for (int Index = 0; Index < Padding; Index++)
+  {
+    int P = 0;
+    while ((P <= 0) || (P > 255) || (P >= '0') && (P <= '9'))
+    {
+      P = (int)((double)rand() / ((double)RAND_MAX / 256.0));
+    }
+    Buf[Index] = (unsigned char)P;
+  }
+  Buf[Padding] = (char)('0' + (Len % 10));
+  Buf[Padding + 1] = (char)('0' + ((Len / 10) % 10));
+  Buf[Padding + 2] = (char)('0' + ((Len / 100) % 10));
+  strcpy(Buf + Padding + 3, Password.c_str());
+  char * S = Buf;
+  int Last = 31;
+  while (*S != '\0')
+  {
+    Last = (Last + (unsigned char)*S) % 255 + 1;
+    *S = ScrambleTable[Last];
+    S++;
+  }
+  Password = Buf;
+  memset(Buf, 0, Len + SCRAMBLE_LENGTH_EXTENSION);
+  delete[] Buf;
+}
+//---------------------------------------------------------------------------
+bool __fastcall UnscramblePassword(AnsiString & Password)
+{
+  Password.Unique();
+  char * S = Password.c_str();
+  int Last = 31;
+  while (*S != '\0')
+  {
+    int X = (int)UnscrambleTable[(unsigned char)*S] - 1 - (Last % 255);
+    if (X <= 0)
+    {
+      X += 255;
+    }
+    *S = (char)X;
+    Last = (Last + X) % 255 + 1;
+    S++;
+  }
+
+  S = Password.c_str();
+  while ((*S != '\0') && ((*S < '0') || (*S > '9')))
+  {
+    S++;
+  }
+  bool Result = false;
+  if (strlen(S) >= 3)
+  {
+    int Len = (S[0] - '0') + 10 * (S[1] - '0') + 100 * (S[2] - '0');
+    int Total = (((Len + 3) / 17) * 17 + 17);
+    if ((Len >= 0) && (Total == Password.Length()) && (Total - (S - Password.c_str()) - 3 == Len))
+    {
+      Password.Delete(1, Password.Length() - Len);
+      Result = true;
+    }
+  }
+  if (!Result)
+  {
+    Password = "";
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall CryptographyInitialize()
+{
+  ScrambleTable = SScrambleTable;
+  UnscrambleTable = new unsigned char[256];
+  for (int Index = 0; Index < 256; Index++)
+  {
+    UnscrambleTable[SScrambleTable[Index]] = (unsigned char)Index;
+  }
+  srand((unsigned int)time(NULL) ^ (unsigned int)getpid());
+}
+//---------------------------------------------------------------------------
+void __fastcall CryptographyFinalize()
+{
+  delete[] UnscrambleTable;
+  UnscrambleTable = NULL;
+  ScrambleTable = NULL;
+}
+//---------------------------------------------------------------------------
+int __fastcall PasswordMaxLength()
+{
+  return 128;
+}
+//---------------------------------------------------------------------------
+int __fastcall IsValidPassword(AnsiString Password)
+{
+  if (Password.IsEmpty() || (Password.Length() > PasswordMaxLength()))
+  {
+    return -1;
+  }
+  else
+  {
+    int A = 0;
+    int B = 0;
+    int C = 0;
+    int D = 0;
+    for (int Index = 1; Index <= Password.Length(); Index++)
+    {
+      if ((Password[Index] >= 'a') && (Password[Index] <= 'z'))
+      {
+        A = 1;
+      }
+      else if ((Password[Index] >= 'A') && (Password[Index] <= 'Z'))
+      {
+        B = 1;
+      }
+      else if ((Password[Index] >= '0') && (Password[Index] <= '9'))
+      {
+        C = 1;
+      }
+      else
+      {
+        D = 1;
+      }
+    }
+    return (Password.Length() >= 6) && ((A + B + C + D) >= 2);
+  }
+}

+ 22 - 0
core/Cryptography.h

@@ -0,0 +1,22 @@
+//---------------------------------------------------------------------------
+#ifndef CryptographyH
+#define CryptographyH
+//---------------------------------------------------------------------------
+void __fastcall CryptographyInitialize();
+void __fastcall CryptographyFinalize();
+void __fastcall ScramblePassword(AnsiString & Password);
+bool __fastcall UnscramblePassword(AnsiString & Password);
+void __fastcall AES256EncyptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Salt, AnsiString & Output, AnsiString & Mac);
+void __fastcall AES256EncyptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Output);
+bool __fastcall AES256DecryptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString Salt, AnsiString & Output, AnsiString Mac);
+bool __fastcall AES256DecryptWithMAC(AnsiString Input, AnsiString Password,
+  AnsiString & Output);
+void __fastcall AES256CreateVerifier(AnsiString Input, AnsiString & Verifier);
+bool __fastcall AES256Verify(AnsiString Input, AnsiString Verifier);
+int __fastcall IsValidPassword(AnsiString Password);
+int __fastcall PasswordMaxLength();
+//---------------------------------------------------------------------------
+#endif

+ 40 - 9
core/FtpFileSystem.cpp

@@ -23,6 +23,8 @@
 //---------------------------------------------------------------------------
 const int DummyCodeClass = 8;
 const int DummyTimeoutCode = 801;
+const int DummyCancelCode = 802;
+const int DummyDisconnectCode = 803;
 //---------------------------------------------------------------------------
 class TFileZillaImpl : public TFileZillaIntf
 {
@@ -219,6 +221,7 @@ __fastcall TFTPFileSystem::TFTPFileSystem(TTerminal * ATerminal):
   FFileSystemInfo.ProtocolBaseName = "FTP";
   FFileSystemInfo.ProtocolName = FFileSystemInfo.ProtocolBaseName;
   FTimeoutStatus = LoadStr(IDS_ERRORMSG_TIMEOUT);
+  FDisconnectStatus = LoadStr(IDS_STATUSMSG_DISCONNECTED);
 }
 //---------------------------------------------------------------------------
 __fastcall TFTPFileSystem::~TFTPFileSystem()
@@ -724,12 +727,18 @@ bool __fastcall TFTPFileSystem::ConfirmOverwrite(AnsiString & FileName,
   const TOverwriteFileParams * FileParams, int Params, bool AutoResume)
 {
   bool Result;
+  bool CanAutoResume = FLAGSET(Params, cpNoConfirmation) && AutoResume;
+  // when resuming transfer after interrupted connection,
+  // do nothing (dummy resume) when the files has the same size.
+  // this is workaround for servers that strangely fails just after successful
+  // upload.
   bool CanResume =
     (FileParams != NULL) &&
-    (FileParams->DestSize < FileParams->SourceSize);
+    (((FileParams->DestSize < FileParams->SourceSize)) ||
+     ((FileParams->DestSize == FileParams->SourceSize) && CanAutoResume));
 
   int Answer;
-  if (FLAGSET(Params, cpNoConfirmation) && CanResume && AutoResume)
+  if (CanAutoResume && CanResume)
   {
     Answer = qaRetry;
   }
@@ -893,6 +902,8 @@ void __fastcall TFTPFileSystem::FileTransfer(const AnsiString & FileName,
   FILE_OPERATION_LOOP(FMTLOAD(TRANSFER_ERROR, (FileName)),
     FFileZillaIntf->FileTransfer(LocalFile.c_str(), RemoteFile.c_str(),
       RemotePath.c_str(), Get, Size, Type, &UserData);
+    // we may actually catch reponse code of the listing
+    // command (when checking for existence of the remote file)
     unsigned int Reply = WaitForCommandReply();
     GotReply(Reply, FLAGMASK(FFileTransferCancelled, REPLY_ALLOW_CANCEL));
   );
@@ -2191,6 +2202,11 @@ void __fastcall TFTPFileSystem::PoolForFatalNonCommandReply()
   }
 }
 //---------------------------------------------------------------------------
+bool __fastcall TFTPFileSystem::NoFinalLastCode()
+{
+  return (FLastCodeClass == 0) || (FLastCodeClass == 1);
+}
+//---------------------------------------------------------------------------
 bool __fastcall TFTPFileSystem::KeepWaitingForReply(unsigned int & ReplyToAwait, bool WantLastCode)
 {
   // to keep waiting,
@@ -2200,7 +2216,7 @@ bool __fastcall TFTPFileSystem::KeepWaitingForReply(unsigned int & ReplyToAwait,
   return
      (FReply == 0) &&
      ((ReplyToAwait == 0) ||
-      (WantLastCode && ((FLastCodeClass == 0) || (FLastCodeClass == 1))));
+      (WantLastCode && NoFinalLastCode()));
 }
 //---------------------------------------------------------------------------
 void __fastcall TFTPFileSystem::DoWaitForReply(unsigned int & ReplyToAwait, bool WantLastCode)
@@ -2386,8 +2402,7 @@ void __fastcall TFTPFileSystem::GotReply(unsigned int Reply, unsigned int Flags,
 
         if (FLastCode == 425)
         {
-          assert(FTerminal->SessionData->FtpPasvMode);
-          if (FTerminal->SessionData->FtpPasvMode)
+          if (!FTerminal->SessionData->FtpPasvMode)
           {
             MoreMessages->Add(LoadStr(FTP_CANNOT_OPEN_ACTIVE_CONNECTION));
           }
@@ -2645,12 +2660,21 @@ bool __fastcall TFTPFileSystem::HandleStatus(const char * AStatus, int Type)
     case TFileZillaIntf::LOG_WARNING:
       // when timeout message occurs, break loop waiting for response code
       // by setting dummy one
-      if ((Type == TFileZillaIntf::LOG_ERROR) &&
-          (Status == FTimeoutStatus))
+      if (Type == TFileZillaIntf::LOG_ERROR)
       {
-        if (FLastCode == 0)
+        if (Status == FTimeoutStatus)
+        {
+          if (NoFinalLastCode())
+          {
+            SetLastCode(DummyTimeoutCode);
+          }
+        }
+        else if (Status == FDisconnectStatus)
         {
-          SetLastCode(DummyTimeoutCode);
+          if (NoFinalLastCode())
+          {
+            SetLastCode(DummyDisconnectCode);
+          }
         }
       }
       // there can be multiple error messages associated with single failure
@@ -2829,6 +2853,13 @@ bool __fastcall TFTPFileSystem::HandleAsynchRequestOverwrite(
     // remember the answer for the retries
     UserData.OverwriteResult = RequestResult;
 
+    if (RequestResult == TFileZillaIntf::FILEEXISTS_SKIP)
+    {
+      // when user chosses not to overwrite, break loop waiting for response code
+      // by setting dummy one, az FZAPI won't do anything then
+      SetLastCode(DummyTimeoutCode);
+    }
+
     return true;
   }
 }

+ 2 - 0
core/FtpFileSystem.h

@@ -111,6 +111,7 @@ protected:
   void __fastcall HandleReplyStatus(AnsiString Response);
   void __fastcall DoWaitForReply(unsigned int& ReplyToAwait, bool WantLastCode);
   bool __fastcall KeepWaitingForReply(unsigned int& ReplyToAwait, bool WantLastCode);
+  inline bool __fastcall NoFinalLastCode();
 
   bool __fastcall HandleStatus(const char * Status, int Type);
   bool __fastcall HandleAsynchRequestOverwrite(
@@ -200,6 +201,7 @@ private:
   int FLastCodeClass;
   int FLastReadDirectoryProgress;
   AnsiString FTimeoutStatus;
+  AnsiString FDisconnectStatus;
   TStrings * FLastResponse;
   TStrings * FLastError;
   AnsiString FSystem;

+ 6 - 0
core/Interface.h

@@ -82,4 +82,10 @@ enum TPromptKind
 
 bool __fastcall IsAuthenticationPrompt(TPromptKind Kind);
 //---------------------------------------------------------------------------
+typedef void __fastcall (__closure *TFileFoundEvent)
+  (TTerminal * Terminal, const AnsiString FileName, const TRemoteFile * File,
+   bool & Cancel);
+typedef void __fastcall (__closure *TFindingFileEvent)
+  (TTerminal * Terminal, const AnsiString Directory, bool & Cancel);
+//---------------------------------------------------------------------------
 #endif

+ 3 - 0
core/PuttyTools.h

@@ -13,4 +13,7 @@ __int64 __fastcall ParseSize(AnsiString SizeStr);
 //---------------------------------------------------------------------------
 bool __fastcall HasGSSAPI();
 //---------------------------------------------------------------------------
+void __fastcall AES256EncodeWithMAC(char * Data, size_t Len, const char * Password,
+  size_t PasswordLen, const char * Salt);
+//---------------------------------------------------------------------------
 #endif

+ 2 - 1
core/RemoteFiles.cpp

@@ -396,7 +396,8 @@ int __fastcall FakeFileImageIndex(AnsiString FileName, unsigned long Attrs,
   // On Win2k we get icon of "ZIP drive" for ".." (parent directory)
   if ((FileName == "..") ||
       ((FileName.Length() == 2) && (FileName[2] == ':') &&
-       (tolower(FileName[1]) >= 'a') && (tolower(FileName[1]) <= 'z')))
+       (tolower(FileName[1]) >= 'a') && (tolower(FileName[1]) <= 'z')) ||
+      IsReservedName(FileName))
   {
     FileName = "dumb";
   }

+ 5 - 1
core/ScpFileSystem.cpp

@@ -741,7 +741,11 @@ void __fastcall TSCPFileSystem::DetectReturnVar()
       {
         FTerminal->LogEvent(FORMAT("Trying \"$%s\".", (ReturnVars[Index])));
         ExecCommand(fsVarValue, ARRAYOFCONST((ReturnVars[Index])));
-        if ((Output->Count != 1) || (StrToIntDef(Output->Strings[0], 256) > 255)) Abort();
+        if ((Output->Count != 1) || (StrToIntDef(Output->Strings[0], 256) > 255))
+        {
+          FTerminal->LogEvent("The response is not numerical exit code");
+          Abort();
+        }
       }
       catch (EFatal &E)
       {

+ 66 - 61
core/Script.cpp

@@ -1142,7 +1142,7 @@ void __fastcall TScript::OptionImpl(AnsiString OptionName, AnsiString ValueName)
     if (SetValue)
     {
       int Value;
-      if (ValueName == ToggleNames[Off])
+      if (SameText(ValueName, ToggleNames[Off]))
       {
         Value = 0;
       }
@@ -1744,87 +1744,92 @@ void __fastcall TManagementScript::Connect(const AnsiString Session,
 {
   try
   {
-    DoConnect(Session, Options, CheckParams);
-  }
-  catch(Exception & E)
-  {
-    if (!HandleExtendedException(&E))
-    {
-      throw;
-    }
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TManagementScript::DoConnect(const AnsiString & Session,
-  TOptions * Options, bool CheckParams)
-{
-  bool DefaultsOnly;
+    bool DefaultsOnly;
 
-  TSessionData * Data = FStoredSessions->ParseUrl(Session, Options, DefaultsOnly);
-  try
-  {
-    if (CheckParams)
-    {
-      TScriptCommands::CheckParams(Options, false);
-    }
-
-    assert(Data != NULL);
-
-    if (!Data->CanLogin || DefaultsOnly)
+    TSessionData * Data = FStoredSessions->ParseUrl(Session, Options, DefaultsOnly);
+    try
     {
-      if (Data->HostName.IsEmpty())
+      if (CheckParams)
       {
-        AnsiString Value;
-        Input(LoadStr(SCRIPT_HOST_PROMPT), Value, false);
-        Data->HostName = Value;
+        TScriptCommands::CheckParams(Options, false);
       }
 
-      assert(Data->CanLogin);
-    }
-
-    TTerminal * Terminal = FTerminalList->NewTerminal(Data);
-    try
-    {
-      Terminal->AutoReadDirectory = false;
-
-      Terminal->OnInformation = TerminalInformation;
-      Terminal->OnPromptUser = TerminalPromptUser;
-      Terminal->OnShowExtendedException = OnShowExtendedException;
-      Terminal->OnQueryUser = OnTerminalQueryUser;
-      Terminal->OnProgress = TerminalOperationProgress;
-      Terminal->OnFinished = TerminalOperationFinished;
+      assert(Data != NULL);
 
-      ConnectTerminal(Terminal);
-    }
-    catch(...)
-    {
-      FTerminalList->FreeTerminal(Terminal);
-      throw;
-    }
+      if (!Data->CanLogin || DefaultsOnly)
+      {
+        if (Data->HostName.IsEmpty())
+        {
+          AnsiString Value;
+          Input(LoadStr(SCRIPT_HOST_PROMPT), Value, false);
+          Data->HostName = Value;
+        }
 
-    FTerminal = Terminal;
+        assert(Data->CanLogin);
+      }
 
-    if (!Data->LocalDirectory.IsEmpty())
-    {
+      TTerminal * Terminal = FTerminalList->NewTerminal(Data);
       try
       {
-        DoChangeLocalDirectory(Data->LocalDirectory);
+        Terminal->AutoReadDirectory = false;
+
+        Terminal->OnInformation = TerminalInformation;
+        Terminal->OnPromptUser = TerminalPromptUser;
+        Terminal->OnShowExtendedException = OnShowExtendedException;
+        Terminal->OnQueryUser = OnTerminalQueryUser;
+        Terminal->OnProgress = TerminalOperationProgress;
+        Terminal->OnFinished = TerminalOperationFinished;
+
+        ConnectTerminal(Terminal);
       }
       catch(Exception & E)
       {
-        if (!HandleExtendedException(&E))
+        // make sure errors (mainly fatal ones) are associated
+        // with this terminal, not the last active one
+        bool Handled = HandleExtendedException(&E, Terminal);
+        FTerminalList->FreeTerminal(Terminal);
+        Terminal = NULL;
+        if (!Handled)
         {
           throw;
         }
       }
+
+      if (Terminal != NULL)
+      {
+        FTerminal = Terminal;
+
+        if (!Data->LocalDirectory.IsEmpty())
+        {
+          try
+          {
+            DoChangeLocalDirectory(ExpandFileName(Data->LocalDirectory));
+          }
+          catch(Exception & E)
+          {
+            if (!HandleExtendedException(&E))
+            {
+              throw;
+            }
+          }
+        }
+
+        PrintActiveSession();
+      }
     }
+    __finally
+    {
+      delete Data;
+    }
+
   }
-  __finally
+  catch(Exception & E)
   {
-    delete Data;
+    if (!HandleExtendedException(&E))
+    {
+      throw;
+    }
   }
-
-  PrintActiveSession();
 }
 //---------------------------------------------------------------------------
 void __fastcall TManagementScript::DoClose(TTerminal * Terminal)

+ 0 - 2
core/Script.h

@@ -173,8 +173,6 @@ protected:
   bool __fastcall QueryCancel();
   void __fastcall TerminalSynchronizeDirectory(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, bool & Continue, bool Collect);
-  void __fastcall DoConnect(const AnsiString & Session, TOptions * Options,
-    bool CheckParams);
   void __fastcall DoChangeLocalDirectory(AnsiString Directory);
   void __fastcall DoClose(TTerminal * Terminal);
   virtual bool __fastcall HandleExtendedException(Exception * E,

+ 26 - 1
core/Security.cpp

@@ -2,10 +2,14 @@
 #include <vcl.h>
 #pragma hdrstop
 
+#include "Common.h"
 #include "Security.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
+#define PWALG_SIMPLE_INTERNAL 0x00
+#define PWALG_SIMPLE_EXTERNAL 0x01
+//---------------------------------------------------------------------------
 AnsiString SimpleEncryptChar(unsigned char Ch)
 {
   Ch = (unsigned char)((~Ch) ^ PWALG_SIMPLE_MAGIC);
@@ -37,7 +41,7 @@ AnsiString EncryptPassword(AnsiString Password, AnsiString Key, Integer /* Algor
   Shift = (Password.Length() < PWALG_SIMPLE_MAXLEN) ?
     (unsigned char)random(PWALG_SIMPLE_MAXLEN - Password.Length()) : 0;
   Result += SimpleEncryptChar((Char)PWALG_SIMPLE_FLAG); // Flag
-  Result += SimpleEncryptChar((Char)0); // Dummy
+  Result += SimpleEncryptChar((Char)PWALG_SIMPLE_INTERNAL); // Dummy
   Result += SimpleEncryptChar((Char)Password.Length());
   Result += SimpleEncryptChar((Char)Shift);
   for (Index = 0; Index < Shift; Index++)
@@ -73,3 +77,24 @@ AnsiString DecryptPassword(AnsiString Password, AnsiString Key, Integer /* Algor
   return Result;
 }
 //---------------------------------------------------------------------------
+AnsiString SetExternalEncryptedPassword(AnsiString Password)
+{
+  AnsiString Result;
+  Result += SimpleEncryptChar((Char)PWALG_SIMPLE_FLAG);
+  Result += SimpleEncryptChar((Char)PWALG_SIMPLE_EXTERNAL);
+  Result += StrToHex(Password);
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool GetExternalEncryptedPassword(AnsiString Encrypted, AnsiString & Password)
+{
+  bool Result =
+    (SimpleDecryptNextChar(Encrypted) == PWALG_SIMPLE_FLAG) &&
+    (SimpleDecryptNextChar(Encrypted) == PWALG_SIMPLE_EXTERNAL);
+  if (Result)
+  {
+    Password = HexToStr(Encrypted);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------

+ 2 - 0
core/Security.h

@@ -9,5 +9,7 @@
 #define PWALG_SIMPLE_FLAG 0xFF
 AnsiString EncryptPassword(AnsiString Password, AnsiString Key, Integer Algorithm = PWALG_SIMPLE);
 AnsiString DecryptPassword(AnsiString Password, AnsiString Key, Integer Algorithm = PWALG_SIMPLE);
+AnsiString SetExternalEncryptedPassword(AnsiString Password);
+bool GetExternalEncryptedPassword(AnsiString Encrypted, AnsiString & Password);
 //---------------------------------------------------------------------------
 #endif

+ 158 - 45
core/SessionData.cpp

@@ -8,7 +8,6 @@
 #include "Exceptions.h"
 #include "FileBuffer.h"
 #include "CoreMain.h"
-#include "Security.h"
 #include "TextsCore.h"
 #include "PuttyIntf.h"
 #include "RemoteFiles.h"
@@ -509,7 +508,15 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     TunnelHostName = Storage->ReadString("TunnelHostName", TunnelHostName);
     if (!Configuration->DisablePasswordStoring)
     {
-      FTunnelPassword = Storage->ReadString("TunnelPassword", FTunnelPassword);
+      if (Storage->ValueExists("TunnelPasswordPlain"))
+      {
+        TunnelPassword = Storage->ReadString("TunnelPasswordPlain", TunnelPassword);
+        RewritePassword = true;
+      }
+      else
+      {
+        FTunnelPassword = Storage->ReadString("TunnelPassword", FTunnelPassword);
+      }
     }
     TunnelPublicKeyFile = Storage->ReadString("TunnelPublicKeyFile", TunnelPublicKeyFile);
     TunnelLocalPortNumber = Storage->ReadInteger("TunnelLocalPortNumber", TunnelLocalPortNumber);
@@ -544,6 +551,11 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
         {
           Storage->WriteString("Password", FPassword);
         }
+        Storage->DeleteValue("TunnelPasswordPlain");
+        if (!TunnelPassword.IsEmpty())
+        {
+          Storage->WriteString("TunnelPassword", FTunnelPassword);
+        }
         Storage->CloseSubKey();
       }
     }
@@ -577,15 +589,6 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
 
     WRITE_DATA(String, HostName);
     WRITE_DATA(Integer, PortNumber);
-    if (!Configuration->DisablePasswordStoring && !PuttyExport && !Password.IsEmpty())
-    {
-      WRITE_DATA_EX(String, "Password", FPassword, );
-    }
-    else
-    {
-      Storage->DeleteValue("Password");
-    }
-    Storage->DeleteValue("PasswordPlain");
     WRITE_DATA(Bool, Passwordless);
     WRITE_DATA_EX(Integer, "PingInterval", PingInterval / 60, );
     WRITE_DATA_EX(Integer, "PingIntervalSecs", PingInterval % 60, );
@@ -720,24 +723,6 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
     WRITE_DATA(String, ProxyHost);
     WRITE_DATA(Integer, ProxyPort);
     WRITE_DATA(String, ProxyUsername);
-    if (PuttyExport)
-    {
-      // save password unencrypted
-      WRITE_DATA(String, ProxyPassword);
-    }
-    else
-    {
-      // save password encrypted
-      if (!ProxyPassword.IsEmpty())
-      {
-        WRITE_DATA_EX(String, "ProxyPasswordEnc", FProxyPassword, );
-      }
-      else
-      {
-        Storage->DeleteValue("ProxyPasswordEnc");
-      }
-      Storage->DeleteValue("ProxyPassword");
-    }
     if (ProxyMethod == pmCmd)
     {
       WRITE_DATA_EX(StringRaw, "ProxyTelnetCommand", ProxyLocalCommand, );
@@ -791,14 +776,6 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
       WRITE_DATA(String, TunnelHostName);
       WRITE_DATA(Integer, TunnelPortNumber);
       WRITE_DATA(String, TunnelUserName);
-      if (!Configuration->DisablePasswordStoring && !TunnelPassword.IsEmpty())
-      {
-        WRITE_DATA_EX(String, "TunnelPassword", FTunnelPassword, );
-      }
-      else
-      {
-        Storage->DeleteValue("TunnelPassword");
-      }
       WRITE_DATA(String, TunnelPublicKeyFile);
       WRITE_DATA(Integer, TunnelLocalPortNumber);
 
@@ -815,6 +792,73 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
       WRITE_DATA(String, CustomParam2);
     }
 
+    SavePasswords(Storage, PuttyExport);
+
+    Storage->CloseSubKey();
+  }
+}
+//---------------------------------------------------------------------
+void __fastcall TSessionData::SavePasswords(THierarchicalStorage * Storage, bool PuttyExport)
+{
+  if (!Configuration->DisablePasswordStoring && !PuttyExport && !FPassword.IsEmpty())
+  {
+    Storage->WriteString("Password", StronglyRecryptPassword(FPassword, UserName+HostName));
+  }
+  else
+  {
+    Storage->DeleteValue("Password");
+  }
+  Storage->DeleteValue("PasswordPlain");
+
+  if (PuttyExport)
+  {
+    // save password unencrypted
+    Storage->WriteString("ProxyPassword", ProxyPassword);
+  }
+  else
+  {
+    // save password encrypted
+    if (!FProxyPassword.IsEmpty())
+    {
+      Storage->WriteString("ProxyPasswordEnc", StronglyRecryptPassword(FProxyPassword, ProxyUsername+ProxyHost));
+    }
+    else
+    {
+      Storage->DeleteValue("ProxyPasswordEnc");
+    }
+    Storage->DeleteValue("ProxyPassword");
+
+    if (!Configuration->DisablePasswordStoring && !FTunnelPassword.IsEmpty())
+    {
+      Storage->WriteString("TunnelPassword", StronglyRecryptPassword(FTunnelPassword, TunnelUserName+TunnelHostName));
+    }
+    else
+    {
+      Storage->DeleteValue("TunnelPassword");
+    }
+  }
+}
+//---------------------------------------------------------------------
+void __fastcall TSessionData::RecryptPasswords()
+{
+  Password = Password;
+  ProxyPassword = ProxyPassword;
+  TunnelPassword = TunnelPassword;
+}
+//---------------------------------------------------------------------
+bool __fastcall TSessionData::HasAnyPassword()
+{
+  return !FPassword.IsEmpty() || !FProxyPassword.IsEmpty() || !FTunnelPassword.IsEmpty();
+}
+//---------------------------------------------------------------------
+void __fastcall TSessionData::SaveRecryptedPasswords(THierarchicalStorage * Storage)
+{
+  if (Storage->OpenSubKey(InternalStorageKey, true))
+  {
+    RecryptPasswords();
+
+    SavePasswords(Storage, false);
+
     Storage->CloseSubKey();
   }
 }
@@ -1096,6 +1140,47 @@ void __fastcall TSessionData::ValidateName(const AnsiString Name)
   }
 }
 //---------------------------------------------------------------------
+AnsiString __fastcall TSessionData::EncryptPassword(const AnsiString & Password, AnsiString Key)
+{
+  if (Password.IsEmpty())
+  {
+    return AnsiString();
+  }
+  else
+  {
+    return Configuration->EncryptPassword(Password, Key);
+  }
+}
+//---------------------------------------------------------------------
+AnsiString __fastcall TSessionData::StronglyRecryptPassword(const AnsiString & Password, AnsiString Key)
+{
+  if (Password.IsEmpty())
+  {
+    return AnsiString();
+  }
+  else
+  {
+    return Configuration->StronglyRecryptPassword(Password, Key);
+  }
+}
+//---------------------------------------------------------------------
+AnsiString __fastcall TSessionData::DecryptPassword(const AnsiString & Password, AnsiString Key)
+{
+  AnsiString Result;
+  if (!Password.IsEmpty())
+  {
+    try
+    {
+      Result = Configuration->DecryptPassword(Password, Key);
+    }
+    catch(EAbort &)
+    {
+      // silently ignore aborted prompts for master password and return empty password
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------
 bool __fastcall TSessionData::GetCanLogin()
 {
   return !FHostName.IsEmpty();
@@ -2049,7 +2134,8 @@ void __fastcall TStoredSessionList::Load()
   }
 }
 //---------------------------------------------------------------------
-void __fastcall TStoredSessionList::Save(THierarchicalStorage * Storage, bool All)
+void __fastcall TStoredSessionList::DoSave(THierarchicalStorage * Storage,
+  bool All, bool RecryptPasswordOnly)
 {
   TSessionData * FactoryDefaults = new TSessionData("");
   try
@@ -2060,10 +2146,17 @@ void __fastcall TStoredSessionList::Save(THierarchicalStorage * Storage, bool Al
     }
     for (int Index = 0; Index < Count+HiddenCount; Index++)
     {
-      TSessionData *SessionData = (TSessionData *)Items[Index];
+      TSessionData * SessionData = (TSessionData *)Items[Index];
       if (All || SessionData->Modified)
       {
-        SessionData->Save(Storage, false, FactoryDefaults);
+        if (RecryptPasswordOnly)
+        {
+          SessionData->SaveRecryptedPasswords(Storage);
+        }
+        else
+        {
+          SessionData->Save(Storage, false, FactoryDefaults);
+        }
       }
     }
   }
@@ -2073,21 +2166,41 @@ void __fastcall TStoredSessionList::Save(THierarchicalStorage * Storage, bool Al
   }
 }
 //---------------------------------------------------------------------
-void __fastcall TStoredSessionList::Save(bool All, bool Explicit)
+void __fastcall TStoredSessionList::Save(THierarchicalStorage * Storage, bool All)
+{
+  DoSave(Storage, All, false);
+}
+//---------------------------------------------------------------------
+void __fastcall TStoredSessionList::DoSave(bool All, bool Explicit, bool RecryptPasswordOnly)
 {
   THierarchicalStorage * Storage = Configuration->CreateScpStorage(true);
-  try {
+  try
+  {
     Storage->AccessMode = smReadWrite;
     Storage->Explicit = Explicit;
-    if (Storage->OpenSubKey(Configuration->StoredSessionsSubKey, True))
-      Save(Storage, All);
-  } __finally {
+    if (Storage->OpenSubKey(Configuration->StoredSessionsSubKey, true))
+    {
+      DoSave(Storage, All, RecryptPasswordOnly);
+    }
+  }
+  __finally
+  {
     delete Storage;
   }
 
   Saved();
 }
 //---------------------------------------------------------------------
+void __fastcall TStoredSessionList::Save(bool All, bool Explicit)
+{
+  DoSave(All, Explicit, false);
+}
+//---------------------------------------------------------------------
+void __fastcall TStoredSessionList::RecryptPasswords()
+{
+  DoSave(true, true, true);
+}
+//---------------------------------------------------------------------
 void __fastcall TStoredSessionList::Saved()
 {
   FDefaultSettings->Modified = false;

+ 12 - 0
core/SessionData.h

@@ -45,6 +45,8 @@ class TStoredSessionList;
 //---------------------------------------------------------------------------
 class TSessionData : public TNamedObject
 {
+friend class TStoredSessionList;
+
 private:
   AnsiString FHostName;
   int FPortNumber;
@@ -272,6 +274,10 @@ private:
   void __fastcall SetNotUtf(TAutoSwitch value);
   void __fastcall SetHostKey(AnsiString value);
   TDateTime __fastcall GetTimeoutDT();
+  void __fastcall SavePasswords(THierarchicalStorage * Storage, bool PuttyExport);
+  static AnsiString __fastcall EncryptPassword(const AnsiString & Password, AnsiString Key);
+  static AnsiString __fastcall DecryptPassword(const AnsiString & Password, AnsiString Key);
+  static AnsiString __fastcall StronglyRecryptPassword(const AnsiString & Password, AnsiString Key);
 
   __property AnsiString InternalStorageKey = { read = GetInternalStorageKey };
 
@@ -282,6 +288,9 @@ public:
   void __fastcall Load(THierarchicalStorage * Storage);
   void __fastcall Save(THierarchicalStorage * Storage, bool PuttyExport,
     const TSessionData * Default = NULL);
+  void __fastcall SaveRecryptedPasswords(THierarchicalStorage * Storage);
+  void __fastcall RecryptPasswords();
+  bool __fastcall HasAnyPassword();
   void __fastcall Remove();
   virtual void __fastcall Assign(TPersistent * Source);
   bool __fastcall ParseUrl(AnsiString Url, TOptions * Options,
@@ -424,6 +433,7 @@ public:
   void __fastcall Save(THierarchicalStorage * Storage, bool All = false);
   void __fastcall SelectAll(bool Select);
   void __fastcall Import(TStoredSessionList * From, bool OnlySelected);
+  void __fastcall RecryptPasswords();
   TSessionData * __fastcall AtSession(int Index)
     { return (TSessionData*)AtObject(Index); }
   void __fastcall SelectSessionsToImport(TStoredSessionList * Dest, bool SSHOnly);
@@ -444,6 +454,8 @@ private:
   TSessionData * FDefaultSettings;
   bool FReadOnly;
   void __fastcall SetDefaultSettings(TSessionData * value);
+  void __fastcall DoSave(THierarchicalStorage * Storage, bool All, bool RecryptPasswordOnly);
+  void __fastcall DoSave(bool All, bool Explicit, bool RecryptPasswordOnly);
 };
 //---------------------------------------------------------------------------
 #endif

+ 2 - 2
core/SftpFileSystem.cpp

@@ -2096,7 +2096,7 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     }
     if (FTerminal->Log->Logging)
     {
-      FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d, Message: %d, Server: %s, Language: %s ",
+      FTerminal->Log->Add(llOutput, FORMAT("Status code: %d, Message: %d, Server: %s, Language: %s ",
         (int(Code), (int)Packet->MessageNumber, ServerMessage, LanguageTag)));
     }
     if (!LanguageTag.IsEmpty())
@@ -2112,7 +2112,7 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
   {
     if (!FNotLoggedPackets || Code)
     {
-      FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d", ((int)Code)));
+      FTerminal->Log->Add(llOutput, FORMAT("Status code: %d", ((int)Code)));
     }
     return Code;
   }

+ 156 - 7
core/Terminal.cpp

@@ -20,7 +20,6 @@
 #endif
 #include "TextsCore.h"
 #include "HelpCore.h"
-#include "Security.h"
 #include "CoreMain.h"
 #include "Queue.h"
 
@@ -69,6 +68,14 @@ struct TMoveFileParams
   AnsiString FileMask;
 };
 //---------------------------------------------------------------------------
+struct TFilesFindParams
+{
+  TFileMasks FileMask;
+  TFileFoundEvent OnFileFound;
+  TFindingFileEvent OnFindingFile;
+  bool Cancel;
+};
+//---------------------------------------------------------------------------
 TCalculateSizeStats::TCalculateSizeStats()
 {
   memset(this, 0, sizeof(*this));
@@ -475,6 +482,7 @@ __fastcall TTerminal::TTerminal(TSessionData * SessionData,
   FOnShowExtendedException = NULL;
   FOnInformation = NULL;
   FOnClose = NULL;
+  FOnFindingFile = NULL;
 
   FUseBusyCursor = True;
   FLockDirectory = "";
@@ -559,6 +567,42 @@ void __fastcall TTerminal::Idle()
     }
   }
 }
+//---------------------------------------------------------------------
+AnsiString __fastcall TTerminal::EncryptPassword(const AnsiString & Password)
+{
+  if (Password.IsEmpty())
+  {
+    return AnsiString();
+  }
+  else
+  {
+    return Configuration->EncryptPassword(Password, SessionData->SessionName);
+  }
+}
+//---------------------------------------------------------------------
+AnsiString __fastcall TTerminal::DecryptPassword(const AnsiString & Password)
+{
+  AnsiString Result;
+  if (!Password.IsEmpty())
+  {
+    try
+    {
+      Result = Configuration->DecryptPassword(Password, SessionData->SessionName);
+    }
+    catch(EAbort &)
+    {
+      // silently ignore aborted prompts for master password and return empty password
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::RecryptPasswords()
+{
+  FSessionData->RecryptPasswords();
+  FPassword = EncryptPassword(DecryptPassword(FPassword));
+  FTunnelPassword = EncryptPassword(DecryptPassword(FTunnelPassword));
+}
 //---------------------------------------------------------------------------
 bool __fastcall TTerminal::IsAbsolutePath(const AnsiString Path)
 {
@@ -997,8 +1041,7 @@ bool __fastcall TTerminal::DoPromptUser(TSessionData * /*Data*/, TPromptKind Kin
       ((Kind == pkPassword) || (Kind == pkPassphrase) || (Kind == pkKeybInteractive) ||
        (Kind == pkTIS) || (Kind == pkCryptoCard)))
   {
-    AnsiString EncryptedPassword =
-      EncryptPassword(Results->Strings[0], SessionData->SessionName);
+    AnsiString EncryptedPassword = EncryptPassword(Results->Strings[0]);
     if (FTunnelOpening)
     {
       FTunnelPassword = EncryptedPassword;
@@ -1582,6 +1625,12 @@ void __fastcall TTerminal::DoReadDirectoryProgress(int Progress, bool & Cancel)
     FOnReadDirectoryProgress(this, Progress, Cancel);
     Guard.Verify();
   }
+  if (FOnFindingFile != NULL)
+  {
+    TCallbackGuard Guard(this);
+    FOnFindingFile(this, "", Cancel);
+    Guard.Verify();
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::BeginTransaction()
@@ -2328,9 +2377,35 @@ TRemoteFileList * TTerminal::DoReadDirectoryListing(AnsiString Directory, bool U
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::ProcessDirectory(const AnsiString DirName,
-  TProcessFileEvent CallBackFunc, void * Param, bool UseCache)
+  TProcessFileEvent CallBackFunc, void * Param, bool UseCache, bool IgnoreErrors)
 {
-  TRemoteFileList * FileList = CustomReadDirectoryListing(DirName, UseCache);
+  TRemoteFileList * FileList = NULL;
+  if (IgnoreErrors)
+  {
+    ExceptionOnFail = true;
+    try
+    {
+      try
+      {
+        FileList = CustomReadDirectoryListing(DirName, UseCache);
+      }
+      catch(...)
+      {
+        if (!Active)
+        {
+          throw;
+        }
+      }
+    }
+    __finally
+    {
+      ExceptionOnFail = false;
+    }
+  }
+  else
+  {
+    FileList = CustomReadDirectoryListing(DirName, UseCache);
+  }
 
   // skip if directory listing fails and user selects "skip"
   if (FileList)
@@ -4383,6 +4458,72 @@ void __fastcall TTerminal::SynchronizeRemoteTimestamp(const AnsiString /*FileNam
     NULL, &Properties);
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::FileFind(AnsiString FileName,
+  const TRemoteFile * File, /*TFilesFindParams*/ void * Param)
+{
+  // see DoFilesFind
+  FOnFindingFile = NULL;
+
+  assert(Param);
+  assert(File);
+  TFilesFindParams * AParams = static_cast<TFilesFindParams*>(Param);
+
+  if (!AParams->Cancel)
+  {
+    if (FileName.IsEmpty())
+    {
+      FileName = File->FileName;
+    }
+
+    TFileMasks::TParams MaskParams;
+    MaskParams.Size = File->Size;
+
+    AnsiString FullFileName = UnixExcludeTrailingBackslash(File->FullFileName);
+    if (AParams->FileMask.Matches(FullFileName, false,
+         File->IsDirectory, &MaskParams))
+    {
+      AParams->OnFileFound(this, FileName, File, AParams->Cancel);
+    }
+
+    if (File->IsDirectory)
+    {
+      DoFilesFind(FullFileName, *AParams);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::DoFilesFind(AnsiString Directory, TFilesFindParams & Params)
+{
+  Params.OnFindingFile(this, Directory, Params.Cancel);
+  if (!Params.Cancel)
+  {
+    assert(FOnFindingFile == NULL);
+    // ideally we should set the handler only around actually reading
+    // of the directory listing, so we at least reset the handler in
+    // FileFind
+    FOnFindingFile = Params.OnFindingFile;
+    try
+    {
+      ProcessDirectory(Directory, FileFind, &Params, false, true);
+    }
+    __finally
+    {
+      FOnFindingFile = NULL;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::FilesFind(AnsiString Directory, const TFileMasks & FileMask,
+  TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile)
+{
+  TFilesFindParams Params;
+  Params.FileMask = FileMask;
+  Params.OnFileFound = OnFileFound;
+  Params.OnFindingFile = OnFindingFile;
+  Params.Cancel = false;
+  DoFilesFind(Directory, Params);
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminal::SpaceAvailable(const AnsiString Path,
   TSpaceAvailable & ASpaceAvailable)
 {
@@ -4418,7 +4559,7 @@ AnsiString __fastcall TTerminal::GetPassword()
   }
   else
   {
-    Result = DecryptPassword(FPassword, SessionData->SessionName);
+    Result = DecryptPassword(FPassword);
   }
   return Result;
 }
@@ -4433,7 +4574,7 @@ AnsiString __fastcall TTerminal::GetTunnelPassword()
   }
   else
   {
-    Result = DecryptPassword(FTunnelPassword, SessionData->SessionName);
+    Result = DecryptPassword(FTunnelPassword);
   }
   return Result;
 }
@@ -4787,3 +4928,11 @@ void __fastcall TTerminalList::Idle()
     }
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall TTerminalList::RecryptPasswords()
+{
+  for (int Index = 0; Index < Count; Index++)
+  {
+    Terminals[Index]->RecryptPasswords();
+  }
+}

+ 12 - 1
core/Terminal.h

@@ -25,6 +25,7 @@ class TSynchronizeChecklist;
 struct TCalculateSizeStats;
 struct TFileSystemInfo;
 struct TSpaceAvailable;
+struct TFilesFindParams;
 class TTunnelUI;
 class TCallbackGuard;
 //---------------------------------------------------------------------------
@@ -195,6 +196,7 @@ private:
   TNotifyEvent FOnClose;
   bool FAnyInformation;
   TCallbackGuard * FCallbackGuard;
+  TFindingFileEvent FOnFindingFile;
 
   void __fastcall CommandError(Exception * E, const AnsiString Msg);
   int __fastcall CommandError(Exception * E, const AnsiString Msg, int Answers);
@@ -252,7 +254,8 @@ protected:
   bool __fastcall ProcessFilesEx(TStrings * FileList, TFileOperation Operation,
     TProcessFileEventEx ProcessFile, void * Param = NULL, TOperationSide Side = osRemote);
   void __fastcall ProcessDirectory(const AnsiString DirName,
-    TProcessFileEvent CallBackFunc, void * Param = NULL, bool UseCache = false);
+    TProcessFileEvent CallBackFunc, void * Param = NULL, bool UseCache = false,
+    bool IgnoreErrors = false);
   void __fastcall AnnounceFileListOperation();
   AnsiString __fastcall TranslateLockedPath(AnsiString Path, bool Lock);
   void __fastcall ReadDirectory(TRemoteFileList * FileList);
@@ -310,6 +313,8 @@ protected:
   bool __fastcall PromptUser(TSessionData * Data, TPromptKind Kind,
     AnsiString Name, AnsiString Instructions, AnsiString Prompt, bool Echo,
     int MaxLen, AnsiString & Result);
+  void __fastcall FileFind(AnsiString FileName, const TRemoteFile * File, void * Param);
+  void __fastcall DoFilesFind(AnsiString Directory, TFilesFindParams & Params);
 
   virtual void __fastcall Information(const AnsiString & Str, bool Status);
   virtual int __fastcall QueryUser(const AnsiString Query,
@@ -332,6 +337,8 @@ protected:
   void __fastcall DoAnyCommand(const AnsiString Command, TCaptureOutputEvent OutputEvent,
     TCallSessionAction * Action);
   TRemoteFileList * DoReadDirectoryListing(AnsiString Directory, bool UseCache);
+  AnsiString __fastcall EncryptPassword(const AnsiString & Password);
+  AnsiString __fastcall DecryptPassword(const AnsiString & Password);
 
   __property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
 
@@ -345,6 +352,7 @@ public:
   virtual void __fastcall DirectoryLoaded(TRemoteFileList * FileList);
   void __fastcall ShowExtendedException(Exception * E);
   void __fastcall Idle();
+  void __fastcall RecryptPasswords();
   bool __fastcall AllowedAnyCommand(const AnsiString Command);
   void __fastcall AnyCommand(const AnsiString Command, TCaptureOutputEvent OutputEvent);
   void __fastcall CloseOnCompletion(TOnceDoneOperation Operation = odoDisconnect, const AnsiString Message = "");
@@ -408,6 +416,8 @@ public:
     const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
     const TCopyParamType * CopyParam, int Params,
     TSynchronizeDirectory OnSynchronizeDirectory);
+  void __fastcall FilesFind(AnsiString Directory, const TFileMasks & FileMask,
+    TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile);
   void __fastcall SpaceAvailable(const AnsiString Path, TSpaceAvailable & ASpaceAvailable);
   bool __fastcall DirectoryFileList(const AnsiString Path,
     TRemoteFileList *& FileList, bool CanLoad);
@@ -499,6 +509,7 @@ public:
   virtual void __fastcall FreeTerminal(TTerminal * Terminal);
   void __fastcall FreeAndNullTerminal(TTerminal * & Terminal);
   virtual void __fastcall Idle();
+  void __fastcall RecryptPasswords();
 
   __property TTerminal * Terminals[int Index]  = { read=GetTerminal };
   __property int ActiveCount = { read = GetActiveCount };

+ 69 - 34
filezilla/AsyncSslSocketLayer.cpp

@@ -568,20 +568,23 @@ void CAsyncSslSocketLayer::OnReceive(int nErrorCode)
 			}
 		}
 
-		if (!m_nShutDown && pSSL_get_shutdown(m_ssl) && pBIO_ctrl_pending(m_sslbio) <= 0)
+		if (!m_nShutDown && pSSL_get_shutdown(m_ssl))
 		{
-			if (ShutDown() || GetLastError() != WSAEWOULDBLOCK)
+			if (pBIO_ctrl_pending(m_sslbio) <= 0)
 			{
-				if (ShutDownComplete())
-					TriggerEvent(FD_CLOSE, 0, TRUE);
-			}
-			else
-			{
-				m_nNetworkError = WSAECONNABORTED;
-				WSASetLastError(WSAECONNABORTED);
-				TriggerEvent(FD_CLOSE, WSAECONNABORTED, TRUE);
+				if (ShutDown() || GetLastError() != WSAEWOULDBLOCK)
+				{
+					if (ShutDownComplete())
+						TriggerEvent(FD_CLOSE, 0, TRUE);
+				}
+				else
+				{
+					m_nNetworkError = WSAECONNABORTED;
+					WSASetLastError(WSAECONNABORTED);
+					TriggerEvent(FD_CLOSE, WSAECONNABORTED, TRUE);
+				}
+				return;
 			}
-			return;
 		}
 
 		if (ShutDownComplete() && m_nShutDown == 1)
@@ -862,25 +865,28 @@ int CAsyncSslSocketLayer::Receive(void* lpBuf, int nBufLen, int nFlags)
 				SetLastError(m_nNetworkError);
 				return SOCKET_ERROR;
 			}
-			else if (pSSL_get_shutdown(m_ssl))
+			else
 			{
-				if (ShutDown() || GetLastError() == WSAEWOULDBLOCK)
+				if (pSSL_get_shutdown(m_ssl))
 				{
-					if (ShutDownComplete())
+					if (ShutDown() || GetLastError() == WSAEWOULDBLOCK)
 					{
-						TriggerEvent(FD_CLOSE, 0, TRUE);
-						return 0;
+						if (ShutDownComplete())
+						{
+							TriggerEvent(FD_CLOSE, 0, TRUE);
+							return 0;
+						}
+						else
+							WSASetLastError(WSAEWOULDBLOCK);
 					}
 					else
-						WSASetLastError(WSAEWOULDBLOCK);
-				}
-				else
-				{
-					m_nNetworkError = WSAECONNABORTED;
-					WSASetLastError(WSAECONNABORTED);
-					TriggerEvent(FD_CLOSE, WSAECONNABORTED, TRUE);
+					{
+						m_nNetworkError = WSAECONNABORTED;
+						WSASetLastError(WSAECONNABORTED);
+						TriggerEvent(FD_CLOSE, WSAECONNABORTED, TRUE);
+					}
+					return SOCKET_ERROR;
 				}
-				return SOCKET_ERROR;
 			}
 			m_mayTriggerReadUp = true;
 			TriggerEvents();
@@ -1073,9 +1079,13 @@ int CAsyncSslSocketLayer::InitSSLConnection(bool clientMode, void* pSslContext /
 	//Init SSL connection
 	pSSL_set_session(m_ssl, NULL);
 	if (clientMode)
+	{
 		pSSL_set_connect_state(m_ssl);
+	}
 	else
+	{
 		pSSL_set_accept_state(m_ssl);
+	}
 	pSSL_set_bio(m_ssl, m_ibio, m_ibio);
 	pBIO_ctrl(m_sslbio, BIO_C_SET_SSL, BIO_NOCLOSE, m_ssl);
 	pBIO_read(m_sslbio, (void *)1, 0);
@@ -1112,13 +1122,21 @@ void CAsyncSslSocketLayer::ResetSslSession()
 
 	m_bSslEstablished = FALSE;
 	if (m_sslbio)
+	{
 		pBIO_free(m_sslbio);
+	}
 	if (m_ssl)
+	{
 		pSSL_set_session(m_ssl,NULL);
+	}
 	if (m_nbio)
+	{
 		pBIO_free(m_nbio);
+	}
 	if (m_ibio)
+	{
 		pBIO_free(m_ibio);
+	}
 
 	m_nNetworkSendBufferLen = 0;
 
@@ -1127,7 +1145,9 @@ void CAsyncSslSocketLayer::ResetSslSession()
 	m_sslbio = 0;
 
 	if (m_ssl)
+	{
 		pSSL_free(m_ssl);
+	}
 
 	m_sCriticalSection.Lock();
 
@@ -1218,7 +1238,9 @@ BOOL CAsyncSslSocketLayer::ShutDown(int nHow /*=sends*/)
 		if (res != -1)
 		{
 			if (!res)
+			{
 				pSSL_shutdown(m_ssl);
+			}
 			if (ShutDownComplete())
 				return ShutDownNext();
 			else
@@ -1920,9 +1942,13 @@ BOOL CAsyncSslSocketLayer::SetCertStorage(CString file)
 void CAsyncSslSocketLayer::OnClose(int nErrorCode)
 {
 	m_onCloseCalled = true;
-	if (m_bUseSSL && pBIO_ctrl && pBIO_ctrl(m_sslbio, BIO_CTRL_PENDING, 0, NULL) > 0)
+	if (m_bUseSSL && pBIO_ctrl)
 	{
-		TriggerEvents();
+		if (pBIO_ctrl(m_sslbio, BIO_CTRL_PENDING, 0, NULL) > 0)
+		{
+			TriggerEvents();
+		}
+		else TriggerEvent(FD_CLOSE, nErrorCode, TRUE);
 	}
 	else
 		TriggerEvent(FD_CLOSE, nErrorCode, TRUE);
@@ -2178,10 +2204,13 @@ void CAsyncSslSocketLayer::TriggerEvents()
 			TriggerEvent(FD_WRITE, 0);
 		}
 	}
-	else if (!m_nNetworkSendBufferLen && m_bSslEstablished && !m_pRetrySendBuffer && pBIO_ctrl_get_write_guarantee(m_sslbio) > 0 && m_mayTriggerWriteUp)
+	else if (!m_nNetworkSendBufferLen && m_bSslEstablished && !m_pRetrySendBuffer)
 	{
-		m_mayTriggerWriteUp = false;
-		TriggerEvent(FD_WRITE, 0, TRUE);
+		if (pBIO_ctrl_get_write_guarantee(m_sslbio) > 0 && m_mayTriggerWriteUp)
+		{
+			m_mayTriggerWriteUp = false;
+			TriggerEvent(FD_WRITE, 0, TRUE);
+		}
 	}
 
 	if (m_bSslEstablished && pBIO_ctrl_pending(m_sslbio) > 0)
@@ -2192,14 +2221,20 @@ void CAsyncSslSocketLayer::TriggerEvents()
 			TriggerEvent(FD_READ, 0, TRUE);
 		}
 	}
-	else if (pBIO_ctrl_get_write_guarantee(m_nbio) > 0 && m_mayTriggerRead)
+	else
 	{
-		m_mayTriggerRead = false;
-		TriggerEvent(FD_READ, 0);
+		if (pBIO_ctrl_get_write_guarantee(m_nbio) > 0 && m_mayTriggerRead)
+		{
+			m_mayTriggerRead = false;
+			TriggerEvent(FD_READ, 0);
+		}
 	}
 
-	if (m_onCloseCalled && m_bSslEstablished && pBIO_ctrl_pending(m_sslbio) <= 0)
-		TriggerEvent(FD_CLOSE, 0, TRUE);
+	if (m_onCloseCalled && m_bSslEstablished)
+	{
+		if (pBIO_ctrl_pending(m_sslbio) <= 0)
+			TriggerEvent(FD_CLOSE, 0, TRUE);
+	}
 }
 
 int CAsyncSslSocketLayer::pem_passwd_cb(char *buf, int size, int rwflag, void *userdata)

+ 3 - 5
forms/Console.cpp

@@ -10,6 +10,7 @@
 #include <CustomWinConfiguration.h>
 
 #include "Console.h"
+#include <Tools.h>
 //---------------------------------------------------------------------
 #pragma link "HistoryComboBox"
 #pragma link "PathLabel"
@@ -141,7 +142,7 @@ bool __fastcall TConsoleDialog::Execute(const AnsiString Command,
     if ((FAutoBounds.Width() != Width) ||
         (FAutoBounds.Height() != Height))
     {
-      ConsoleWin.WindowSize = FORMAT("%d,%d", (Width, Height));
+      ConsoleWin.WindowSize = StoreFormSize(this);
     }
     CustomWinConfiguration->ConsoleWin = ConsoleWin;
   }
@@ -301,10 +302,7 @@ void __fastcall TConsoleDialog::ActionListUpdate(TBasicAction * Action,
 void __fastcall TConsoleDialog::FormShow(TObject * /*Sender*/)
 {
   UpdateFormPosition(this, poMainFormCenter);
-  AnsiString WindowSize = CustomWinConfiguration->ConsoleWin.WindowSize;
-  int Width = StrToIntDef(CutToChar(WindowSize, ',', true), Width);
-  int Height = StrToIntDef(CutToChar(WindowSize, ',', true), Height);
-  ResizeForm(this, Width, Height);
+  RestoreFormSize(CustomWinConfiguration->ConsoleWin.WindowSize, this);
   FAutoBounds = BoundsRect;
 }
 //---------------------------------------------------------------------------

+ 1 - 0
forms/Copy.cpp

@@ -173,6 +173,7 @@ void __fastcall TCopyDialog::AdjustControls()
     QueueLabel = FMTLOAD(COPY_QUEUE, (QueueLabel));
   }
   QueueCheck2->Caption = QueueLabel;
+  QueueIndividuallyCheck->Visible = FLAGCLEAR(Options, coNoQueueIndividually);
 
   AdjustTransferControls();
 

+ 269 - 194
forms/Custom.cpp

@@ -5,6 +5,7 @@
 #include <Dialogs.hpp>
 //---------------------------------------------------------------------
 #include <Common.h>
+#include <CustomWinConfiguration.h>
 #include <WinInterface.h>
 #include <VCLCommon.h>
 #include <TextsWin.h>
@@ -14,283 +15,357 @@
 #include "Custom.h"
 //---------------------------------------------------------------------
 #ifndef NO_RESOURCES
+#pragma link "PasswordEdit"
 #pragma resource "*.dfm"
 #endif
 //---------------------------------------------------------------------
-bool __fastcall DoCustomDialog(const TDialogParams & Params, TDialogData & Data)
+__fastcall TCustomDialog::TCustomDialog(AnsiString AHelpKeyword)
+  : TForm(Application)
 {
-  bool Result;
-  TCustomDialog * CustomDialog = new TCustomDialog(Application, Params);
-  try
+  UseSystemSettings(this);
+
+  FPos = 8;
+
+  HelpKeyword = AHelpKeyword;
+
+  TBorderIcons BI = BorderIcons;
+  if (HelpKeyword.IsEmpty())
   {
-    Result = CustomDialog->Execute(Data);
+    BI >> biHelp;
+
+    OKButton->Left = CancelButton->Left;
+    CancelButton->Left = HelpButton->Left;
+    HelpButton->Visible = false;
   }
-  __finally
+  else
   {
-    delete CustomDialog;
+    BI << biHelp;
   }
-  return Result;
+  BorderIcons = BI;
+}
+//---------------------------------------------------------------------
+bool __fastcall TCustomDialog::Execute()
+{
+  Changed();
+  return (ShowModal() == mrOk);
+}
+//---------------------------------------------------------------------
+void __fastcall TCustomDialog::DoChange(bool & /*CanSubmit*/)
+{
+  // noop
+}
+//---------------------------------------------------------------------
+void __fastcall TCustomDialog::Changed()
+{
+  bool CanSubmit = true;
+  DoChange(CanSubmit);
+  EnableControl(OKButton, CanSubmit);
+}
+//---------------------------------------------------------------------
+void __fastcall TCustomDialog::Change(TObject * /*Sender*/)
+{
+  Changed();
 }
 //---------------------------------------------------------------------------
-void __fastcall SaveSessionDialogShow(void *, const TDialogControls & Controls)
+void __fastcall TCustomDialog::HelpButtonClick(TObject * /*Sender*/)
 {
-  InstallPathWordBreakProc(Controls.Combo);
-  int P = Controls.Combo->Text.LastDelimiter("/");
-  if (P > 0)
-  {
-    Controls.Combo->SetFocus();
-    Controls.Combo->SelStart = P;
-    Controls.Combo->SelLength = Controls.Combo->Text.Length() - P;
-  }
-
-  EnableControl(Controls.Check, bool(Controls.Token));
+  FormHelp(this);
 }
 //---------------------------------------------------------------------------
-void __fastcall SaveSessionDialogValidate(void * OriginalSession, const TDialogData & Data)
+void __fastcall TCustomDialog::DoShow()
 {
-  SessionNameValidate(Data.Combo, static_cast<TSessionData *>(OriginalSession));
+  OKButton->TabOrder = FCount;
+  CancelButton->TabOrder = static_cast<short>(FCount + 1);
+  HelpButton->TabOrder = static_cast<short>(FCount + 2);
+  Changed();
+  TForm::DoShow();
 }
 //---------------------------------------------------------------------------
-bool __fastcall DoSaveSessionDialog(AnsiString & SessionName,
-  bool * SavePassword, TSessionData * OriginalSession)
+void __fastcall TCustomDialog::DoValidate()
 {
-  bool Result;
-  TDialogParams Params;
-  try
-  {
-    Params.Caption = LoadStr(SAVE_SESSION_CAPTION);
-    Params.HelpKeyword = HELP_SESSION_SAVE;
-    Params.ComboLabel = LoadStr(SAVE_SESSION_PROMPT);
-    Params.ComboEmptyValid = false;
-    Params.CheckLabel = LoadStr(SAVE_SESSION_PASSWORD);
-    bool CanSavePassword = (SavePassword != NULL);
-    Params.Token = (void *)(CanSavePassword);
-    MakeMethod(NULL, SaveSessionDialogShow, Params.OnShow);
-    MakeMethod(OriginalSession, SaveSessionDialogValidate, Params.OnValidate);
-
-    Params.ComboItems = new TStringList();
-    for (int Index = 0; Index < StoredSessions->Count; Index++)
-    {
-      TSessionData * Data = StoredSessions->Sessions[Index];
-      if (!Data->Special)
-      {
-        Params.ComboItems->Add(Data->Name);
-      }
-    }
-
-    TDialogData Data;
-    Data.Combo = SessionName;
-    Data.Check = CanSavePassword ? *SavePassword : false;
-
-    Result = DoCustomDialog(Params, Data);
-
-    if (Result)
-    {
-      SessionName = Data.Combo;
-      if (CanSavePassword)
-      {
-        *SavePassword = Data.Check;
-      }
-    }
-  }
-  __finally
-  {
-    delete Params.ComboItems;
-  }
-  return Result;
+  // noop
 }
 //---------------------------------------------------------------------------
-void __fastcall SessionNameValidate(const AnsiString & Text,
-  TSessionData * RenamingSession)
+bool __fastcall TCustomDialog::CloseQuery()
 {
-  TSessionData::ValidatePath(Text);
-
-  assert(StoredSessions);
-  TSessionData * Data = (TSessionData *)StoredSessions->FindByName(Text);
-  if (Data && Data->Special)
+  if (ModalResult == mrOk)
   {
-    MessageDialog(FMTLOAD(CANNOT_OVERWRITE_SPECIAL_SESSION, (Text)),
-      qtError, qaOK, HELP_NONE);
-    Abort();
-  }
-  else if (Data && (Data != RenamingSession) &&
-    MessageDialog(FMTLOAD(CONFIRM_OVERWRITE_SESSION, (Text)),
-      qtConfirmation, qaYes | qaNo, HELP_SESSION_SAVE_OVERWRITE) != qaYes)
-  {
-    Abort();
+    DoValidate();
   }
+  return TForm::CloseQuery();
 }
 //---------------------------------------------------------------------------
-struct TShortCutData
+void __fastcall TCustomDialog::AddWinControl(TWinControl * Control)
 {
-  TShortCut ShortCut;
-  const TShortCuts * ShortCuts;
-};
+  Control->TabOrder = FCount;
+  FCount++;
+}
 //---------------------------------------------------------------------------
-TShortCutData & __fastcall ShortCutData(const TDialogControls & Controls)
+TLabel * __fastcall TCustomDialog::CreateLabel(AnsiString Label)
 {
-  return *reinterpret_cast<TShortCutData *>(Controls.Token);
+  TLabel * Result = new TLabel(this);
+  Result->Caption = Label;
+  return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall ShortCutDialogInit(void *, const TDialogControls & Controls)
+void __fastcall TCustomDialog::AddEditLikeControl(TWinControl * Edit, TLabel * Label)
 {
-  InitializeShortCutCombo(Controls.Combo, *ShortCutData(Controls).ShortCuts);
+  int PrePos = FPos;
+  Label->Parent = this;
+  Label->Left = 8;
+  Label->Top = FPos;
+  FPos += 16;
+
+  Edit->Parent = this;
+  Edit->Left = 8;
+  Edit->Top = FPos;
+  Edit->Width = ClientWidth - (Edit->Left * 2);
+  // this updates Height property to real value
+  Edit->HandleNeeded();
+  FPos += Edit->Height + 8;
+
+  if (Label->FocusControl == NULL)
+  {
+    Label->FocusControl = Edit;
+  }
+  else
+  {
+    assert(Label->FocusControl == Edit);
+  }
+
+  ClientHeight = ClientHeight + (FPos - PrePos);
+
+  AddWinControl(Edit);
 }
 //---------------------------------------------------------------------------
-void __fastcall ShortCutDialogLoad(void *, const TDialogData & /*Data*/,
-  TDialogControls & Controls)
+void __fastcall TCustomDialog::AddEdit(TCustomEdit * Edit, TLabel * Label)
 {
-  SetShortCutCombo(Controls.Combo, ShortCutData(Controls).ShortCut);
+  AddEditLikeControl(Edit, Label);
+
+  TEdit * PublicEdit = reinterpret_cast<TEdit *>(Edit);
+  if (PublicEdit->OnChange == NULL)
+  {
+    PublicEdit->OnChange = Change;
+  }
 }
 //---------------------------------------------------------------------------
-void __fastcall ShortCutDialogSave(void *, const TDialogControls & Controls,
-  TDialogData & /*Data*/)
+void __fastcall TCustomDialog::AddComboBox(TCustomCombo * Combo, TLabel * Label)
 {
-  ShortCutData(Controls).ShortCut = GetShortCutCombo(Controls.Combo);
+  AddEditLikeControl(Combo, Label);
+
+  TComboBox * PublicCombo = reinterpret_cast<TComboBox *>(Combo);
+  if (PublicCombo->OnChange == NULL)
+  {
+    PublicCombo->OnChange = Change;
+  }
 }
 //---------------------------------------------------------------------------
-bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
-  const TShortCuts & ShortCuts, AnsiString HelpKeyword)
+void __fastcall TCustomDialog::AddButtonControl(TButtonControl * Control)
 {
-  TShortCutData Token;
-  Token.ShortCut = ShortCut;
-  Token.ShortCuts = &ShortCuts;
-
-  TDialogParams Params;
-  Params.Token = &Token;
-  Params.Caption = LoadStr(SHORTCUT_CAPTION);
-  Params.HelpKeyword = HelpKeyword;
-  Params.ComboLabel = LoadStr(SHORTCUT_LABEL);
-  MakeMethod(NULL, ShortCutDialogInit, Params.OnInit);
-  MakeMethod(NULL, ShortCutDialogLoad, Params.OnLoad);
-  MakeMethod(NULL, ShortCutDialogSave, Params.OnSave);
+  int PrePos = FPos;
+  Control->Parent = this;
+  Control->Left = 14;
+  Control->Top = FPos;
+  Control->Width = ClientWidth - Control->Left - 8;
+  // this updates Height property to real value
+  Control->HandleNeeded();
+  FPos += Control->Height + 8;
 
-  TDialogData Data;
+  ClientHeight = ClientHeight + (FPos - PrePos);
 
-  bool Result = DoCustomDialog(Params, Data);
+  AddWinControl(Control);
 
-  if (Result)
+  TCheckBox * PublicControl = reinterpret_cast<TCheckBox *>(Control);
+  if (PublicControl->OnClick == NULL)
   {
-    ShortCut = Token.ShortCut;
+    PublicControl->OnClick = Change;
   }
-
-  return Result;
 }
-//---------------------------------------------------------------------
-__fastcall TCustomDialog::TCustomDialog(TComponent * AOwner, const TDialogParams & Params)
-  : TForm(AOwner),
-  FParams(Params)
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TSaveSessionDialog : public TCustomDialog
 {
-  UseSystemSettings(this);
+public:
+  __fastcall TSaveSessionDialog(TSessionData * OriginalSession, bool CanSavePassword);
 
-  FControls.Token = Params.Token;
-  FControls.Form = this;
-  FControls.Combo = Combo;
-  FControls.Check = Check;
+  bool __fastcall Execute(AnsiString & SessionName, bool & SavePassword);
 
-  Caption = Params.Caption;
-  HelpKeyword = Params.HelpKeyword;
-  ComboLabel->Caption = Params.ComboLabel;
-  if (Params.ComboItems != NULL)
+protected:
+  DYNAMIC void __fastcall DoShow();
+  virtual void __fastcall DoValidate();
+  virtual void __fastcall DoChange(bool & CanSubmit);
+
+private:
+  TSessionData * FOriginalSession;
+  TComboBox * SessionNameCombo;
+  TCheckBox * SavePasswordCheck;
+};
+//---------------------------------------------------------------------------
+__fastcall TSaveSessionDialog::TSaveSessionDialog(
+    TSessionData * OriginalSession, bool CanSavePassword) :
+  TCustomDialog(HELP_SESSION_SAVE),
+  FOriginalSession(OriginalSession)
+{
+  Caption = LoadStr(SAVE_SESSION_CAPTION);
+
+  SessionNameCombo = new TComboBox(this);
+  AddComboBox(SessionNameCombo, CreateLabel(LoadStr(SAVE_SESSION_PROMPT)));
+  // parent has to be set before following
+  InstallPathWordBreakProc(SessionNameCombo);
+  SessionNameCombo->Items->BeginUpdate();
+  try
   {
-    Combo->Items = Params.ComboItems;
+    for (int Index = 0; Index < StoredSessions->Count; Index++)
+    {
+      TSessionData * Data = StoredSessions->Sessions[Index];
+      if (!Data->Special)
+      {
+        SessionNameCombo->Items->Add(Data->Name);
+      }
+    }
   }
-  Check->Caption = Params.CheckLabel;
-  if (Params.CheckLabel.IsEmpty())
+  __finally
   {
-    ClientHeight -= (Check->Top + Check->Height) - (Combo->Top + Combo->Height);
-    Check->Visible = false;
+    SessionNameCombo->Items->EndUpdate();
   }
 
-  TBorderIcons BI = BorderIcons;
-  if (HelpKeyword.IsEmpty())
-  {
-    BI >> biHelp;
+  SavePasswordCheck = new TCheckBox(this);
+  SavePasswordCheck->Caption = LoadStr(
+    CustomWinConfiguration->UseMasterPassword ? SAVE_SESSION_PASSWORD_MASTER : SAVE_SESSION_PASSWORD);
+  AddButtonControl(SavePasswordCheck);
 
-    OKButton->Left = CancelButton->Left;
-    CancelButton->Left = HelpButton->Left;
-    HelpButton->Visible = false;
-  }
-  else
+  EnableControl(SavePasswordCheck, CanSavePassword);
+}
+//---------------------------------------------------------------------------
+bool __fastcall TSaveSessionDialog::Execute(AnsiString & SessionName, bool & SavePassword)
+{
+  SessionNameCombo->Text = SessionName;
+  SavePasswordCheck->Checked = SavePassword;
+  bool Result = TCustomDialog::Execute();
+  if (Result)
   {
-    BI << biHelp;
+    SessionName = SessionNameCombo->Text;
+    SavePassword = SavePasswordCheck->Checked;
   }
-  BorderIcons = BI;
-
-  if (FParams.OnInit != NULL)
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TSaveSessionDialog::DoShow()
+{
+  int P = SessionNameCombo->Text.LastDelimiter("/");
+  if (P > 0)
   {
-    FParams.OnInit(FControls);
+    SessionNameCombo->SetFocus();
+    SessionNameCombo->SelStart = P;
+    SessionNameCombo->SelLength = SessionNameCombo->Text.Length() - P;
   }
-
-  DoChange();
+  TCustomDialog::DoShow();
 }
-//---------------------------------------------------------------------
-bool __fastcall TCustomDialog::Execute(TDialogData & Data)
+//---------------------------------------------------------------------------
+void __fastcall TSaveSessionDialog::DoValidate()
+{
+  SessionNameValidate(SessionNameCombo->Text, FOriginalSession);
+  TCustomDialog::DoValidate();
+}
+//---------------------------------------------------------------------------
+void __fastcall TSaveSessionDialog::DoChange(bool & CanSubmit)
 {
-  Combo->Text = Data.Combo;
-  Check->Checked = Data.Check;
-  if (FParams.OnLoad != NULL)
+  CanSubmit = !SessionNameCombo->Text.IsEmpty();
+  TCustomDialog::DoChange(CanSubmit);
+}
+//---------------------------------------------------------------------------
+bool __fastcall DoSaveSessionDialog(AnsiString & SessionName,
+  bool * SavePassword, TSessionData * OriginalSession)
+{
+  bool Result;
+  TSaveSessionDialog * Dialog = new TSaveSessionDialog(
+    OriginalSession, (SavePassword != NULL));
+  try
   {
-    FParams.OnLoad(Data, FControls);
+    bool Dummy = false;
+    if (SavePassword == NULL)
+    {
+      SavePassword = &Dummy;
+    }
+    Result = Dialog->Execute(SessionName, *SavePassword);
+    if (Result && (SavePassword != NULL) && *SavePassword)
+    {
+      CustomWinConfiguration->AskForMasterPasswordIfNotSet();
+    }
   }
-  bool Result = (ShowModal() == mrOk);
-  if (Result)
+  __finally
   {
-    SaveData(Data);
+    delete Dialog;
   }
   return Result;
 }
-//---------------------------------------------------------------------
-void __fastcall TCustomDialog::SaveData(TDialogData & Data)
+//---------------------------------------------------------------------------
+void __fastcall SessionNameValidate(const AnsiString & Text,
+  TSessionData * RenamingSession)
 {
-  Data.Combo = Combo->Text;
-  Data.Check = Check->Checked;
-  if (FParams.OnSave != NULL)
+  TSessionData::ValidatePath(Text);
+
+  assert(StoredSessions);
+  TSessionData * Data = (TSessionData *)StoredSessions->FindByName(Text);
+  if (Data && Data->Special)
   {
-    FParams.OnSave(FControls, Data);
+    MessageDialog(FMTLOAD(CANNOT_OVERWRITE_SPECIAL_SESSION, (Text)),
+      qtError, qaOK, HELP_NONE);
+    Abort();
   }
-}
-//---------------------------------------------------------------------
-void __fastcall TCustomDialog::DoChange()
-{
-  bool Valid = !Combo->Text.IsEmpty() || FParams.ComboEmptyValid;
-  if (FParams.OnChange != NULL)
+  else if (Data && (Data != RenamingSession) &&
+    MessageDialog(FMTLOAD(CONFIRM_OVERWRITE_SESSION, (Text)),
+      qtConfirmation, qaYes | qaNo, HELP_SESSION_SAVE_OVERWRITE) != qaYes)
   {
-    FParams.OnChange(FControls, Valid);
+    Abort();
   }
-  EnableControl(OKButton, Valid);
 }
-//---------------------------------------------------------------------
-void __fastcall TCustomDialog::Change(TObject * /*Sender*/)
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TShortCutDialog : public TCustomDialog
 {
-  DoChange();
-}
+public:
+  __fastcall TShortCutDialog(const TShortCuts & ShortCuts, AnsiString HelpKeyword);
+
+  bool __fastcall Execute(TShortCut & ShortCut);
+
+private:
+  TComboBox * ShortCutCombo;
+};
 //---------------------------------------------------------------------------
-void __fastcall TCustomDialog::HelpButtonClick(TObject * /*Sender*/)
+__fastcall TShortCutDialog::TShortCutDialog(const TShortCuts & ShortCuts, AnsiString HelpKeyword) :
+  TCustomDialog(HelpKeyword)
 {
-  FormHelp(this);
+  Caption = LoadStr(SHORTCUT_CAPTION);
+
+  ShortCutCombo = new TComboBox(this);
+  AddComboBox(ShortCutCombo, CreateLabel(LoadStr(SHORTCUT_LABEL)));
+  InitializeShortCutCombo(ShortCutCombo, ShortCuts);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomDialog::FormShow(TObject * /*Sender*/)
+bool __fastcall TShortCutDialog::Execute(TShortCut & ShortCut)
 {
-  if (FParams.OnShow != NULL)
+  SetShortCutCombo(ShortCutCombo, ShortCut);
+  bool Result = TCustomDialog::Execute();
+  if (Result)
   {
-    FParams.OnShow(FControls);
+    ShortCut = GetShortCutCombo(ShortCutCombo);
   }
-  DoChange();
+  return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomDialog::FormCloseQuery(TObject * /*Sender*/,
-  bool & /*CanClose*/)
+bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
+  const TShortCuts & ShortCuts, AnsiString HelpKeyword)
 {
-  if (ModalResult == mrOk)
+  bool Result;
+  TShortCutDialog * Dialog = new TShortCutDialog(ShortCuts, HelpKeyword);
+  try
   {
-    if (FParams.OnValidate != NULL)
-    {
-      TDialogData Data;
-      SaveData(Data);
-      FParams.OnValidate(Data);
-    }
+    Result = Dialog->Execute(ShortCut);
+  }
+  __finally
+  {
+    delete Dialog;
   }
+  return Result;
 }
-//---------------------------------------------------------------------------

+ 6 - 36
forms/Custom.dfm

@@ -4,30 +4,20 @@ object CustomDialog: TCustomDialog
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'Save session asX'
-  ClientHeight = 108
+  ClientHeight = 41
   ClientWidth = 326
   Color = clBtnFace
   ParentFont = True
   OldCreateOrder = True
   Position = poMainFormCenter
-  OnCloseQuery = FormCloseQuery
-  OnShow = FormShow
   DesignSize = (
     326
-    108)
+    41)
   PixelsPerInch = 96
   TextHeight = 13
-  object ComboLabel: TLabel
-    Left = 8
-    Top = 8
-    Width = 87
-    Height = 13
-    Caption = '&Save session as:X'
-    FocusControl = Combo
-  end
   object OKButton: TButton
     Left = 69
-    Top = 76
+    Top = 9
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -38,43 +28,23 @@ object CustomDialog: TCustomDialog
   end
   object CancelButton: TButton
     Left = 157
-    Top = 76
+    Top = 9
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Cancel = True
     Caption = 'Cancel'
     ModalResult = 2
-    TabOrder = 3
-  end
-  object Combo: TComboBox
-    Left = 8
-    Top = 24
-    Width = 312
-    Height = 21
-    Anchors = [akLeft, akTop, akRight]
-    ItemHeight = 13
-    MaxLength = 255
     TabOrder = 0
-    OnChange = Change
   end
   object HelpButton: TButton
     Left = 244
-    Top = 76
+    Top = 9
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Caption = '&Help'
-    TabOrder = 4
-    OnClick = HelpButtonClick
-  end
-  object Check: TCheckBox
-    Left = 14
-    Top = 51
-    Width = 299
-    Height = 17
-    Caption = 'Save &password (not recommended)X'
     TabOrder = 1
-    OnClick = Change
+    OnClick = HelpButtonClick
   end
 end

+ 24 - 15
forms/Custom.h

@@ -13,32 +13,41 @@
 #include <vcl\Buttons.hpp>
 #include <vcl\ExtCtrls.hpp>
 #include <Windows.h>
+#include "PasswordEdit.hpp"
 //----------------------------------------------------------------------------
 class TCustomDialog : public TForm
 {
 __published:
   TButton * OKButton;
   TButton * CancelButton;
-  TComboBox *Combo;
-  TLabel *ComboLabel;
-  TButton *HelpButton;
-  TCheckBox *Check;
-  void __fastcall Change(TObject * Sender);
+  TButton * HelpButton;
   void __fastcall HelpButtonClick(TObject *Sender);
-  void __fastcall FormShow(TObject *Sender);
-  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
 
-public:
-  __fastcall TCustomDialog(TComponent * AOwner, const TDialogParams & Params);
+private:
+  int FPos;
+  short FCount;
 
-  bool __fastcall Execute(TDialogData & Data);
+  void __fastcall Change(TObject * Sender);
+  void __fastcall AddWinControl(TWinControl * Control);
+  void __fastcall Changed();
 
-private:
-  TDialogControls FControls;
-  const TDialogParams & FParams;
+protected:
+  DYNAMIC void __fastcall DoShow();
+  virtual bool __fastcall CloseQuery();
+
+  virtual void __fastcall DoChange(bool & CanSubmit);
+  virtual void __fastcall DoValidate();
+
+public:
+  __fastcall TCustomDialog(AnsiString HelpKeyword);
+
+  TLabel * __fastcall CreateLabel(AnsiString Label);
+  void __fastcall AddEditLikeControl(TWinControl * Edit, TLabel * Label);
+  void __fastcall AddEdit(TCustomEdit * Edit, TLabel * Label);
+  void __fastcall AddComboBox(TCustomCombo * Combo, TLabel * Label);
+  void __fastcall AddButtonControl(TButtonControl * Control);
 
-  void __fastcall DoChange();
-  void __fastcall SaveData(TDialogData & Data);
+  bool __fastcall Execute();
 };
 //----------------------------------------------------------------------------
 #endif

+ 34 - 13
forms/CustomScpExplorer.cpp

@@ -230,16 +230,8 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
 
   reinterpret_cast<TLabel*>(QueueSplitter)->OnDblClick = QueueSplitterDblClick;
 
-  TSHFileInfo FileInfo;
-  FSystemImageList = new TImageList(this);
-  int ImageListHandle = SHGetFileInfo("", 0, &FileInfo, sizeof(FileInfo),
-    SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
-  if (ImageListHandle != 0)
-  {
-    FSystemImageList->ShareImages = true;
-    FSystemImageList->Handle = ImageListHandle;
-    FSystemImageList->DrawingStyle = dsTransparent;
-  }
+  FSystemImageList = SharedSystemImageList(false);
+  FSystemImageList->DrawingStyle = dsTransparent;
 
   FCustomCommandMenu = CreateTBXPopupMenu(this);
   FCustomCommandLocalFileList = NULL;
@@ -5429,7 +5421,9 @@ void __fastcall TCustomScpExplorerForm::RemoteFileContolDDChooseEffect(
       if (dwEffect == DROPEFFECT_Copy)
       {
         bool MoveCapable = FTerminal->IsCapable[fcRemoteMove];
-        bool CopyCapable = FTerminal->IsCapable[fcRemoteCopy] || FTerminal->IsCapable[fcSecondaryShell];
+        // currently we support copying always (at least via temporary directory);
+        // remove associated checks once this all proves stable andworking
+        bool CopyCapable = true;
         // if we do not support neither of operations, there's no discussion
         if (!MoveCapable && !CopyCapable)
         {
@@ -6410,8 +6404,9 @@ bool __fastcall TCustomScpExplorerForm::MainWindowHook(TMessage & AMessage)
   // which calculates size according to number of all visible windows.
   // hence they count VCL application twice.
 
-  // code is said to be from Issue 4/95 of The Delphi Magazine
-  if (AMessage.Msg == WM_WINDOWPOSCHANGING)
+  // code is said to be from Issue 4/95 of The Delphi Magazine;
+  // ignore this all if main window is hidden (e.g. /upload)
+  if ((AMessage.Msg == WM_WINDOWPOSCHANGING) && Visible)
   {
     TWMWindowPosMsg & Message = reinterpret_cast<TWMWindowPosMsg &>(AMessage);
 
@@ -6473,3 +6468,29 @@ void __fastcall TCustomScpExplorerForm::FormShow(TObject * /*Sender*/)
   SideEnter(FCurrentSide);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::DoFindFiles(
+  AnsiString Directory, const TFileMasks & FileMask,
+  TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile)
+{
+  FTerminal->FilesFind(Directory, FileMask, OnFileFound, OnFindingFile);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::DoFocusRemotePath(AnsiString Path)
+{
+  RemoteDirView->Path = UnixExtractFilePath(Path);
+  TListItem * Item = RemoteDirView->FindFileItem(UnixExtractFileName(Path));
+  if (Item != NULL)
+  {
+    RemoteDirView->ItemFocused = Item;
+    RemoteDirView->ItemFocused->MakeVisible(false);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::FindFiles()
+{
+  AnsiString Path;
+  if (DoFileFindDialog(RemoteDirView->Path, DoFindFiles, Path))
+  {
+    DoFocusRemotePath(Path);
+  }
+}

+ 4 - 0
forms/CustomScpExplorer.h

@@ -431,6 +431,9 @@ protected:
   virtual void __fastcall ToolbarItemResize(TTBXCustomDropDownItem * Item, int Width);
   void __fastcall ClickToolbarItem(TTBCustomItem * Item, bool PositionCursor);
   virtual bool __fastcall OpenBookmark(AnsiString Local, AnsiString Remote);
+  void __fastcall DoFindFiles(AnsiString Directory, const TFileMasks & FileMask,
+    TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile);
+  virtual void __fastcall DoFocusRemotePath(AnsiString Path);
 
 public:
   virtual __fastcall ~TCustomScpExplorerForm();
@@ -520,6 +523,7 @@ public:
   void __fastcall ToggleAutoReadDirectoryAfterOp();
   void __fastcall PopupTrayBalloon(TTerminal * Terminal, const AnsiString & Str,
     TQueryType Type, Exception * E = NULL, unsigned int Seconds = 0);
+  void __fastcall FindFiles();
 
   __property bool ComponentVisible[Word Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };

+ 373 - 0
forms/FileFind.cpp

@@ -0,0 +1,373 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include <Common.h>
+#include <WinInterface.h>
+#include <VCLCommon.h>
+#include <TextsWin1.h>
+#include <WinConfiguration.h>
+#include <CoreMain.h>
+#include <Tools.h>
+#include "FileFind.h"
+//---------------------------------------------------------------------------
+#pragma package(smart_init)
+#pragma link "HistoryComboBox"
+#pragma link "IEListView"
+#pragma link "NortonLikeListView"
+#ifndef NO_RESOURCES
+#pragma resource "*.dfm"
+#endif
+//---------------------------------------------------------------------------
+bool __fastcall DoFileFindDialog(AnsiString Directory,
+  TFindEvent OnFind, AnsiString & Path)
+{
+  bool Result;
+  TFileFindDialog * Dialog = new TFileFindDialog(Application, OnFind);
+  try
+  {
+    Result = Dialog->Execute(Directory, Path);
+  }
+  __finally
+  {
+    delete Dialog;
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+__fastcall TFileFindDialog::TFileFindDialog(TComponent * Owner, TFindEvent OnFind)
+  : TForm(Owner)
+{
+  UseSystemSettings(this);
+  FOnFind = OnFind;
+  FState = ffInit;
+  FMinimizedByMe = false;
+
+  InstallPathWordBreakProc(MaskEdit);
+  InstallPathWordBreakProc(RemoteDirectoryEdit);
+  FixComboBoxResizeBug(MaskEdit);
+  FixComboBoxResizeBug(RemoteDirectoryEdit);
+  HintLabel(MaskHintText, LoadStr(MASK_HINT2));
+
+  FSystemImageList = SharedSystemImageList(false);
+  FileView->SmallImages = FSystemImageList;
+
+  if (!IsGlobalMinimizeHandler())
+  {
+    SetGlobalMinimizeHandler(GlobalMinimize);
+  };
+}
+//---------------------------------------------------------------------------
+__fastcall TFileFindDialog::~TFileFindDialog()
+{
+  if (GetGlobalMinimizeHandler() == GlobalMinimize)
+  {
+    SetGlobalMinimizeHandler(NULL);
+  }
+
+  Clear();
+  delete FSystemImageList;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TFileFindDialog::IsFinding()
+{
+  return (FState == ffFinding) || (FState == ffAborting);
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::UpdateControls()
+{
+  bool Finding = IsFinding();
+  Caption = LoadStr(Finding ? FIND_FILE_FINDING : FIND_FILE_TITLE);
+  AnsiString StartStopCaption;
+  if (Finding)
+  {
+    EnableControl(StartStopButton, true);
+    StartStopCaption = LoadStr(FIND_FILE_STOP);
+  }
+  else
+  {
+    EnableControl(StartStopButton, !RemoteDirectoryEdit->Text.IsEmpty());
+    StartStopCaption = LoadStr(FIND_FILE_START);
+  }
+  StartStopButton->Caption = StartStopCaption;
+  CancelButton->Visible = !Finding;
+  EnableControl(FilterGroup, !Finding);
+  MinimizeButton->Visible = Finding;
+  EnableControl(FocusButton, (FileView->ItemFocused != NULL));
+  switch (FState)
+  {
+    case ffInit:
+      StatusBar->SimpleText = "";
+
+    case ffFinding:
+    case ffAborting:
+      if (!FFindingInDirectory.IsEmpty())
+      {
+        StatusBar->SimpleText = FMTLOAD(FIND_FILE_IN_DIRECTORY, (FFindingInDirectory));
+      }
+      else
+      {
+        StatusBar->SimpleText = "";
+      }
+      break;
+
+    case ffAborted:
+      StatusBar->SimpleText = LoadStr(FIND_FILE_ABORTED);
+      break;
+
+    case ffDone:
+      StatusBar->SimpleText = LoadStr(FIND_FILE_DONE);
+      break;
+
+    default:
+      assert(false);
+      break;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::ControlChange(TObject * /*Sender*/)
+{
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+bool __fastcall TFileFindDialog::Execute(AnsiString Directory, AnsiString & Path)
+{
+  MaskEdit->Text = WinConfiguration->SelectMask;
+  RemoteDirectoryEdit->Text = UnixExcludeTrailingBackslash(Directory);
+
+  // have to set history after value, to prevent autocompletition
+  MaskEdit->Items = WinConfiguration->History["Mask"];
+  RemoteDirectoryEdit->Items = CustomWinConfiguration->History["RemoteDirectory"];
+
+  bool Result = (ShowModal() != mrCancel);
+  if (Result)
+  {
+    Path = static_cast<TRemoteFile *>(FileView->ItemFocused->Data)->FullFileName;
+  }
+
+  TFindFileConfiguration FormConfiguration = CustomWinConfiguration->FindFile;
+  FormConfiguration.ListParams = FileView->ColProperties->ParamsStr;
+  FormConfiguration.WindowParams = StoreFormSize(this);
+  CustomWinConfiguration->FindFile = FormConfiguration;
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::Clear()
+{
+  for (int Index = 0; Index < FileView->Items->Count; Index++)
+  {
+    TListItem * Item = FileView->Items->Item[Index];
+    TRemoteFile * File = static_cast<TRemoteFile *>(Item->Data);
+    Item->Data = NULL;
+    delete File;
+  }
+
+  FileView->Items->Clear();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::Start()
+{
+  if (MaskEdit->Focused())
+  {
+    MaskEditExit(NULL);
+  }
+
+  RemoteDirectoryEdit->SaveToHistory();
+  CustomWinConfiguration->History["RemoteDirectory"] = RemoteDirectoryEdit->Items;
+  MaskEdit->SaveToHistory();
+  WinConfiguration->History["Mask"] = MaskEdit->Items;
+  WinConfiguration->SelectMask = MaskEdit->Text;
+
+  assert(FState != ffFinding);
+
+  FState = ffFinding;
+  try
+  {
+    UpdateControls();
+    Repaint();
+    Busy(true);
+
+    assert(FOnFind != NULL);
+    FDirectory = UnixExcludeTrailingBackslash(RemoteDirectoryEdit->Text);
+    FOnFind(FDirectory, MaskEdit->Text, FileFound, FindingFile);
+  }
+  __finally
+  {
+    Busy(false);
+    FFindingInDirectory = "";
+    if (FState == ffFinding)
+    {
+      FState = ffDone;
+    }
+    if (FState == ffAborting)
+    {
+      FState = ffAborted;
+    }
+    if (IsIconic(Application->Handle) && FMinimizedByMe)
+    {
+      ShowNotification(NULL, LoadStr(BALLOON_OPERATION_COMPLETE), qtInformation);
+      FMinimizedByMe = false;
+    }
+    UpdateControls();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FileFound(TTerminal * /*Terminal*/,
+  const AnsiString FileName, const TRemoteFile * AFile, bool & Cancel)
+{
+  TListItem * Item = FileView->Items->Add();
+  TRemoteFile * File = AFile->Duplicate(true);
+  Item->Data = File;
+
+  Item->ImageIndex = File->IconIndex;
+  Item->Caption = File->FileName;
+
+  AnsiString Directory = UnixExtractFilePath(File->FullFileName);
+  if (AnsiSameText(FDirectory, Directory.SubString(1, FDirectory.Length())))
+  {
+    Directory[1] = '.';
+    Directory.Delete(2, FDirectory.Length() - 1);
+  }
+  else
+  {
+    assert(false);
+  }
+  Item->SubItems->Add(Directory);
+
+  if (File->IsDirectory)
+  {
+    Item->SubItems->Add("");
+  }
+  else
+  {
+    Item->SubItems->Add(FormatFloat("#,##0", File->Size));
+  }
+  Item->SubItems->Add(UserModificationStr(File->Modification, File->ModificationFmt));
+
+  UpdateControls();
+  Cancel = (FState == ffAborting);
+  Application->ProcessMessages();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FindingFile(TTerminal * /*Terminal*/,
+  const AnsiString Directory, bool & Cancel)
+{
+  if (!Directory.IsEmpty() && (FFindingInDirectory != Directory))
+  {
+    FFindingInDirectory = Directory;
+    UpdateControls();
+  }
+
+  Cancel = (FState == ffAborting);
+  Application->ProcessMessages();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::StartStopButtonClick(TObject * /*Sender*/)
+{
+  if (IsFinding())
+  {
+    Stop();
+  }
+  else
+  {
+    Clear();
+    if (ActiveControl->Parent == FilterGroup)
+    {
+      FileView->SetFocus();
+    }
+    Start();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::StopButtonClick(TObject * /*Sender*/)
+{
+  Stop();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::Stop()
+{
+  FState = ffAborting;
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::MinimizeButtonClick(TObject * /*Sender*/)
+{
+  MinimizeApp();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::GlobalMinimize(TObject * /*Sender*/)
+{
+  MinimizeApp();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::MinimizeApp()
+{
+  Application->Minimize();
+  FMinimizedByMe = true;
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FormShow(TObject * /*Sender*/)
+{
+  UpdateFormPosition(this, poMainFormCenter);
+  RestoreFormSize(CustomWinConfiguration->FindFile.WindowParams, this);
+  FileView->ColProperties->ParamsStr = CustomWinConfiguration->FindFile.ListParams;
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+bool __fastcall TFileFindDialog::StopIfFinding()
+{
+  bool Result = IsFinding();
+  if (Result)
+  {
+    Stop();
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & /*CanClose*/)
+{
+  StopIfFinding();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FormKeyDown(TObject * /*Sender*/, WORD & Key,
+  TShiftState /*Shift*/)
+{
+  if ((Key == VK_ESCAPE) && StopIfFinding())
+  {
+    Key = 0;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::MaskEditExit(TObject * /*Sender*/)
+{
+  ValidateMaskEdit(MaskEdit);
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FileViewDblClick(TObject * /*Sender*/)
+{
+  if (FileView->ItemFocused != NULL)
+  {
+    StopIfFinding();
+    ModalResult = FocusButton->ModalResult;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FocusButtonClick(TObject * /*Sender*/)
+{
+  StopIfFinding();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileFindDialog::FileViewSelectItem(TObject * /*Sender*/,
+  TListItem * /*Item*/, bool /*Selected*/)
+{
+  UpdateControls();
+}
+//---------------------------------------------------------------------------

+ 194 - 0
forms/FileFind.dfm

@@ -0,0 +1,194 @@
+object FileFindDialog: TFileFindDialog
+  Left = 367
+  Top = 198
+  Width = 578
+  Height = 455
+  HelpType = htKeyword
+  HelpKeyword = 'ui_find'
+  BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
+  Caption = 'FindX'
+  Color = clBtnFace
+  Constraints.MinHeight = 240
+  Constraints.MinWidth = 350
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'MS Sans Serif'
+  Font.Style = []
+  KeyPreview = True
+  OldCreateOrder = False
+  Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
+  OnKeyDown = FormKeyDown
+  OnShow = FormShow
+  DesignSize = (
+    570
+    421)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object FilterGroup: TGroupBox
+    Left = 8
+    Top = 6
+    Width = 467
+    Height = 127
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Filter'
+    TabOrder = 0
+    DesignSize = (
+      467
+      127)
+    object MaskLabel: TLabel
+      Left = 11
+      Top = 20
+      Width = 47
+      Height = 13
+      Caption = '&File mask:'
+      FocusControl = MaskEdit
+    end
+    object RemoteDirectoryLabel: TLabel
+      Left = 11
+      Top = 71
+      Width = 48
+      Height = 13
+      Caption = 'Sear&ch in:'
+      FocusControl = RemoteDirectoryEdit
+    end
+    object RemoteDirectoryEdit: THistoryComboBox
+      Left = 11
+      Top = 87
+      Width = 445
+      Height = 21
+      AutoComplete = False
+      Anchors = [akLeft, akTop, akRight]
+      ItemHeight = 13
+      MaxLength = 1000
+      TabOrder = 2
+      Text = 'RemoteDirectoryEdit'
+      OnChange = ControlChange
+    end
+    object MaskEdit: THistoryComboBox
+      Left = 11
+      Top = 36
+      Width = 445
+      Height = 21
+      AutoComplete = False
+      Anchors = [akLeft, akTop, akRight]
+      ItemHeight = 13
+      MaxLength = 1000
+      TabOrder = 0
+      Text = 'MaskEdit'
+      OnChange = ControlChange
+      OnExit = MaskEditExit
+    end
+    object MaskHintText: TStaticText
+      Left = 359
+      Top = 64
+      Width = 97
+      Height = 17
+      Alignment = taRightJustify
+      Anchors = [akTop, akRight]
+      AutoSize = False
+      Caption = 'mask hi&nts'
+      TabOrder = 1
+      TabStop = True
+    end
+  end
+  object CancelButton: TButton
+    Left = 482
+    Top = 43
+    Width = 80
+    Height = 25
+    Anchors = [akTop, akRight]
+    Cancel = True
+    Caption = 'Close'
+    ModalResult = 2
+    TabOrder = 2
+  end
+  object StartStopButton: TButton
+    Left = 482
+    Top = 11
+    Width = 80
+    Height = 25
+    Anchors = [akTop, akRight]
+    Caption = '&StartX'
+    Default = True
+    TabOrder = 1
+    OnClick = StartStopButtonClick
+  end
+  object HelpButton: TButton
+    Left = 482
+    Top = 75
+    Width = 80
+    Height = 25
+    Anchors = [akTop, akRight]
+    Caption = '&Help'
+    TabOrder = 4
+    OnClick = HelpButtonClick
+  end
+  object FileView: TIEListView
+    Left = 8
+    Top = 142
+    Width = 467
+    Height = 252
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    ColumnClick = False
+    FullDrag = True
+    ReadOnly = True
+    RowSelect = True
+    TabOrder = 5
+    ViewStyle = vsReport
+    OnDblClick = FileViewDblClick
+    NortonLike = nlOff
+    Columns = <
+      item
+        Caption = 'Name'
+        Width = 80
+      end
+      item
+        Caption = 'Directory'
+        Width = 120
+      end
+      item
+        Alignment = taRightJustify
+        Caption = 'Size'
+        Width = 80
+      end
+      item
+        Caption = 'Changed'
+        Width = 90
+      end>
+    MultiSelect = False
+    OnSelectItem = FileViewSelectItem
+  end
+  object StatusBar: TStatusBar
+    Left = 0
+    Top = 402
+    Width = 570
+    Height = 19
+    Panels = <>
+    ParentShowHint = False
+    ShowHint = True
+    SimplePanel = True
+  end
+  object FocusButton: TButton
+    Left = 482
+    Top = 142
+    Width = 80
+    Height = 25
+    Anchors = [akTop, akRight]
+    Caption = 'Fo&cus'
+    ModalResult = 1
+    TabOrder = 6
+    OnClick = FocusButtonClick
+  end
+  object MinimizeButton: TButton
+    Left = 482
+    Top = 43
+    Width = 80
+    Height = 25
+    Anchors = [akTop, akRight]
+    Caption = '&Minimize'
+    TabOrder = 3
+    OnClick = MinimizeButtonClick
+  end
+end

+ 78 - 0
forms/FileFind.h

@@ -0,0 +1,78 @@
+//---------------------------------------------------------------------------
+#ifndef FileFindH
+#define FileFindH
+//---------------------------------------------------------------------------
+#include <Classes.hpp>
+#include <Controls.hpp>
+#include <StdCtrls.hpp>
+#include <Forms.hpp>
+#include <HistoryComboBox.hpp>
+
+#include <WinInterface.h>
+#include <ComCtrls.hpp>
+#include <ExtCtrls.hpp>
+#include "IEListView.hpp"
+#include "NortonLikeListView.hpp"
+//---------------------------------------------------------------------------
+class TFileFindDialog : public TForm
+{
+__published:
+  TGroupBox *FilterGroup;
+  TButton *CancelButton;
+  TLabel *MaskLabel;
+  TLabel *RemoteDirectoryLabel;
+  THistoryComboBox *RemoteDirectoryEdit;
+  THistoryComboBox *MaskEdit;
+  TButton *StartStopButton;
+  TButton *HelpButton;
+  TIEListView *FileView;
+  TStatusBar *StatusBar;
+  TButton *FocusButton;
+  TButton *MinimizeButton;
+  TStaticText *MaskHintText;
+  void __fastcall ControlChange(TObject *Sender);
+  void __fastcall StartStopButtonClick(TObject *Sender);
+  void __fastcall StopButtonClick(TObject *Sender);
+  void __fastcall MinimizeButtonClick(TObject *Sender);
+  void __fastcall FormShow(TObject *Sender);
+  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
+  void __fastcall HelpButtonClick(TObject *Sender);
+  void __fastcall FormKeyDown(TObject *Sender, WORD &Key,
+          TShiftState Shift);
+  void __fastcall MaskEditExit(TObject *Sender);
+  void __fastcall FileViewDblClick(TObject *Sender);
+  void __fastcall FocusButtonClick(TObject *Sender);
+  void __fastcall FileViewSelectItem(TObject *Sender, TListItem *Item,
+          bool Selected);
+
+public:
+  __fastcall TFileFindDialog(TComponent * Owner, TFindEvent OnFind);
+  virtual __fastcall ~TFileFindDialog();
+
+  bool __fastcall Execute(AnsiString Directory, AnsiString & Path);
+
+protected:
+  void __fastcall Clear();
+  void __fastcall Start();
+  void __fastcall Stop();
+  bool __fastcall StopIfFinding();
+  void __fastcall GlobalMinimize(TObject * Sender);
+  void __fastcall MinimizeApp();
+  void __fastcall UpdateControls();
+  bool __fastcall IsFinding();
+
+private:
+  enum { ffInit, ffFinding, ffAborting, ffAborted, ffDone } FState;
+  bool FMinimizedByMe;
+  AnsiString FFindingInDirectory;
+  AnsiString FDirectory;
+  TFindEvent FOnFind;
+  TImageList * FSystemImageList;
+
+  void __fastcall FileFound(TTerminal * Terminal,
+    const AnsiString FileName, const TRemoteFile * File, bool & Cancel);
+  void __fastcall FindingFile(TTerminal * Terminal, const AnsiString Directory,
+    bool & Cancel);
+};
+//---------------------------------------------------------------------------
+#endif

+ 31 - 31
forms/Glyphs.dfm

@@ -8,7 +8,7 @@ object GlyphsModule: TGlyphsModule
     Left = 32
     Top = 16
     Bitmap = {
-      494C01015F006300040010001000FFFFFFFFFF00FFFFFFFFFFFFFFFF424D3600
+      494C010160006300040010001000FFFFFFFFFF00FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000009001000001002000000000000090
       0100000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -161,23 +161,23 @@ object GlyphsModule: TGlyphsModule
       840084848400000000000000000000000000C0C0C0009CFFFF00848484008484
       840084848400848484008484840084848400C0C0C00084848400848484008484
       8400848484000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
+      0000FFFFFF000000000000000000000000000000000000000000000000000000
+      0000FFFFFF0000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0000000000000000000000000000000000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF009CFFFF009CFF
       FF00FFFFFF009CFFFF00FFFFFF009CFFFF00C0C0C00084848400C0C0C000C0C0
       C00084848400000000000000000000000000C0C0C000FFFFFF009CFFFF009CFF
       FF00FFFFFF009CFFFF00FFFFFF009CFFFF00C0C0C00084848400C0C0C000C0C0
       C000848484000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
+      0000FFFFFF000000000000000000000000000000000000000000000000000000
+      0000FFFFFF0000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0086868600FFFFCC00FFCC330000000000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF00848484008484
       840084848400848484008484840084848400C0C0C00084848400FFFFFF00C0C0
       C00084848400848484008484840084848400C0C0C000FFFFFF00848484008484
       840084848400848484008484840084848400C0C0C00084848400FFFFFF00C0C0
       C000848484008484840084848400848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000FFFFFF000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0086868600FFFFCC00FFCC330000000000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF009CFFFF009CFF
@@ -185,7 +185,7 @@ object GlyphsModule: TGlyphsModule
       C00084848400C0C0C000C0C0C00084848400C0C0C000FFFFFF009CFFFF009CFF
       FF00FFFFFF009CFFFF00FFFFFF00FFFFFF00C0C0C00084848400FFFFFF00C0C0
       C00084848400C0C0C000C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000FFFFFF000000000000000000C0C0C00000000000FFFFFF000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0086868600FFFFCC00FFCC330000000000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF00848484008484
@@ -193,7 +193,7 @@ object GlyphsModule: TGlyphsModule
       C00084848400FFFFFF00C0C0C00084848400C0C0C000FFFFFF00848484008484
       8400848484008484840084848400FFFFFF00C0C0C00084848400FFFFFF00C0C0
       C00084848400FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000C0C0C00000000000000000000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF0086868600FFFFCC00FFFFCC00FFFF9900FFCC330000000000FFFF
       FF00FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF00FFFFFF00FFFF
@@ -201,71 +201,71 @@ object GlyphsModule: TGlyphsModule
       C00084848400FFFFFF00C0C0C00084848400C0C0C000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C0C0C00084848400FFFFFF00C0C0
       C00084848400FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000FFFFFF0000000000000000000000000000000000FFFFFF000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF0086868600FFFFCC00FFFFCC00FFFFCC00FFFF9900FFFF9900FFCC33000000
       0000FFFFFF00FFFFFF00CCCCCC0000000000C0C0C000FFFFFF00848484008484
       8400848484009CFFFF0084848400848484008484840084848400FFFFFF00C0C0
       C00084848400FFFFFF00C0C0C00084848400C0C0C000FFFFFF00848484008484
       8400848484009CFFFF0084848400848484008484840084848400FFFFFF00C0C0
-      C00084848400FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      C00084848400FFFFFF00C0C0C000848484000066660099FFFF0000CCFF0000CC
+      FF00000000000000000000000000000000000000000000000000000000000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF008686
       8600FFFFCC00FFFFFF00FFFFFF00FFFFCC00FFFF9900FFFF9900FFFF9900FFCC
       330000000000FFFFFF00CCCCCC0000000000C0C0C000FFFFFF000000FF000000
       9C0000009C0000009C00C0C0C000FFFFFF0084848400FFFFFF00FFFFFF00C0C0
       C00084848400FFFFFF00C0C0C00084848400C0C0C000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00C0C0C000FFFFFF0084848400FFFFFF00FFFFFF00C0C0
-      C00084848400FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000086868600FFFFFF00868686008686
+      C00084848400FFFFFF00C0C0C000848484000066660099FFFF0099FFFF0000CC
+      FF0000CCFF0000000000FFFFFF000000000000CCFF000099CC0000000000FFFF
+      FF000000000000000000000000000000000086868600FFFFFF00868686008686
       8600868686008686860086868600868686008686860086868600868686008686
       86008686860086868600CCCCCC0000000000C0C0C0000000FF0000009C00FFFF
       FF00FFFFFF000000FF0000009C0084848400FFFFFF0084848400848484008484
       840084848400FFFFFF00C0C0C00084848400C0C0C000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00C0C0C00084848400FFFFFF0084848400848484008484
-      840084848400FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      840084848400FFFFFF00C0C0C000848484000066660099FFFF0000CCFF0099FF
+      FF0000CCFF000000000000000000000000000099CC000099CC00000000000000
       00000000000000000000000000000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00CCCCCC00000000000000FF0000009C00C0C0C000C0C0
       C000C0C0C000C0C0C0000000FF0000009C00FFFFFF00C0C0C000FFFFFF008484
       8400FFFFFF00FFFFFF00C0C0C00084848400C0C0C000C0C0C000C0C0C000C0C0
       C000C0C0C000C0C0C000C0C0C000C0C0C000FFFFFF00C0C0C000FFFFFF008484
-      8400FFFFFF00FFFFFF00C0C0C000848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      8400FFFFFF00FFFFFF00C0C0C000848484000066660099FFFF00FFFFFF0000CC
+      FF0099FFFF0000CCFF0000CCFF0000CCFF0000CCFF000099CC00000000000000
       0000000000000000000000000000000000008000000080000000800000008000
       0000800000008000000080000000800000008000000080000000800000008000
       0000800000008000000080000000000000000000FF0000009C00000000000000
       FF0000009C00FFFFFF000000FF0000009C00FFFFFF00C0C0C00084848400FFFF
       FF0084848400848484008484840084848400000000000000000000000000C0C0
       C000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C0C0C00084848400FFFF
-      FF00848484008484840084848400848484000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      FF00848484008484840084848400848484000066660099FFFF0000CCFF00FFFF
+      FF0000CCFF0099FFFF0000CCFF0000CCFF0000CCFF000099CC00000000000000
       000000000000000000000000000000000000FF000000FF993300FF663300FF66
       3300FF663300FF663300FF663300FF663300FF663300FF663300FF663300FF66
       3300FF663300FF663300FF663300000000000000FF0000009C00000000000000
       FF0000009C00C0C0C0000000FF0000009C00C0C0C000C0C0C000FFFFFF00FFFF
       FF00C0C0C000FFFFFF008484840000000000000000000000000000000000C0C0
       C000C0C0C000C0C0C000C0C0C000C0C0C000C0C0C000C0C0C000FFFFFF00FFFF
-      FF00C0C0C000FFFFFF0084848400000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      FF00C0C0C000FFFFFF0084848400000000000066660099FFFF0099FFFF0099FF
+      FF0099FFFF0099FFFF0099FFFF0099FFFF0099FFFF0099FFFF00000000000000
       000000000000000000000000000000000000FF000000FF000000FF000000FF00
       0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF00
       0000FF000000FF000000FF00000000000000000000000000FF0000009C000000
       FF0000009C000000FF0000009C00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
       FF00C0C0C0008484840000000000000000000000000000000000000000000000
       00000000000000000000C0C0C000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00C0C0C0008484840000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      FF00C0C0C0008484840000000000000000000066660099FFFF0099FFFF0099FF
+      FF00006666000066660000666600006666000066660000666600000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       FF0000009C0000000000C0C0C000C0C0C000C0C0C000C0C0C000C0C0C000C0C0
       C000C0C0C0000000000000000000000000000000000000000000000000000000
       00000000000000000000C0C0C000C0C0C000C0C0C000C0C0C000C0C0C000C0C0
-      C000C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      C000C0C0C0000000000000000000000000000000000000666600006666000066
+      6600000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -3216,11 +3216,11 @@ object GlyphsModule: TGlyphsModule
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000FFFF003F003F00000000003F003F0000
-      0000000700070000000000070007000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000002000E000000000002001E0010000
-      00008003FC030000FFFFE407FC070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      00000000000000000000000000000000FFFF003F003FFFFF0000003F003FE1E1
+      000000070007E1E1000000070007E081000000000000E001000000000000E001
+      000000000000F003000000000000000300000000000000070000000000000007
+      0000000000000007000000000000001F00002000E000001F00002001E001001F
+      00008003FC03003FFFFFE407FC0787FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
       FFFFFFFFFEABE000FF3FF9FFEFFFE000FF1FF1FFC67FE000910FE113AA3F8000
       9107C113EA1F800091038113E60F80009107C113CE078000910FE113AF038000
       FF1FF1FFAB038000FF3FF9FFC7878003FFFFFFFFEF8F8003FFFFFFFFFFFF8003

+ 218 - 126
forms/LocationProfiles.cpp

@@ -12,6 +12,7 @@
 
 #include "LocationProfiles.h"
 #include "WinConfiguration.h"
+#include "Custom.h"
 //---------------------------------------------------------------------
 #pragma link "IEComboBox"
 #ifndef NO_RESOURCES
@@ -47,6 +48,133 @@ bool __fastcall LocationProfilesDialog(TOpenDirectoryMode Mode,
   }
   return Result;
 }
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+void __fastcall BookmarkNameValidateName(const AnsiString Name)
+{
+  if (Name.IsEmpty() || IsNumber(Name))
+  {
+    throw Exception(FMTLOAD(BOOKMARK_INVALID_NAME, (Name)));
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall BookmarkFolderValidateName(const AnsiString Name,
+  bool AllowEmpty)
+{
+  if ((!AllowEmpty && Name.IsEmpty()) || Name.Pos("\\"))
+  {
+    throw Exception(FMTLOAD(BOOKMARK_FOLDER_INVALID_NAME, (Name)));
+  }
+}
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TBookmarkNameDialog : public TCustomDialog
+{
+public:
+  __fastcall TBookmarkNameDialog(TStrings * PeerBookmarks, bool AllowShared);
+
+  bool __fastcall Execute(AnsiString & Name, bool & Shared);
+
+protected:
+  virtual void __fastcall DoValidate();
+
+private:
+  TComboBox * NameCombo;
+  TCheckBox * SharedCheck;
+};
+//---------------------------------------------------------------------
+__fastcall TBookmarkNameDialog::TBookmarkNameDialog(TStrings * PeerBookmarks,
+    bool AllowShared) :
+  TCustomDialog(HELP_LOCATION_PROFILE_ADD)
+{
+  Caption = LoadStr(ADD_BOOKMARK_CAPTION);
+
+  NameCombo = new TComboBox(this);
+  AddComboBox(NameCombo, CreateLabel(LoadStr(ADD_BOOKMARK_PROMPT)));
+  NameCombo->Items = PeerBookmarks;
+
+  if (AllowShared)
+  {
+    SharedCheck = new TCheckBox(this);
+    SharedCheck->Caption = LoadStr(ADD_BOOKMARK_SHARED);
+    AddButtonControl(SharedCheck);
+  }
+  else
+  {
+    SharedCheck = NULL;
+  }
+}
+//---------------------------------------------------------------------
+void __fastcall TBookmarkNameDialog::DoValidate()
+{
+  if (NameCombo->Text.IsEmpty() || IsNumber(NameCombo->Text))
+  {
+    throw Exception(FMTLOAD(BOOKMARK_INVALID_NAME, (NameCombo->Text)));
+  }
+  TCustomDialog::DoValidate();
+}
+//---------------------------------------------------------------------
+bool __fastcall TBookmarkNameDialog::Execute(AnsiString & Name, bool & Shared)
+{
+  NameCombo->Text = Name;
+  if (SharedCheck != NULL)
+  {
+    SharedCheck->Checked = Shared;
+  }
+  bool Result = TCustomDialog::Execute();
+  if (Result)
+  {
+    Name = NameCombo->Text;
+    if (SharedCheck != NULL)
+    {
+      Shared = SharedCheck->Checked;
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TBookmarkFolderDialog : public TCustomDialog
+{
+public:
+  __fastcall TBookmarkFolderDialog(TStrings * Folders);
+
+  bool __fastcall Execute(AnsiString & Name);
+
+protected:
+  virtual void __fastcall DoValidate();
+
+private:
+  TComboBox * NameCombo;
+};
+//---------------------------------------------------------------------
+__fastcall TBookmarkFolderDialog::TBookmarkFolderDialog(TStrings * Folders) :
+  TCustomDialog(HELP_LOCATION_PROFILE_MOVE)
+{
+  Caption = LoadStr(MOVE_BOOKMARK_CAPTION);
+
+  NameCombo = new TComboBox(this);
+  AddComboBox(NameCombo, CreateLabel(LoadStr(MOVE_BOOKMARK_PROMPT)));
+  NameCombo->Items = Folders;
+}
+//---------------------------------------------------------------------
+void __fastcall TBookmarkFolderDialog::DoValidate()
+{
+  BookmarkFolderValidateName(NameCombo->Text, true);
+  TCustomDialog::DoValidate();
+}
+//---------------------------------------------------------------------
+bool __fastcall TBookmarkFolderDialog::Execute(AnsiString & Name)
+{
+  NameCombo->Text = Name;
+  bool Result = TCustomDialog::Execute();
+  if (Result)
+  {
+    Name = NameCombo->Text;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------
 //---------------------------------------------------------------------
 __fastcall TLocationProfilesDialog::TLocationProfilesDialog(TComponent * AOwner):
   TForm(AOwner)
@@ -340,19 +468,6 @@ TTreeViewScrollOnDragOver * TLocationProfilesDialog::GetScrollOnDragOver(TObject
   return GetProfilesObject(Sender, FSessionScrollOnDragOver, FSharedScrollOnDragOver);
 }
 //---------------------------------------------------------------------------
-void __fastcall TLocationProfilesDialog::BookmarkNameValidateName(const AnsiString Name)
-{
-  if (Name.IsEmpty() || IsNumber(Name))
-  {
-    throw Exception(FMTLOAD(BOOKMARK_INVALID_NAME, (Name)));
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TLocationProfilesDialog::BookmarkNameValidate(const TDialogData & Data)
-{
-  BookmarkNameValidateName(Data.Combo);
-}
-//---------------------------------------------------------------------------
 bool __fastcall TLocationProfilesDialog::AddAsBookmark(TObject * Sender, bool Initial)
 {
   TBookmarkList * BookmarkList = GetBookmarkList(Sender);
@@ -401,77 +516,68 @@ bool __fastcall TLocationProfilesDialog::AddAsBookmark(TObject * Sender, bool In
       }
     }
 
-    TDialogParams Params;
-    Params.Caption = LoadStr(ADD_BOOKMARK_CAPTION);
-    Params.HelpKeyword = HELP_LOCATION_PROFILE_ADD;
-    Params.ComboLabel = LoadStr(ADD_BOOKMARK_PROMPT);
-    Params.ComboItems = PeerBookmarks;
-    Params.ComboEmptyValid = false;
-    if (Initial)
-    {
-      Params.CheckLabel = LoadStr(ADD_BOOKMARK_SHARED);
-    }
-    Params.OnValidate = BookmarkNameValidate;
-    TDialogData Data;
-    Data.Combo = BookmarkName;
-    if (Initial)
-    {
-      Data.Check = WinConfiguration->UseSharedBookmarks;
-    }
-    Result = DoCustomDialog(Params, Data);
-    if (Result)
+    TBookmarkNameDialog * Dialog = new TBookmarkNameDialog(PeerBookmarks, Initial);
+    try
     {
-      BookmarkName = Data.Combo;
-      if (Initial)
-      {
-        WinConfiguration->UseSharedBookmarks = Data.Check;
-        BookmarkList = GetBookmarkList(GetProfilesSheet());
-        ProfilesView = GetProfilesView(GetProfilesSheet());
-      }
-
-      TBookmark * Bookmark = BookmarkList->FindByName(SelectedNode, BookmarkName);
-      if (Bookmark != NULL)
+      bool Shared = WinConfiguration->UseSharedBookmarks;
+      Result = Dialog->Execute(BookmarkName, Shared);
+      if (Result)
       {
-        Bookmark->Local = LocalDirectory;
-        Bookmark->Remote = RemoteDirectory;
+        if (Initial)
+        {
+          WinConfiguration->UseSharedBookmarks = Shared;
+          BookmarkList = GetBookmarkList(GetProfilesSheet());
+          ProfilesView = GetProfilesView(GetProfilesSheet());
+        }
 
-        for (int Index = 0; Index < ProfilesView->Items->Count; Index++)
+        TBookmark * Bookmark = BookmarkList->FindByName(SelectedNode, BookmarkName);
+        if (Bookmark != NULL)
         {
-          TTreeNode * Node = ProfilesView->Items->Item[Index];
-          if (Node->Data == Bookmark)
+          Bookmark->Local = LocalDirectory;
+          Bookmark->Remote = RemoteDirectory;
+
+          for (int Index = 0; Index < ProfilesView->Items->Count; Index++)
           {
-            Selected = Node;
-            break;
+            TTreeNode * Node = ProfilesView->Items->Item[Index];
+            if (Node->Data == Bookmark)
+            {
+              Selected = Node;
+              break;
+            }
           }
         }
-      }
-      else
-      {
-        Bookmark = new TBookmark();
-        Bookmark->Name = BookmarkName;
-        Bookmark->Local = LocalDirectory;
-        Bookmark->Remote = RemoteDirectory;
-        if (SelectedBookmark != NULL)
-        {
-          Bookmark->Node = SelectedBookmark->Node;
-          BookmarkList->InsertBefore(SelectedBookmark, Bookmark);
-          Selected = ProfilesView->Items->InsertObject(Selected, BookmarkText(Bookmark), Bookmark);
-        }
-        else if ((Selected != NULL) && (SelectedBookmark == NULL))
-        {
-          // must be a folder
-          assert(!Selected->Parent); // more than one level of folders is not supported
-          Bookmark->Node = Selected->Text;
-          BookmarkList->Add(Bookmark);
-          Selected = ProfilesView->Items->AddChildObject(Selected, BookmarkText(Bookmark), Bookmark);
-        }
         else
         {
-          BookmarkList->Add(Bookmark);
-          Selected = ProfilesView->Items->AddObject(NULL, BookmarkText(Bookmark), Bookmark);
+          Bookmark = new TBookmark();
+          Bookmark->Name = BookmarkName;
+          Bookmark->Local = LocalDirectory;
+          Bookmark->Remote = RemoteDirectory;
+          if (SelectedBookmark != NULL)
+          {
+            Bookmark->Node = SelectedBookmark->Node;
+            BookmarkList->InsertBefore(SelectedBookmark, Bookmark);
+            Selected = ProfilesView->Items->InsertObject(Selected, BookmarkText(Bookmark), Bookmark);
+          }
+          else if ((Selected != NULL) && (SelectedBookmark == NULL))
+          {
+            // must be a folder
+            assert(!Selected->Parent); // more than one level of folders is not supported
+            Bookmark->Node = Selected->Text;
+            BookmarkList->Add(Bookmark);
+            Selected = ProfilesView->Items->AddChildObject(Selected, BookmarkText(Bookmark), Bookmark);
+          }
+          else
+          {
+            BookmarkList->Add(Bookmark);
+            Selected = ProfilesView->Items->AddObject(NULL, BookmarkText(Bookmark), Bookmark);
+          }
         }
+        ProfilesView->Selected = Selected;
       }
-      ProfilesView->Selected = Selected;
+    }
+    __finally
+    {
+      delete Dialog;
     }
   }
   __finally
@@ -722,20 +828,6 @@ void __fastcall TLocationProfilesDialog::ProfilesViewChange(
   UpdateControls();
 }
 //---------------------------------------------------------------------------
-void __fastcall TLocationProfilesDialog::BookmarkFolderValidateName(const AnsiString Name,
-  bool AllowEmpty)
-{
-  if ((!AllowEmpty && Name.IsEmpty()) || Name.Pos("\\"))
-  {
-    throw Exception(FMTLOAD(BOOKMARK_FOLDER_INVALID_NAME, (Name)));
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TLocationProfilesDialog::BookmarkFolderValidate(const TDialogData & Data)
-{
-  BookmarkFolderValidateName(Data.Combo, true);
-}
-//---------------------------------------------------------------------------
 void __fastcall TLocationProfilesDialog::BookmarkMoveToButtonClick(TObject * Sender)
 {
   TTreeView * ProfilesView = GetProfilesView(Sender);
@@ -744,54 +836,54 @@ void __fastcall TLocationProfilesDialog::BookmarkMoveToButtonClick(TObject * Sen
   assert(ProfilesView->Selected->Data);
   TBookmark * Bookmark = (TBookmark *)ProfilesView->Selected->Data;
 
-  TDialogParams Params;
-  Params.Caption = LoadStr(MOVE_BOOKMARK_CAPTION);
-  Params.HelpKeyword = HELP_LOCATION_PROFILE_MOVE;
-  Params.ComboLabel = LoadStr(MOVE_BOOKMARK_PROMPT);
-  Params.ComboItems = Folders;
-  Params.ComboEmptyValid = true;
-  Params.OnValidate = BookmarkFolderValidate;
-  TDialogData Data;
-  Data.Combo = Bookmark->Node;
-  if (DoCustomDialog(Params, Data) &&
-      (Data.Combo != Bookmark->Node))
-  {
-    TTreeNode * FolderNode;
-    int I = Folders->IndexOf(Data.Combo);
-    if (Data.Combo.IsEmpty())
-    {
-      FolderNode = NULL;
-    }
-    else if (I >= 0)
-    {
-      FolderNode = dynamic_cast<TTreeNode *>(Folders->Objects[I]);
-      assert(FolderNode);
-    }
-    else
+  TBookmarkFolderDialog * Dialog = new TBookmarkFolderDialog(Folders);
+  try
+  {
+    AnsiString NodeName = Bookmark->Node;
+    if (Dialog->Execute(NodeName) &&
+        (NodeName != Bookmark->Node))
     {
-      I = Folders->Add(Data.Combo);
-      TTreeNode * NextNode;
-      // duplicated in RenameButtonClick()
-      if (I < Folders->Count-1)
+      TTreeNode * FolderNode;
+      int I = Folders->IndexOf(NodeName);
+      if (NodeName.IsEmpty())
       {
-        NextNode = dynamic_cast<TTreeNode *>(Folders->Objects[I+1]);
-        assert(NextNode);
+        FolderNode = NULL;
       }
-      else if (Folders->Count > 1)
+      else if (I >= 0)
       {
-        NextNode = (dynamic_cast<TTreeNode *>(Folders->Objects[I-1]))->getNextSibling();
+        FolderNode = dynamic_cast<TTreeNode *>(Folders->Objects[I]);
+        assert(FolderNode);
       }
       else
       {
-        assert(ProfilesView->Items->Count);
-        NextNode = ProfilesView->Items->Item[0];
+        I = Folders->Add(NodeName);
+        TTreeNode * NextNode;
+        // duplicated in RenameButtonClick()
+        if (I < Folders->Count-1)
+        {
+          NextNode = dynamic_cast<TTreeNode *>(Folders->Objects[I+1]);
+          assert(NextNode);
+        }
+        else if (Folders->Count > 1)
+        {
+          NextNode = (dynamic_cast<TTreeNode *>(Folders->Objects[I-1]))->getNextSibling();
+        }
+        else
+        {
+          assert(ProfilesView->Items->Count);
+          NextNode = ProfilesView->Items->Item[0];
+        }
+        FolderNode = ProfilesView->Items->Insert(NextNode, NodeName);
+        assert(FolderNode);
+        Folders->Objects[I] = FolderNode;
       }
-      FolderNode = ProfilesView->Items->Insert(NextNode, Data.Combo);
-      assert(FolderNode);
-      Folders->Objects[I] = FolderNode;
-    }
 
-    BookmarkMove(Sender, ProfilesView->Selected, FolderNode);
+      BookmarkMove(Sender, ProfilesView->Selected, FolderNode);
+    }
+  }
+  __finally
+  {
+    delete Dialog;
   }
 }
 //---------------------------------------------------------------------------

+ 0 - 4
forms/LocationProfiles.h

@@ -152,10 +152,6 @@ private:
   void __fastcall LoadBookmarks(
     TTreeView * ProfilesView, TStringList * Folders, TBookmarkList * BookmarkList,
     TBookmarkList * Source);
-  void __fastcall BookmarkNameValidate(const TDialogData & Data);
-  void __fastcall BookmarkNameValidateName(const AnsiString Name);
-  void __fastcall BookmarkFolderValidate(const TDialogData & Data);
-  void __fastcall BookmarkFolderValidateName(const AnsiString Name, bool AllowEmpty);
   bool __fastcall ProfileMatch(TTreeNode * Node);
   AnsiString __fastcall BookmarkText(TBookmark * Bookmark);
 };

+ 21 - 5
forms/Login.cpp

@@ -11,8 +11,8 @@
 #include <HelpWin.h>
 #include <VCLCommon.h>
 
-#include "Login.h"
 #include "WinInterface.h"
+#include "Login.h"
 #include "GUITools.h"
 #include "Tools.h"
 #include "Setup.h"
@@ -1296,7 +1296,7 @@ void __fastcall TLoginDialog::SaveSessionActionExecute(TObject * /*Sender*/)
   bool * PSavePassword;
 
   if (Configuration->DisablePasswordStoring ||
-      FSessionData->Password.IsEmpty())
+      !FSessionData->HasAnyPassword())
   {
     PSavePassword = NULL;
   }
@@ -1304,8 +1304,9 @@ void __fastcall TLoginDialog::SaveSessionActionExecute(TObject * /*Sender*/)
   {
     PSavePassword = &SavePassword;
     SavePassword =
-      (FEditingSessionData != NULL) &&
-      (FEditingSessionData->Password == FSessionData->Password);
+      ((FEditingSessionData != NULL) &&
+       (FEditingSessionData->Password == FSessionData->Password)) ||
+      CustomWinConfiguration->UseMasterPassword;
   }
 
   AnsiString SessionName = FSessionData->SessionName;
@@ -1550,9 +1551,24 @@ void __fastcall TLoginDialog::PreferencesButtonClick(TObject * /*Sender*/)
   UpdateControls();
 }
 //---------------------------------------------------------------------------
+void __fastcall TLoginDialog::MasterPasswordRecrypt(TObject * /*Sender*/)
+{
+  FSessionData->RecryptPasswords();
+}
+//---------------------------------------------------------------------------
 void __fastcall TLoginDialog::ShowPreferencesDialog()
 {
-  DoPreferencesDialog(pmLogin);
+  assert(CustomWinConfiguration->OnMasterPasswordRecrypt == NULL);
+  CustomWinConfiguration->OnMasterPasswordRecrypt = MasterPasswordRecrypt;
+  try
+  {
+    DoPreferencesDialog(pmLogin);
+  }
+  __finally
+  {
+    assert(CustomWinConfiguration->OnMasterPasswordRecrypt == MasterPasswordRecrypt);
+    CustomWinConfiguration->OnMasterPasswordRecrypt = NULL;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::NewSessionActionExecute(TObject * /*Sender*/)

+ 1 - 1
forms/Login.dfm

@@ -953,7 +953,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Anchors = [akLeft, akTop, akRight]
             ItemHeight = 13
-            MaxLength = 50
+            MaxLength = 255
             TabOrder = 0
             Text = 'SftpServerEdit'
             Items.Strings = (

+ 1 - 0
forms/Login.h

@@ -420,6 +420,7 @@ private:
   void __fastcall SessionTreeProc(TMessage & Message);
   bool __fastcall SessionAllowDrop(TTreeNode * DropTarget);
   int __fastcall DefaultPort();
+  void __fastcall MasterPasswordRecrypt(TObject * Sender);
 
 protected:
   void __fastcall Default();

+ 4 - 10
forms/MessageDlg.cpp

@@ -57,18 +57,12 @@ void __fastcall TMessageForm::HelpButtonClick(TObject * /*Sender*/)
   }
   else
   {
-    TMessageParams Params;
-    Params.AllowHelp = false; // to avoid recursion
-    if (MessageDialog(LoadStr(HELP_SEND_MESSAGE), qtConfirmation,
-          qaOK | qaCancel, HELP_NONE, &Params) == qaOK)
+    AnsiString Text = Message->Caption;
+    if (MessageMemo != NULL)
     {
-      AnsiString Text = Message->Caption;
-      if (MessageMemo != NULL)
-      {
-        Text += "\n" + MessageMemo->Text;
-      }
-      SearchHelp(Text);
+      Text += "\n" + MessageMemo->Text;
     }
+    MessageWithNoHelp(Text);
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 0
forms/NonVisual.cpp

@@ -202,6 +202,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   // directory
   UPD(CurrentCreateDirAction, true)
   UPD(NewDirAction, true)
+  UPD(FindFilesAction, (DirView(osRemote) == DirView(osCurrent)))
   // selection
   UPD(SelectOneAction, DirView(osCurrent)->FilesCount)
   UPD(SelectAction, DirView(osCurrent)->FilesCount)
@@ -492,6 +493,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     // directory
     EXE(CurrentCreateDirAction, ScpExplorer->CreateDirectory(osCurrent))
     EXE(NewDirAction, ScpExplorer->CreateDirectory(osCurrent))
+    EXE(FindFilesAction, ScpExplorer->FindFiles())
     //selection
     EXE(SelectOneAction, DirView(osCurrent)->SelectCurrentItem(DirView(osCurrent)->NortonLike))
     EXE(SelectAction, DirView(osCurrent)->DoSelectByMask(true))

+ 9 - 0
forms/NonVisual.dfm

@@ -1833,6 +1833,15 @@ object NonVisualDataModule: TNonVisualDataModule
       Hint = 'Filter|Filter displayed files'
       ImageIndex = 92
     end
+    object FindFilesAction: TAction
+      Tag = 14
+      Category = 'Command'
+      Caption = '&Find Files...'
+      HelpKeyword = 'task_find'
+      Hint = 'Find files|Find files and directories'
+      ImageIndex = 95
+      ShortCut = 32886
+    end
   end
   object ExplorerBarPopup: TTBXPopupMenu
     Images = GlyphsModule.ExplorerImages

+ 1 - 0
forms/NonVisual.h

@@ -496,6 +496,7 @@ __published:    // IDE-managed Components
   TTBXItem *TBXItem28;
   TTBXItem *TBXItem29;
   TAction *QueueCycleOnceEmptyAction;
+  TAction *FindFilesAction;
   void __fastcall LogActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall LogActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);

+ 43 - 3
forms/Preferences.cpp

@@ -24,9 +24,6 @@
 #pragma link "LogSettings"
 #pragma link "CopyParams"
 #pragma link "UpDownEdit"
-#pragma link "IEComboBox"
-#pragma link "HistoryComboBox"
-#pragma link "PasswordEdit"
 #ifndef NO_RESOURCES
 #pragma resource "*.dfm"
 #endif
@@ -364,6 +361,9 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
       ThemeCombo->ItemIndex = 0;
     }
 
+    // security
+    UseMasterPasswordCheck->Checked = WinConfiguration->UseMasterPassword;
+
     #undef BOOLPROP
   }
   __finally
@@ -729,6 +729,8 @@ void __fastcall TPreferencesDialog::UpdateControls()
     EnableControl(PuttyPasswordCheck2, !PuttyPathEdit->Text.IsEmpty());
     EnableControl(AutoOpenInPuttyCheck, PuttyPasswordCheck2->Enabled);
     EnableControl(TelnetForFtpInPuttyCheck, PuttyPasswordCheck2->Enabled);
+
+    EnableControl(SetMasterPasswordButton, WinConfiguration->UseMasterPassword);
   }
 }
 //---------------------------------------------------------------------------
@@ -1526,3 +1528,41 @@ void __fastcall TPreferencesDialog::SessionReopenTimeoutEditGetValue(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::UseMasterPasswordCheckClick(
+  TObject * /*Sender*/)
+{
+  if (UseMasterPasswordCheck->Checked != WinConfiguration->UseMasterPassword)
+  {
+    try
+    {
+      if (UseMasterPasswordCheck->Checked)
+      {
+        if (DoChangeMasterPasswordDialog())
+        {
+          MessageDialog(LoadStr(MASTER_PASSWORD_SET), qtInformation, qaOK, HELP_MASTER_PASSWORD);
+        }
+      }
+      else
+      {
+        if (DoMasterPasswordDialog())
+        {
+          WinConfiguration->ClearMasterPassword();
+          MessageDialog(LoadStr(MASTER_PASSWORD_CLEARED), qtInformation, qaOK, HELP_MASTER_PASSWORD);
+        }
+      }
+    }
+    __finally
+    {
+      UseMasterPasswordCheck->Checked = WinConfiguration->UseMasterPassword;
+      UpdateControls();
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::SetMasterPasswordButtonClick(
+  TObject * /*Sender*/)
+{
+  DoChangeMasterPasswordDialog();
+  MessageDialog(LoadStr(MASTER_PASSWORD_CHANGED), qtInformation, qaOK, HELP_MASTER_PASSWORD);
+}
+//---------------------------------------------------------------------------

+ 54 - 9
forms/Preferences.dfm

@@ -782,6 +782,7 @@ object PreferencesDialog: TPreferencesDialog
             Alignment = taRightJustify
             MaxValue = 99
             MinValue = 1
+            Value = 1
             MaxLength = 2
             TabOrder = 2
             OnChange = ControlChange
@@ -1736,6 +1737,7 @@ object PreferencesDialog: TPreferencesDialog
             Alignment = taRightJustify
             MaxValue = 65535
             MinValue = 1
+            Value = 1
             Anchors = [akTop, akRight]
             TabOrder = 4
           end
@@ -2013,6 +2015,48 @@ object PreferencesDialog: TPreferencesDialog
           end
         end
       end
+      object SecuritySheet: TTabSheet
+        Tag = 19
+        Hint = 'Security'
+        Caption = 'Security'
+        ImageIndex = 18
+        TabVisible = False
+        DesignSize = (
+          373
+          353)
+        object MasterPasswordGroup: TGroupBox
+          Left = 8
+          Top = 8
+          Width = 357
+          Height = 92
+          Anchors = [akLeft, akTop, akRight]
+          Caption = 'Master password'
+          TabOrder = 0
+          DesignSize = (
+            357
+            92)
+          object SetMasterPasswordButton: TButton
+            Left = 16
+            Top = 51
+            Width = 325
+            Height = 25
+            Anchors = [akLeft, akTop, akRight]
+            Caption = '&Change master password...'
+            TabOrder = 1
+            OnClick = SetMasterPasswordButtonClick
+          end
+          object UseMasterPasswordCheck: TCheckBox
+            Left = 17
+            Top = 24
+            Width = 331
+            Height = 17
+            Anchors = [akLeft, akTop, akRight]
+            Caption = '&Use master password'
+            TabOrder = 0
+            OnClick = UseMasterPasswordCheckClick
+          end
+        end
+      end
       object IntegrationAppSheet: TTabSheet
         Tag = 18
         Hint = 'Applications'
@@ -2127,7 +2171,7 @@ object PreferencesDialog: TPreferencesDialog
         OnChange = NavigationTreeChange
         OnCollapsing = NavigationTreeCollapsing
         Items.Data = {
-          08000000250000000000000001000000FFFFFFFFFFFFFFFF0000000005000000
+          09000000250000000000000001000000FFFFFFFFFFFFFFFF0000000005000000
           0C456E7669726F6E6D656E7458230000000000000003000000FFFFFFFFFFFFFF
           FF00000000000000000A496E7465726661636558200000000000000011000000
           FFFFFFFFFFFFFFFF00000000000000000757696E646F77582000000000000000
@@ -2141,14 +2185,15 @@ object PreferencesDialog: TPreferencesDialog
           0B000000FFFFFFFFFFFFFFFF0000000000000000094472616744726F70582400
           0000000000000C000000FFFFFFFFFFFFFFFF00000000000000000B4261636B67
           726F756E645820000000000000000E000000FFFFFFFFFFFFFFFF000000000000
-          000007526573756D6558210000000000000002000000FFFFFFFFFFFFFFFF0000
-          000000000000084C6F6767696E6758250000000000000009000000FFFFFFFFFF
-          FFFFFF00000000010000000C496E746567726174696F6E582600000000000000
-          12000000FFFFFFFFFFFFFFFF00000000000000000D4170706C69636174696F6E
-          735822000000000000000A000000FFFFFFFFFFFFFFFF00000000000000000943
-          6F6D6D616E64735821000000000000000D000000FFFFFFFFFFFFFFFF00000000
-          000000000853746F726167655821000000000000000F000000FFFFFFFFFFFFFF
-          FF0000000000000000085570646174657358}
+          000007526573756D6558220000000000000013000000FFFFFFFFFFFFFFFF0000
+          00000000000009536563757269747958210000000000000002000000FFFFFFFF
+          FFFFFFFF0000000000000000084C6F6767696E67582500000000000000090000
+          00FFFFFFFFFFFFFFFF00000000010000000C496E746567726174696F6E582600
+          00000000000012000000FFFFFFFFFFFFFFFF00000000000000000D4170706C69
+          636174696F6E735822000000000000000A000000FFFFFFFFFFFFFFFF00000000
+          0000000009436F6D6D616E64735821000000000000000D000000FFFFFFFFFFFF
+          FFFF00000000000000000853746F726167655821000000000000000F000000FF
+          FFFFFFFFFFFFFF0000000000000000085570646174657358}
       end
     end
   end

+ 6 - 3
forms/Preferences.h

@@ -21,9 +21,6 @@
 #include "GeneralSettings.h"
 #include "LogSettings.h"
 #include "UpDownEdit.hpp"
-#include "IEComboBox.hpp"
-#include "HistoryComboBox.hpp"
-#include "PasswordEdit.hpp"
 #include <Dialogs.hpp>
 #include <PasTools.hpp>
 //----------------------------------------------------------------------------
@@ -229,6 +226,10 @@ __published:
   TLabel *SessionReopenTimeoutLabel;
   TUpDownEdit *SessionReopenTimeoutEdit;
   TLabel *SessionReopenTimeoutSecLabel;
+  TTabSheet *SecuritySheet;
+  TGroupBox *MasterPasswordGroup;
+  TButton *SetMasterPasswordButton;
+  TCheckBox *UseMasterPasswordCheck;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);
@@ -295,6 +296,8 @@ __published:
           Extended Value, AnsiString &Text, bool &Handed);
   void __fastcall SessionReopenTimeoutEditGetValue(TObject *Sender,
           AnsiString Text, Extended &Value, bool &Handed);
+  void __fastcall UseMasterPasswordCheckClick(TObject *Sender);
+  void __fastcall SetMasterPasswordButtonClick(TObject *Sender);
 private:
   TPreferencesMode FPreferencesMode;
   TFont * FEditorFont;

+ 1 - 5
forms/Properties.cpp

@@ -52,11 +52,7 @@ __fastcall TPropertiesDialog::TPropertiesDialog(TComponent* AOwner,
   FOnCalculateChecksum = OnCalculateChecksum;
   RightsFrame->OnChange = ControlChange;
 
-  TSHFileInfo FileInfo;
-  FShellImageList = new TImageList(this);
-  FShellImageList->Handle = SHGetFileInfo("", 0, &FileInfo, sizeof(FileInfo),
-      SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
-  FShellImageList->ShareImages = True;
+  FShellImageList = SharedSystemImageList(true);
 
   FFileList = new TStringList();
   FFileList->Assign(FileList);

+ 16 - 2
forms/ScpCommander.cpp

@@ -433,7 +433,7 @@ void __fastcall TScpCommanderForm::TerminalChanged()
     if (WinConfiguration->DefaultDirIsHome &&
         !Terminal->SessionData->UpdateDirectories)
     {
-      LocalDirView->HomeDirectory = Terminal->SessionData->LocalDirectory;
+      LocalDirView->HomeDirectory = ExpandFileName(Terminal->SessionData->LocalDirectory);
     }
 
     if (WinConfiguration->PreservePanelState &&
@@ -462,7 +462,7 @@ void __fastcall TScpCommanderForm::ConfigurationChanged()
   if (WinConfiguration->DefaultDirIsHome && Terminal &&
       !Terminal->SessionData->UpdateDirectories)
   {
-    LocalDirView->HomeDirectory = Terminal->SessionData->LocalDirectory;
+    LocalDirView->HomeDirectory = ExpandFileName(Terminal->SessionData->LocalDirectory);
   }
   else
   {
@@ -1834,3 +1834,17 @@ void __fastcall TScpCommanderForm::QueueSubmenuItemPopup(
   NonVisualDataModule->QueueSpeedComboBoxItemUpdate(QueueSpeedComboBoxItem);
 }
 //---------------------------------------------------------------------------
+void __fastcall TScpCommanderForm::DoFocusRemotePath(AnsiString Path)
+{
+  bool WasSynchronisingBrowsing = NonVisualDataModule->SynchronizeBrowsingAction->Checked;
+  NonVisualDataModule->SynchronizeBrowsingAction->Checked = false;
+  try
+  {
+    TCustomScpExplorerForm::DoFocusRemotePath(Path);
+  }
+  __finally
+  {
+    NonVisualDataModule->SynchronizeBrowsingAction->Checked = WasSynchronisingBrowsing;
+  }
+}
+//---------------------------------------------------------------------------

+ 6 - 0
forms/ScpCommander.dfm

@@ -287,6 +287,9 @@ inherited ScpCommanderForm: TScpCommanderForm
         object TBXItem45: TTBXItem
           Action = NonVisualDataModule.SynchronizeBrowsingAction
         end
+        object TBXItem210: TTBXItem
+          Action = NonVisualDataModule.FindFilesAction
+        end
         object QueueSubmenuItem: TTBXSubmenuItem
           Caption = '&Queue'
           HelpKeyword = 'ui_queue#managing_the_queue'
@@ -974,6 +977,9 @@ inherited ScpCommanderForm: TScpCommanderForm
       object TBXItem158: TTBXItem
         Action = NonVisualDataModule.SynchronizeBrowsingAction
       end
+      object TBXItem227: TTBXItem
+        Action = NonVisualDataModule.FindFilesAction
+      end
     end
     object UpdatesToolbar: TTBXToolbar
       Left = 0

+ 3 - 0
forms/ScpCommander.h

@@ -379,6 +379,8 @@ __published:
   TTBXItem *TBXItem222;
   TTBXItem *TBXItem223;
   TTBXItem *TBXItem224;
+  TTBXItem *TBXItem210;
+  TTBXItem *TBXItem227;
   void __fastcall SplitterMoved(TObject *Sender);
   void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
     bool &Accept);
@@ -504,6 +506,7 @@ protected:
   virtual void __fastcall ToolbarItemResize(TTBXCustomDropDownItem * Item, int Width);
   void __fastcall DoOpenBookmark(AnsiString Local, AnsiString Remote);
   virtual bool __fastcall OpenBookmark(AnsiString Local, AnsiString Remote);
+  virtual void __fastcall DoFocusRemotePath(AnsiString Path);
 
 public:
   __fastcall TScpCommanderForm(TComponent* Owner);

+ 6 - 0
forms/ScpExplorer.dfm

@@ -130,6 +130,9 @@ inherited ScpExplorerForm: TScpExplorerForm
         object TBXItem44: TTBXItem
           Action = NonVisualDataModule.FullSynchronizeAction
         end
+        object TBXItem3: TTBXItem
+          Action = NonVisualDataModule.FindFilesAction
+        end
         object QueueSubmenuItem: TTBXSubmenuItem
           Caption = '&Queue'
           HelpKeyword = 'ui_queue#managing_the_queue'
@@ -633,6 +636,9 @@ inherited ScpExplorerForm: TScpExplorerForm
       object TBXItem65: TTBXItem
         Action = NonVisualDataModule.FullSynchronizeAction
       end
+      object TBXItem139: TTBXItem
+        Action = NonVisualDataModule.FindFilesAction
+      end
     end
     object SelectionToolbar: TTBXToolbar
       Left = 0

+ 2 - 0
forms/ScpExplorer.h

@@ -284,6 +284,8 @@ __published:
   TTBXItem *TBXItem222;
   TTBXItem *TBXItem223;
   TTBXItem *TBXItem224;
+  TTBXItem *TBXItem3;
+  TTBXItem *TBXItem139;
   void __fastcall RemoteDirViewUpdateStatusBar(TObject *Sender,
           const TStatusFileInfo &FileInfo);
   void __fastcall UnixPathComboBoxBeginEdit(TTBEditItem *Sender,

+ 1 - 1
forms/SelectMask.dfm

@@ -63,7 +63,7 @@ object SelectMaskDialog: TSelectMaskDialog
       Alignment = taRightJustify
       Anchors = [akTop, akRight]
       AutoSize = False
-      Caption = 'mask &hints'
+      Caption = 'mask hi&nts'
       TabOrder = 1
       TabStop = True
     end

+ 2 - 6
forms/SynchronizeChecklist.cpp

@@ -61,11 +61,7 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   FOrigListViewWindowProc = ListView->WindowProc;
   ListView->WindowProc = ListViewWindowProc;
 
-  FSystemImageList = new TImageList(this);
-  SHFILEINFO FileInfo;
-  FSystemImageList->Handle = SHGetFileInfo(NULL, 0, &FileInfo, sizeof(FileInfo),
-    SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
-  FSystemImageList->ShareImages = true;
+  FSystemImageList = SharedSystemImageList(false);
   ListView->SmallImages = FSystemImageList;
 
   // header images mut be assigned after the small images, so it cannot
@@ -805,7 +801,7 @@ void __fastcall TSynchronizeChecklistDialog::CustomCommandsButtonClick(
       LocalFileList->Add(LocalPath);
 
       AnsiString RemotePath =
-        IncludeTrailingBackslash(ChecklistItem->Remote.Directory) +
+        UnixIncludeTrailingBackslash(ChecklistItem->Remote.Directory) +
         ChecklistItem->Remote.FileName;
 
       RemoteFileList->AddObject(RemotePath, ChecklistItem->RemoteFile);

+ 1 - 1
packages/ThemeManagerC6D.bpk

@@ -25,7 +25,7 @@
     <SYSDEFINES value="_RTLDLL;NO_STRICT;USEPACKAGES"/>
     <MAINSOURCE value="ThemeManagerC6D.cpp"/>
     <INCLUDEPATH value="theme;theme;$(BCB)\include;$(BCB)\include\vcl"/>
-    <LIBPATH value="..\lib\;theme;theme;$(BCB)\lib\obj;$(BCB)\lib;c:\temp"/>
+    <LIBPATH value="..\lib\;theme;theme;$(BCB)\lib\obj;$(BCB)\lib"/>
     <WARNINGS value="-w-par"/>
     <OTHERFILES value=""/>
   </MACROS>

+ 0 - 3
packages/filemng/DriveView.pas

@@ -480,9 +480,6 @@ procedure Register;
 
 implementation
 
-uses
-  IEComboBox;
-
 resourcestring
    SErrorInvalidDirName = 'New name contains invalid characters %s';
 

+ 12 - 2
packages/my/PathLabel.pas

@@ -304,11 +304,21 @@ begin
 
   if FUnixPath then
     for i := 1 to Length(FDisplayPath) do
-      if FDisplayPath[i] = '/' then FDisplayPath[i] := '\';
+    begin
+      case FDisplayPath[i] of
+        '/': FDisplayPath[i] := '\';
+        '\': FDisplayPath[i] := '/';
+      end;
+    end;
   FDisplayPath := MinimizeName(FDisplayPath, Canvas, WidthPath);
   if FUnixPath then
     for i := 1 to Length(FDisplayPath) do
-      if FDisplayPath[i] = '\' then FDisplayPath[i] := '/';
+    begin
+      case FDisplayPath[i] of
+        '\': FDisplayPath[i] := '/';
+        '/': FDisplayPath[i] := '\';
+      end;
+    end;
 
   WidthPath := Canvas.TextWidth(FDisplayPath);
 

+ 6 - 2
putty/LOGGING.C

@@ -46,6 +46,9 @@ static void logwrite(struct LogContext *ctx, void *data, int len)
 	if (fwrite(data, 1, len, ctx->lgfp) < len) {
 	    logfclose(ctx);
 	    ctx->state = L_ERROR;
+	    /* Log state is L_ERROR so this won't cause a loop */
+	    logevent(ctx->frontend,
+		     "Disabled writing session log due to error while writing");
 	}
     }				       /* else L_ERROR, so ignore the write */
 }
@@ -104,8 +107,9 @@ static void logfopen_callback(void *handle, int mode)
     }
 
     event = dupprintf("%s session log (%s mode) to file: %s",
-		      (mode == 0 ? "Disabled writing" :
-                       mode == 1 ? "Appending" : "Writing new"),
+		      ctx->state == L_ERROR ?
+		      (mode == 0 ? "Disabled writing" : "Error writing") :
+		      (mode == 1 ? "Appending" : "Writing new"),
 		      (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
 		       ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
 		       ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :

+ 11 - 4
putty/SSH.C

@@ -4436,12 +4436,19 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
 
 	    epfrec = add234(ssh->portfwds, pfrec);
 	    if (epfrec != pfrec) {
+		if (epfrec->status == DESTROY) {
+		    /*
+		     * We already have a port forwarding up and running
+		     * with precisely these parameters. Hence, no need
+		     * to do anything; simply re-tag the existing one
+		     * as KEEP.
+		     */
+		    epfrec->status = KEEP;
+		}
 		/*
-		 * We already have a port forwarding with precisely
-		 * these parameters. Hence, no need to do anything;
-		 * simply tag the existing one as KEEP.
+		 * Anything else indicates that there was a duplicate
+		 * in our input, which we'll silently ignore.
 		 */
-		epfrec->status = KEEP;
 		free_portfwd(pfrec);
 	    } else {
 		pfrec->status = CREATE;

+ 29 - 0
putty/SSHAES_.C

@@ -0,0 +1,29 @@
+#include "sshaes.c"
+
+#include "puttyexp.h"
+
+void * call_aes_make_context()
+{
+  return aes_make_context();
+}
+
+void call_aes_free_context(void * handle)
+{
+  aes_free_context(handle);
+}
+
+void call_aes_setup(void * ctx, int blocklen, unsigned char * key, int keylen)
+{
+  aes_setup((AESContext *)ctx, blocklen, key, keylen);
+}
+
+void call_aes_encrypt(void * ctx, unsigned int * block)
+{
+  aes_encrypt((AESContext *)ctx, block);
+}
+
+void call_aes_decrypt(void * ctx, unsigned int * block)
+{
+  aes_decrypt((AESContext *)ctx, block);
+}
+

+ 8 - 0
putty/SSHSHA_.C

@@ -0,0 +1,8 @@
+#include "sshsha.c"
+
+#include "puttyexp.h"
+
+void call_sha1_key_internal(void * handle, unsigned char * key, int len)
+{
+  sha1_key_internal(handle, key, len);
+}

+ 12 - 0
putty/puttyexp.h

@@ -70,4 +70,16 @@ void write_utf8(charset_spec const *, long int,
 
 extern const struct ssh_compress ssh_zlib;
 
+// from sshaes.c
+
+void * call_aes_make_context();
+void call_aes_free_context(void * handle);
+void call_aes_setup(void * ctx, int blocklen, unsigned char * key, int keylen);
+void call_aes_encrypt(void * ctx, unsigned int * block);
+void call_aes_decrypt(void * ctx, unsigned int * block);
+
+// from sshsha.c
+
+void call_sha1_key_internal(void * handle, unsigned char * key, int len);
+
 #endif

+ 8 - 5
putty/windows/WINNET.C

@@ -208,7 +208,7 @@ DECL_WINSOCK_FUNCTION(static, int, getnameinfo,
 DECL_WINSOCK_FUNCTION(static, char *, gai_strerror, (int ecode));
 DECL_WINSOCK_FUNCTION(static, int, WSAAddressToStringA,
 		      (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,
-		       LPTSTR, LPDWORD));
+		       LPSTR, LPDWORD));
 #endif
 
 static HMODULE winsock_module = NULL;
@@ -339,7 +339,8 @@ void sk_cleanup(void)
 	sktree = NULL;
     }
 
-    p_WSACleanup();
+    if (p_WSACleanup)
+	p_WSACleanup();
     if (winsock_module)
 	FreeLibrary(winsock_module);
 #ifndef NO_IPV6
@@ -432,10 +433,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
 {
     SockAddr ret = snew(struct SockAddr_tag);
     unsigned long a;
-    struct hostent *h = NULL;
     char realhost[8192];
     int hint_family;
-    int err;
 
     /* Default to IPv4. */
     hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
@@ -455,6 +454,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
     *realhost = '\0';
 
     if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
+	struct hostent *h = NULL;
+	int err;
 #ifndef NO_IPV6
 	/*
 	 * Use getaddrinfo when it's available
@@ -588,8 +589,10 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen)
 #ifndef NO_IPV6
     if (step.ai) {
 	if (p_WSAAddressToStringA) {
+	    DWORD dwbuflen;
 	    p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
-				  NULL, buf, &buflen);
+				  NULL, buf, &dwbuflen);
+	    buflen = dwbuflen;
 	} else
 	    strncpy(buf, "IPv6", buflen);
     } else

+ 1 - 1
putty/windows/WINPROXY.C

@@ -209,7 +209,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
 		  NULL, NULL, &si, &pi);
     #ifdef MPEXT
     sfree(cmd);
-    #endif  
+    #endif
 
     CloseHandle(cmd_from_us);
     CloseHandle(cmd_to_us);

+ 1 - 1
release/winscp.u3i

@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <u3manifest version="1.0">
-  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.2.3">
+  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.2.4">
     <icon>winscp.ico</icon>
     <name>WinSCP</name>
     <description>Freeware SFTP (SSH File Transfer Protocol), FTP (File Transfer Protocol) and SCP (Secure CoPy) client for Windows using SSH (Secure SHell). Its main function is safe copying of files between a local and a remote computer.</description>

+ 4 - 0
resource/HelpWin.h

@@ -40,5 +40,9 @@
 #define HELP_TOO_MANY_WATCH_DIRECTORIES "ui_keepuptodate"
 #define HELP_LOGIN_KEY_TYPE          "public_key#private"
 #define HELP_SYNCHRONIZE_NO_DIFFERENCES "task_synchronize_full"
+#define HELP_MASTER_PASSWORD_CURRENT "ui_master_password"
+#define HELP_MASTER_PASSWORD_CHANGE  "ui_master_password_change"
+#define HELP_MASTER_PASSWORD         "master_password"
+#define HELP_MASTER_PASSWORD_SIMPLE  HELP_MASTER_PASSWORD
 
 #endif // TextsWin

+ 1 - 1
resource/TextsCore2.rc

@@ -18,7 +18,7 @@ BEGIN
     "WARNING - POTENTIAL SECURITY BREACH!\n"
     "\n"
     "The server's host key does not match the one WinSCP has "
-    "is cache. This means that either the "
+    "in cache. This means that either the "
     "server administrator has changed the host key, "
     "the server presents different key under certain circumstance, "
     "or you have actually connected to another computer pretending "

+ 21 - 0
resource/TextsWin.h

@@ -56,6 +56,10 @@
 #define CUSTOM_COMMAND_IMPOSSIBLE 1164
 #define EDIT_SESSION_CLOSED_RELOAD 1165
 #define UNSAFE_ACTIONS_DISABLED 1167
+#define DECRYPT_PASSWORD_ERROR  1168
+#define MASTER_PASSWORD_INCORRECT 1169
+#define MASTER_PASSWORD_DIFFERENT 1170
+#define MASTER_PASSWORD_INVALID 1171
 
 #define WIN_CONFIRMATION_STRINGS 1300
 #define CONFIRM_OVERWRITE_SESSION 1301
@@ -103,6 +107,7 @@
 #define SYNC_DIR_BROWSE_CREATE  1348
 #define ADD_BOOKMARK_SHARED     1349
 #define HELP_SEND_MESSAGE       1350
+#define MASTER_PASSWORD_SIMPLE  1351
 
 #define WIN_INFORMATION_STRINGS 1400
 #define APP_CAPTION             1401
@@ -170,6 +175,10 @@
 #define AUTO_SWITCH_ON          1508
 #define AUTO_SWITCH_OFF         1509
 #define AUTO_SWITCH_AUTO        1510
+#define MASTER_PASSWORD_SET     1511
+#define MASTER_PASSWORD_CHANGED 1512
+#define MASTER_PASSWORD_CLEARED 1513
+#define CONSOLE_MASTER_PASSWORD_PROMPT 1514
 
 #define WIN_FORMS_STRINGS       1600
 #define LOG_NOLOG               1601
@@ -362,6 +371,18 @@
 #define SHORTCUT_CAPTION        1827
 #define SHORTCUT_LABEL          1828
 #define PREFERENCES_RECONNECT_TIMEOUT_UNLIMITED 1829
+#define MASTER_PASSWORD_CAPTION 1830
+#define MASTER_PASSWORD_CURRENT 1831
+#define MASTER_PASSWORD_NEW     1832
+#define MASTER_PASSWORD_CONFIRM 1833
+#define SAVE_SESSION_PASSWORD_MASTER 1834
+#define FIND_FILE_IN_DIRECTORY  1835
+#define FIND_FILE_TITLE         1836
+#define FIND_FILE_FINDING       1837
+#define FIND_FILE_DONE          1838
+#define FIND_FILE_ABORTED       1839
+#define FIND_FILE_START         1840
+#define FIND_FILE_STOP          1841
 
 #define WIN_VARIABLE_STRINGS    1900
 #define WINSCP_COPYRIGHT        1901

+ 20 - 0
resource/TextsWin1.rc

@@ -61,6 +61,10 @@ BEGIN
         CUSTOM_COMMAND_AD_HOC_NAME, "Ad Hoc"
         EDIT_SESSION_CLOSED_RELOAD, "Cannot reload file '%s', the session '%s' has been already closed."
         UNSAFE_ACTIONS_DISABLED, "Automatic actions are disabled when URL address is provided on command-line."
+        DECRYPT_PASSWORD_ERROR, "The password cannot be decrypted."
+        MASTER_PASSWORD_INCORRECT, "You did not enter the correct current master password."
+        MASTER_PASSWORD_DIFFERENT, "New and re-entered master passwords are not the same."
+        CONSOLE_MASTER_PASSWORD_PROMPT, "Master password: "
 
         WIN_CONFIRMATION_STRINGS, "WIN_CONFIRMATION"
         CONFIRM_OVERWRITE_SESSION, "Session with name '%s' already exists. Overwrite?"
@@ -105,6 +109,7 @@ BEGIN
         SYNC_DIR_BROWSE_CREATE, "Cannot open corresponding directory in the opposite panel.\n \nDo you want to try to create directory '%s'?"
         ADD_BOOKMARK_SHARED, "Add to &shared bookmarks"
         HELP_SEND_MESSAGE, "Do you want to send the message to WinSCP site?\n\nThere is no help page associated with the message. WinSCP can search its documentation site for the message text for you.\n\nNote: WinSCP will send the message as is over unsecure Internet connection. Please check that the message does not contain any data you want to protect, such as names of files, accounts or hosts."
+        MASTER_PASSWORD_SIMPLE, "Your password is too simple and may not provide enough security against dictionary or brute force attacks.\nAre you sure you want to use it?\n\nNote: good passwords have at least six characters and contain both lowercase and uppercase letters, numbers and special characters, such as delimiters, symbols, letters with accent, etc."
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         APP_CAPTION, "%s - %s"
@@ -173,6 +178,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_CHANGED, "Master password successfully changed."
+        MASTER_PASSWORD_CLEARED, "You have deleted your master password. Your stored passwords are not secured now."
 
         WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
         LOG_NOLOG, "No session log."
@@ -363,6 +371,18 @@ BEGIN
         SHORTCUT_CAPTION, "Select keyboard shortcut"
         SHORTCUT_LABEL, "&Keyboard shortcut:"
         PREFERENCES_RECONNECT_TIMEOUT_UNLIMITED, "Unlimited"
+        MASTER_PASSWORD_CAPTION, "Master password"
+        MASTER_PASSWORD_CURRENT, "&Current master password:"
+        MASTER_PASSWORD_NEW, "&New master password:"
+        MASTER_PASSWORD_CONFIRM, "&Re-enter master password:"
+        SAVE_SESSION_PASSWORD_MASTER, "Save &password (protected by master password)"
+        FIND_FILE_TITLE, "Find"
+        FIND_FILE_FINDING, "Finding ..."
+        FIND_FILE_IN_DIRECTORY, "Searching in %s"
+        FIND_FILE_DONE, "Done."
+        FIND_FILE_ABORTED, "Aborted."
+        FIND_FILE_START, "&Start"
+        FIND_FILE_STOP, "&Stop"
 
         WIN_VARIABLE_STRINGS, "WIN_VARIABLE"
         WINSCP_COPYRIGHT, "Copyright © 2000-2009 Martin Prikryl"

+ 25 - 20
windows/ConsoleRunner.cpp

@@ -202,19 +202,6 @@ void __fastcall TOwnConsole::TrayIconClick(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 void __fastcall TOwnConsole::BreakInput()
 {
-  FlushConsoleInputBuffer(FInput);
-  INPUT_RECORD InputRecord;
-  memset(&InputRecord, 0, sizeof(InputRecord));
-  InputRecord.EventType = KEY_EVENT;
-  InputRecord.Event.KeyEvent.bKeyDown = true;
-  InputRecord.Event.KeyEvent.wRepeatCount = 1;
-  InputRecord.Event.KeyEvent.uChar.AsciiChar = '\r';
-
-  unsigned long Written;
-  // this assertion occasionally fails (when console is being exited)
-  CHECK(WriteConsoleInput(FInput, &InputRecord, 1, &Written));
-  assert(Written == 1);
-
   FPendingAbort = true;
 
   PostMessage(Application->Handle, WM_INTERUPT_IDLE, 0, 0);
@@ -857,13 +844,15 @@ public:
   void __fastcall ShowException(Exception * E);
 
 protected:
-  bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer);
+  bool __fastcall DoInput(AnsiString & Str, bool Echo, unsigned int Timer);
+  void __fastcall Input(const AnsiString Prompt, AnsiString & Str, bool Echo);
   inline void __fastcall Print(const AnsiString & Str, bool FromBeginning = false);
   inline void __fastcall PrintLine(const AnsiString & Str);
   inline void __fastcall PrintMessage(const AnsiString & Str);
   void __fastcall UpdateTitle();
   inline void __fastcall NotifyAbort();
   inline bool __fastcall Aborted(bool AllowCompleteAbort = true);
+  void __fastcall MasterPasswordPrompt();
 
 private:
   TManagementScript * FScript;
@@ -921,10 +910,14 @@ TConsoleRunner::TConsoleRunner(TConsole * Console) :
   Timer->OnTimer = TimerTimer;
   Timer->Interval = 1000;
   Timer->Enabled = true;
+  assert(WinConfiguration->OnMasterPasswordPrompt == NULL);
+  WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt;
 }
 //---------------------------------------------------------------------------
 TConsoleRunner::~TConsoleRunner()
 {
+  assert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt);
+  WinConfiguration->OnMasterPasswordPrompt = NULL;
   delete Timer;
 }
 //---------------------------------------------------------------------------
@@ -939,17 +932,22 @@ unsigned int TConsoleRunner::InputTimeout()
   return (FScript->Batch != TScript::BatchOff ? BATCH_INPUT_TIMEOUT : 0);
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
-  const AnsiString Prompt, AnsiString & Str)
+void __fastcall TConsoleRunner::Input(const AnsiString Prompt, AnsiString & Str, bool Echo)
 {
   Print(Prompt);
 
-  if (!Input(Str, true, InputTimeout()))
+  if (!DoInput(Str, Echo, InputTimeout()))
   {
     Abort();
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
+  const AnsiString Prompt, AnsiString & Str)
+{
+  Input(Prompt, Str, true);
+}
+//---------------------------------------------------------------------------
 void __fastcall TConsoleRunner::Print(const AnsiString & Str, bool FromBeginning)
 {
   if (FLastProgressLen > 0)
@@ -1053,7 +1051,7 @@ void __fastcall TConsoleRunner::ScriptTerminalPromptUser(TTerminal * /*Terminal*
     Print(Prompt);
 
     AnsiString AResult = Results->Strings[Index]; // useless
-    Result = Input(AResult, bool(Prompts->Objects[Index]), InputTimeout());
+    Result = DoInput(AResult, bool(Prompts->Objects[Index]), InputTimeout());
     Results->Strings[Index] = AResult;
   }
 }
@@ -1512,7 +1510,7 @@ void __fastcall TConsoleRunner::ShowException(Exception * E)
   }
 }
 //---------------------------------------------------------------------------
-bool __fastcall TConsoleRunner::Input(AnsiString & Str, bool Echo, unsigned int Timeout)
+bool __fastcall TConsoleRunner::DoInput(AnsiString & Str, bool Echo, unsigned int Timeout)
 {
   bool Result = FConsole->Input(Str, Echo, Timeout);
   if (Result)
@@ -1531,6 +1529,13 @@ bool __fastcall TConsoleRunner::Input(AnsiString & Str, bool Echo, unsigned int
   return Result;
 }
 //---------------------------------------------------------------------------
+void __fastcall TConsoleRunner::MasterPasswordPrompt()
+{
+  AnsiString Password;
+  Input(LoadStr(CONSOLE_MASTER_PASSWORD_PROMPT), Password, false);
+  WinConfiguration->SetMasterPassword(Password);
+}
+//---------------------------------------------------------------------------
 AnsiString TConsoleRunner::ExpandCommand(AnsiString Command, TStrings * ScriptParameters)
 {
   assert(ScriptParameters != NULL);
@@ -1593,7 +1598,7 @@ int __fastcall TConsoleRunner::Run(const AnsiString Session, TOptions * Options,
           // no longer batch
           FBatchScript = false;
           Print("winscp> ");
-          Result = Input(Command, true, 0);
+          Result = DoInput(Command, true, 0);
         }
 
         if (Result)

+ 38 - 0
windows/CustomWinConfiguration.cpp

@@ -4,6 +4,9 @@
 
 #include <Common.h>
 #include <TextsCore.h>
+#include <SessionData.h>
+#include <CoreMain.h>
+#include <Interface.h>
 #include "CustomWinConfiguration.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
@@ -77,7 +80,10 @@ void __fastcall TCustomWinConfiguration::Default()
   FLogView = lvNone;
   FSynchronizeChecklist.WindowParams = "0;-1;-1;600;450;0";
   FSynchronizeChecklist.ListParams = "1;1|150,1;100,1;80,1;130,1;25,1;100,1;80,1;130,1|0;1;2;3;4;5;6;7";
+  FFindFile.WindowParams = "646,481";
+  FFindFile.ListParams = "3;1|125,1;181,1;80,1;122,1|0;1;2;3";
   FConsoleWin.WindowSize = "570,430";
+  FConfirmExitOnCompletion = true;
 
   DefaultHistory();
 }
@@ -104,6 +110,7 @@ void __fastcall TCustomWinConfiguration::Saved()
   BLOCK("Interface", CANCREATE, \
     KEY(Integer,  Interface); \
     KEY(Bool,     ShowAdvancedLoginOptions); \
+    KEY(Bool,     ConfirmExitOnCompletion); \
   ) \
   BLOCK("Logging", CANCREATE, \
     KEY(Integer, LogView); \
@@ -112,6 +119,10 @@ void __fastcall TCustomWinConfiguration::Saved()
     KEY(String,   SynchronizeChecklist.WindowParams); \
     KEY(String,   SynchronizeChecklist.ListParams); \
   ); \
+  BLOCK("Interface\\FindFile", CANCREATE, \
+    KEY(String,   FindFile.WindowParams); \
+    KEY(String,   FindFile.ListParams); \
+  ); \
   BLOCK("Interface\\ConsoleWin", CANCREATE, \
     KEY(String,   ConsoleWin.WindowSize); \
   ); \
@@ -306,6 +317,23 @@ void __fastcall TCustomWinConfiguration::LoadAdmin(THierarchicalStorage * Storag
   FDefaultShowAdvancedLoginOptions = Storage->ReadBool("DefaultInterfaceShowAdvancedLoginOptions", FDefaultShowAdvancedLoginOptions);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomWinConfiguration::RecryptPasswords()
+{
+  Busy(true);
+  try
+  {
+    StoredSessions->RecryptPasswords();
+    if (OnMasterPasswordRecrypt != NULL)
+    {
+      OnMasterPasswordRecrypt(NULL);
+    }
+  }
+  __finally
+  {
+    Busy(false);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomWinConfiguration::SetShowAdvancedLoginOptions(bool value)
 {
   SET_CONFIG_PROPERTY(ShowAdvancedLoginOptions);
@@ -370,7 +398,17 @@ void __fastcall TCustomWinConfiguration::SetSynchronizeChecklist(TSynchronizeChe
   SET_CONFIG_PROPERTY(SynchronizeChecklist);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomWinConfiguration::SetFindFile(TFindFileConfiguration value)
+{
+  SET_CONFIG_PROPERTY(FindFile);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomWinConfiguration::SetConsoleWin(TConsoleWinConfiguration value)
 {
   SET_CONFIG_PROPERTY(ConsoleWin);
 }
+//---------------------------------------------------------------------------
+void __fastcall TCustomWinConfiguration::SetConfirmExitOnCompletion(bool value)
+{
+  SET_CONFIG_PROPERTY(ConfirmExitOnCompletion);
+}

+ 13 - 0
windows/CustomWinConfiguration.h

@@ -19,6 +19,7 @@ struct TSynchronizeChecklistConfiguration
   bool __fastcall operator !=(TSynchronizeChecklistConfiguration & rhc)
     { return C(WindowParams) C(ListParams) 0; };
 };
+typedef TSynchronizeChecklistConfiguration TFindFileConfiguration;
 //---------------------------------------------------------------------------
 struct TConsoleWinConfiguration
 {
@@ -37,9 +38,12 @@ private:
   TStringList * FHistory;
   TStrings * FEmptyHistory;
   TSynchronizeChecklistConfiguration FSynchronizeChecklist;
+  TFindFileConfiguration FFindFile;
   TConsoleWinConfiguration FConsoleWin;
   TInterface FDefaultInterface;
   bool FDefaultShowAdvancedLoginOptions;
+  bool FConfirmExitOnCompletion;
+  TNotifyEvent FOnMasterPasswordRecrypt;
 
   void __fastcall SetInterface(TInterface value);
   void __fastcall SetLogView(TLogView value);
@@ -47,7 +51,9 @@ private:
   void __fastcall SetHistory(const AnsiString Index, TStrings * value);
   TStrings * __fastcall GetHistory(const AnsiString Index);
   void __fastcall SetSynchronizeChecklist(TSynchronizeChecklistConfiguration value);
+  void __fastcall SetFindFile(TFindFileConfiguration value);
   void __fastcall SetConsoleWin(TConsoleWinConfiguration value);
+  void __fastcall SetConfirmExitOnCompletion(bool value);
 
 protected:
   virtual void __fastcall SaveData(THierarchicalStorage * Storage, bool All);
@@ -56,18 +62,25 @@ protected:
   virtual void __fastcall Saved();
   void __fastcall ClearHistory();
   void __fastcall DefaultHistory();
+  void __fastcall RecryptPasswords();
+  virtual bool __fastcall GetUseMasterPassword() = 0;
 
 public:
   __fastcall TCustomWinConfiguration();
   virtual __fastcall ~TCustomWinConfiguration();
   virtual void __fastcall Default();
+  virtual void __fastcall AskForMasterPasswordIfNotSet() = 0;
 
   __property TLogView LogView = { read = FLogView, write = SetLogView };
   __property TInterface Interface = { read = FInterface, write = SetInterface };
   __property bool ShowAdvancedLoginOptions = { read = FShowAdvancedLoginOptions, write = SetShowAdvancedLoginOptions};
   __property TStrings * History[AnsiString Name] = { read = GetHistory, write = SetHistory };
   __property TSynchronizeChecklistConfiguration SynchronizeChecklist = { read = FSynchronizeChecklist, write = SetSynchronizeChecklist };
+  __property TFindFileConfiguration FindFile = { read = FFindFile, write = SetFindFile };
   __property TConsoleWinConfiguration ConsoleWin = { read = FConsoleWin, write = SetConsoleWin };
+  __property bool ConfirmExitOnCompletion  = { read=FConfirmExitOnCompletion, write=SetConfirmExitOnCompletion };
+  __property bool UseMasterPassword = { read = GetUseMasterPassword };
+  __property TNotifyEvent OnMasterPasswordRecrypt = { read = FOnMasterPasswordRecrypt, write = FOnMasterPasswordRecrypt };
 };
 //---------------------------------------------------------------------------
 #define CustomWinConfiguration \

+ 28 - 6
windows/TerminalManager.cpp

@@ -26,7 +26,7 @@ __fastcall TManagedTerminal::TManagedTerminal(TSessionData * SessionData,
   TConfiguration * Configuration) :
   TTerminal(SessionData, Configuration),
   Color((TColor)SessionData->Color), SynchronizeBrowsing(false),
-  LocalDirectory(SessionData->LocalDirectory),
+  LocalDirectory(::ExpandFileName(SessionData->LocalDirectory)),
   RemoteDirectory(SessionData->RemoteDirectory),
   LocalExplorerState(NULL), RemoteExplorerState(NULL),
   ReopenStart(0)
@@ -74,6 +74,8 @@ __fastcall TTerminalManager::TTerminalManager() :
   Application->OnShowHint = ApplicationShowHint;
   assert(Application->OnActivate == NULL);
   Application->OnActivate = ApplicationActivate;
+  assert(WinConfiguration->OnMasterPasswordPrompt == NULL);
+  WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt;
 
   assert(Configuration && !Configuration->OnChange);
   Configuration->OnChange = ConfigurationChange;
@@ -100,6 +102,8 @@ __fastcall TTerminalManager::~TTerminalManager()
   Application->OnShowHint = ApplicationShowHint;
   assert(Application->OnActivate == ApplicationActivate);
   Application->OnActivate = NULL;
+  assert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt);
+  WinConfiguration->OnMasterPasswordPrompt = NULL;
 
   delete FQueues;
   delete FTerminationMessages;
@@ -194,11 +198,11 @@ void TTerminalManager::ConnectTerminal(TTerminal * Terminal, bool Reopen)
     if (ManagedTerminal != NULL)
     {
       Terminal->SessionData->RemoteDirectory = ManagedTerminal->RemoteDirectory;
-    }
 
-    if ((double)ManagedTerminal->ReopenStart == 0)
-    {
-      ManagedTerminal->ReopenStart = Now();
+      if ((double)ManagedTerminal->ReopenStart == 0)
+      {
+        ManagedTerminal->ReopenStart = Now();
+      }
     }
 
     if (Reopen)
@@ -213,7 +217,7 @@ void TTerminalManager::ConnectTerminal(TTerminal * Terminal, bool Reopen)
   __finally
   {
     Terminal->SessionData->RemoteDirectory = OrigRemoteDirectory;
-    if (Terminal->Active)
+    if (Terminal->Active && (ManagedTerminal != NULL))
     {
       ManagedTerminal->ReopenStart = 0;
     }
@@ -1158,3 +1162,21 @@ void __fastcall TTerminalManager::Idle()
     }
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall TTerminalManager::MasterPasswordPrompt()
+{
+  if (GetCurrentThreadId() == MainThreadID)
+  {
+    if (!DoMasterPasswordDialog())
+    {
+      Abort();
+    }
+  }
+  else
+  {
+    // this can happen only when we keep cancelling all master password prompts
+    // as long as the sessing finally connects (session password has to be
+    // explictly typed in), and background transfer is started
+    Abort();
+  }
+}

+ 1 - 0
windows/TerminalManager.h

@@ -129,6 +129,7 @@ private:
   void __fastcall DeleteLocalFile(const AnsiString FileName, bool Alternative);
   void __fastcall QueueEvent(TTerminalQueue * Queue, TQueueEvent Event);
   TAuthenticateForm * __fastcall MakeAuthenticateForm(TSessionData * Data);
+  void __fastcall MasterPasswordPrompt();
 };
 //---------------------------------------------------------------------------
 #endif

+ 12 - 0
windows/Tools.cpp

@@ -180,6 +180,18 @@ AnsiString __fastcall StoreForm(TCustomForm * Form)
     (int)(Form->WindowState == wsMinimized ? wsNormal : Form->WindowState)));
 }
 //---------------------------------------------------------------------------
+void __fastcall RestoreFormSize(AnsiString Data, TForm * Form)
+{
+  int Width = StrToIntDef(CutToChar(Data, ',', true), Form->Width);
+  int Height = StrToIntDef(CutToChar(Data, ',', true), Form->Height);
+  ResizeForm(Form, Width, Height);
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall StoreFormSize(TForm * Form)
+{
+  return FORMAT("%d,%d", (Form->Width, Form->Height));
+}
+//---------------------------------------------------------------------------
 bool __fastcall ExecuteShellAndWait(const AnsiString Path, const AnsiString Params)
 {
   return ExecuteShellAndWait(Application->Handle, Path, Params,

+ 2 - 0
windows/Tools.h

@@ -17,6 +17,8 @@ AnsiString __fastcall GetListViewStr(TListView * ListView);
 void __fastcall LoadListViewStr(TListView * ListView, AnsiString LayoutStr);
 void __fastcall RestoreForm(AnsiString Data, TForm * Form);
 AnsiString __fastcall StoreForm(TCustomForm * Form);
+void __fastcall RestoreFormSize(AnsiString Data, TForm * Form);
+AnsiString __fastcall StoreFormSize(TForm * Form);
 TFontStyles __fastcall IntToFontStyles(int value);
 int __fastcall FontStylesToInt(const TFontStyles value);
 void __fastcall ValidateMaskEdit(TComboBox * Edit);

+ 151 - 0
windows/UserInterface.cpp

@@ -8,6 +8,7 @@
 #include <CoreMain.h>
 #include <Common.h>
 #include <Exceptions.h>
+#include <Cryptography.h>
 #include "ProgParams.h"
 #include "VCLCommon.h"
 #include "WinConfiguration.h"
@@ -18,6 +19,8 @@
 #include "TBXOffice2003Theme.hpp"
 #include "ProgParams.h"
 #include "Tools.h"
+#include "Custom.h"
+#include "HelpWin.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -557,3 +560,151 @@ bool __fastcall IsCustomShortCut(TShortCut ShortCut)
     ((FirstCtrlNumberShortCut <= ShortCut) && (ShortCut <= LastCtrlNumberShortCut)) ||
     ((FirstShiftCtrlAltLetterShortCut <= ShortCut) && (ShortCut <= LastShiftCtrlAltLetterShortCut));
 }
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+class TMasterPasswordDialog : public TCustomDialog
+{
+public:
+  __fastcall TMasterPasswordDialog(bool Current);
+
+  bool __fastcall Execute();
+
+protected:
+  virtual void __fastcall DoValidate();
+  virtual void __fastcall DoChange(bool & CanSubmit);
+
+private:
+  TPasswordEdit * CurrentEdit;
+  TPasswordEdit * NewEdit;
+  TPasswordEdit * ConfirmEdit;
+};
+//---------------------------------------------------------------------------
+__fastcall TMasterPasswordDialog::TMasterPasswordDialog(bool Current) :
+  TCustomDialog(Current ? HELP_MASTER_PASSWORD_CURRENT : HELP_MASTER_PASSWORD_CHANGE)
+{
+  Caption = LoadStr(MASTER_PASSWORD_CAPTION);
+
+  CurrentEdit = new TPasswordEdit(this);
+  AddEdit(CurrentEdit, CreateLabel(LoadStr(MASTER_PASSWORD_CURRENT)));
+  EnableControl(CurrentEdit, Current || WinConfiguration->UseMasterPassword);
+  CurrentEdit->MaxLength = PasswordMaxLength();
+
+  if (!Current)
+  {
+    NewEdit = new TPasswordEdit(this);
+    AddEdit(NewEdit, CreateLabel(LoadStr(MASTER_PASSWORD_NEW)));
+    NewEdit->MaxLength = CurrentEdit->MaxLength;
+
+    if (!WinConfiguration->UseMasterPassword)
+    {
+      ActiveControl = NewEdit;
+    }
+
+    ConfirmEdit = new TPasswordEdit(this);
+    AddEdit(ConfirmEdit, CreateLabel(LoadStr(MASTER_PASSWORD_CONFIRM)));
+    ConfirmEdit->MaxLength = CurrentEdit->MaxLength;
+  }
+  else
+  {
+    NewEdit = NULL;
+    ConfirmEdit = NULL;
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TMasterPasswordDialog::Execute()
+{
+  bool Result = TCustomDialog::Execute();
+  if (Result)
+  {
+    if (CurrentEdit->Enabled)
+    {
+      WinConfiguration->SetMasterPassword(CurrentEdit->Text);
+    }
+    if (NewEdit != NULL)
+    {
+      WinConfiguration->SetMasterPassword(NewEdit->Text);
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TMasterPasswordDialog::DoChange(bool & CanSubmit)
+{
+  CanSubmit =
+    (!WinConfiguration->UseMasterPassword || (IsValidPassword(CurrentEdit->Text) >= 0)) &&
+    ((NewEdit == NULL) || (IsValidPassword(NewEdit->Text) >= 0)) &&
+    ((ConfirmEdit == NULL) || (IsValidPassword(ConfirmEdit->Text) >= 0));
+  TCustomDialog::DoChange(CanSubmit);
+}
+//---------------------------------------------------------------------------
+void __fastcall TMasterPasswordDialog::DoValidate()
+{
+  TCustomDialog::DoValidate();
+
+  if (WinConfiguration->UseMasterPassword &&
+      !WinConfiguration->ValidateMasterPassword(CurrentEdit->Text))
+  {
+    CurrentEdit->SetFocus();
+    CurrentEdit->SelectAll();
+    throw Exception(LoadStr(MASTER_PASSWORD_INCORRECT));
+  }
+
+  if (NewEdit != NULL)
+  {
+    if (NewEdit->Text != ConfirmEdit->Text)
+    {
+      ConfirmEdit->SetFocus();
+      ConfirmEdit->SelectAll();
+      throw Exception(LoadStr(MASTER_PASSWORD_DIFFERENT));
+    }
+
+    int Valid = IsValidPassword(NewEdit->Text);
+    if (Valid <= 0)
+    {
+      assert(Valid == 0);
+      if (MessageDialog(LoadStr(MASTER_PASSWORD_SIMPLE), qtWarning,
+            qaOK | qaCancel, HELP_MASTER_PASSWORD_SIMPLE) == qaCancel)
+      {
+        NewEdit->SetFocus();
+        NewEdit->SelectAll();
+        Abort();
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
+static bool __fastcall DoMasterPasswordDialog(bool Current)
+{
+  bool Result;
+  TMasterPasswordDialog * Dialog = new TMasterPasswordDialog(Current);
+  try
+  {
+    Result = Dialog->Execute();
+  }
+  __finally
+  {
+    delete Dialog;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool __fastcall DoMasterPasswordDialog()
+{
+  return DoMasterPasswordDialog(true);
+}
+//---------------------------------------------------------------------------
+bool __fastcall DoChangeMasterPasswordDialog()
+{
+  return DoMasterPasswordDialog(false);
+}
+//---------------------------------------------------------------------------
+void __fastcall MessageWithNoHelp(const AnsiString & Message)
+{
+  TMessageParams Params;
+  Params.AllowHelp = false; // to avoid recursion
+  if (MessageDialog(LoadStr(HELP_SEND_MESSAGE), qtConfirmation,
+        qaOK | qaCancel, HELP_NONE, &Params) == qaOK)
+  {
+    SearchHelp(Message);
+  }
+}

+ 51 - 0
windows/VCLCommon.cpp

@@ -1282,6 +1282,43 @@ void __fastcall HintLabelRestore(TStaticText * StaticText)
   StaticText->Cursor = crDefault;
 }
 //---------------------------------------------------------------------------
+static void __fastcall ComboBoxFixWindowProc(void * Data, TMessage & Message)
+{
+  TComboBox * ComboBox = static_cast<TComboBox *>(Data);
+  if (Message.Msg == WM_SIZE)
+  {
+    AnsiString Text = ComboBox->Text;
+    try
+    {
+      ControlWndProc(ComboBox)(Message);
+    }
+    __finally
+    {
+      // workaround for bug in combo box, that causes it to change text to any
+      // item from drop down list which starts with current text,
+      // after control is resized (unless the text is in drop down list as well)
+      ComboBox->Text = Text;
+      // hide selection, which is wrongly shown when form is resized, even when the box has not focus
+      if (!ComboBox->Focused())
+      {
+        ComboBox->SelLength = 0;
+      }
+    }
+  }
+  else
+  {
+    ControlWndProc(ComboBox)(Message);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall FixComboBoxResizeBug(TComboBox * ComboBox)
+{
+  TWndMethod WindowProc;
+  ((TMethod*)&WindowProc)->Data = ComboBox;
+  ((TMethod*)&WindowProc)->Code = ComboBoxFixWindowProc;
+  ComboBox->WindowProc = WindowProc;
+}
+//---------------------------------------------------------------------------
 static void __fastcall LinkLabelClick(TStaticText * StaticText)
 {
   if (StaticText->OnClick != NULL)
@@ -1489,3 +1526,17 @@ TForm * __fastcall _SafeFormCreate(TMetaClass * FormClass, TComponent * Owner)
 
   return Form;
 }
+//---------------------------------------------------------------------------
+TImageList * __fastcall SharedSystemImageList(bool Large)
+{
+  TSHFileInfo FileInfo;
+  TImageList * Result = new TImageList(Application);
+  int ImageListHandle = SHGetFileInfo("", 0, &FileInfo, sizeof(FileInfo),
+    SHGFI_SYSICONINDEX | (Large ? SHGFI_LARGEICON : SHGFI_SMALLICON));
+  if (ImageListHandle != 0)
+  {
+    Result->ShareImages = true;
+    Result->Handle = ImageListHandle;
+  }
+  return Result;
+}

+ 2 - 0
windows/VCLCommon.h

@@ -22,9 +22,11 @@ void __fastcall LinkLabel(TStaticText * StaticText, AnsiString Url = "",
   TNotifyEvent OnEnter = NULL);
 void __fastcall HintLabel(TStaticText * StaticText, AnsiString Hint = "");
 void __fastcall HintLabelRestore(TStaticText * StaticText);
+void __fastcall FixComboBoxResizeBug(TComboBox * ComboBox);
 void __fastcall ShowAsModal(TForm * Form, void *& Storage);
 void __fastcall HideAsModal(TForm * Form, void *& Storage);
 void __fastcall ReleaseAsModal(TForm * Form, void *& Storage);
+TImageList * __fastcall SharedSystemImageList(bool Large);
 bool __fastcall SelectDirectory(AnsiString & Path, const AnsiString Prompt,
   bool PreserveFileName);
 enum TListViewCheckAll { caCheck, caUncheck, caToggle };

+ 137 - 7
windows/WinConfiguration.cpp

@@ -11,6 +11,9 @@
 #include "GUITools.h"
 #include "Tools.h"
 #include "Setup.h"
+#include "Security.h"
+#include "TerminalManager.h"
+#include "Cryptography.h"
 #include <VCLCommon.h>
 #include <ResourceModule.hpp>
 #include <LanguagesDEPfix.hpp>
@@ -404,7 +407,6 @@ void __fastcall TWinConfiguration::Default()
   FConfirmDeleting = true;
   FConfirmRecycling = true;
   FConfirmClosingSession = true;
-  FConfirmExitOnCompletion = true;
   FDoubleClickAction = dcaEdit;
   FCopyOnDoubleClickConfirmation = false;
   FDimmHiddenFiles = true;
@@ -432,6 +434,10 @@ void __fastcall TWinConfiguration::Default()
   FAutoOpenInPutty = false;
   FVersionHistory = "";
   AddVersionToHistory(FVersionHistory);
+  FUseMasterPassword = false;
+  FPlainMasterPasswordEncrypt = "";
+  FPlainMasterPasswordDecrypt = "";
+  FMasterPasswordVerifier = "";
 
   FEditor.FontName = "Courier New";
   FEditor.FontHeight = -12;
@@ -656,6 +662,22 @@ void __fastcall TWinConfiguration::Saved()
   FEditorList->Saved();
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::RecryptPasswords()
+{
+  TCustomWinConfiguration::RecryptPasswords();
+  TTerminalManager * Manager = TTerminalManager::Instance(false);
+  assert(Manager != NULL);
+  if (Manager != NULL)
+  {
+    Manager->RecryptPasswords();
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TWinConfiguration::GetUseMasterPassword()
+{
+  return FUseMasterPassword;
+}
+//---------------------------------------------------------------------------
 THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
 {
   if (SessionList && !FTemporarySessionFile.IsEmpty())
@@ -695,7 +717,6 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(Bool,     ConfirmDeleting); \
     KEY(Bool,     ConfirmRecycling); \
     KEY(Bool,     ConfirmClosingSession); \
-    KEY(Bool,     ConfirmExitOnCompletion); \
     KEY(String,   AutoStartSession); \
     KEY(Bool,     UseLocationProfiles); \
     KEY(Bool,     UseSharedBookmarks); \
@@ -805,6 +826,10 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
   BLOCK("Logging", CANCREATE, \
     KEY(Bool,    LogWindowOnStartup); \
     KEY(String,  LogWindowParams); \
+  ); \
+  BLOCK("Security", CANCREATE, \
+    KEYEX(Bool,  FUseMasterPassword, UseMasterPassword); \
+    KEYEX(String,FMasterPasswordVerifier, MasterPasswordVerifier); \
   );
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SaveData(THierarchicalStorage * Storage, bool All)
@@ -1049,6 +1074,116 @@ bool __fastcall TWinConfiguration::GetDDExtInstalled()
   return (FDDExtInstalled > 0);
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TWinConfiguration::StronglyRecryptPassword(AnsiString Password, AnsiString Key)
+{
+  AnsiString Dummy;
+  AnsiString Result;
+  if (GetExternalEncryptedPassword(Password, Dummy) ||
+      !FUseMasterPassword)
+  {
+    // already-strongly encrypted
+    // or no master password set, so we cannot strongly-encrypt it
+    Result = Password;
+  }
+  else
+  {
+    Password = TCustomWinConfiguration::DecryptPassword(Password, Key);
+    assert(!FPlainMasterPasswordEncrypt.IsEmpty());
+    ScramblePassword(Password);
+    AES256EncyptWithMAC(Password, FPlainMasterPasswordEncrypt, Result);
+    Result = SetExternalEncryptedPassword(Result);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TWinConfiguration::DecryptPassword(AnsiString Password, AnsiString Key)
+{
+  AnsiString Result;
+  if (GetExternalEncryptedPassword(Password, Result))
+  {
+    if (FPlainMasterPasswordDecrypt.IsEmpty())
+    {
+      assert(FOnMasterPasswordPrompt != NULL);
+      FOnMasterPasswordPrompt();
+    }
+    if (!AES256DecryptWithMAC(Result, FPlainMasterPasswordDecrypt, Result) ||
+        !UnscramblePassword(Result))
+    {
+      throw Exception(LoadStr(DECRYPT_PASSWORD_ERROR));
+    }
+  }
+  else
+  {
+    Result = TCustomWinConfiguration::DecryptPassword(Password, Key);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetMasterPassword(AnsiString value)
+{
+  if (FUseMasterPassword && ValidateMasterPassword(value))
+  {
+    // just store the plain-text version of the password
+    FPlainMasterPasswordEncrypt = value;
+    FPlainMasterPasswordDecrypt = value;
+  }
+  else
+  {
+    AnsiString Verifier;
+    AES256CreateVerifier(value, Verifier);
+    FMasterPasswordVerifier = StrToHex(Verifier);
+    FPlainMasterPasswordEncrypt = value;
+    FUseMasterPassword = true;
+    try
+    {
+      RecryptPasswords();
+    }
+    __finally
+    {
+      FPlainMasterPasswordDecrypt = value;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TWinConfiguration::ValidateMasterPassword(AnsiString value)
+{
+  assert(UseMasterPassword);
+  assert(!FMasterPasswordVerifier.IsEmpty());
+  return AES256Verify(value, HexToStr(FMasterPasswordVerifier));
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::PurgePassword(AnsiString & Password)
+{
+  Password.Unique();
+  memset(Password.c_str(), 0, Password.Length());
+  Password = "";
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::ClearMasterPassword()
+{
+  FMasterPasswordVerifier = "";
+  FUseMasterPassword = false;
+  PurgePassword(FPlainMasterPasswordEncrypt);
+  try
+  {
+    RecryptPasswords();
+  }
+  __finally
+  {
+    PurgePassword(FPlainMasterPasswordDecrypt);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::AskForMasterPasswordIfNotSet()
+{
+  if (FUseMasterPassword && FPlainMasterPasswordEncrypt.IsEmpty())
+  {
+    assert(FOnMasterPasswordPrompt != NULL);
+    FOnMasterPasswordPrompt();
+    assert(!FPlainMasterPasswordDecrypt.IsEmpty());
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetLogWindowOnStartup(bool value)
 {
   SET_CONFIG_PROPERTY(LogWindowOnStartup);
@@ -1191,11 +1326,6 @@ void __fastcall TWinConfiguration::SetConfirmClosingSession(bool value)
   SET_CONFIG_PROPERTY(ConfirmClosingSession);
 }
 //---------------------------------------------------------------------------
-void __fastcall TWinConfiguration::SetConfirmExitOnCompletion(bool value)
-{
-  SET_CONFIG_PROPERTY(ConfirmExitOnCompletion);
-}
-//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetDoubleClickAction(TDoubleClickAction value)
 {
   SET_CONFIG_PROPERTY(DoubleClickAction);

+ 17 - 3
windows/WinConfiguration.h

@@ -239,6 +239,8 @@ enum TPathInCaption { picShort, picFull, picNone };
 // constants must be compatible with legacy CopyOnDoubleClick
 enum TDoubleClickAction { dcaOpen = 0, dcaCopy = 1, dcaEdit = 2 };
 //---------------------------------------------------------------------------
+typedef void __fastcall (__closure *TMasterPasswordPromptEvent)();
+//---------------------------------------------------------------------------
 class TWinConfiguration : public TCustomWinConfiguration
 {
 private:
@@ -270,7 +272,6 @@ private:
   int FDDExtInstalled;
   int FDDExtTimeout;
   bool FConfirmClosingSession;
-  bool FConfirmExitOnCompletion;
   double FDDWarnLackOfTempSpaceRatio;
   AnsiString FTemporarySessionFile;
   AnsiString FTemporaryKeyFile;
@@ -308,6 +309,11 @@ private:
   bool FAutoOpenInPutty;
   bool FTelnetForFtpInPutty;
   TDateTime FDefaultUpdatesPeriod;
+  bool FUseMasterPassword;
+  AnsiString FPlainMasterPasswordEncrypt;
+  AnsiString FPlainMasterPasswordDecrypt;
+  AnsiString FMasterPasswordVerifier;
+  TMasterPasswordPromptEvent FOnMasterPasswordPrompt;
 
   void __fastcall SetDoubleClickAction(TDoubleClickAction value);
   void __fastcall SetCopyOnDoubleClickConfirmation(bool value);
@@ -334,7 +340,6 @@ private:
   void __fastcall SetDDExtEnabled(bool value);
   void __fastcall SetDDExtTimeout(int value);
   void __fastcall SetConfirmClosingSession(bool value);
-  void __fastcall SetConfirmExitOnCompletion(bool value);
   void __fastcall SetDDWarnLackOfTempSpaceRatio(double value);
   void __fastcall SetBookmarks(AnsiString Key, TBookmarkList * value);
   TBookmarkList * __fastcall GetBookmarks(AnsiString Key);
@@ -373,6 +378,7 @@ private:
   bool __fastcall GetDDExtInstalled();
   void __fastcall AddVersionToHistory(AnsiString & VersionHistory);
   bool __fastcall GetAnyBetaInVersionHistory();
+  void __fastcall PurgePassword(AnsiString & Password);
 
 protected:
   virtual TStorage __fastcall GetStorage();
@@ -382,6 +388,8 @@ protected:
   virtual void __fastcall LoadAdmin(THierarchicalStorage * Storage);
   virtual AnsiString __fastcall GetDefaultKeyFile();
   virtual void __fastcall Saved();
+  void __fastcall RecryptPasswords();
+  virtual bool __fastcall GetUseMasterPassword();
   bool __fastcall SameStringLists(TStrings * Strings1, TStrings * Strings2);
   bool __fastcall InternalReloadComponentRes(const AnsiString ResName,
     HINSTANCE HInst, TComponent * Instance);
@@ -409,6 +417,12 @@ public:
   void __fastcall CheckDefaultTranslation();
   const TEditorPreferences * __fastcall DefaultEditorForFile(
     const AnsiString FileName, bool Local, const TFileMasks::TParams & MaskParams);
+  virtual AnsiString __fastcall DecryptPassword(AnsiString Password, AnsiString Key);
+  virtual AnsiString __fastcall StronglyRecryptPassword(AnsiString Password, AnsiString Key);
+  void __fastcall SetMasterPassword(AnsiString value);
+  bool __fastcall ValidateMasterPassword(AnsiString value);
+  void __fastcall ClearMasterPassword();
+  virtual void __fastcall AskForMasterPasswordIfNotSet();
 
   __property TScpCommanderConfiguration ScpCommander = { read = FScpCommander, write = SetScpCommander };
   __property TScpExplorerConfiguration ScpExplorer = { read = FScpExplorer, write = SetScpExplorer };
@@ -443,7 +457,6 @@ public:
   __property bool DDExtInstalled = { read=GetDDExtInstalled };
   __property int DDExtTimeout = { read=FDDExtTimeout, write=SetDDExtTimeout };
   __property bool ConfirmClosingSession  = { read=FConfirmClosingSession, write=SetConfirmClosingSession };
-  __property bool ConfirmExitOnCompletion  = { read=FConfirmExitOnCompletion, write=SetConfirmExitOnCompletion };
   __property double DDWarnLackOfTempSpaceRatio  = { read=FDDWarnLackOfTempSpaceRatio, write=SetDDWarnLackOfTempSpaceRatio };
   __property TBookmarkList * Bookmarks[AnsiString Key] = { read = GetBookmarks, write = SetBookmarks };
   __property TBookmarkList * SharedBookmarks = { read = GetSharedBookmarks, write = SetSharedBookmarks };
@@ -472,6 +485,7 @@ public:
   __property int LastMonitor = { read = GetLastMonitor, write = SetLastMonitor };
   __property const TEditorList * EditorList = { read = GetEditorList, write = SetEditorList };
   __property AnsiString DefaultKeyFile = { read = GetDefaultKeyFile, write = FDefaultKeyFile };
+  __property TMasterPasswordPromptEvent OnMasterPasswordPrompt = { read = FOnMasterPasswordPrompt, write = FOnMasterPasswordPrompt };
 };
 //---------------------------------------------------------------------------
 class TCustomCommandType

+ 0 - 20
windows/WinInterface.cpp

@@ -77,26 +77,6 @@ inline void TMessageParams::Reset()
   AllowHelp = true;
 }
 //---------------------------------------------------------------------------
-TDialogParams::TDialogParams()
-{
-  Token = NULL;
-
-  ComboItems = NULL;
-  ComboEmptyValid = true;
-
-  OnInit = NULL;
-  OnShow = NULL;
-  OnChange = NULL;
-  OnValidate = NULL;
-  OnSave = NULL;
-  OnLoad = NULL;
-}
-//---------------------------------------------------------------------------
-TDialogData::TDialogData()
-{
-  Check = false;
-}
-//---------------------------------------------------------------------------
 inline bool MapButton(unsigned int Answer, TMsgDlgBtn & Button)
 {
   bool Result = true;

+ 14 - 53
windows/WinInterface.h

@@ -42,57 +42,6 @@ private:
   inline void Reset();
 };
 
-struct TDialogControls
-{
-  void * Token;
-  TForm * Form;
-  TComboBox * Combo;
-  TCheckBox * Check;
-};
-
-struct TDialogData
-{
-  TDialogData();
-
-  AnsiString Combo;
-  bool Check;
-};
-
-typedef void __fastcall (__closure * TInitDialogEvent)
-  (const TDialogControls & Controls);
-typedef void __fastcall (__closure * TShowDialogEvent)
-  (const TDialogControls & Controls);
-typedef void __fastcall (__closure * TChangeDialogEvent)
-  (const TDialogControls & Controls, bool & Valid);
-typedef void __fastcall (__closure * TValidateDialogEvent)
-  (const TDialogData & Data);
-typedef void __fastcall (__closure * TLoadDialogEvent)
-  (const TDialogData & Data, TDialogControls & Controls);
-typedef void __fastcall (__closure * TSaveDialogEvent)
-  (const TDialogControls & Controls, TDialogData & Data);
-
-struct TDialogParams
-{
-  TDialogParams();
-
-  void * Token;
-
-  AnsiString Caption;
-  AnsiString HelpKeyword;
-
-  AnsiString ComboLabel;
-  TStrings * ComboItems;
-  bool ComboEmptyValid;
-  AnsiString CheckLabel;
-
-  TInitDialogEvent OnInit;
-  TShowDialogEvent OnShow;
-  TChangeDialogEvent OnChange;
-  TValidateDialogEvent OnValidate;
-  TLoadDialogEvent OnLoad;
-  TSaveDialogEvent OnSave;
-};
-
 class TCustomScpExplorerForm;
 TCustomScpExplorerForm * __fastcall CreateScpExplorer();
 
@@ -108,6 +57,7 @@ void __fastcall FlashOnBackground();
 void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal, Exception * E);
 void __fastcall FormHelp(TForm * Form);
 void __fastcall SearchHelp(const AnsiString & Message);
+void __fastcall MessageWithNoHelp(const AnsiString & Message);
 
 AnsiString __fastcall GetToolbarsLayoutStr(const TComponent * OwnerComponent);
 void __fastcall LoadToolbarsLayoutStr(const TComponent * OwnerComponent, AnsiString LayoutStr);
@@ -138,7 +88,6 @@ int __fastcall FatalExceptionMessageDialog(Exception * E, TQueryType Type,
   AnsiString HelpKeyword = HELP_NONE, const TMessageParams * Params = NULL);
 
 // forms\Custom.cpp
-bool __fastcall DoCustomDialog(const TDialogParams & Params, TDialogData & Data);
 bool __fastcall DoSaveSessionDialog(AnsiString & SessionName,
   bool * SavePassword, TSessionData * OriginalSession);
 void __fastcall SessionNameValidate(const AnsiString & Text,
@@ -147,6 +96,10 @@ class TShortCuts;
 bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
   const TShortCuts & ShortCuts, AnsiString HelpKeyword);
 
+// windows\UserInterface.cpp
+bool __fastcall DoMasterPasswordDialog();
+bool __fastcall DoChangeMasterPasswordDialog();
+
 // windows\WinMain.cpp
 int __fastcall Execute();
 
@@ -196,6 +149,7 @@ const coDisableSaveSettings = 0x040; // not used anymore
 const coDoNotUsePresets     = 0x080;
 const coAllowRemoteTransfer = 0x100;
 const coNoQueue             = 0x200;
+const coNoQueueIndividually = 0x400;
 const cooDoNotShowAgain     = 0x01;
 const cooRemoteTransfer     = 0x02;
 const cooSaveSettings       = 0x04;
@@ -382,12 +336,19 @@ TForm * __fastcall CreateMoreMessageDialog(const AnsiString & Msg,
 // windows\Console.cpp
 int __fastcall Console(bool Help);
 
-// windows\EditorPreferences.cpp
+// forms\EditorPreferences.cpp
 enum TEditorPreferencesMode { epmAdd, epmEdit, epmAdHoc };
 class TEditorData;
 bool __fastcall DoEditorPreferencesDialog(TEditorData * Editor,
   bool & Remember, TEditorPreferencesMode Mode, bool MayRemote);
 
+// forms\Find.cpp
+typedef void __fastcall (__closure *TFindEvent)
+  (AnsiString Directory, const TFileMasks & FileMask,
+   TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile);
+bool __fastcall DoFileFindDialog(AnsiString Directory,
+  TFindEvent OnFind, AnsiString & Path);
+
 const int cplNone =             0x00;
 const int cplCustomize =        0x01;
 const int cplCustomizeDefault = 0x02;

+ 2 - 2
windows/WinMain.cpp

@@ -76,7 +76,7 @@ void __fastcall Download(TTerminal * Terminal, const AnsiString FileName,
       throw Exception(FMTLOAD(FILE_NOT_EXISTS, (FileName)));
     }
     FileList->AddObject(FileName, File);
-    AnsiString LocalDirectory = Terminal->SessionData->LocalDirectory;
+    AnsiString LocalDirectory = ExpandFileName(Terminal->SessionData->LocalDirectory);
     if (LocalDirectory.IsEmpty())
     {
       ::SpecialFolderLocation(CSIDL_PERSONAL, LocalDirectory);
@@ -108,7 +108,7 @@ void __fastcall SynchronizeDirectories(TTerminal * Terminal,
   }
   else if (!Terminal->SessionData->LocalDirectory.IsEmpty())
   {
-    LocalDirectory = Terminal->SessionData->LocalDirectory;
+    LocalDirectory = ExpandFileName(Terminal->SessionData->LocalDirectory);
   }
   else
   {