Martin Prikryl 20 years ago
parent
commit
f0c2d90a17
100 changed files with 4905 additions and 4893 deletions
  1. 1 1
      Console.bpf
  2. 7 7
      Console.bpr
  3. BIN
      Console.res
  4. 5 5
      DScpComp.bpk
  5. 1 1
      DragExt.bpf
  6. 8 8
      DragExt.bpr
  7. BIN
      DragExt.res
  8. 2 2
      Putty.bpr
  9. 3 3
      RScpComp.bpr
  10. 2 2
      ScpCore.bpr
  11. 2 0
      ScpForms.bpf
  12. 10 6
      ScpForms.bpr
  13. 17 15
      WinSCP3.bpr
  14. 442 448
      WinSCP3.drc
  15. BIN
      WinSCP3.res
  16. 3 0
      components/UnixDirView.cpp
  17. 2 1
      components/UnixDirView.h
  18. 13 0
      components/UnixDriveView.cpp
  19. 3 3
      console/Main.cpp
  20. 53 0
      core/Common.cpp
  21. 12 0
      core/Common.h
  22. 11 7
      core/Configuration.cpp
  23. 0 2
      core/Configuration.h
  24. 159 6
      core/CopyParam.cpp
  25. 11 2
      core/CopyParam.h
  26. 40 4
      core/FileMasks.cpp
  27. 3 1
      core/FileMasks.h
  28. 22 5
      core/FileOperationProgress.cpp
  29. 6 3
      core/FileOperationProgress.h
  30. 5 1
      core/FileSystems.h
  31. 18 13
      core/HierarchicalStorage.cpp
  32. 1 0
      core/HierarchicalStorage.h
  33. 3 1
      core/Interface.h
  34. 18 5
      core/PuttyIntf.c
  35. 4 2
      core/PuttyIntf.h
  36. 242 160
      core/Queue.cpp
  37. 28 14
      core/Queue.h
  38. 53 52
      core/RemoteFiles.cpp
  39. 11 10
      core/RemoteFiles.h
  40. 64 42
      core/ScpFileSystem.cpp
  41. 3 1
      core/ScpFileSystem.h
  42. 2 1
      core/ScpMain.cpp
  43. 138 24
      core/Script.cpp
  44. 25 8
      core/Script.h
  45. 54 55
      core/SecureShell.cpp
  46. 15 13
      core/SecureShell.h
  47. 62 29
      core/SessionData.cpp
  48. 3 1
      core/SessionData.h
  49. 267 164
      core/SftpFileSystem.cpp
  50. 4 1
      core/SftpFileSystem.h
  51. 350 75
      core/Terminal.cpp
  52. 66 19
      core/Terminal.h
  53. 12 10
      forms/About.cpp
  54. 84 12
      forms/About.dfm
  55. 8 2
      forms/About.h
  56. 6 0
      forms/Cleanup.cpp
  57. 12 2
      forms/Cleanup.dfm
  58. 2 0
      forms/Cleanup.h
  59. 30 8
      forms/ComboInput.cpp
  60. 16 5
      forms/ComboInput.dfm
  61. 3 0
      forms/ComboInput.h
  62. 11 6
      forms/Console.cpp
  63. 25 13
      forms/Console.dfm
  64. 5 2
      forms/Console.h
  65. 75 19
      forms/Copy.cpp
  66. 26 8
      forms/Copy.dfm
  67. 13 2
      forms/Copy.h
  68. 76 0
      forms/CopyParamCustom.cpp
  69. 156 0
      forms/CopyParamCustom.dfm
  70. 30 0
      forms/CopyParamCustom.h
  71. 211 0
      forms/CopyParamPreset.cpp
  72. 276 0
      forms/CopyParamPreset.dfm
  73. 58 0
      forms/CopyParamPreset.h
  74. 9 1
      forms/CopyParams.cpp
  75. 23 11
      forms/CopyParams.dfm
  76. 3 1
      forms/CopyParams.h
  77. 89 21
      forms/CustomCommand.cpp
  78. 16 6
      forms/CustomCommand.dfm
  79. 9 6
      forms/CustomCommand.h
  80. 396 188
      forms/CustomScpExplorer.cpp
  81. 34 91
      forms/CustomScpExplorer.dfm
  82. 80 49
      forms/CustomScpExplorer.h
  83. 19 7
      forms/Editor.cpp
  84. 234 156
      forms/Editor.dfm
  85. 42 34
      forms/Editor.h
  86. 8 2
      forms/FileSystemInfo.cpp
  87. 11 1
      forms/FileSystemInfo.dfm
  88. 2 0
      forms/FileSystemInfo.h
  89. 146 13
      forms/FullSynchronize.cpp
  90. 121 53
      forms/FullSynchronize.dfm
  91. 30 5
      forms/FullSynchronize.h
  92. 2 0
      forms/Glyphs.cpp
  93. 123 2906
      forms/Glyphs.dfm
  94. 2 2
      forms/Glyphs.h
  95. 8 1
      forms/ImportSessions.cpp
  96. 18 8
      forms/ImportSessions.dfm
  97. 2 0
      forms/ImportSessions.h
  98. 48 6
      forms/InputDlg.cpp
  99. 1 0
      forms/Licence.cpp
  100. 20 13
      forms/LocationProfiles.cpp

+ 1 - 1
Console.bpf

@@ -1,3 +1,3 @@
 This file is used by the project manager only and should be treated like the project file
-
+
 
main 

+ 7 - 7
Console.bpr

@@ -9,9 +9,9 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <SPARELIBS value=""/>
-    <PACKAGES value="rtl.bpi vcl.bpi vclx.bpi"/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value=""/>
+    <PACKAGES value="rtl.bpi vcl.bpi vclx.bpi"/>
     <PATHCPP value=".;console"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>
@@ -55,8 +55,8 @@ IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=1
 MinorVer=2
-Release=0
-Build=53
+Release=1
+Build=57
 Debug=0
 PreRelease=0
 Special=0
@@ -68,13 +68,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Console interface for WinSCP
-FileVersion=1.2.0.53
+FileVersion=1.2.1.57
 InternalName=console
 LegalCopyright=(c) 2004-2005 Martin Prikryl
 LegalTrademarks=
 OriginalFilename=winscp3.com
 ProductName=WinSCP
-ProductVersion=3.7.4.0
+ProductVersion=3.7.5.0
 Comments=
 
 [Compiler]

BIN
Console.res


+ 5 - 5
DScpComp.bpk

@@ -10,11 +10,11 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <SPARELIBS value="DRAGDROP_B5.lib DriveDir_B5.lib Moje_B5.lib
-      rtl.lib vcl.lib vclx.lib"/>
-    <PACKAGES value="DragDrop_B5.bpi DriveDir_B5.bpi Moje_B5.bpi
-      rtl.bpi vcl.bpi vclx.bpi"/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value="DRAGDROP_B5.lib DriveDir_B5.lib Moje_B5.lib
+      rtl.lib vcl.lib vclx.lib"/>
+    <PACKAGES value="DragDrop_B5.bpi DriveDir_B5.bpi Moje_B5.bpi
+      rtl.bpi vcl.bpi vclx.bpi"/>
     <PATHCPP value=".;components"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>

+ 1 - 1
DragExt.bpf

@@ -1,3 +1,3 @@
 This file is used by the project manager only and should be treated like the project file
-
+
 
DllMain 

+ 8 - 8
DragExt.bpr

@@ -9,10 +9,10 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <SPARELIBS value=""/>
-    <PACKAGES value="bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi
-      rtl.bpi vcl.bpi vclie.bpi vclx.bpi visualclx.bpi"/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value=""/>
+    <PACKAGES value="bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi
+      rtl.bpi vcl.bpi vclie.bpi vclx.bpi visualclx.bpi"/>
     <PATHCPP value=".;dragext"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>
@@ -57,7 +57,7 @@ AutoIncBuild=1
 MajorVer=1
 MinorVer=1
 Release=4
-Build=58
+Build=60
 Debug=0
 PreRelease=0
 Special=0
@@ -69,15 +69,15 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Drag&amp;Drop shell extension for WinSCP
-FileVersion=1.1.4.58
+FileVersion=1.1.4.60
 InternalName=dragext
 LegalCopyright=(c) 2004-2005 Martin Prikryl
 LegalTrademarks=
 OriginalFilename=dragext.dll
 ProductName=WinSCP
-ProductVersion=3.7.4.0
+ProductVersion=3.7.5.0
 Comments=
-WWW=http://winscp.sourceforge.net/
+WWW=http://winscp.net/
 
 [Compiler]
 ShowInfoMsgs=0

BIN
DragExt.res


+ 2 - 2
Putty.bpr

@@ -19,8 +19,8 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <PACKAGES value=""/>
+    <LIBRARIES value=""/>
+    <PACKAGES value=""/>
     <PATHCPP value=".;putty;putty\CHARSET;putty\WINDOWS"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>

+ 3 - 3
RScpComp.bpr

@@ -10,9 +10,9 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <SPARELIBS value=""/>
-    <PACKAGES value=""/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value=""/>
+    <PACKAGES value=""/>
     <PATHCPP value=".;components"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>

+ 2 - 2
ScpCore.bpr

@@ -17,8 +17,8 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <PACKAGES value=""/>
+    <LIBRARIES value=""/>
+    <PACKAGES value=""/>
     <PATHCPP value=".;core"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>

+ 2 - 0
ScpForms.bpf

@@ -8,6 +8,8 @@ USEFORM("forms\ComboInput.cpp", ComboInputDialog);
 USEFORM("forms\Console.cpp", ConsoleDialog);
 USEFORM("forms\Copy.cpp", CopyDialog);
 USEFORM("forms\CopyParams.cpp", CopyParamsFrame); /* TFrame: File Type */
+USEFORM("forms\CopyParamCustom.cpp", CopyParamCustomDialog);
+USEFORM("forms\CopyParamPreset.cpp", CopyParamPresetDialog);
 USEFORM("forms\CustomCommand.cpp", CustomCommandDialog);
 USEFORM("forms\Editor.cpp", EditorForm);
 USEFORM("forms\FileSystemInfo.cpp", FileSystemInfoDialog);

+ 10 - 6
ScpForms.bpr

@@ -7,6 +7,7 @@
     <OBJFILES value="forms\InputDlg.obj forms\MessageDlg.obj windows\VCLCommon.obj 
       forms\About.obj forms\Cleanup.obj forms\ComboInput.obj 
       forms\Console.obj forms\Copy.obj forms\CopyParams.obj 
+      forms\CopyParamCustom.obj forms\CopyParamPreset.obj 
       forms\CustomCommand.obj forms\Editor.obj forms\FileSystemInfo.obj 
       forms\FullSynchronize.obj forms\GeneralSettings.obj forms\Glyphs.obj 
       forms\ImportSessions.obj forms\Licence.obj 
@@ -20,6 +21,7 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES) forms\About.dfm forms\Cleanup.dfm forms\ComboInput.dfm 
       forms\Console.dfm forms\Copy.dfm forms\CopyParams.dfm 
+      forms\CopyParamCustom.dfm forms\CopyParamPreset.dfm 
       forms\CustomCommand.dfm forms\Editor.dfm forms\FileSystemInfo.dfm 
       forms\FullSynchronize.dfm forms\GeneralSettings.dfm forms\Glyphs.dfm 
       forms\ImportSessions.dfm forms\Licence.dfm forms\LocationProfiles.dfm 
@@ -29,11 +31,11 @@
       forms\Rights.dfm forms\RightsExt.dfm forms\SelectMask.dfm 
       forms\Symlink.dfm forms\Synchronize.dfm forms\SynchronizeProgress.dfm"/>
     <LIBFILES value=""/>
-    <LIBRARIES value=""/>
-    <SPARELIBS value=""/>
-    <PACKAGES value="DragDrop_B5.bpi DriveDir_B5.bpi Moje_B5.bpi
-      bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi rtl.bpi vcl.bpi
-      vclie.bpi vclx.bpi visualclx.bpi"/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value=""/>
+    <PACKAGES value="DragDrop_B5.bpi DriveDir_B5.bpi Moje_B5.bpi
+      bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi rtl.bpi vcl.bpi
+      vclie.bpi vclx.bpi visualclx.bpi"/>
     <PATHCPP value=".;forms;windows"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;"/>
@@ -42,7 +44,7 @@
     <USERDEFINES value="OLD_DND"/>
     <SYSDEFINES value="NO_STRICT"/>
     <MAINSOURCE value="ScpForms.bpf"/>
-    <INCLUDEPATH value="forms;core;components;windows;resource;packages\filemng;packages\dragndrop;packages\my;$(BCB)\include;$(BCB)\include\vcl"/>
+    <INCLUDEPATH value="forms;core;components;windows;resource;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BCB)\include;$(BCB)\include\vcl"/>
     <LIBPATH value="windows;forms;$(BCB)\lib\obj;$(BCB)\lib"/>
     <WARNINGS value="-w8092 -w8091 -w8090 -w8089 -w8087 -wprc -wuse -wucp -wstv -wstu -wsig 
       -wpin -wnod -wnak -wdef -wcln -wbbf -wasm -wamp -wamb"/>
@@ -73,6 +75,8 @@
       <FILE FILENAME="forms\Console.cpp" FORMNAME="ConsoleDialog" UNITNAME="Console" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Copy.cpp" FORMNAME="CopyDialog" UNITNAME="Copy" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\CopyParams.cpp" FORMNAME="CopyParamsFrame" UNITNAME="CopyParams" CONTAINERID="CCompiler" DESIGNCLASS="TFrame" LOCALCOMMAND=""/>
+      <FILE FILENAME="forms\CopyParamCustom.cpp" FORMNAME="CopyParamCustomDialog" UNITNAME="CopyParamCustom" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="forms\CopyParamPreset.cpp" FORMNAME="CopyParamPresetDialog" UNITNAME="CopyParamPreset" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\CustomCommand.cpp" FORMNAME="CustomCommandDialog" UNITNAME="CustomCommand" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Editor.cpp" FORMNAME="EditorForm" UNITNAME="Editor" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\FileSystemInfo.cpp" FORMNAME="FileSystemInfoDialog" UNITNAME="FileSystemInfo" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

+ 17 - 15
WinSCP3.bpr

@@ -18,12 +18,14 @@
     <RESDEPEN value="$(RESFILES) forms\CustomScpExplorer.dfm forms\NonVisual.dfm 
       forms\ScpCommander.dfm forms\ScpExplorer.dfm"/>
     <LIBFILES value="lib\Putty.lib lib\RScpComp.lib lib\ScpCore.lib lib\ScpForms.lib"/>
-    <LIBRARIES value="DragDrop_B5.lib DriveDir_B5.lib Moje_B5.lib
-      bcb2kaxserver.lib bcbie.lib bcbsmp.lib dclocx.lib rtl.lib vcl.lib vclx.lib"/>
-    <SPARELIBS value="DragDrop_B5.lib DriveDir_B5.lib Moje_B5.lib
-      bcb2kaxserver.lib bcbie.lib bcbsmp.lib dclocx.lib rtl.lib vcl.lib vclx.lib"/>
-    <PACKAGES value="bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi
-      rtl.bpi vcl.bpi vclie.bpi vclx.bpi visualclx.bpi"/>
+    <LIBRARIES value="DragDrop_B5.lib DriveDir_B5.lib Moje_B5.lib
+      bcb2kaxserver.lib bcbie.lib bcbsmp.lib dclocx.lib rtl.lib tb2k_cb6.lib
+      tbx_cb6.lib vcl.lib vclx.lib"/>
+    <SPARELIBS value="DragDrop_B5.lib DriveDir_B5.lib Moje_B5.lib
+      bcb2kaxserver.lib bcbie.lib bcbsmp.lib dclocx.lib rtl.lib tb2k_cb6.lib
+      tbx_cb6.lib vcl.lib vclx.lib"/>
+    <PACKAGES value="bcb2kaxserver.bpi bcbie.bpi bcbsmp.bpi dclocx.bpi
+      rtl.bpi vcl.bpi vclie.bpi vclx.bpi visualclx.bpi"/>
     <PATHCPP value=".;forms;windows"/>
     <PATHPAS value=".;"/>
     <PATHRC value=".;windows"/>
@@ -34,8 +36,8 @@
     <USERDEFINES value="OLD_DND"/>
     <SYSDEFINES value="NO_STRICT"/>
     <MAINSOURCE value="WinSCP3.cpp"/>
-    <INCLUDEPATH value="core;console;forms;windows;resource;components;dragext;packages\filemng;packages\dragndrop;packages\my;$(BCB)\include;$(BCB)\include\vcl"/>
-    <LIBPATH value="lib;windows;forms;packages\filemng;$(BCB)\lib\obj;$(BCB)\lib"/>
+    <INCLUDEPATH value="core;console;forms;windows;resource;components;dragext;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BCB)\include;$(BCB)\include\vcl"/>
+    <LIBPATH value="lib;windows;forms;packages\filemng;packages\tbx;$(BCB)\lib\obj;$(BCB)\lib"/>
     <WARNINGS value="-w8092 -w8091 -w8090 -w8089 -w8087 -wprc -wuse -wucp -wstv -wstu -wsig 
       -wpin -wnod -wnak -wdef -wcln -wbbf -wasm -wamp -wamb"/>
     <OTHERFILES value=""/>
@@ -92,8 +94,8 @@ IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=3
 MinorVer=7
-Release=4
-Build=271
+Release=5
+Build=293
 Debug=0
 PreRelease=0
 Special=0
@@ -104,15 +106,15 @@ CodePage=1252
 
 [Version Info Keys]
 CompanyName=Martin Prikryl
-FileDescription=Windows SCP/SFTP client
-FileVersion=3.7.4.271
+FileDescription=Windows SFTP/SCP client
+FileVersion=3.7.5.293
 InternalName=winscp3
 LegalCopyright=(c) 2000-2005 Martin Prikryl
 LegalTrademarks=
-OriginalFilename=winscp374.exe
+OriginalFilename=winscp375.exe
 ProductName=WinSCP
-ProductVersion=3.7.4.0
-WWW=http://winscp.sourceforge.net/
+ProductVersion=3.7.5.0
+WWW=http://winscp.net/
 
 [Compiler]
 ShowInfoMsgs=0

File diff suppressed because it is too large
+ 442 - 448
WinSCP3.drc


BIN
WinSCP3.res


+ 3 - 0
components/UnixDirView.cpp

@@ -71,6 +71,7 @@ DEFINE_COMPARE_FUNC(RightsStr, AnsiCompareText);
 DEFINE_COMPARE_FUNC(Owner, AnsiCompareText);
 DEFINE_COMPARE_FUNC(Group, AnsiCompareText);
 DEFINE_COMPARE_FUNC(Extension, AnsiCompareText);
+DEFINE_COMPARE_FUNC(LinkTo, AnsiCompareText);
 //---------------------------------------------------------------------------
 #undef DEFINE_COMPARE_FUNC
 #undef COMPARE_NUMBER
@@ -380,6 +381,7 @@ void __fastcall TUnixDirView::GetDisplayInfo(TListItem * Item, tagLVITEMA &DispI
         case uvOwner: Value = File->Owner; break;
         case uvGroup: Value = File->Group; break;
         case uvExt: Value = File->Extension; break;
+        case uvLinkTarget: Value = File->LinkTo; break;
         default: assert(false);
       }
       StrPLCopy(DispInfo.pszText, Value, DispInfo.cchTextMax);
@@ -617,6 +619,7 @@ void __fastcall TUnixDirView::SortItems()
       case uvOwner: SortProc = (PFNLVCOMPARE)CompareOwner; break;
       case uvGroup: SortProc = (PFNLVCOMPARE)CompareGroup; break;
       case uvExt: SortProc = (PFNLVCOMPARE)CompareExtension; break;
+      case uvLinkTarget: SortProc = (PFNLVCOMPARE)CompareLinkTo; break;
       default: assert(false);
     }
     CustomSortItems(SortProc);

+ 2 - 1
components/UnixDirView.h

@@ -101,7 +101,7 @@ __published:
       default=true  };
 
   __property PathComboBox;
-  __property StatusBar;
+  __property OnUpdateStatusBar;
   __property PathLabel;
   __property LoadAnimation;
   __property OnGetSelectFilter;
@@ -143,6 +143,7 @@ __published:
   __property OnContextPopup;
   __property OnBeginRename;
   __property OnEndRename;
+  __property OnHistoryChange;
 
   __property ColumnClick;
   __property MultiSelect;

+ 13 - 0
components/UnixDriveView.cpp

@@ -75,6 +75,19 @@ void __fastcall TCustomUnixDriveView::SetTerminal(TTerminal * value)
   {
     FTerminal = value;
     Items->Clear();
+    #ifndef DESIGN_ONLY
+    if (FTerminal != NULL)
+    {
+      TStrings * FixedPaths = FTerminal->FixedPaths;
+      if (FixedPaths != NULL)
+      {
+        for (int i = 0; i < FixedPaths->Count; i++)
+        {
+          LoadPath(FixedPaths->Strings[i]);
+        }
+      }
+    }
+    #endif
   }
 }
 //---------------------------------------------------------------------------

+ 3 - 3
console/Main.cpp

@@ -193,10 +193,10 @@ inline void Print(bool FromBeginning, const char * Message)
     {
       if (LastFromBeginning[0] != '\0')
       {
-        printf(LastFromBeginning);
+        printf("%s", LastFromBeginning);
         LastFromBeginning[0] = '\0';
       }
-      printf(Message);
+      printf("%s", Message);
     }
   }
   else
@@ -205,7 +205,7 @@ inline void Print(bool FromBeginning, const char * Message)
     {
       printf("\r");
     }
-    printf(Message);
+    printf("%s", Message);
   }
 }
 //---------------------------------------------------------------------------

+ 53 - 0
core/Common.cpp

@@ -74,6 +74,20 @@ __fastcall TGuard::~TGuard()
   FCriticalSection->Leave();
 }
 //---------------------------------------------------------------------------
+// TUnguard
+//---------------------------------------------------------------------------
+__fastcall TUnguard::TUnguard(TCriticalSection * ACriticalSection) :
+  FCriticalSection(ACriticalSection)
+{
+  assert(ACriticalSection != NULL);
+  FCriticalSection->Leave();
+}
+//---------------------------------------------------------------------------
+__fastcall TUnguard::~TUnguard()
+{
+  FCriticalSection->Enter();
+}
+//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 const char EngShortMonthNames[12][4] =
   {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -759,6 +773,32 @@ AnsiString __fastcall FixedLenDateTimeFormat(const AnsiString & Format)
   return Result;
 }
 //---------------------------------------------------------------------------
+int __fastcall CompareFileTime(TDateTime T1, TDateTime T2)
+{
+  // "FAT" time precision
+  // 1 ms more solves the rounding issues (see also CustomDirView.pas)
+  static TDateTime Second(0, 0, 1, 1);
+  int Result;
+  if (T1 == T2)
+  {
+    // just optimalisation
+    Result = 0;
+  }
+  else if ((T1 < T2) && (T2 - T1 > Second))
+  {
+    Result = -1;
+  }
+  else if ((T1 > T2) && (T1 - T2 > Second))
+  {
+    Result = 1;
+  }
+  else
+  {
+    Result = 0;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 bool __fastcall RecursiveDeleteFile(const AnsiString FileName, bool ToRecycleBin)
 {
   SHFILEOPSTRUCT Data;
@@ -855,6 +895,19 @@ AnsiString __fastcall LoadStr(int Ident, unsigned int MaxLength)
   return Result;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall LoadStrPart(int Ident, int Part)
+{
+  AnsiString Result;
+  AnsiString Str = LoadStr(Ident);
+
+  while (Part > 0)
+  {
+    Result = CutToChar(Str, '|', false);
+    Part--;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall DecodeUrlChars(AnsiString S)
 {
   int i = 1;

+ 12 - 0
core/Common.h

@@ -50,6 +50,7 @@ bool __fastcall RecursiveDeleteFile(const AnsiString FileName, bool ToRecycleBin
 int __fastcall CancelAnswer(int Answers);
 int __fastcall AbortAnswer(int Answers);
 AnsiString __fastcall LoadStr(int Ident, unsigned int MaxLength);
+AnsiString __fastcall LoadStrPart(int Ident, int Part);
 struct TPasLibModule;
 TPasLibModule * __fastcall FindModule(void * Instance);
 //---------------------------------------------------------------------------
@@ -66,6 +67,7 @@ void __fastcall UnifyDateTimePrecision(TDateTime & DateTime1, TDateTime & DateTi
 __int64 __fastcall ConvertTimestampToUnix(const FILETIME & FileTime,
   bool ConsiderDST);
 AnsiString __fastcall FixedLenDateTimeFormat(const AnsiString & Format);
+int __fastcall CompareFileTime(TDateTime T1, TDateTime T2);
 //---------------------------------------------------------------------------
 class TCriticalSection
 {
@@ -86,6 +88,16 @@ public:
   __fastcall TGuard(TCriticalSection * ACriticalSection);
   __fastcall ~TGuard();
 
+private:
+  TCriticalSection * FCriticalSection;
+};
+//---------------------------------------------------------------------------
+class TUnguard
+{
+public:
+  __fastcall TUnguard(TCriticalSection * ACriticalSection);
+  __fastcall ~TUnguard();
+
 private:
   TCriticalSection * FCriticalSection;
 };

+ 11 - 7
core/Configuration.cpp

@@ -19,10 +19,13 @@ __fastcall TConfiguration::TConfiguration()
   FCriticalSection = new TCriticalSection();
   FUpdating = 0;
   FStorage = stDetect;
-  DontSave = false;
-  RandomSeedSave = true;
+  FDontSave = false;
+  FRandomSeedSave = true;
   FApplicationInfo = NULL;
   FGSSAPIInstalled = -1;
+  // make sure random generator is initialised, so random_save_seed() 
+  // in destructor can proceed
+  random_ref();
 }
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::Default()
@@ -52,7 +55,8 @@ void __fastcall TConfiguration::Default()
 __fastcall TConfiguration::~TConfiguration()
 {
   assert(!FUpdating);
-  if (RandomSeedSave) random_save_seed();
+  if (FRandomSeedSave) random_save_seed();
+  random_unref();
   if (FApplicationInfo) FreeFileInfo(FApplicationInfo);
   delete FCriticalSection;
 }
@@ -102,7 +106,7 @@ void __fastcall TConfiguration::SaveSpecial(THierarchicalStorage * /*Storage*/)
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::Save()
 {
-  if (DontSave) return;
+  if (FDontSave) return;
 
   if (Storage == stRegistry) CleanupIniFile();
 
@@ -231,7 +235,7 @@ void __fastcall TConfiguration::CleanupConfiguration()
     CleanupRegistry(ConfigurationSubKey);
     if (Storage == stRegistry)
     {
-      DontSave = true;
+      FDontSave = true;
     }
   }
   catch (Exception &E)
@@ -269,7 +273,7 @@ void __fastcall TConfiguration::CleanupRandomSeedFile()
 {
   try
   {
-    RandomSeedSave = false;
+    FRandomSeedSave = false;
     if (FileExists(RandomSeedFile))
     {
       if (!DeleteFile(RandomSeedFile)) Abort();
@@ -291,7 +295,7 @@ void __fastcall TConfiguration::CleanupIniFile()
     }
     if (Storage == stIniFile)
     {
-      DontSave = true;
+      FDontSave = true;
     }
   }
   catch (Exception &E)

+ 0 - 2
core/Configuration.h

@@ -126,8 +126,6 @@ public:
   __property AnsiString SshHostKeysSubKey  = { read=GetSshHostKeysSubKey };
   __property AnsiString RootKeyStr  = { read=GetRootKeyStr };
   __property AnsiString ConfigurationSubKey  = { read=GetConfigurationSubKey };
-  __property bool DontSave  = { read=FDontSave, write=FDontSave };
-  __property bool RandomSeedSave  = { read=FRandomSeedSave, write=FRandomSeedSave };
   __property TEOLType LocalEOLType = { read = GetLocalEOLType };
   __property AnsiString VersionStr = { read=GetVersionStr };
   __property AnsiString Version = { read=GetVersion };

+ 159 - 6
core/CopyParam.cpp

@@ -4,6 +4,8 @@
 
 #include "Common.h"
 #include "CopyParam.h"
+#include "HierarchicalStorage.h"
+#include "TextsCore.h"
 //---------------------------------------------------------------------------
 __fastcall TCopyParamType::TCopyParamType()
 {
@@ -21,6 +23,7 @@ __fastcall TCopyParamType::~TCopyParamType()
 //---------------------------------------------------------------------------
 void __fastcall TCopyParamType::Default()
 {
+  // when changing defaults, make sure GetInfoStr() can handle it
   FileNameCase = ncNoChange;
   PreserveReadOnly = true;
   PreserveTime = true;
@@ -37,9 +40,93 @@ void __fastcall TCopyParamType::Default()
   CalculateSize = true;
   FileMask = "*.*";
   ExcludeFileMask.Masks = "";
+  NegativeExclude = false;
   ClearArchive = false;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TCopyParamType::GetInfoStr(AnsiString Separator) const
+{
+  TCopyParamType Defaults;
+  AnsiString Result;
+
+  #define ADD(STR) Result += (Result.IsEmpty() ? AnsiString() : Separator) + (STR)
+  if ((TransferMode != Defaults.TransferMode) ||
+      ((TransferMode == tmAutomatic) && !(AsciiFileMask == Defaults.AsciiFileMask)))
+  {
+    AnsiString S = FORMAT(LoadStrPart(COPY_INFO_TRANSFER_TYPE, 1),
+      (LoadStrPart(COPY_INFO_TRANSFER_TYPE, TransferMode + 2)));
+    if (TransferMode == tmAutomatic)
+    {
+      S = FORMAT(S, (AsciiFileMask.Masks));
+    }
+    ADD(S);
+  }
+
+  if (FileNameCase != Defaults.FileNameCase)
+  {
+    ADD(FORMAT(LoadStrPart(COPY_INFO_FILENAME, 1),
+      (LoadStrPart(COPY_INFO_FILENAME, FileNameCase + 2))));
+  }
+
+  if (ReplaceInvalidChars != Defaults.ReplaceInvalidChars)
+  {
+    assert(!ReplaceInvalidChars);
+    ADD(LoadStr(COPY_INFO_DONT_REPLACE_INV_CHARS));
+  }
+
+  if ((PreserveRights != Defaults.PreserveRights) ||
+      (PreserveRights && 
+       ((Rights != Defaults.Rights) || (AddXToDirectories != Defaults.AddXToDirectories))))
+  {
+    assert(PreserveRights);
+    
+    AnsiString RightsStr = Rights.Text;
+    if (AddXToDirectories)
+    {
+      RightsStr += ", " + LoadStr(COPY_INFO_ADD_X_TO_DIRS);
+    }
+    ADD(FORMAT(LoadStr(COPY_INFO_PERMISSIONS), (RightsStr)));
+  }
+
+  if (PreserveReadOnly != Defaults.PreserveReadOnly)
+  {
+    assert(!PreserveReadOnly);
+    ADD(LoadStr(COPY_INFO_DONT_PRESERVE_READONLY));
+  }
+
+  if (PreserveTime != Defaults.PreserveTime)
+  {
+    ADD(LoadStr(PreserveTime ? COPY_INFO_TIMESTAMP : COPY_INFO_DONT_PRESERVE_TIME));
+  }
+
+  if (CalculateSize != Defaults.CalculateSize)
+  {
+    assert(!CalculateSize);
+    ADD(LoadStr(COPY_INFO_DONT_CALCULATE_SIZE));
+  }
+
+  if ((NegativeExclude != Defaults.NegativeExclude) ||
+      !(ExcludeFileMask == Defaults.ExcludeFileMask))
+  {
+    ADD(FORMAT(LoadStr(NegativeExclude ? COPY_INFO_INCLUDE_MASK : COPY_INFO_EXCLUDE_MASK),
+      (ExcludeFileMask.Masks)));
+  }
+
+  if (ClearArchive != Defaults.ClearArchive)
+  {
+    assert(ClearArchive);
+    ADD(LoadStr(COPY_INFO_CLEAR_ARCHIVE));
+  }
+  #undef ADD
+
+  if (Result.IsEmpty())
+  {
+    Result = LoadStr(COPY_INFO_DEFAULT);
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCopyParamType::Assign(const TCopyParamType * Source)
 {
   assert(Source != NULL);
@@ -59,6 +146,7 @@ void __fastcall TCopyParamType::Assign(const TCopyParamType * Source)
   COPY(CalculateSize);
   COPY(FileMask);
   COPY(ExcludeFileMask);
+  COPY(NegativeExclude);
   COPY(ClearArchive);
   #undef COPY
 }
@@ -110,12 +198,13 @@ AnsiString __fastcall TCopyParamType::ChangeFileName(AnsiString FileName,
   return FileName;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TCopyParamType::UseAsciiTransfer(const AnsiString FileName) const
+bool __fastcall TCopyParamType::UseAsciiTransfer(AnsiString FileName,
+  TOperationSide Side) const
 {
   switch (TransferMode) {
     case tmBinary: return false;
     case tmAscii: return true;
-    case tmAutomatic: return AsciiFileMask.Matches(FileName);
+    case tmAutomatic: return AsciiFileMask.Matches(FileName, (Side == osLocal));
     default: assert(false); return false;
   }
 }
@@ -138,7 +227,7 @@ AnsiString __fastcall TCopyParamType::GetLogStr() const
   return FORMAT(
     "  PrTime: %s; PrRO: %s; Rght: %s; PrR: %s; FnCs: %s; RIC: %s; "
       "Resume: %s (%d); CalcS: %s; Mask: %s\n"
-    "  TM: %s; ClAr: %s; ExclM: %s\n"
+    "  TM: %s; ClAr: %s; ExclM(%s): %s\n"
     "  AscM: %s\n",
     (BooleanToEngStr(PreserveTime),
      BooleanToEngStr(PreserveReadOnly),
@@ -152,6 +241,7 @@ AnsiString __fastcall TCopyParamType::GetLogStr() const
      FileMask,
      ModeC[TransferMode],
      BooleanToEngStr(ClearArchive),
+     BooleanToEngStr(NegativeExclude),
      ExcludeFileMask.Masks,
      AsciiFileMask.Masks));
 }
@@ -176,13 +266,76 @@ bool __fastcall TCopyParamType::AllowResume(__int64 Size) const
   }
 }
 //---------------------------------------------------------------------------
-bool __fastcall TCopyParamType::AllowTransfer(AnsiString FileName) const
+bool __fastcall TCopyParamType::AllowTransfer(AnsiString FileName,
+  TOperationSide Side) const
 {
   bool Result = true;
   if (!ExcludeFileMask.Masks.IsEmpty())
   {
-    Result = !ExcludeFileMask.Matches(FileName);
+    Result = (ExcludeFileMask.Matches(FileName, (Side == osLocal)) == NegativeExclude);
   }
   return Result;
 }
-
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamType::Load(THierarchicalStorage * Storage)
+{
+  AddXToDirectories = Storage->ReadBool("AddXToDirectories", AddXToDirectories);
+  AsciiFileMask.Masks = Storage->ReadString("Masks", AsciiFileMask.Masks);
+  FileNameCase = (TFileNameCase)Storage->ReadInteger("FileNameCase", FileNameCase);
+  PreserveReadOnly = Storage->ReadBool("PreserveReadOnly", PreserveReadOnly);
+  PreserveTime = Storage->ReadBool("PreserveTime", PreserveTime);
+  PreserveRights = Storage->ReadBool("PreserveRights", PreserveRights);
+  Rights.Text = Storage->ReadString("Text", Rights.Text);
+  TransferMode = (TTransferMode)Storage->ReadInteger("TransferMode", TransferMode);
+  ResumeSupport = (TResumeSupport)Storage->ReadInteger("ResumeSupport", ResumeSupport);
+  ResumeThreshold = Storage->ReadInt64("ResumeThreshold", ResumeThreshold);
+  ReplaceInvalidChars = Storage->ReadBool("ReplaceInvalidChars", ReplaceInvalidChars);
+  LocalInvalidChars = Storage->ReadString("LocalInvalidChars", LocalInvalidChars);
+  CalculateSize = Storage->ReadBool("CalculateSize", CalculateSize);
+  ExcludeFileMask.Masks = Storage->ReadString("ExcludeFileMask", ExcludeFileMask.Masks);
+  ClearArchive = Storage->ReadBool("ClearArchive", ClearArchive);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamType::Save(THierarchicalStorage * Storage) const
+{
+  Storage->WriteBool("AddXToDirectories", AddXToDirectories);
+  Storage->WriteString("Masks", AsciiFileMask.Masks);
+  Storage->WriteInteger("FileNameCase", FileNameCase);
+  Storage->WriteBool("PreserveReadOnly", PreserveReadOnly);
+  Storage->WriteBool("PreserveTime", PreserveTime);
+  Storage->WriteBool("PreserveRights", PreserveRights);
+  Storage->WriteString("Text", Rights.Text);
+  Storage->WriteInteger("TransferMode", TransferMode);
+  Storage->WriteInteger("ResumeSupport", ResumeSupport);
+  Storage->WriteInt64("ResumeThreshold", ResumeThreshold);
+  Storage->WriteBool("ReplaceInvalidChars", ReplaceInvalidChars);
+  Storage->WriteString("LocalInvalidChars", LocalInvalidChars);
+  Storage->WriteBool("CalculateSize", CalculateSize);
+  Storage->WriteString("ExcludeFileMask", ExcludeFileMask.Masks);
+  Storage->WriteBool("ClearArchive", ClearArchive);
+}
+//---------------------------------------------------------------------------
+#define C(Property) (Property == rhp.Property)
+bool __fastcall TCopyParamType::operator==(const TCopyParamType & rhp) const
+{
+  return
+    C(AddXToDirectories) &&
+    C(AsciiFileMask) &&
+    C(FileNameCase) &&
+    C(PreserveReadOnly) &&
+    C(PreserveTime) &&
+    C(PreserveRights) &&
+    C(Rights) &&
+    C(TransferMode) &&
+    C(ResumeSupport) &&
+    C(ResumeThreshold) &&
+    C(ReplaceInvalidChars) &&
+    C(LocalInvalidChars) &&
+    C(CalculateSize) &&
+    C(ExcludeFileMask) &&
+    C(NegativeExclude) &&
+    C(ClearArchive) &&
+    true;
+}
+#undef C
+//---------------------------------------------------------------------------

+ 11 - 2
core/CopyParam.h

@@ -11,6 +11,7 @@ enum TFileNameCase { ncNoChange, ncUpperCase, ncLowerCase, ncFirstUpperCase, ncL
 // TScript::OptionProc depend on the order
 enum TTransferMode { tmBinary, tmAscii, tmAutomatic };
 enum TResumeSupport { rsOn, rsSmart, rsOff };
+class THierarchicalStorage;
 //---------------------------------------------------------------------------
 class TCopyParamType {
 private:
@@ -30,6 +31,7 @@ private:
   bool FCalculateSize;
   AnsiString FFileMask;
   TFileMasks FExcludeFileMask;
+  bool FNegativeExclude;
   bool FClearArchive;
 
 public:
@@ -43,10 +45,16 @@ public:
     TOperationSide Side, bool FirstLevel) const;
   int __fastcall LocalFileAttrs(const TRights & Rights) const;
   TRights __fastcall RemoteFileRights(int Attrs) const;
-  bool __fastcall UseAsciiTransfer(const AnsiString FileName) const;
+  bool __fastcall UseAsciiTransfer(AnsiString FileName, TOperationSide Side) const;
   bool __fastcall AllowResume(__int64 Size) const;
   AnsiString __fastcall ValidLocalFileName(AnsiString FileName) const;
-  bool __fastcall AllowTransfer(AnsiString FileName) const;
+  bool __fastcall AllowTransfer(AnsiString FileName, TOperationSide Side) const;
+
+  void __fastcall Load(THierarchicalStorage * Storage);
+  void __fastcall Save(THierarchicalStorage * Storage) const;
+  AnsiString __fastcall GetInfoStr(AnsiString Separator) const;
+
+  bool __fastcall operator==(const TCopyParamType & rhp) const;
 
   __property TFileMasks AsciiFileMask = { read = FAsciiFileMask, write = FAsciiFileMask };
   __property TFileNameCase FileNameCase = { read = FFileNameCase, write = FFileNameCase };
@@ -64,6 +72,7 @@ public:
   __property bool CalculateSize = { read = FCalculateSize, write = FCalculateSize };
   __property AnsiString FileMask = { read = FFileMask, write = FFileMask };
   __property TFileMasks ExcludeFileMask = { read = FExcludeFileMask, write = FExcludeFileMask };
+  __property bool NegativeExclude = { read = FNegativeExclude, write = FNegativeExclude };
   __property bool ClearArchive = { read = FClearArchive, write = FClearArchive };
 };
 //---------------------------------------------------------------------------

+ 40 - 4
core/FileMasks.cpp

@@ -12,6 +12,7 @@ using namespace Masks;
 
 #include "Common.h"
 #include "TextsCore.h"
+#include "RemoteFiles.h"
 //---------------------------------------------------------------------------
 AnsiString __fastcall MaskFilePart(const AnsiString Part, const AnsiString Mask, bool& Masked)
 {
@@ -139,19 +140,48 @@ __fastcall TFileMasks::TFileMasks(const AnsiString AMasks)
   FMasks = AMasks;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TFileMasks::Matches(AnsiString FileName) const
+bool __fastcall TFileMasks::Matches(AnsiString FileName, AnsiString Path) const
 {
   AnsiString S = Masks;
-  FileName = ExtractFileName(FileName);
   while (!S.IsEmpty())
   {
     AnsiString M;
     M = CutToChar(S, ';', True);
-    if (MatchesMask(FileName, M)) return true;
+    int D = M.LastDelimiter("\\/");
+    bool PathMatch = true;
+    if (D > 0)
+    {
+      AnsiString MP = ToUnixPath(M.SubString(1, D - 1));
+      // 'Path' must already have unix slashes
+      PathMatch = MatchesMask(Path, MP);
+      M = M.SubString(D + 1, M.Length() - D);
+    }
+
+    if (PathMatch && MatchesMask(FileName, M)) return true;
   }
   return false;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Local) const
+{
+  AnsiString Path;
+  if (Local)
+  {
+    Path = ExtractFilePath(FileName);
+    if (!Path.IsEmpty())
+    {
+      Path = ToUnixPath(ExcludeTrailingBackslash(Path));
+    }
+    FileName = ExtractFileName(FileName);
+  }
+  else
+  {
+    Path = UnixExcludeTrailingBackslash(UnixExtractFilePath(FileName));
+    FileName = UnixExtractFileName(FileName);
+  }
+  return Matches(FileName, Path);
+}
+//---------------------------------------------------------------------------
 bool __fastcall TFileMasks::IsValid()
 {
   int Start, Length;
@@ -366,7 +396,8 @@ bool __fastcall TCustomCommand::FindPattern(const AnsiString & Command,
     int Len;
     char APatternCmd;
     GetToken(Command, Index, Len, APatternCmd);
-    if (PatternCmd == APatternCmd)
+    if (((PatternCmd != '!') && (PatternCmd == APatternCmd)) ||
+        ((PatternCmd == '!') && (Len == 1) && (APatternCmd != TEXT_TOKEN)))
     {
       Result = true;
     }
@@ -519,4 +550,9 @@ bool __fastcall TFileCustomCommand::IsFileListCommand(const AnsiString & Command
   return FindPattern(Command, '&');
 }
 //---------------------------------------------------------------------------
+bool __fastcall TFileCustomCommand::IsFileCommand(const AnsiString & Command)
+{
+  return FindPattern(Command, '!') || FindPattern(Command, '&');
+}
+//---------------------------------------------------------------------------
 

+ 3 - 1
core/FileMasks.h

@@ -16,7 +16,8 @@ public:
   TFileMasks & __fastcall operator =(const AnsiString rhs);
   bool __fastcall operator ==(const TFileMasks & rhm) const;
   bool __fastcall operator ==(const AnsiString rhs) const;
-  bool __fastcall Matches(AnsiString FileName) const;
+  bool __fastcall Matches(AnsiString FileName, AnsiString Path = "") const;
+  bool __fastcall Matches(AnsiString FileName, bool Local) const;
   bool __fastcall IsValid();
   bool __fastcall IsValid(int & Start, int & Length);
 
@@ -85,6 +86,7 @@ public:
     int Index, int Len, char PatternCmd, void * Arg);
 
   bool __fastcall IsFileListCommand(const AnsiString & Command);
+  virtual bool __fastcall IsFileCommand(const AnsiString & Command);
 
 protected:
   virtual int __fastcall PatternLen(int Index, char PatternCmd);

+ 22 - 5
core/FileOperationProgress.cpp

@@ -44,10 +44,12 @@ void __fastcall TFileOperationProgressType::Clear()
   TotalSize = 0;
   TotalSizeSet = false;
   Operation = foNone;
-  DragDrop = false;
+  Temp = false;
   YesToAll = false;
   YesToNewer = false;
   NoToAll = false;
+  SkipToAll = false;
+  AlternateResumeAlways = false;
   // to bypass check in ClearTransfer()
   TransferSize = 0;
   ClearTransfer();
@@ -69,7 +71,7 @@ void __fastcall TFileOperationProgressType::ClearTransfer()
 }
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
-  TOperationSide ASide, int ACount, bool ADragDrop,
+  TOperationSide ASide, int ACount, bool ATemp,
   const AnsiString ADirectory)
 {
   Clear();
@@ -79,7 +81,7 @@ void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
   InProgress = true;
   Cancel = csContinue;
   Directory = ADirectory;
-  DragDrop = ADragDrop;
+  Temp = ATemp;
   DoProgress();
 }
 //---------------------------------------------------------------------------
@@ -161,7 +163,7 @@ void __fastcall TFileOperationProgressType::Finish(AnsiString FileName,
 
   if (FOnFinished)
   {
-    FOnFinished(Operation, Side, DragDrop, FileName,
+    FOnFinished(Operation, Side, Temp, FileName,
       /* TODO : There wasn't 'Success' condition, was it by mistake or by purpose? */
       Success && (Cancel == csContinue), DisconnectWhenComplete);
   }
@@ -331,4 +333,19 @@ TDateTime __fastcall TFileOperationProgressType::TotalTimeExpected()
     return 0;
   }
 }
-
+//---------------------------------------------------------------------------
+TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
+{
+  assert(TotalSizeSet);
+  unsigned int CurCps = CPS();
+  // sanity check
+  if ((CurCps > 0) && (TotalSize > TotalSkipped + TotalTransfered))
+  {
+    return TDateTime((double)((double)(TotalSize - TotalSkipped - TotalTransfered) / CurCps) /
+      (24 * 60 * 60));
+  }
+  else
+  {
+    return 0;
+  }
+}

+ 6 - 3
core/FileOperationProgress.h

@@ -13,7 +13,7 @@ enum TResumeStatus { rsNotAvailable, rsEnabled, rsDisabled };
 typedef void __fastcall (__closure *TFileOperationProgressEvent)
   (TFileOperationProgressType & ProgressData, TCancelStatus & Cancel);
 typedef void __fastcall (__closure *TFileOperationFinished)
-  (TFileOperation Operation, TOperationSide Side, bool DragDrop,
+  (TFileOperation Operation, TOperationSide Side, bool Temp,
     const AnsiString FileName, bool Success, bool & DisconnectWhenComplete);
 //---------------------------------------------------------------------------
 class TFileOperationProgressType
@@ -44,7 +44,7 @@ public:
   AnsiString Directory;
   bool AsciiTransfer;
   bool TransferingFile;
-  bool DragDrop;
+  bool Temp;
 
   // file size to read/write
   __int64 LocalSize;
@@ -64,6 +64,8 @@ public:
   bool YesToAll;
   bool YesToNewer;
   bool NoToAll;
+  bool SkipToAll;
+  bool AlternateResumeAlways;
 
   bool TotalSizeSet;
 
@@ -96,7 +98,7 @@ public:
   void __fastcall ChangeTransferSize(__int64 ASize);
   void __fastcall SetTotalSize(__int64 ASize);
   void __fastcall Start(TFileOperation AOperation,
-    TOperationSide ASide, int ACount, bool ADragDrop = false,
+    TOperationSide ASide, int ACount, bool ATemp = false,
     const AnsiString ADirectory = "");
   void __fastcall Stop();
   void __fastcall Suspend();
@@ -105,6 +107,7 @@ public:
   // only current file
   TDateTime __fastcall TimeExpected();
   TDateTime __fastcall TotalTimeExpected();
+  TDateTime __fastcall TotalTimeLeft();
   int __fastcall TransferProgress();
   int __fastcall OverallProgress();
   int __fastcall TotalTransferProgress();

+ 5 - 1
core/FileSystems.h

@@ -19,7 +19,10 @@ enum TFSCommand { fsNull = 0, fsVarValue, fsLastLine, fsFirstLine,
   fsHomeDirectory, fsUnset, fsUnalias, fsAliasGroupList, fsCreateLink, fsCopyFile, 
   fsAnyCommand, fsReadSymlink, fsChangeProperties, fsMoveFile };
 //---------------------------------------------------------------------------
-typedef void __fastcall (__closure *TLogAddLineEvent)(System::TObject* Sender, const AnsiString AddedLine);
+// Duplicated in LogMemo.h for design-time-only purposes
+enum TLogLineType {llOutput, llInput, llStdError, llMessage, llException};
+typedef void __fastcall (__closure *TLogAddLineEvent)(System::TObject* Sender,
+  TLogLineType Type, const AnsiString AddedLine);
 //---------------------------------------------------------------------------
 class TCustomFileSystem : public TObject
 {
@@ -62,6 +65,7 @@ public:
   virtual void __fastcall CopyFile(const AnsiString FileName,
     const AnsiString NewName) = 0;
   virtual AnsiString __fastcall FileUrl(const AnsiString FileName) = 0;
+  virtual TStrings * __fastcall GetFixedPaths() = 0;
 
   __property AnsiString CurrentDirectory = { read = GetCurrentDirectory, write = SetCurrentDirectory };
   __property AnsiString ProtocolName = { read = GetProtocolName };

+ 18 - 13
core/HierarchicalStorage.cpp

@@ -92,23 +92,28 @@ void __fastcall THierarchicalStorage::CloseSubKey()
     else FKeyHistory->Delete(FKeyHistory->Count-1);
 }
 //---------------------------------------------------------------------------
-void __fastcall THierarchicalStorage::RecursiveDeleteSubKey(const AnsiString Key)
+void __fastcall THierarchicalStorage::ClearSubKeys()
 {
-  if (OpenSubKey(Key, false))
+  TStringList *SubKeys = new TStringList();
+  try
   {
-    TStringList *SubKeys = new TStringList();
-    try
-    {
-      GetSubKeyNames(SubKeys);
-      for (int Index = 0; Index < SubKeys->Count; Index++)
-      {
-        RecursiveDeleteSubKey(SubKeys->Strings[Index]);
-      }
-    }
-    __finally
+    GetSubKeyNames(SubKeys);
+    for (int Index = 0; Index < SubKeys->Count; Index++)
     {
-      delete SubKeys;
+      RecursiveDeleteSubKey(SubKeys->Strings[Index]);
     }
+  }
+  __finally
+  {
+    delete SubKeys;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall THierarchicalStorage::RecursiveDeleteSubKey(const AnsiString Key)
+{
+  if (OpenSubKey(Key, false))
+  {
+    ClearSubKeys();
     CloseSubKey();
   }
   DeleteSubKey(Key);

+ 1 - 0
core/HierarchicalStorage.h

@@ -23,6 +23,7 @@ public:
   virtual bool __fastcall KeyExists(const AnsiString SubKey) = 0;
   virtual bool __fastcall ValueExists(const AnsiString Value) = 0;
   virtual void __fastcall RecursiveDeleteSubKey(const AnsiString Key);
+  virtual void __fastcall ClearSubKeys();
   virtual void __fastcall ReadValues(Classes::TStrings* Strings, bool MaintainKeys = false);
   virtual void __fastcall WriteValues(Classes::TStrings* Strings, bool MaintainKeys = false);
   virtual void __fastcall ClearValues();

+ 3 - 1
core/Interface.h

@@ -4,6 +4,7 @@
 //---------------------------------------------------------------------------
 #include "Configuration.h"
 #include "SessionData.h"
+#define HELP_NONE ""
 //---------------------------------------------------------------------------
 TConfiguration * __fastcall CreateConfiguration();
 
@@ -42,7 +43,7 @@ typedef void __fastcall (__closure *TQueryParamsTimerEvent)(unsigned int & Resul
 
 struct TQueryParams
 {
-  TQueryParams(unsigned int AParams = 0);
+  TQueryParams(unsigned int AParams = 0, AnsiString AHelpKeyword = HELP_NONE);
 
   const TQueryButtonAlias * Aliases;
   unsigned int AliasesCount;
@@ -51,6 +52,7 @@ struct TQueryParams
   TQueryParamsTimerEvent TimerEvent;
   AnsiString TimerMessage;
   unsigned int TimerAnswers;
+  AnsiString HelpKeyword;
 };
 
 enum TQueryType { qtConfirmation, qtWarning, qtError, qtInformation };

+ 18 - 5
core/PuttyIntf.c

@@ -75,15 +75,19 @@ void connection_fatal(void * frontend, char * fmt, ...)
   SSHConnectionFatal(frontend, stuff);
 }
 //---------------------------------------------------------------------------
-void verify_ssh_host_key(void * frontend, char * host, int port, char * keytype,
-  char * keystr, char * fingerprint)
+int verify_ssh_host_key(void * frontend, char * host, int port, char * keytype,
+  char * keystr, char * fingerprint, void (*callback)(void * ctx, int result),
+  void * ctx)
 {
   SSHVerifyHostKey(frontend, host, port, keytype, keystr, fingerprint);
+  // We should return 0 when key was not confirmed, we throw exception instead.
+  return 1;
 }
 //---------------------------------------------------------------------------
-int askappend(void * frontend, Filename filename)
+int askappend(void * frontend, Filename filename,
+  void (*callback)(void * ctx, int result), void * ctx)
 {
-  // this is called from loggin.c of putty, which is never used with WinSCP
+  // this is called from logging.c of putty, which is never used with WinSCP
   assert(0);
   return 0;
 }
@@ -93,9 +97,12 @@ void old_keyfile_warning(void)
   SSHOldKeyfileWarning();
 }
 //---------------------------------------------------------------------------
-void askalg(void * frontend, const char * algtype, const char * algname)
+int askalg(void * frontend, const char * algtype, const char * algname,
+  void (*callback)(void * ctx, int result), void * ctx)
 {
   SSHAskAlg(frontend, algtype, algname);
+  // We should return 0 when key was not confirmed, we throw exception instead.
+  return 1;
 }
 //---------------------------------------------------------------------------
 void cleanup_exit(int code)
@@ -182,4 +189,10 @@ void pinger_free(Pinger pinger)
 {
   // nothing
 }
+//---------------------------------------------------------------------------
+void set_busy_status(void * frontend, int status)
+{
+  // nothing
+}
+//---------------------------------------------------------------------------
 #pragma option pop // -w-par

+ 4 - 2
core/PuttyIntf.h

@@ -16,11 +16,13 @@ extern "C"
   void sk_init();
   int select_result(WPARAM wParam, LPARAM lParam);
   int socket_writable(SOCKET skt);
+  void random_ref(void);
+  void random_unref(void);
   void random_save_seed(void);
   int verify_host_key(char * hostname, int port, char * keytype, char * key);
   void store_host_key(char * hostname, int port, char * keytype, char * key);
-  void * saferealloc(void * ptr, size_t size);
-  void * safemalloc(size_t size);
+  void * saferealloc(void * ptr, size_t n, size_t size);
+  void * safemalloc(size_t n, size_t size);
   void safefree(void * ptr);
   void noise_regular(void);
   void * log_init(void * frontend, void * cfg);

+ 242 - 160
core/Queue.cpp

@@ -17,7 +17,7 @@ public:
   __fastcall ~TTerminalItem();
 
   void __fastcall Process(TQueueItem * Item);
-  void __fastcall ProcessUserAction();
+  bool __fastcall ProcessUserAction(void * Arg);
   void __fastcall Cancel();
   void __fastcall Idle();
 
@@ -62,13 +62,13 @@ protected:
   void __fastcall TerminalClose(TObject * Sender);
   void __fastcall TerminalQueryUser(TObject * Sender,
     const AnsiString Query, TStrings * MoreMessages, int Answers,
-    const TQueryParams * Params, int & Answer, TQueryType Type);
+    const TQueryParams * Params, int & Answer, TQueryType Type, void * Arg);
   void __fastcall TerminalPromptUser(TSecureShell * SecureShell,
-    AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result);
+    AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result, void * Arg);
   void __fastcall TerminalShowExtendedException(TSecureShell * SecureShell,
-    Exception * E);
+    Exception * E, void * Arg);
   void __fastcall OperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool DragDrop, const AnsiString FileName, bool Success,
+    bool Temp, const AnsiString FileName, bool Success,
     bool & DisconnectWhenFinished);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData,
     TCancelStatus & Cancel);
@@ -78,7 +78,7 @@ protected:
 //---------------------------------------------------------------------------
 int __fastcall ThreadProc(void * Thread)
 {
-  TSignalThread * SimpleThread = reinterpret_cast<TSignalThread*>(Thread);
+  TSimpleThread * SimpleThread = reinterpret_cast<TSimpleThread*>(Thread);
   assert(SimpleThread != NULL);
   try
   {
@@ -93,19 +93,15 @@ int __fastcall ThreadProc(void * Thread)
   return 0;
 }
 //---------------------------------------------------------------------------
-__fastcall TSignalThread::TSignalThread() :
-  FTerminated(true), FFinished(true), FThread(NULL), FEvent(NULL)
+__fastcall TSimpleThread::TSimpleThread() :
+  FThread(NULL), FFinished(true)
 {
-  FEvent = CreateEvent(NULL, false, false, NULL);
-  assert(FEvent != NULL);
-
   unsigned ThreadID;
   FThread = reinterpret_cast<HANDLE>(
     BeginThread(NULL, 0, ThreadProc, this, CREATE_SUSPENDED, ThreadID));
-  ::SetThreadPriority(FThread, THREAD_PRIORITY_BELOW_NORMAL); 
 }
 //---------------------------------------------------------------------------
-__fastcall TSignalThread::~TSignalThread()
+__fastcall TSimpleThread::~TSimpleThread()
 {
   Close();
 
@@ -113,24 +109,62 @@ __fastcall TSignalThread::~TSignalThread()
   {
     CloseHandle(FThread);
   }
-
-  if (FEvent)
-  {
-    CloseHandle(FEvent);
-  }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSignalThread::Start()
+void __fastcall TSimpleThread::Start()
 {
-  FTerminated = false;
   if (ResumeThread(FThread) == 1)
   {
     FFinished = false;
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSignalThread::Finished()
+void __fastcall TSimpleThread::Finished()
+{
+}
+//---------------------------------------------------------------------------
+void __fastcall TSimpleThread::Close()
+{
+  if (!FFinished)
+  {
+    Terminate();
+    WaitFor();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TSimpleThread::WaitFor()
+{
+  WaitForSingleObject(FThread, INFINITE);
+}
+//---------------------------------------------------------------------------
+// TSignalThread
+//---------------------------------------------------------------------------
+__fastcall TSignalThread::TSignalThread() :
+  TSimpleThread(),
+  FTerminated(true), FEvent(NULL)
+{
+  FEvent = CreateEvent(NULL, false, false, NULL);
+  assert(FEvent != NULL);
+
+  ::SetThreadPriority(FThread, THREAD_PRIORITY_BELOW_NORMAL); 
+}
+//---------------------------------------------------------------------------
+__fastcall TSignalThread::~TSignalThread()
+{
+  // cannot leave closing to TSimpleThread as we need to close it before
+  // destroying the event
+  Close();
+
+  if (FEvent)
+  {
+    CloseHandle(FEvent);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TSignalThread::Start()
 {
+  FTerminated = false;
+  TSimpleThread::Start();
 }
 //---------------------------------------------------------------------------
 void __fastcall TSignalThread::TriggerEvent()
@@ -155,26 +189,12 @@ void __fastcall TSignalThread::Execute()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSignalThread::Close()
-{
-  if (!FFinished)
-  {
-    Terminate();
-    WaitFor();
-  }
-}
-//---------------------------------------------------------------------------
 void __fastcall TSignalThread::Terminate()
 {
   FTerminated = true;
   TriggerEvent();
 }
 //---------------------------------------------------------------------------
-void __fastcall TSignalThread::WaitFor()
-{
-  WaitForSingleObject(FThread, INFINITE);
-}
-//---------------------------------------------------------------------------
 // TTerminalQueue
 //---------------------------------------------------------------------------
 __fastcall TTerminalQueue::TTerminalQueue(TTerminal * Terminal,
@@ -249,7 +269,7 @@ void __fastcall TTerminalQueue::TerminalFinished(TTerminalItem * TerminalItem)
       // Index may be >= FTransfersLimit also when the transfer limit was
       // recently decresed, then
       // FTemporaryTerminals < FTerminals->Count - FTransfersLimit
-      if ((Index >= FTransfersLimit) && (FTemporaryTerminals > 0))
+      if ((FTransfersLimit > 0) && (Index >= FTransfersLimit) && (FTemporaryTerminals > 0))
       {
         FTemporaryTerminals--;
       }
@@ -276,7 +296,7 @@ bool __fastcall TTerminalQueue::TerminalFree(TTerminalItem * TerminalItem)
       assert(Index >= 0);
       assert(Index >= FFreeTerminals);
 
-      Result = (Index < FTransfersLimit);
+      Result = (FTransfersLimit <= 0) || (Index < FTransfersLimit);
       if (Result)
       {
         FTerminals->Move(Index, 0);
@@ -406,36 +426,45 @@ TTerminalQueueStatus * __fastcall TTerminalQueue::CreateStatus(TTerminalQueueSta
 bool __fastcall TTerminalQueue::ItemGetData(TQueueItem * Item,
   TQueueItemProxy * Proxy)
 {
-  TGuard Guard(FItemsSection);
-
-  bool Result = (FItems->IndexOf(Item) >= 0);
+  // to prevent deadlocks when closing queue from other thread 
+  bool Result = !FFinished;
   if (Result)
   {
-    Item->GetData(Proxy);
+    TGuard Guard(FItemsSection);
+
+    Result = (FItems->IndexOf(Item) >= 0);
+    if (Result)
+    {
+      Item->GetData(Proxy);
+    }
   }
 
   return Result;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TTerminalQueue::ItemProcessUserAction(TQueueItem * Item)
+bool __fastcall TTerminalQueue::ItemProcessUserAction(TQueueItem * Item, void * Arg)
 {
-  bool Result;
-  TTerminalItem * TerminalItem;
-
+  // to prevent deadlocks when closing queue from other thread 
+  bool Result = !FFinished;
+  if (Result)
   {
-    TGuard Guard(FItemsSection);
+    TTerminalItem * TerminalItem;
 
-    Result = (FItems->IndexOf(Item) >= 0) &&
-      TQueueItem::IsUserActionStatus(Item->Status);
-    if (Result)
     {
-      TerminalItem = Item->FTerminalItem;
+      TGuard Guard(FItemsSection);
+
+      Result = (FItems->IndexOf(Item) >= 0) &&
+        TQueueItem::IsUserActionStatus(Item->Status);
+      if (Result)
+      {
+        TerminalItem = Item->FTerminalItem;
+      }
     }
-  }
 
-  if (Result)
-  {
-    TerminalItem->ProcessUserAction();
+    if (Result)
+    {
+      Result = TerminalItem->ProcessUserAction(Arg);
+    }
   }
 
   return Result;
@@ -443,59 +472,65 @@ bool __fastcall TTerminalQueue::ItemProcessUserAction(TQueueItem * Item)
 //---------------------------------------------------------------------------
 bool __fastcall TTerminalQueue::ItemMove(TQueueItem * Item, TQueueItem * BeforeItem)
 {
-  bool Result;
-  
+  // to prevent deadlocks when closing queue from other thread 
+  bool Result = !FFinished;
+  if (Result)
   {
-    TGuard Guard(FItemsSection);
+    {
+      TGuard Guard(FItemsSection);
+
+      int Index = FItems->IndexOf(Item);
+      int IndexDest = FItems->IndexOf(BeforeItem);
+      Result = (Index >= 0) && (IndexDest >= 0) &&
+        (Item->GetStatus() == TQueueItem::qsPending) &&
+        (BeforeItem->GetStatus() == TQueueItem::qsPending);
+      if (Result)
+      {
+        FItems->Move(Index, IndexDest);
+      }
+    }
 
-    int Index = FItems->IndexOf(Item);
-    int IndexDest = FItems->IndexOf(BeforeItem);
-    Result = (Index >= 0) && (IndexDest >= 0) &&
-      (Item->GetStatus() == TQueueItem::qsPending) &&
-      (BeforeItem->GetStatus() == TQueueItem::qsPending);
     if (Result)
     {
-      FItems->Move(Index, IndexDest);
+      DoListUpdate();
+      TriggerEvent();
     }
   }
 
-  if (Result)
-  {
-    DoListUpdate();
-    TriggerEvent();
-  }
-
   return Result;
 }
 //---------------------------------------------------------------------------
 bool __fastcall TTerminalQueue::ItemExecuteNow(TQueueItem * Item)
 {
-  bool Result;
-
+  // to prevent deadlocks when closing queue from other thread 
+  bool Result = !FFinished;
+  if (Result)
   {
-    TGuard Guard(FItemsSection);
-
-    int Index = FItems->IndexOf(Item);
-    Result = (Index >= 0) && (Item->GetStatus() == TQueueItem::qsPending);
-    if (Result)
     {
-      if (Index != FItemsInProcess)
-      {
-        assert(Index > FItemsInProcess);
-        FItems->Move(Index, FItemsInProcess);
-      }
+      TGuard Guard(FItemsSection);
 
-      if (FTerminals->Count >= FTransfersLimit)
+      int Index = FItems->IndexOf(Item);
+      Result = (Index >= 0) && (Item->GetStatus() == TQueueItem::qsPending);
+      if (Result)
       {
-        FTemporaryTerminals++;
+        if (Index != FItemsInProcess)
+        {
+          assert(Index > FItemsInProcess);
+          FItems->Move(Index, FItemsInProcess);
+        }
+
+        if ((FTransfersLimit > 0) && (FTerminals->Count >= FTransfersLimit))
+        {
+          FTemporaryTerminals++;
+        }
       }
     }
-  }
 
-  if (Result)
-  {
-    DoListUpdate();
-    TriggerEvent();
+    if (Result)
+    {
+      DoListUpdate();
+      TriggerEvent();
+    }
   }
 
   return Result;
@@ -503,32 +538,36 @@ bool __fastcall TTerminalQueue::ItemExecuteNow(TQueueItem * Item)
 //---------------------------------------------------------------------------
 bool __fastcall TTerminalQueue::ItemDelete(TQueueItem * Item)
 {
-  bool Result;
-  bool UpdateList = false;
-
+  // to prevent deadlocks when closing queue from other thread 
+  bool Result = !FFinished;
+  if (Result)
   {
-    TGuard Guard(FItemsSection);
+    bool UpdateList = false;
 
-    int Index = FItems->IndexOf(Item);
-    Result = (Index >= 0);
-    if (Result)
     {
-      if (Item->Status == TQueueItem::qsPending)
-      {
-        FItems->Delete(Index);
-        UpdateList = true;
-      }
-      else
+      TGuard Guard(FItemsSection);
+
+      int Index = FItems->IndexOf(Item);
+      Result = (Index >= 0);
+      if (Result)
       {
-        Item->FTerminalItem->Cancel();
+        if (Item->Status == TQueueItem::qsPending)
+        {
+          FItems->Delete(Index);
+          UpdateList = true;
+        }
+        else
+        {
+          Item->FTerminalItem->Cancel();
+        }
       }
     }
-  }
 
-  if (UpdateList)
-  {
-    DoListUpdate();
-    TriggerEvent();
+    if (UpdateList)
+    {
+      DoListUpdate();
+      TriggerEvent();
+    }
   }
 
   return Result;
@@ -577,7 +616,8 @@ void __fastcall TTerminalQueue::ProcessEvent()
       TGuard Guard(FItemsSection);
 
       if ((FFreeTerminals == 0) &&
-          (FTerminals->Count < FTransfersLimit + FTemporaryTerminals))
+          ((FTransfersLimit <= 0) ||
+           (FTerminals->Count < FTransfersLimit + FTemporaryTerminals)))
       {
         TerminalItem = new TTerminalItem(this);
         FTerminals->Add(TerminalItem);
@@ -622,29 +662,29 @@ void __fastcall TTerminalQueue::DoListUpdate()
 //---------------------------------------------------------------------------
 void __fastcall TTerminalQueue::DoQueryUser(TObject * Sender,
   const AnsiString Query, TStrings * MoreMessages, int Answers,
-  const TQueryParams * Params, int & Answer, TQueryType Type)
+  const TQueryParams * Params, int & Answer, TQueryType Type, void * Arg)
 {
   if (OnQueryUser != NULL)
   {
-    OnQueryUser(Sender, Query, MoreMessages, Answers, Params, Answer, Type);
+    OnQueryUser(Sender, Query, MoreMessages, Answers, Params, Answer, Type, Arg);
   }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalQueue::DoPromptUser(TSecureShell * SecureShell,
-  AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result)
+  AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result, void * Arg)
 {
   if (OnPromptUser != NULL)
   {
-    OnPromptUser(SecureShell, Prompt, Kind, Response, Result);
+    OnPromptUser(SecureShell, Prompt, Kind, Response, Result, Arg);
   }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalQueue::DoShowExtendedException(
-  TSecureShell * SecureShell, Exception * E)
+  TSecureShell * SecureShell, Exception * E, void * Arg)
 {
   if (OnShowExtendedException != NULL)
   {
-    OnShowExtendedException(SecureShell, E);
+    OnShowExtendedException(SecureShell, E, Arg);
   }
 }
 //---------------------------------------------------------------------------
@@ -655,7 +695,7 @@ void __fastcall TTerminalQueue::SetTransfersLimit(int value)
     {
       TGuard Guard(FItemsSection);
 
-      if (value < FItemsInProcess)
+      if ((value > 0) && (value < FItemsInProcess))
       {
         FTemporaryTerminals = (FItemsInProcess - value);
       }
@@ -814,40 +854,51 @@ void __fastcall TTerminalItem::Cancel()
   FCancel = true;
 }
 //---------------------------------------------------------------------------
-void __fastcall TTerminalItem::ProcessUserAction()
+bool __fastcall TTerminalItem::ProcessUserAction(void * Arg)
 {
-  assert(FItem != NULL);
-  assert(FUserActionParams != NULL);
-
-  if (FItem->GetStatus() == TQueueItem::qsQuery)
+  // When status is changed twice quickly, the controller when responding
+  // to the first change (non-user-action) can be so slow to check only after
+  // the second (user-action) change occurs. Thus it responds it.
+  // Then as reaction to the second (user-action) change there will not be
+  // any outstanding user-action.
+  bool Result = (FUserActionParams != NULL);
+  if (Result)
   {
-    TQueryUserRec * Params;
-    Params = reinterpret_cast<TQueryUserRec *>(FUserActionParams);
+    assert(FItem != NULL);
 
-    FQueue->DoQueryUser(Params->Sender, Params->Query, Params->MoreMessages,
-      Params->Answers, Params->Params, Params->Answer, Params->Type);
-  }
-  else if (FItem->GetStatus() == TQueueItem::qsPrompt)
-  {
-    TPromptUserRec * Params;
-    Params = reinterpret_cast<TPromptUserRec *>(FUserActionParams);
+    if (FItem->GetStatus() == TQueueItem::qsQuery)
+    {
+      TQueryUserRec * Params;
+      Params = reinterpret_cast<TQueryUserRec *>(FUserActionParams);
 
-    FQueue->DoPromptUser(Params->SecureShell, Params->Prompt,
-      Params->Kind, Params->Response, Params->Result);
-  }
-  else if (FItem->GetStatus() == TQueueItem::qsError)
-  {
-    TShowExtendedExceptionRec * Params;
-    Params = reinterpret_cast<TShowExtendedExceptionRec *>(FUserActionParams);
+      FQueue->DoQueryUser(Params->Sender, Params->Query, Params->MoreMessages,
+        Params->Answers, Params->Params, Params->Answer, Params->Type, Arg);
+    }
+    else if (FItem->GetStatus() == TQueueItem::qsPrompt)
+    {
+      TPromptUserRec * Params;
+      Params = reinterpret_cast<TPromptUserRec *>(FUserActionParams);
 
-    FQueue->DoShowExtendedException(Params->SecureShell, Params->E);
-  }
-  else
-  {
-    assert(false);
-  }
+      FQueue->DoPromptUser(Params->SecureShell, Params->Prompt,
+        Params->Kind, Params->Response, Params->Result, Arg);
+    }
+    else if (FItem->GetStatus() == TQueueItem::qsError)
+    {
+      TShowExtendedExceptionRec * Params;
+      Params = reinterpret_cast<TShowExtendedExceptionRec *>(FUserActionParams);
 
-  TriggerEvent();
+      FQueue->DoShowExtendedException(Params->SecureShell, Params->E, Arg);
+    }
+    else
+    {
+      assert(false);
+    }
+
+    FUserActionParams = NULL;
+
+    TriggerEvent();
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 bool __fastcall TTerminalItem::WaitForUserAction(
@@ -892,8 +943,11 @@ void __fastcall TTerminalItem::TerminalClose(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 void __fastcall TTerminalItem::TerminalQueryUser(TObject * Sender,
   const AnsiString Query, TStrings * MoreMessages, int Answers,
-  const TQueryParams * Params, int & Answer, TQueryType Type)
+  const TQueryParams * Params, int & Answer, TQueryType Type, void * Arg)
 {
+  USEDPARAM(Arg);
+  assert(Arg == NULL);
+
   TQueryUserRec QueryUserRec;
   QueryUserRec.Sender = Sender;
   QueryUserRec.Query = Query;
@@ -910,25 +964,32 @@ void __fastcall TTerminalItem::TerminalQueryUser(TObject * Sender,
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalItem::TerminalPromptUser(TSecureShell * SecureShell,
-  AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result)
+  AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result, 
+  void * Arg)
 {
+  USEDPARAM(Arg);
+  assert(Arg == NULL);
+
   TPromptUserRec PromptUserRec;
   PromptUserRec.SecureShell = SecureShell;
   PromptUserRec.Prompt = Prompt;
   PromptUserRec.Kind = Kind;
-  PromptUserRec.Response = Response;
+  PromptUserRec.Response = Response.c_str();
   PromptUserRec.Result = Result;
 
   if (WaitForUserAction(TQueueItem::qsPrompt, &PromptUserRec))
   {
-    Response = PromptUserRec.Response;
+    Response = PromptUserRec.Response.c_str();
     Result = PromptUserRec.Result;
   }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalItem::TerminalShowExtendedException(
-  TSecureShell * SecureShell, Exception * E)
+  TSecureShell * SecureShell, Exception * E, void * Arg)
 {
+  USEDPARAM(Arg);
+  assert(Arg == NULL);
+
   if (!E->Message.IsEmpty() &&
       (dynamic_cast<EAbort*>(E) == NULL))
   {
@@ -941,7 +1002,7 @@ void __fastcall TTerminalItem::TerminalShowExtendedException(
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalItem::OperationFinished(TFileOperation /*Operation*/,
-  TOperationSide /*Side*/, bool /*DragDrop*/, const AnsiString /*FileName*/,
+  TOperationSide /*Side*/, bool /*Temp*/, const AnsiString /*FileName*/,
   bool /*Success*/, bool & /*DisconnectWhenFinished*/)
 {
   // nothing
@@ -1070,8 +1131,8 @@ void __fastcall TQueueItem::Execute(TTerminalItem * TerminalItem)
 __fastcall TQueueItemProxy::TQueueItemProxy(TTerminalQueue * Queue,
   TQueueItem * QueueItem) :
   FQueue(Queue), FQueueItem(QueueItem), FProgressData(NULL),
-  FQueueStatus(NULL), FInfo(NULL), FFlag(false),
-  FProcessingUserAction(false)
+  FQueueStatus(NULL), FInfo(NULL),
+  FProcessingUserAction(false), FUserData(NULL)
 {
   FProgressData = new TFileOperationProgressType();
   FInfo = new TQueueItem::TInfo();
@@ -1142,7 +1203,7 @@ bool __fastcall TQueueItemProxy::Delete()
   return FQueue->ItemDelete(FQueueItem);
 }
 //---------------------------------------------------------------------------
-bool __fastcall TQueueItemProxy::ProcessUserAction()
+bool __fastcall TQueueItemProxy::ProcessUserAction(void * Arg)
 {
   assert(FQueueItem != NULL);
 
@@ -1150,7 +1211,7 @@ bool __fastcall TQueueItemProxy::ProcessUserAction()
   FProcessingUserAction = true;
   try
   {
-    Result = FQueue->ItemProcessUserAction(FQueueItem);
+    Result = FQueue->ItemProcessUserAction(FQueueItem, Arg);
   }
   __finally
   {
@@ -1308,14 +1369,28 @@ __fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
 {
   if (FilesToCopy->Count > 1)
   {
-    ExtractCommonPath(FilesToCopy, FInfo->Source);
-    // this way the trailing backslash is preserved for root directories like D:\\
-    FInfo->Source = ExtractFileDir(IncludeTrailingBackslash(FInfo->Source));
+    if (FLAGSET(Params, cpTemporary))
+    {
+      FInfo->Source = "";
+    }
+    else
+    {
+      ExtractCommonPath(FilesToCopy, FInfo->Source);
+      // this way the trailing backslash is preserved for root directories like D:\\
+      FInfo->Source = ExtractFileDir(IncludeTrailingBackslash(FInfo->Source));
+    }
   }
   else
   {
-    assert(FilesToCopy->Count > 0);
-    FInfo->Source = FilesToCopy->Strings[0];
+    if (FLAGSET(Params, cpTemporary))
+    {
+      FInfo->Source = ExtractFileName(FilesToCopy->Strings[0]);
+    }
+    else
+    {
+      assert(FilesToCopy->Count > 0);
+      FInfo->Source = FilesToCopy->Strings[0];
+    }
   }
 
   FInfo->Destination =
@@ -1358,8 +1433,15 @@ __fastcall TDownloadQueueItem::TDownloadQueueItem(TTerminal * Terminal,
     }
   }
 
-  FInfo->Destination =
-    IncludeTrailingBackslash(TargetDir) + CopyParam->FileMask;
+  if (FLAGSET(Params, cpTemporary))
+  {
+    FInfo->Destination = "";
+  }
+  else
+  {
+    FInfo->Destination =
+      IncludeTrailingBackslash(TargetDir) + CopyParam->FileMask;
+  }
   FInfo->ModifiesLocal = true;
   FInfo->ModifiesRemote = ((Params & cpDelete) != 0);
 }

+ 28 - 14
core/Queue.h

@@ -5,30 +5,44 @@
 #include "SecureShell.h"
 #include "FileOperationProgress.h"
 //---------------------------------------------------------------------------
-class TSignalThread
+class TSimpleThread
 {
 friend int __fastcall ThreadProc(void * Thread);
 
 public:
-  void __fastcall Start();
-  void __fastcall Terminate();
+  __fastcall TSimpleThread();
+  virtual __fastcall ~TSimpleThread();
+
+  virtual void __fastcall Start();
   void __fastcall WaitFor();
-  void __fastcall TriggerEvent();
+  virtual void __fastcall Terminate() = 0;
+  void __fastcall Close();
 
 protected:
   HANDLE FThread;
+  bool FFinished;
+
+  virtual void __fastcall Execute() = 0;
+  virtual void __fastcall Finished();
+};
+//---------------------------------------------------------------------------
+class TSignalThread : public TSimpleThread
+{
+public:
+  virtual void __fastcall Start();
+  virtual void __fastcall Terminate();
+  void __fastcall TriggerEvent();
+
+protected:
   HANDLE FEvent;
   bool FTerminated;
-  bool FFinished;
 
   __fastcall TSignalThread();
   virtual __fastcall ~TSignalThread();
 
   bool __fastcall WaitForEvent();
   virtual void __fastcall Execute();
-  virtual void __fastcall Finished();
   virtual void __fastcall ProcessEvent() = 0;
-  void __fastcall Close();
 };
 //---------------------------------------------------------------------------
 class TTerminal;
@@ -86,7 +100,7 @@ protected:
 
   TQueueItem * __fastcall GetItem(int Index);
   bool __fastcall ItemGetData(TQueueItem * Item, TQueueItemProxy * Proxy);
-  bool __fastcall ItemProcessUserAction(TQueueItem * Item);
+  bool __fastcall ItemProcessUserAction(TQueueItem * Item, void * Arg);
   bool __fastcall ItemMove(TQueueItem * Item, TQueueItem * BeforeItem);
   bool __fastcall ItemExecuteNow(TQueueItem * Item);
   bool __fastcall ItemDelete(TQueueItem * Item);
@@ -100,11 +114,11 @@ protected:
 
   void __fastcall DoQueryUser(TObject * Sender, const AnsiString Query,
     TStrings * MoreMessages, int Answers, const TQueryParams * Params, int & Answer,
-    TQueryType Type);
+    TQueryType Type, void * Arg);
   void __fastcall DoPromptUser(TSecureShell * SecureShell, AnsiString Prompt,
-    TPromptKind Kind, AnsiString & Response, bool & Result);
+    TPromptKind Kind, AnsiString & Response, bool & Result, void * Arg);
   void __fastcall DoShowExtendedException(TSecureShell * SecureShell,
-    Exception * E);
+    Exception * E, void * Arg);
   void __fastcall DoQueueItemUpdate(TQueueItem * Item);
   void __fastcall DoListUpdate();
 
@@ -164,7 +178,7 @@ friend class TTerminalQueue;
 
 public:
   bool __fastcall Update();
-  bool __fastcall ProcessUserAction();
+  bool __fastcall ProcessUserAction(void * Arg = NULL);
   bool __fastcall Move(bool Sooner);
   bool __fastcall Move(TQueueItemProxy * BeforeItem);
   bool __fastcall ExecuteNow();
@@ -173,9 +187,9 @@ public:
   __property TFileOperationProgressType * ProgressData = { read = GetProgressData };
   __property TQueueItem::TInfo * Info = { read = FInfo };
   __property TQueueItem::TStatus Status = { read = FStatus };
-  __property bool Flag = { read = FFlag, write = FFlag };
   __property bool ProcessingUserAction = { read = FProcessingUserAction };
   __property int Index = { read = GetIndex };
+  __property void * UserData = { read = FUserData, write = FUserData };
 
 private:
   TFileOperationProgressType * FProgressData;
@@ -184,8 +198,8 @@ private:
   TQueueItem * FQueueItem;
   TTerminalQueueStatus * FQueueStatus;
   TQueueItem::TInfo * FInfo;
-  bool FFlag;
   bool FProcessingUserAction;
+  void * FUserData;
 
   __fastcall TQueueItemProxy(TTerminalQueue * Queue, TQueueItem * QueueItem);
   virtual __fastcall ~TQueueItemProxy();

+ 53 - 52
core/RemoteFiles.cpp

@@ -79,47 +79,6 @@ AnsiString __fastcall UnixExtractFileExt(const AnsiString Path)
   return (Pos > 0) ? Path.SubString(Pos, Path.Length() - Pos + 1) : AnsiString();
 }
 //---------------------------------------------------------------------------
-void __fastcall SkipPathComponent(const AnsiString & Text,
-  int & SelStart, int & SelLength, bool Left, bool Unix)
-{
-  AnsiString Delimiter = Unix ? "/" : "\\";
-
-  int P;
-  bool WholeSelected = (SelLength >= Text.Length());
-  if (!Left)
-  {
-    int ASelStart = WholeSelected ? 0 : SelStart;
-    int P2;
-    P = Text.SubString(ASelStart + 1, Text.Length()).Pos(Delimiter);
-    P2 = Text.SubString(ASelStart + 1, Text.Length()).Pos(" ");
-    if ((P2 >= 1) && ((P2 < P) || (P < 1)))
-    {
-      P = P2;
-    }
-
-    if (P < 1)
-    {
-      P = Text.Length();
-    }
-    else
-    {
-      P += ASelStart;
-    }
-  }
-  else
-  {
-    int ASelStart = WholeSelected ? Text.Length() : SelStart;
-    P = Text.SubString(1, ASelStart - 1).LastDelimiter(Delimiter + " ");
-    if (P < 1)
-    {
-      P = 0;
-    }
-  }
-
-  SelStart = P;
-  SelLength = 0;
-}
-//---------------------------------------------------------------------------
 bool __fastcall ExtractCommonPath(TStrings * Files, AnsiString & Path)
 {
   assert(Files->Count > 0);
@@ -312,6 +271,37 @@ AnsiString __fastcall MakeFileList(TStrings * FileList)
   }
   return Result;
 }
+//---------------------------------------------------------------------------
+// copy from BaseUtils.pas
+void __fastcall ReduceDateTimePrecision(TDateTime & DateTime,
+  TModificationFmt Precision)
+{
+  if (Precision != mfFull)
+  {
+    unsigned short Y, M, D, H, N, S, MS;
+  
+    DecodeDate(DateTime, Y, M, D);
+    DecodeTime(DateTime, H, N, S, MS);
+    switch (Precision)
+    {
+      case mfMDHM:
+        S = 0;
+        MS = 0;
+        break;
+
+      case mfMDY:
+        H = 0;
+        N = 0;
+        S = 0;
+        MS = 0;
+
+      default:
+        assert(false);
+    }
+
+    DateTime = EncodeDate(Y, M, D) + EncodeTime(H, N, S, MS);
+  }
+}
 //- TRemoteFiles ------------------------------------------------------------
 __fastcall TRemoteFile::TRemoteFile(TRemoteFile * ALinkedByFile):
   TPersistent()
@@ -333,7 +323,7 @@ __fastcall TRemoteFile::~TRemoteFile()
   delete FLinkedFile;
 }
 //---------------------------------------------------------------------------
-TRemoteFile * __fastcall TRemoteFile::Duplicate()
+TRemoteFile * __fastcall TRemoteFile::Duplicate(bool Standalone)
 {
   TRemoteFile * Result;
   Result = new TRemoteFile();
@@ -341,7 +331,7 @@ TRemoteFile * __fastcall TRemoteFile::Duplicate()
   {
     if (FLinkedFile)
     {
-      Result->FLinkedFile = FLinkedFile->Duplicate();
+      Result->FLinkedFile = FLinkedFile->Duplicate(true);
       Result->FLinkedFile->FLinkedByFile = Result;
     }
     *Result->Rights = *FRights;
@@ -362,6 +352,10 @@ TRemoteFile * __fastcall TRemoteFile::Duplicate()
     COPY_FP(Selected);
     COPY_FP(CyclicLink);
     #undef COPY_FP
+    if (Standalone && (!FFullFileName.IsEmpty() || (Directory != NULL)))
+    {
+      Result->FFullFileName = FullFileName;
+    }
   }
   catch(...)
   {
@@ -432,17 +426,17 @@ Boolean __fastcall TRemoteFile::GetIsDirectory() const
   return (toupper(Type) == FILETYPE_DIRECTORY);
 }
 //---------------------------------------------------------------------------
-Boolean __fastcall TRemoteFile::GetIsParentDirectory()
+Boolean __fastcall TRemoteFile::GetIsParentDirectory() const
 {
   return (FileName == PARENTDIRECTORY);
 }
 //---------------------------------------------------------------------------
-Boolean __fastcall TRemoteFile::GetIsThisDirectory()
+Boolean __fastcall TRemoteFile::GetIsThisDirectory() const
 {
   return (FileName == THISDIRECTORY);
 }
 //---------------------------------------------------------------------------
-Boolean __fastcall TRemoteFile::GetIsInaccesibleDirectory()
+Boolean __fastcall TRemoteFile::GetIsInaccesibleDirectory() const
 {
   Boolean Result;
   if (IsDirectory)
@@ -880,7 +874,7 @@ AnsiString __fastcall TRemoteFile::GetListingStr()
     (IsSymLink ? AnsiString(SYMLINKSTR) + LinkTo : AnsiString()))));
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall TRemoteFile::GetFullFileName()
+AnsiString __fastcall TRemoteFile::GetFullFileName() const
 {
   if (FFullFileName.IsEmpty())
   {
@@ -949,7 +943,7 @@ void __fastcall TRemoteFileList::DuplicateTo(TRemoteFileList * Copy)
   for (int Index = 0; Index < Count; Index++)
   {
     TRemoteFile * File = Files[Index];
-    Copy->AddFile(File->Duplicate());
+    Copy->AddFile(File->Duplicate(false));
   }
   Copy->FDirectory = Directory;
   Copy->FTimestamp = FTimestamp;
@@ -1051,11 +1045,11 @@ void __fastcall TRemoteDirectory::DuplicateTo(TRemoteFileList * Copy)
   TRemoteFileList::DuplicateTo(Copy);
   if (ThisDirectory && !IncludeThisDirectory)
   {
-    Copy->AddFile(ThisDirectory->Duplicate());
+    Copy->AddFile(ThisDirectory->Duplicate(false));
   }
   if (ParentDirectory && !IncludeParentDirectory)
   {
-    Copy->AddFile(ParentDirectory->Duplicate());
+    Copy->AddFile(ParentDirectory->Duplicate(false));
   }
 }
 //---------------------------------------------------------------------------
@@ -1890,7 +1884,9 @@ bool __fastcall TRemoteProperties::operator ==(const TRemoteProperties & rhp) co
     if ((Valid.Contains(vpRights) &&
           (Rights != rhp.Rights || AddXToDirectories != rhp.AddXToDirectories)) ||
         (Valid.Contains(vpOwner) && Owner != rhp.Owner) ||
-        (Valid.Contains(vpGroup) && Group != rhp.Group))
+        (Valid.Contains(vpGroup) && Group != rhp.Group) ||
+        (Valid.Contains(vpModification) && (Modification != rhp.Modification)) ||
+        (Valid.Contains(vpLastAccess) && (LastAccess != rhp.LastAccess)))
     {
       Result = false;
     }
@@ -1905,6 +1901,7 @@ bool __fastcall TRemoteProperties::operator !=(const TRemoteProperties & rhp) co
 //---------------------------------------------------------------------------
 TRemoteProperties __fastcall TRemoteProperties::CommonProperties(TStrings * FileList)
 {
+  // TODO: Modification and LastAccess
   TRemoteProperties CommonProperties;
   for (int Index = 0; Index < FileList->Count; Index++)
   {
@@ -1913,7 +1910,10 @@ TRemoteProperties __fastcall TRemoteProperties::CommonProperties(TStrings * File
     if (!Index)
     {
       CommonProperties.Rights = *(File->Rights);
-      CommonProperties.Rights.AllowUndef = File->IsDirectory || File->Rights->IsUndef;
+      // previously we allowed undef implicitly for directories,
+      // now we do it explicitly in properties dialog and only in combination
+      // with "recursive" option
+      CommonProperties.Rights.AllowUndef = File->Rights->IsUndef;
       CommonProperties.Valid << vpRights;
       if (!File->Owner.IsEmpty())
       {
@@ -1948,6 +1948,7 @@ TRemoteProperties __fastcall TRemoteProperties::CommonProperties(TStrings * File
 TRemoteProperties __fastcall TRemoteProperties::ChangedProperties(
   const TRemoteProperties & OriginalProperties, TRemoteProperties NewProperties)
 {
+  // TODO: Modification and LastAccess
   if (!NewProperties.Recursive)
   {
     if (NewProperties.Rights == OriginalProperties.Rights &&

+ 11 - 10
core/RemoteFiles.h

@@ -54,13 +54,13 @@ private:
   void __fastcall SetType(char AType);
   void __fastcall SetTerminal(TTerminal * value);
   void __fastcall SetRights(TRights * value);
-  AnsiString __fastcall GetFullFileName();
+  AnsiString __fastcall GetFullFileName() const;
   int __fastcall GetIconIndex();
   bool __fastcall GetIsHidden();
   void __fastcall SetIsHidden(bool value);
-  bool __fastcall GetIsParentDirectory();
-  bool __fastcall GetIsThisDirectory();
-  bool __fastcall GetIsInaccesibleDirectory();
+  bool __fastcall GetIsParentDirectory() const;
+  bool __fastcall GetIsThisDirectory() const;
+  bool __fastcall GetIsInaccesibleDirectory() const;
   AnsiString __fastcall GetExtension();
   AnsiString __fastcall GetUserModificationStr();
 
@@ -70,7 +70,7 @@ protected:
 public:
   __fastcall TRemoteFile(TRemoteFile * ALinkedByFile = NULL);
   virtual __fastcall ~TRemoteFile();
-  TRemoteFile * __fastcall Duplicate();
+  TRemoteFile * __fastcall Duplicate(bool Standalone = true);
 
   void __fastcall ShiftTime(const TDateTime & Difference);
   void __fastcall Complete();
@@ -320,8 +320,8 @@ private:
   void __fastcall SetRightUndef(TRight Right, TState value);
 };
 //---------------------------------------------------------------------------
-enum TValidProperty { vpRights, vpGroup, vpOwner };
-typedef Set<TValidProperty, vpRights, vpOwner> TValidProperties;
+enum TValidProperty { vpRights, vpGroup, vpOwner, vpModification, vpLastAccess };
+typedef Set<TValidProperty, vpRights, vpLastAccess> TValidProperties;
 class TRemoteProperties
 {
 public:
@@ -331,6 +331,8 @@ public:
   bool AddXToDirectories;
   AnsiString Group;
   AnsiString Owner;
+  __int64 Modification; // unix time
+  __int64 LastAccess; // unix time
 
   __fastcall TRemoteProperties();
   bool __fastcall operator ==(const TRemoteProperties & rhp) const;
@@ -349,8 +351,6 @@ AnsiString __fastcall UnixExtractFileName(const AnsiString Path);
 AnsiString __fastcall UnixExtractFileExt(const AnsiString Path);
 Boolean __fastcall ComparePaths(const AnsiString Path1, const AnsiString Path2);
 Boolean __fastcall UnixComparePaths(const AnsiString Path1, const AnsiString Path2);
-void __fastcall SkipPathComponent(const AnsiString & Text,
-  int & SelStart, int & SelLength, bool Left, bool Unix);
 bool __fastcall ExtractCommonPath(TStrings * Files, AnsiString & Path);
 bool __fastcall UnixExtractCommonPath(TStrings * Files, AnsiString & Path);
 bool __fastcall IsUnixRootPath(const AnsiString Path);
@@ -359,6 +359,7 @@ AnsiString __fastcall FromUnixPath(const AnsiString Path);
 AnsiString __fastcall ToUnixPath(const AnsiString Path);
 AnsiString __fastcall MinimizeName(const AnsiString FileName, int MaxLen, bool Unix);
 AnsiString __fastcall MakeFileList(TStrings * FileList);
+void __fastcall ReduceDateTimePrecision(TDateTime & DateTime,
+  TModificationFmt Precision);
 //---------------------------------------------------------------------------
 #endif
-

+ 64 - 42
core/ScpFileSystem.cpp

@@ -331,6 +331,7 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
     case fcSymbolicLink:
     case fcResolveSymlink:
     case fcRename:
+    case fcRemoteMove:
     case fcRemoteCopy:
       return true;
 
@@ -339,6 +340,7 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
 
     case fcNativeTextMode:
     case fcNewerOnlyUpload:
+    case fcTimestampChanging:
       return false;
 
     default:
@@ -957,26 +959,26 @@ void __fastcall TSCPFileSystem::ReadDirectory(TRemoteFileList * FileList)
       }
       catch(Exception & E)
       {
-      	if (FTerminal->Active)
-      	{
-      	  if (FLsFullTime == asAuto)
-      	  {
+        if (FTerminal->Active)
+        {
+          if (FLsFullTime == asAuto)
+          {
             FTerminal->DoHandleExtendedException(&E);
             FLsFullTime = asOff;
             Again = true;
             FTerminal->LogEvent(
               FORMAT("Directory listing with %s failed, try again regular listing.",
               (FullTimeOption)));
-      	  }
-      	  else
-      	  {
-      	    throw;
-      	  }
-      	}
-      	else
-      	{
-      	  throw;
-      	}
+          }
+          else
+          {
+            throw;
+          }
+        }
+        else
+        {
+          throw;
+        }
       }
     }
     while (Again);
@@ -1117,6 +1119,8 @@ void __fastcall TSCPFileSystem::ChangeFileProperties(const AnsiString FileName,
     ExecCommand(fsChangeOwner,
       ARRAYOFCONST((RecursiveStr, DelimitStr(Properties->Owner), DelimitedName)));
   }
+  assert(!Properties->Valid.Contains(vpLastAccess));
+  assert(!Properties->Valid.Contains(vpModification));
 }
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::CustomCommandOnFile(const AnsiString FileName,
@@ -1158,7 +1162,8 @@ void __fastcall TSCPFileSystem::CustomCommandOnFile(const AnsiString FileName,
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::AnyCommand(const AnsiString Command)
 {
-  ExecCommand(fsAnyCommand, ARRAYOFCONST((Command)));
+  ExecCommand(fsAnyCommand, ARRAYOFCONST((Command)),
+    ecDefault | ecIgnoreWarnings);
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall TSCPFileSystem::FileUrl(const AnsiString FileName)
@@ -1168,6 +1173,11 @@ AnsiString __fastcall TSCPFileSystem::FileUrl(const AnsiString FileName)
     (FileName[1] == '/' ? "" : "/") + FileName;
 }
 //---------------------------------------------------------------------------
+TStrings * __fastcall TSCPFileSystem::GetFixedPaths()
+{
+  return NULL;
+}
+//---------------------------------------------------------------------------
 // transfer protocol
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::SCPResponse(bool * GotLastLine)
@@ -1308,7 +1318,7 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
             (
               Answer = FTerminal->DoQueryUser(
                 FMTLOAD(DIRECTORY_OVERWRITE, (FileNameOnly)),
-                qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll,
+                qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll,
                 &Params);
             );
             switch (Answer)
@@ -1351,7 +1361,7 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
             (
               Answer = FTerminal->ConfirmFileOverwrite(
                 FileNameOnly, &FileParams,
-                qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll | qaAll,
+                qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll,
                 &Params, osRemote, OperationProgress);
             );
           }
@@ -1360,7 +1370,7 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
               CanProceed = true;
               break;
 
-            case qaAbort:
+            case qaCancel:
               if (!OperationProgress->Cancel) OperationProgress->Cancel = csCancel;
             case qaNo:
               CanProceed = false;
@@ -1397,7 +1407,7 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
           TQueryParams Params(qpAllowContinueOnError);
           SUSPEND_OPERATION (
             if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (FileName)), E.Message,
-              qaOK | qaAbort, &Params) == qaAbort)
+              qaOK | qaAbort, &Params, qtError) == qaAbort)
             {
               OperationProgress->Cancel = csCancel;
             }
@@ -1460,7 +1470,7 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
   TFileOperationProgressType * OperationProgress, int Level)
 {
   if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(ExtractFileName(FileName)))
+      !CopyParam->AllowTransfer(FileName, osLocal))
   {
     FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
     THROW_SKIP_FILE_NULL;  
@@ -1501,7 +1511,7 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
     OperationProgress->TransferingFile = false;
 
     // Will we use ASCII of BINARY file tranfer?
-    OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName));
+    OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName, osLocal));
     FTerminal->LogEvent(
       AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
         " transfer mode selected.");
@@ -1738,19 +1748,28 @@ void __fastcall TSCPFileSystem::SCPDirectorySource(const AnsiString DirectoryNam
           SCPSource(FileName, CopyParam, Params, OperationProgress, Level + 1);
         }
       }
-      catch (EScpSkipFile &E)
+      // Previously we catched EScpSkipFile, making error being displayed
+      // even when file was excluded by mask. Now the EScpSkipFile is special
+      // case without error message.
+      catch (EScpFileSkipped &E)
       {
         TQueryParams Params(qpAllowContinueOnError);
-        // If ESkipFile occurs, just log it and continue with next file
         SUSPEND_OPERATION (
           if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (FileName)), E.Message,
-                qaOK | qaAbort, &Params) == qaAbort)
+                qaOK | qaAbort, &Params, qtError) == qaAbort)
           {
             OperationProgress->Cancel = csCancel;
           }
           if (!FTerminal->HandleException(&E)) throw;
         );
       }
+      catch (EScpSkipFile &E)
+      {
+        // If ESkipFile occurs, just log it and continue with next file
+        SUSPEND_OPERATION (
+          if (!FTerminal->HandleException(&E)) throw;
+        );
+      }
 
       FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (DirectoryName)),
         FindOK = (FindNext(SearchRec) == 0);
@@ -1817,9 +1836,11 @@ void __fastcall TSCPFileSystem::CopyToLocal(TStrings * FilesToCopy,
           ARRAYOFCONST((Options, DelimitStr(FileName)))));
         SkipFirstLine();
 
-        // Filename is used only for error messaging
-        SCPSink(TargetDir, FileName, CopyParam, Success, OperationProgress,
-          Params, 0);
+        // Filename is used for error messaging and excluding files only
+        // Send in full path to allow path-based excluding
+        AnsiString FullFileName = UnixExcludeTrailingBackslash(File->FullFileName);
+        SCPSink(TargetDir, FullFileName, UnixExtractFilePath(FullFileName),
+          CopyParam, Success, OperationProgress, Params, 0);
         // operation succeded (no exception), so it's ok that
         // remote side closed SCP, but we continue with next file
         if (OperationProgress->Cancel == csRemoteAbort)
@@ -1926,7 +1947,8 @@ void __fastcall TSCPFileSystem::SCPSendError(const AnsiString Message, bool Fata
 }
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
-  const AnsiString FileName, const TCopyParamType * CopyParam, bool & Success,
+  const AnsiString FileName, const AnsiString SourceDir,
+  const TCopyParamType * CopyParam, bool & Success,
   TFileOperationProgressType * OperationProgress, int Params,
   int Level)
 {
@@ -1953,12 +1975,11 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
     // See (switch ... case 'T':)
     if (FileData.SetTime) FileData.SetTime--;
 
-    // In case of error occured before control record arrived
-    // (we used to set whole path here, but it was inconsistent with only-filename
-    // set later)
+    // In case of error occured before control record arrived.
+    // We can finally use full path here, as we get current path in FileName param
     // (we used to set the file into OperationProgress->FileName, but it collided
     // with progress outputing, particularly for scripting)
-    AnsiString ErrorFileName = UnixExtractFileName(FileName);
+    AnsiString ErrorFileName = FileName;
 
     try
     {
@@ -2064,7 +2085,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
           }
 
           OperationProgress->SetFile(OnlyFileName);
-          ErrorFileName = OnlyFileName;
+          ErrorFileName = SourceDir + OnlyFileName;
           OperationProgress->SetTransferSize(TSize);
         }
         catch (Exception &E)
@@ -2087,8 +2108,9 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
           FTerminal->FatalError(LoadStr(ATTEMPT_TO_WRITE_TO_PARENT_DIR));
         }
 
+        AnsiString SourceFullName = SourceDir + OperationProgress->FileName;
         if (FLAGCLEAR(Params, cpDelete) &&
-            !CopyParam->AllowTransfer(OperationProgress->FileName))
+            !CopyParam->AllowTransfer(SourceFullName, osRemote))
         {
           FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer",
             (ErrorFileName)));
@@ -2118,10 +2140,9 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
             );
             /* SCP: can we set the timestamp for directories ? */
           }
-          /* TODO 1 : Send whole path, CopyData.SourceFileName is not enough
-             (just error messaging)*/
-          SCPSink(DestFileName, OperationProgress->FileName, CopyParam,
-            Success, OperationProgress, Params, Level + 1);
+          AnsiString FullFileName = SourceDir + OperationProgress->FileName;
+          SCPSink(DestFileName, FullFileName, UnixIncludeTrailingBackslash(FullFileName),
+            CopyParam, Success, OperationProgress, Params, Level + 1);
           continue;
         }
           else
@@ -2169,14 +2190,14 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
                   SUSPEND_OPERATION (
                     Answer = FTerminal->ConfirmFileOverwrite(
                       OperationProgress->FileName, &FileParams,
-                      qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll | qaAll,
+                      qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll,
                       &Params, osLocal, OperationProgress);
                   );
                 }
 
                 switch (Answer)
                 {
-                  case qaAbort: OperationProgress->Cancel = csCancel; // continue on next case
+                  case qaCancel: OperationProgress->Cancel = csCancel; // continue on next case
                   case qaNo: SkipConfirmed = true; EXCEPTION;
                 }
               }
@@ -2206,7 +2227,8 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
             OperationProgress->SetLocalSize(OperationProgress->TransferSize);
 
             // Will we use ASCII of BINARY file tranfer?
-            OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(DestFileName));
+            OperationProgress->SetAsciiTransfer(
+              CopyParam->UseAsciiTransfer(SourceFullName, osRemote));
             FTerminal->LogEvent(AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
               " transfer mode selected.");
 
@@ -2311,7 +2333,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
         SUSPEND_OPERATION (
           TQueryParams Params(qpAllowContinueOnError);
           if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (ErrorFileName)),
-            E.Message, qaOK | qaAbort, &Params) == qaAbort)
+            E.Message, qaOK | qaAbort, &Params, qtError) == qaAbort)
           {
             OperationProgress->Cancel = csCancel;
           }

+ 3 - 1
core/ScpFileSystem.h

@@ -50,6 +50,7 @@ public:
   virtual void __fastcall CopyFile(const AnsiString FileName,
     const AnsiString NewName);
   virtual AnsiString __fastcall FileUrl(const AnsiString FileName);
+  virtual TStrings * __fastcall GetFixedPaths();
 
   static bool __fastcall RemoveLastLine(AnsiString & Line,
     int & ReturnCode, AnsiString LastLine = "");
@@ -91,7 +92,8 @@ private:
   void __fastcall SCPError(const AnsiString Message, bool Fatal);
   void __fastcall SCPSendError(const AnsiString Message, bool Fatal);
   void __fastcall SCPSink(const AnsiString TargetDir,
-    const AnsiString FileName, const TCopyParamType * CopyParam, bool & Success,
+    const AnsiString FileName, const AnsiString SourceDir,
+    const TCopyParamType * CopyParam, bool & Success,
     TFileOperationProgressType * OperationProgress, int Params, int Level);
   void __fastcall SCPSource(const AnsiString FileName,
     const TCopyParamType * CopyParam, int Params,

+ 2 - 1
core/ScpMain.cpp

@@ -61,7 +61,7 @@ void __fastcall TCallExceptionClass::QueryUser(TObject* Sender, const AnsiString
 }
 #endif
 //---------------------------------------------------------------------------
-TQueryParams::TQueryParams(unsigned int AParams)
+TQueryParams::TQueryParams(unsigned int AParams, AnsiString AHelpKeyword)
 {
   Params = AParams;
   Aliases = NULL;
@@ -69,6 +69,7 @@ TQueryParams::TQueryParams(unsigned int AParams)
   Timer = NULL;
   TimerEvent = NULL;
   TimerAnswers = 0;
+  HelpKeyword = AHelpKeyword;
 }
 //---------------------------------------------------------------------------
 void Initialize(const AnsiString IniFileName)

+ 138 - 24
core/Script.cpp

@@ -10,12 +10,15 @@
 #include "Terminal.h"
 #include "SessionData.h"
 #include "ScpMain.h"
+#include "ScpFileSystem.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
-__fastcall TScriptProcParams::TScriptProcParams(TStrings * Params)
+__fastcall TScriptProcParams::TScriptProcParams(TStrings * Params,
+  const AnsiString & ParamsStr)
 {
   FParams = Params;
+  FParamsStr = ParamsStr;
   FSkipParams = 0;
   FArg = 0;
 }
@@ -45,7 +48,7 @@ public:
   __fastcall TScriptCommands();
 
   void __fastcall Execute(TScriptProcParams * Parameters);
-  void __fastcall Execute(TStrings * Tokens);
+  void __fastcall Execute(TStrings * Tokens, AnsiString Params);
 
   void __fastcall Register(const char * Command,
     const AnsiString Description, const AnsiString Help, TCommandProc Proc,
@@ -268,9 +271,9 @@ void __fastcall TScriptCommands::Execute(TScriptProcParams * Parameters)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TScriptCommands::Execute(TStrings * Tokens)
+void __fastcall TScriptCommands::Execute(TStrings * Tokens, AnsiString Params)
 {
-  TScriptProcParams * Parameters = new TScriptProcParams(Tokens);
+  TScriptProcParams * Parameters = new TScriptProcParams(Tokens, Params);
   try
   {
     Execute(Parameters);
@@ -314,8 +317,12 @@ void __fastcall TScript::Init()
   FLastPrintedLineTime = 0;
 
   FCommands = new TScriptCommands;
+  FCommands->Register(";", 0, 0, &DummyProc, 0, -1);
+  FCommands->Register("#", 0, 0, &DummyProc, 0, -1);
   FCommands->Register("help", SCRIPT_HELP_DESC, SCRIPT_HELP_HELP, &HelpProc, 0, -1);
   FCommands->Register("man", 0, SCRIPT_HELP_HELP, &HelpProc, 0, -1);
+  FCommands->Register("call", SCRIPT_CALL_DESC, SCRIPT_CALL_HELP, &CallProc, 1, -1);
+  FCommands->Register("!", 0, SCRIPT_CALL_HELP, &CallProc, 1, -1);
   FCommands->Register("pwd", SCRIPT_PWD_DESC, SCRIPT_PWD_HELP, &PwdProc, 0, 0);
   FCommands->Register("cd", SCRIPT_CD_DESC, SCRIPT_CD_HELP, &CdProc, 0, 1);
   FCommands->Register("ls", SCRIPT_LS_DESC, SCRIPT_LS_HELP, &LsProc, 0, 1);
@@ -356,10 +363,11 @@ void __fastcall TScript::Command(const AnsiString Cmd)
     TStrings * Tokens = new TStringList();
     try
     {
-      Tokenize(Cmd, Tokens);
+      AnsiString AllButFirst;
+      Tokenize(Cmd, Tokens, AllButFirst);
       if (Tokens->Count > 0)
       {
-        FCommands->Execute(Tokens);
+        FCommands->Execute(Tokens, AllButFirst);
       }
     }
     __finally
@@ -376,8 +384,10 @@ void __fastcall TScript::Command(const AnsiString Cmd)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TScript::Tokenize(const AnsiString Str, TStrings * Tokens)
+void __fastcall TScript::Tokenize(const AnsiString Str, TStrings * Tokens,
+  AnsiString & AllButFirst)
 {
+  assert(Tokens->Count == 0);
   // inspired by Putty's sftp_getcmd() from PSFTP.C
   int Index = 1;
   while (Index <= Str.Length())
@@ -388,6 +398,11 @@ void __fastcall TScript::Tokenize(const AnsiString Str, TStrings * Tokens)
       Index++;
     }
 
+    if (Tokens->Count == 1)
+    {
+      AllButFirst = Str.SubString(Index, Str.Length() - Index + 1);
+    }
+
     if (Index <= Str.Length())
     {
       bool Quoting = false;
@@ -589,6 +604,12 @@ void __fastcall TScript::FreeFileList(TStrings * FileList)
   delete FileList;
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::ConnectTerminal(TTerminal * Terminal)
+{
+  Terminal->Open();
+  Terminal->DoStartup();
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::Print(const AnsiString Str)
 {
   if (FOnPrint != NULL)
@@ -604,13 +625,18 @@ void __fastcall TScript::PrintLine(const AnsiString Str)
   Print(Str + "\n");
 }
 //---------------------------------------------------------------------------
-bool __fastcall TScript::HandleExtendedException(Exception * E)
+bool __fastcall TScript::HandleExtendedException(Exception * E, TTerminal * Terminal)
 {
   bool Result = (OnShowExtendedException != NULL);
 
   if (Result)
   {
-    OnShowExtendedException(FTerminal, E);
+    if (Terminal == NULL)
+    {
+      Terminal = FTerminal;
+    }
+
+    OnShowExtendedException(Terminal, E, NULL);
   }
 
   return Result;
@@ -628,6 +654,27 @@ void __fastcall TScript::ResetTransfer()
 {
 }
 //---------------------------------------------------------------------------
+bool __fastcall TScript::EnsureCommandSessionFallback(TFSCapability Capability)
+{
+  bool Result = FTerminal->IsCapable[Capability] ||
+    FTerminal->CommandSessionOpened;
+
+  if (!Result)
+  {
+    try
+    {
+      ConnectTerminal(FTerminal->CommandSession);
+      Result = true;
+    }
+    catch(Exception & E)
+    {
+      HandleExtendedException(&E, FTerminal->CommandSession);
+      Result = false;
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::SecondaryProc(TScriptProcParams * Parameters)
 {
   TScriptCommands * Commands = static_cast<TScriptCommands *>(Parameters->Arg);
@@ -644,6 +691,11 @@ void __fastcall TScript::SecondaryProc(TScriptProcParams * Parameters)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::DummyProc(TScriptProcParams * /*Parameters*/)
+{
+  // noop
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::HelpProc(TScriptProcParams * Parameters)
 {
   AnsiString Output;
@@ -681,6 +733,40 @@ void __fastcall TScript::HelpProc(TScriptProcParams * Parameters)
   Print(Output);
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::CallProc(TScriptProcParams * Parameters)
+{
+  CheckSession();
+
+  if (EnsureCommandSessionFallback(fcAnyCommand))
+  {
+    assert(FTerminal->Log->OnAddLine == NULL);
+    FTerminal->Log->OnAddLine = TerminalCaptureLog;
+    try
+    {
+      FTerminal->AnyCommand(Parameters->ParamsStr);
+    }
+    __finally
+    {
+      FTerminal->Log->OnAddLine = NULL;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TScript::TerminalCaptureLog(TObject* /*Sender*/,
+  TLogLineType Type, const AnsiString AddedLine)
+{
+  if ((Type == llOutput) || (Type == llStdError))
+  {
+    AnsiString Line = AddedLine;
+    int ReturnCode;
+    if (!TSCPFileSystem::RemoveLastLine(Line, ReturnCode) ||
+        !Line.IsEmpty())
+    {
+      PrintLine(Line);
+    }
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::PwdProc(TScriptProcParams * /*Parameters*/)
 {
   CheckSession();
@@ -1087,7 +1173,7 @@ void __fastcall TScript::SynchronizeProc(TScriptProcParams * Parameters)
     FTerminal->Synchronize(LocalDirectory, RemoteDirectory,
       static_cast<TTerminal::TSynchronizeMode>(FSynchronizeMode),
       &CopyParam, FSynchronizeParams | TTerminal::spNoConfirmation,
-      OnTerminalSynchronizeDirectory);
+      OnTerminalSynchronizeDirectory, NULL);
   }
   __finally
   {
@@ -1096,20 +1182,20 @@ void __fastcall TScript::SynchronizeProc(TScriptProcParams * Parameters)
 }
 //---------------------------------------------------------------------------
 void __fastcall TScript::Synchronize(const AnsiString LocalDirectory,
-  const AnsiString RemoteDirectory)
+  const AnsiString RemoteDirectory, const TCopyParamType & ACopyParam,
+  TSynchronizeStats * /*Stats*/)
 {
   try
   {
     FKeepingUpToDate = true;
 
-    TCopyParamType CopyParam = FCopyParam;
+    TCopyParamType CopyParam = ACopyParam;
     CopyParam.CalculateSize = false;
-    CopyParam.PreserveTime = true;
 
     FTerminal->Synchronize(LocalDirectory, RemoteDirectory, TTerminal::smRemote, &CopyParam,
       FSynchronizeParams | TTerminal::spNoConfirmation | TTerminal::spNoRecurse |
-      TTerminal::spUseCache | TTerminal::spDelayProgress,
-      OnTerminalSynchronizeDirectory);
+      TTerminal::spUseCache | TTerminal::spDelayProgress | TTerminal::spSubDirs,
+      OnTerminalSynchronizeDirectory, NULL);
 
     // to break line after the last transfer (if any); 
     Print("");    
@@ -1260,7 +1346,7 @@ bool __fastcall TManagementScript::QueryCancel()
 }
 //---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalOnStdError(TObject * Sender,
-  const AnsiString AddedLine)
+  TLogLineType /*Type*/, const AnsiString AddedLine)
 {
   TTerminal * Terminal = dynamic_cast<TTerminal*>(Sender);
   assert(Terminal != NULL);
@@ -1270,6 +1356,16 @@ void __fastcall TManagementScript::TerminalOnStdError(TObject * Sender,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TManagementScript::TerminalPromptUser(TSecureShell * SecureShell,
+  AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result,
+  void * Arg)
+{
+  if (!SecureShell->StoredPasswordTried && (OnTerminalPromptUser != NULL))
+  {
+    OnTerminalPromptUser(SecureShell, Prompt, Kind, Response, Result, Arg);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalOperationProgress(
   TFileOperationProgressType & ProgressData, TCancelStatus & Cancel)
 {
@@ -1322,7 +1418,7 @@ void __fastcall TManagementScript::TerminalOperationProgress(
 //---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalOperationFinished(
   TFileOperation Operation, TOperationSide /*Side*/,
-  bool /*DragDrop*/, const AnsiString FileName, Boolean Success,
+  bool /*Temp*/, const AnsiString FileName, Boolean Success,
   bool & /*DisconnectWhenComplete*/)
 {
   if (Success && (Operation != foCalculateSize) && (Operation != foCopy))
@@ -1400,15 +1496,21 @@ void __fastcall TManagementScript::PrintActiveSession()
     (FTerminalList->IndexOf(FTerminal) + 1, FTerminal->SessionData->SessionName)));
 }
 //---------------------------------------------------------------------------
-bool __fastcall TManagementScript::HandleExtendedException(Exception * E)
+bool __fastcall TManagementScript::HandleExtendedException(Exception * E,
+  TTerminal * Terminal)
 {
   bool Result = TScript::HandleExtendedException(E);
 
-  if ((FTerminal != NULL) && (dynamic_cast<EFatal*>(E) != NULL))
+  if (Terminal == NULL)
+  {
+    Terminal = FTerminal;
+  }
+
+  if ((Terminal != NULL) && (Terminal == FTerminal) && (dynamic_cast<EFatal*>(E) != NULL))
   {
     try
     {
-      DoClose(FTerminal);
+      DoClose(Terminal);
     }
     catch(...)
     {
@@ -1468,16 +1570,14 @@ void __fastcall TManagementScript::DoConnect(const AnsiString Session)
     {
       Terminal->AutoReadDirectory = false;
 
-      Terminal->OnUpdateStatus = OnTerminalUpdateStatus;
       Terminal->OnStdError = TerminalOnStdError;
-      Terminal->OnPromptUser = OnTerminalPromptUser;
+      Terminal->OnPromptUser = TerminalPromptUser;
       Terminal->OnShowExtendedException = OnShowExtendedException;
       Terminal->OnQueryUser = OnTerminalQueryUser;
       Terminal->OnProgress = TerminalOperationProgress;
       Terminal->OnFinished = TerminalOperationFinished;
 
-      Terminal->Open();
-      Terminal->DoStartup();
+      ConnectTerminal(Terminal);
     }
     catch(...)
     {
@@ -1495,6 +1595,20 @@ void __fastcall TManagementScript::DoConnect(const AnsiString Session)
   PrintActiveSession();
 }
 //---------------------------------------------------------------------------
+void __fastcall TManagementScript::ConnectTerminal(TTerminal * Terminal)
+{
+  Terminal->OnUpdateStatus = OnTerminalUpdateStatus;
+
+  try
+  {
+    TScript::ConnectTerminal(Terminal);
+  }
+  __finally
+  {
+    Terminal->OnUpdateStatus = NULL;
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TManagementScript::DoClose(TTerminal * Terminal)
 {
   int Index = FTerminalList->IndexOf(Terminal);

+ 25 - 8
core/Script.h

@@ -19,17 +19,19 @@ typedef void __fastcall (__closure *TScriptSynchronizeStartStop)(TScript * Scrip
 class TScriptProcParams
 {
 public:
-  __fastcall TScriptProcParams(TStrings * Params);
+  __fastcall TScriptProcParams(TStrings * Params, const AnsiString & ParamsStr);
   void __fastcall SkipParam();
 
   __property AnsiString Param[int Index] = { read = GetParam };
   __property int ParamCount = { read = GetParamCount };
   __property void * Arg = { read = FArg, write = FArg };
+  __property AnsiString ParamsStr = { read = FParamsStr };
 
 private:
   TStrings * FParams;
   int FSkipParams;
   void * FArg;
+  AnsiString FParamsStr;
 
   AnsiString __fastcall GetParam(int Index);
   int __fastcall GetParamCount();
@@ -45,7 +47,8 @@ public:
   void __fastcall Command(const AnsiString Cmd);
 
   void __fastcall Synchronize(const AnsiString LocalDirectory,
-    const AnsiString RemoteDirectory);
+    const AnsiString RemoteDirectory, const TCopyParamType & CopyParam,
+    TSynchronizeStats * Stats);
 
   __property TScriptPrintEvent OnPrint = { read = FOnPrint, write = FOnPrint };
   __property TExtendedExceptionEvent OnShowExtendedException = { read = FOnShowExtendedException, write = FOnShowExtendedException };
@@ -73,12 +76,15 @@ protected:
   time_t FLastPrintedLineTime;
 
   virtual void __fastcall ResetTransfer();
+  virtual void __fastcall ConnectTerminal(TTerminal * Terminal);
+  bool __fastcall EnsureCommandSessionFallback(TFSCapability Capability);
   void __fastcall Print(const AnsiString Str);
   void __fastcall PrintLine(const AnsiString Str);
-  void __fastcall Tokenize(const AnsiString Str, TStrings * Tokens);
+  void __fastcall Tokenize(const AnsiString Str, TStrings * Tokens,
+    AnsiString & AllButFirst);
   void __fastcall CheckSession();
   enum TFileListType
-  { 
+  {
     fltDefault =     0x00,
     fltDirectories = 0x01,
     fltQueryServer = 0x02,
@@ -91,7 +97,9 @@ protected:
   void __fastcall FreeFileList(TStrings * FileList);
 
   void __fastcall SecondaryProc(TScriptProcParams * Parameters);
+  void __fastcall DummyProc(TScriptProcParams * Parameters);
   void __fastcall HelpProc(TScriptProcParams * Parameters);
+  void __fastcall CallProc(TScriptProcParams * Parameters);
   void __fastcall PwdProc(TScriptProcParams * Parameters);
   void __fastcall CdProc(TScriptProcParams * Parameters);
   void __fastcall LsProc(TScriptProcParams * Parameters);
@@ -112,7 +120,10 @@ protected:
   void __fastcall OptionImpl(AnsiString OptionName, AnsiString ValueName);
   void __fastcall SynchronizeDirectories(TScriptProcParams * Parameters,
     AnsiString & LocalDirectory, AnsiString & RemoteDirectory, int FirstParam);
-  virtual bool __fastcall HandleExtendedException(Exception * E);
+  virtual bool __fastcall HandleExtendedException(Exception * E,
+    TTerminal * Terminal = NULL);
+  void __fastcall TerminalCaptureLog(TObject * Sender, TLogLineType Type,
+    const AnsiString AddedLine);
 
 private:
   void __fastcall Init();
@@ -155,12 +166,14 @@ protected:
   bool FContinue;
 
   virtual void __fastcall ResetTransfer();
+  virtual void __fastcall ConnectTerminal(TTerminal * Terminal);
   void __fastcall Input(const AnsiString Prompt, AnsiString & Str, bool AllowEmpty);
-  void __fastcall TerminalOnStdError(TObject * Sender, const AnsiString AddedLine);
+  void __fastcall TerminalOnStdError(TObject * Sender, TLogLineType Type,
+    const AnsiString AddedLine);
   void __fastcall TerminalOperationProgress(TFileOperationProgressType & ProgressData,
     TCancelStatus & Cancel);
   void __fastcall TerminalOperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool DragDrop, const AnsiString FileName, Boolean Success,
+    bool Temp, const AnsiString FileName, Boolean Success,
     bool & DisconnectWhenComplete);
 
   void __fastcall PrintActiveSession();
@@ -172,7 +185,11 @@ protected:
     const AnsiString RemoteDirectory, bool & Continue);
   void __fastcall DoConnect(const AnsiString Session);
   void __fastcall DoClose(TTerminal * Terminal);
-  virtual bool __fastcall HandleExtendedException(Exception * E);
+  virtual bool __fastcall HandleExtendedException(Exception * E,
+    TTerminal * Terminal = NULL);
+  void __fastcall TerminalPromptUser(TSecureShell * SecureShell,
+    AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result,
+    void * Arg);
 
   void __fastcall ExitProc(TScriptProcParams * Parameters);
   void __fastcall OpenProc(TScriptProcParams * Parameters);

+ 54 - 55
core/SecureShell.cpp

@@ -9,6 +9,7 @@
 #include "Interface.h"
 #include "SecureShell.h"
 #include "TextsCore.h"
+#include "HelpCore.h"
 #include "Common.h"
 #include "ScpMain.h"
 #include "Security.h"
@@ -67,8 +68,8 @@ void __fastcall TSecureShell::Open()
   const char * InitError;
   char * RealHost;
 
-  FPasswordTried = false;
-  FPasswordTriedForKI = false;
+  FStoredPasswordTried = false;
+  FStoredPasswordTriedForKI = false;
   FReachedStatus = 0;
 
   {
@@ -183,6 +184,11 @@ AnsiString __fastcall TSecureShell::GetPassword()
   return (FPassword.IsEmpty() ? AnsiString() : 
     DecryptPassword(FPassword, SessionData->SessionName));
 }
+//---------------------------------------------------------------------
+bool __fastcall TSecureShell::GetStoredPasswordTried()
+{
+  return FStoredPasswordTried || FStoredPasswordTriedForKI;
+}
 //---------------------------------------------------------------------------
 TDateTime __fastcall TSecureShell::GetIdleInterval()
 {
@@ -193,7 +199,8 @@ TDateTime __fastcall TSecureShell::GetIdleInterval()
 bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
   AnsiString & Response, bool IsPassword)
 {
-  assert(IsPassword);
+  USEDPARAM(IsPassword);
+  assert(IsPassword); // false only for username prompts
 
   bool Result;
   if (Prompt.Pos("Passphrase for key ") == 1)
@@ -219,11 +226,12 @@ bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
   {
     LogEvent(FORMAT("Session password prompt (%s)", (Prompt)));
 
-    if (!SessionData->Password.IsEmpty() && !FPasswordTried)
+    if (!SessionData->Password.IsEmpty() && !FStoredPasswordTried)
     {
       LogEvent("Using stored password.");
       Result = true;
       Response = SessionData->Password;
+      FStoredPasswordTried = true;
     }
     else
     {
@@ -232,20 +240,19 @@ bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
         FMTLOAD(PROMPT_SESSION_PASSWORD, (SessionData->SessionName)),
         pkPassword, Response);
     }
-    FPasswordTried = true;
   }
   else
   {
     // in other cases we assume TIS/Cryptocard/keyboard-interactive authentification prompt
-    LogEvent(FORMAT("%s prompt from server (%s)",
-      (IsPassword ? "Password" : "Normal", Prompt)));
+    LogEvent(FORMAT("%s prompt from server", (Prompt)));
 
-    if (!SessionData->Password.IsEmpty() && IsPassword &&
-        SessionData->AuthKIPassword && !FPasswordTriedForKI)
+    if (!SessionData->Password.IsEmpty() &&
+        SessionData->AuthKIPassword && !FStoredPasswordTriedForKI)
     {
       LogEvent("Responding with stored password.");
       Result = true;
       Response = SessionData->Password;
+      FStoredPasswordTriedForKI = true;
     }
     else
     {
@@ -263,7 +270,6 @@ bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
 
       Result = DoPromptUser(UserPrompt, pkServerPrompt, Response);
     }
-    FPasswordTriedForKI = true;
   };
 
   if (Configuration->RememberPassword)
@@ -280,7 +286,7 @@ bool __fastcall TSecureShell::DoPromptUser(AnsiString Prompt, TPromptKind Kind,
   bool Result = false;
   if (OnPromptUser != NULL)
   {
-    OnPromptUser(this, Prompt, Kind, Response, Result);
+    OnPromptUser(this, Prompt, Kind, Response, Result, NULL);
   }
   return Result;
 }
@@ -523,7 +529,7 @@ void __fastcall TSecureShell::AddStdErrorLine(const AnsiString Str)
   }
   else
   {
-    OnStdError(this, Str);
+    OnStdError(this, llStdError, Str);
   }
   Log->Add(llStdError, Str);
 }
@@ -566,7 +572,10 @@ void __fastcall TSecureShell::FatalError(AnsiString Error)
 void __fastcall TSecureShell::SetSocket(void * value)
 {
   assert(value);
-  if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET))
+  // now this can be called repeatedly, so allow being called again with
+  // the same socket
+  if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET) &&
+      (*static_cast<SOCKET*>(FSocket) != *static_cast<SOCKET*>(value)))
     FatalError("Cannot set socket during connection");
   assert(FSocket);
   *static_cast<SOCKET*>(FSocket) = *static_cast<SOCKET*>(value);
@@ -974,7 +983,7 @@ void __fastcall TSecureShell::DoShowExtendedException(Exception * E)
   DoHandleExtendedException(E);
   if (OnShowExtendedException != NULL)
   {
-    OnShowExtendedException(this, E);
+    OnShowExtendedException(this, E, NULL);
   }
 }
 //---------------------------------------------------------------------------
@@ -990,19 +999,20 @@ int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
   int Answer = qaCancel;
   if (FOnQueryUser)
   {
-    FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type);
+    FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type, NULL);
   }
   return Answer;
 }
 //---------------------------------------------------------------------------
 int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
-  const AnsiString OtherMessage, int Answers, const TQueryParams * Params)
+  const AnsiString OtherMessage, int Answers, const TQueryParams * Params,
+  TQueryType Type)
 {
   TStrings * MoreMessages = new TStringList();
   Integer Result;
   try {
     if (!OtherMessage.IsEmpty()) MoreMessages->Add(OtherMessage);
-    Result = DoQueryUser(Query, MoreMessages, Answers, Params);
+    Result = DoQueryUser(Query, MoreMessages, Answers, Params, Type);
   } __finally {
     delete MoreMessages;
   }
@@ -1010,13 +1020,13 @@ int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
 }
 //---------------------------------------------------------------------------
 int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
-  int Answers, const TQueryParams * Params)
+  int Answers, const TQueryParams * Params, TQueryType Type)
 {
-  return DoQueryUser(Query, "", Answers, Params);
-}
+  return DoQueryUser(Query, "", Answers, Params, Type);
+} 
 //---------------------------------------------------------------------------
 int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
-  Exception * E, int Answers, const TQueryParams * Params)
+  Exception * E, int Answers, const TQueryParams * Params, TQueryType Type)
 {
   int Result;
   TStrings * MoreMessages = new TStringList();
@@ -1030,7 +1040,7 @@ int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
     if (!E->Message.IsEmpty() && !Query.IsEmpty()) MoreMessages->Insert(0, E->Message);
     Result = DoQueryUser(!Query.IsEmpty() ? Query : E->Message,
       MoreMessages->Count ? MoreMessages : NULL,
-      Answers, Params);
+      Answers, Params, Type);
   }
   __finally
   {
@@ -1053,9 +1063,11 @@ void __fastcall TSecureShell::VerifyHostKey(const AnsiString Host, int Port,
   
   if (Result != 0)
   {
+    TQueryParams Params;
+    Params.HelpKeyword = (Result == 1 ? HELP_UNKNOWN_KEY : HELP_DIFFERENT_KEY);
     int R = DoQueryUser(
-      FMTLOAD((Result == 1 ? UNKNOWN_KEY : DIFFERENT_KEY), (Fingerprint)),
-      NULL, qaYes | qaNo | qaCancel, 0, qtWarning);
+      FMTLOAD((Result == 1 ? UNKNOWN_KEY2 : DIFFERENT_KEY2), (KeyType, Fingerprint)),
+      qaYes | qaNo | qaCancel, &Params, qtWarning);
 
     switch (R) {
       case qaYes:
@@ -1099,7 +1111,7 @@ void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
     Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD, (LoadStr(CipherType), AlgName));
   }
 
-  if (DoQueryUser(Msg, NULL, qaYes | qaNo, 0, qtWarning) == qaNo)
+  if (DoQueryUser(Msg, qaYes | qaNo, NULL, qtWarning) == qaNo)
   {
     Abort();
   }
@@ -1107,7 +1119,7 @@ void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::OldKeyfileWarning()
 {
-  DoQueryUser(LoadStr(OLD_KEY), NULL, qaOK, 0, qtWarning);
+  DoQueryUser(LoadStr(OLD_KEY), qaOK, NULL, qtWarning);
 }
 //---------------------------------------------------------------------------
 int __fastcall TSecureShell::GetStatus() const
@@ -1190,7 +1202,7 @@ TLogLineType __fastcall TSessionLog::GetType(Integer Index)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSessionLog::DoAdd(bool Formatted, TLogLineType aType, AnsiString aLine)
+void __fastcall TSessionLog::DoAdd(TLogLineType aType, AnsiString aLine)
 {
   assert(Configuration);
   if (IsLogging())
@@ -1206,38 +1218,25 @@ void __fastcall TSessionLog::DoAdd(bool Formatted, TLogLineType aType, AnsiStrin
       {
         while (!aLine.IsEmpty())
         {
-          AnsiString NewStr;
-          AnsiString FileStr;
-
-          static int TimestampLen = 25;
-          if (Formatted)
-          {
-            assert(aLine.Length() >= 2);
-            FileStr = aLine;
-            NewStr = aLine;
-            NewStr.Delete(2, TimestampLen);
-            aLine = "";
-          }
-          else
-          {
-            NewStr = AnsiString(LogLineMarks[aType]) + CutToChar(aLine, '\n', False);
-            FileStr = NewStr;
-            AnsiString Timestamp = FormatDateTime(" yyyy-mm-dd hh:nn:ss.zzz ", Now());
-            assert(Timestamp.Length() == TimestampLen);
-            FileStr.Insert(Timestamp, 2);
-          }
+          AnsiString MarkStr = LogLineMarks[aType];
+          AnsiString NewStr = CutToChar(aLine, '\n', False);
 
           if (Configuration->Logging)
           {
-            TStringList::Add(NewStr);
+            TStringList::Add(MarkStr + NewStr);
             FLoggedLines++;
           }
 
-          DoAddLine(FileStr);
+          DoAddLine(aType, NewStr);
           if (Configuration->Logging && Configuration->LogToFile)
           {
             if (!FFile) OpenLogFile();
-            if (FFile) fprintf((FILE *)FFile, "%s\n", FileStr.c_str());
+            if (FFile) 
+            {
+              AnsiString Timestamp = FormatDateTime(" yyyy-mm-dd hh:nn:ss.zzz ", Now());
+              fprintf((FILE *)FFile, "%s\n",
+                (MarkStr + Timestamp + NewStr).c_str());
+            }
           }
         }
         if (Configuration->Logging)
@@ -1272,13 +1271,13 @@ void __fastcall TSessionLog::DoAdd(bool Formatted, TLogLineType aType, AnsiStrin
 //---------------------------------------------------------------------------
 void __fastcall TSessionLog::Add(TLogLineType aType, AnsiString aLine)
 {
-  DoAdd(false, aType, aLine);
+  DoAdd(aType, aLine);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSessionLog::AddFromOtherLog(TObject * /*Sender*/,
-  const AnsiString AddedLine)
+  TLogLineType aType, const AnsiString AddedLine)
 {
-  DoAdd(true, llMessage, AddedLine);
+  DoAdd(aType, AddedLine);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSessionLog::AddException(Exception * E)
@@ -1403,11 +1402,11 @@ TColor __fastcall TSessionLog::GetColor(int Index)
   return LogLineColors[Type[Index]];
 }
 //---------------------------------------------------------------------------
-void __fastcall TSessionLog::DoAddLine(const AnsiString AddedLine)
+void __fastcall TSessionLog::DoAddLine(TLogLineType Type, const AnsiString AddedLine)
 {
   if (FOnAddLine)
   {
-    FOnAddLine(this, AddedLine);
+    FOnAddLine(this, Type, AddedLine);
   }
 }
 //---------------------------------------------------------------------------

+ 15 - 13
core/SecureShell.h

@@ -27,15 +27,13 @@ enum TCompressionType { ctNone, ctZLib };
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TQueryUserEvent)
   (TObject* Sender, const AnsiString Query, TStrings * MoreMessages, int Answers,
-   const TQueryParams * Params, int & Answer, TQueryType QueryType);
+   const TQueryParams * Params, int & Answer, TQueryType QueryType, void * Arg);
 typedef void __fastcall (__closure *TPromptUserEvent)
   (TSecureShell * SecureShell, AnsiString Prompt, TPromptKind Kind,
-   AnsiString & Response, bool & Result);
+   AnsiString & Response, bool & Result, void * Arg);
 typedef void __fastcall (__closure *TExtendedExceptionEvent)
-  (TSecureShell * SecureShell, Exception * E);
+  (TSecureShell * SecureShell, Exception * E, void * Arg);
 //---------------------------------------------------------------------------
-// Duplicated in LogMemo.h for design-time-only purposes
-enum TLogLineType {llOutput, llInput, llStdError, llMessage, llException};
 typedef Set<TLogLineType, llOutput, llException> TLogLineTypes;
 extern const TColor LogLineColors[];
 //---------------------------------------------------------------------------
@@ -58,7 +56,7 @@ private:
   void DeleteUnnecessary();
   void OpenLogFile();
   TColor __fastcall GetColor(Integer Index);
-  void __fastcall DoAddLine(const AnsiString AddedLine);
+  void __fastcall DoAddLine(TLogLineType Type, const AnsiString AddedLine);
   Integer __fastcall GetBottomIndex();
   Integer __fastcall GetIndexes(Integer Index);
   AnsiString __fastcall GetLogFileName();
@@ -67,7 +65,7 @@ private:
   void __fastcall SetEnabled(bool value);
   void __fastcall SetConfiguration(TConfiguration * value);
   AnsiString __fastcall GetSessionName();
-  void __fastcall DoAdd(bool Formatted, TLogLineType aType, AnsiString aLine);
+  void __fastcall DoAdd(TLogLineType aType, AnsiString aLine);
 
 public:
   __fastcall TSessionLog(TSecureShell * AOwner);
@@ -76,7 +74,8 @@ public:
   void __fastcall AddStartupInfo();
   void __fastcall AddException(Exception * E);
   void __fastcall AddSeparator();
-  void __fastcall AddFromOtherLog(TObject * Sender, const AnsiString AddedLine);
+  void __fastcall AddFromOtherLog(TObject * Sender, TLogLineType aType, 
+    const AnsiString AddedLine);
   virtual void __fastcall Clear();
   void __fastcall ReflectSettings();
   bool __fastcall inline IsLogging()
@@ -112,8 +111,8 @@ struct Config;
 class TSecureShell : public TObject
 {
 private:
-  bool FPasswordTried;
-  bool FPasswordTriedForKI;
+  bool FStoredPasswordTried;
+  bool FStoredPasswordTriedForKI;
   void * FSocket;
   TSessionData * FSessionData;
   bool FActive;
@@ -177,6 +176,7 @@ private:
   bool __fastcall Select(int Sec);
   void __fastcall PoolForData(unsigned int & Result);
   TDateTime __fastcall GetIdleInterval();
+  bool __fastcall GetStoredPasswordTried();
 
 protected:
   AnsiString StdError;
@@ -221,10 +221,11 @@ public:
   virtual int __fastcall DoQueryUser(const AnsiString Query, TStrings * MoreMessages,
     int Answers, const TQueryParams * Params, TQueryType Type = qtConfirmation);
   int __fastcall DoQueryUser(const AnsiString Query, const AnsiString OtherMessage,
-    int Answers, const TQueryParams * Params);
-  int __fastcall DoQueryUser(const AnsiString Query, int Answers, const TQueryParams * Params);
+    int Answers, const TQueryParams * Params, TQueryType Type);
+  int __fastcall DoQueryUser(const AnsiString Query, int Answers,
+    const TQueryParams * Params, TQueryType Type = qtConfirmation);
   int __fastcall DoQueryUser(const AnsiString Query, Exception * E,
-    int Answers, const TQueryParams * Params);
+    int Answers, const TQueryParams * Params, TQueryType Type);
   virtual void __fastcall DoShowExtendedException(Exception * E);
   void __fastcall DoHandleExtendedException(Exception * E);
   virtual bool __fastcall DoPromptUser(AnsiString Prompt, TPromptKind Kind,
@@ -267,6 +268,7 @@ public:
   __property TObject * UserObject = { read = FUserObject, write = SetUserObject };
   __property AnsiString Password = { read = GetPassword };
   __property TDateTime IdleInterval = { read = GetIdleInterval };
+  __property bool StoredPasswordTried = { read = GetStoredPasswordTried };
 };
 //---------------------------------------------------------------------------
 #endif

+ 62 - 29
core/SessionData.cpp

@@ -27,6 +27,8 @@ const TKex DefaultKexList[KEX_COUNT] =
   { kexDHGEx, kexDHGroup14, kexDHGroup1, kexWarn };
 const char FSProtocolNames[FSPROTOCOL_COUNT][11] = { "SCP", "SFTP (SCP)", "SFTP" };
 //--- TSessionData ----------------------------------------------------
+AnsiString TSessionData::FInvalidChars("/\\[]");
+//---------------------------------------------------------------------
 __fastcall TSessionData::TSessionData(AnsiString aName):
   TNamedObject(aName)
 {
@@ -329,27 +331,19 @@ void __fastcall TSessionData::StoreToConfig(void * config)
     {
       ASCOPY(cfg->remote_cmd, Shell);
     }
-    cfg->remote_cmd_ptr = &cfg->remote_cmd[0];
-    cfg->remote_cmd_ptr2 = NULL; // no second attempt for SCPonly
   }
   else
   {
     cfg->ssh_subsys = TRUE;
     strcpy(cfg->remote_cmd, "sftp");
-    cfg->remote_cmd_ptr = &cfg->remote_cmd[0];
 
     if (FSProtocol != fsSFTPonly)
     {
       cfg->ssh_subsys2 = FALSE;
-      if (Shell.IsEmpty())
-      {
-        cfg->remote_cmd2[0] = '\0';
-      }
-      else
+      if (!Shell.IsEmpty())
       {
         ASCOPY(cfg->remote_cmd2, Shell);
       }
-      cfg->remote_cmd_ptr2 = &cfg->remote_cmd2[0];
     }
     else
     {
@@ -469,7 +463,16 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     ProxyHost = Storage->ReadString("ProxyHost", ProxyHost);
     ProxyPort = Storage->ReadInteger("ProxyPort", ProxyPort);
     ProxyUsername = Storage->ReadString("ProxyUsername", ProxyUsername);
-    FProxyPassword = Storage->ReadString("ProxyPassword", FProxyPassword);
+    if (Storage->ValueExists("ProxyPassword"))
+    {
+      // encrypt unencrypted password
+      ProxyPassword = Storage->ReadString("ProxyPassword", "");
+    }
+    else
+    {
+      // load encrypted password
+      FProxyPassword = Storage->ReadString("ProxyPasswordEnc", FProxyPassword);
+    }
     ProxyTelnetCommand = Storage->ReadStringRaw("ProxyTelnetCommand", ProxyTelnetCommand);
     ProxyDNS = TAutoSwitch((Storage->ReadInteger("ProxyDNS", (ProxyDNS + 2) % 3) + 1) % 3);
     ProxyLocalhost = Storage->ReadBool("ProxyLocalhost", ProxyLocalhost);
@@ -640,7 +643,17 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
     WRITE_DATA(String, ProxyHost);
     WRITE_DATA(Integer, ProxyPort);
     WRITE_DATA(String, ProxyUsername);
-    WRITE_DATA_EX(String, "ProxyPassword", FProxyPassword, );
+    if (PuttyExport)
+    {
+      // save password unencrypted
+      WRITE_DATA(String, ProxyPassword);
+    }
+    else
+    {
+      // save password encrypted
+      WRITE_DATA_EX(String, "ProxyPasswordEnc", FProxyPassword, );
+      Storage->DeleteValue("ProxyPassword");
+    }
     WRITE_DATA(StringRaw, ProxyTelnetCommand);
     #define WRITE_DATA_CONV_FUNC(X) (((X) + 2) % 3)
     WRITE_DATA_CONV(Integer, "ProxyDNS", ProxyDNS);
@@ -823,6 +836,14 @@ bool __fastcall TSessionData::ParseUrl(AnsiString Url, int Params,
   return Result;
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::ValidateName(const AnsiString Name)
+{
+  if (Name.LastDelimiter(FInvalidChars) > 0)
+  {
+    throw Exception(FMTLOAD(ITEM_NAME_INVALID, (Name, FInvalidChars)));
+  }
+}
+//---------------------------------------------------------------------
 bool __fastcall TSessionData::GetCanLogin()
 {
   return !FHostName.IsEmpty() && !FUserName.IsEmpty();
@@ -1323,13 +1344,13 @@ void __fastcall TSessionData::SetProxyUsername(AnsiString value)
 //---------------------------------------------------------------------
 void __fastcall TSessionData::SetProxyPassword(AnsiString value)
 {
-  // proxy password unencrypted to maintain compatibility with Putty
+  value = EncryptPassword(value, ProxyUsername+ProxyHost);
   SET_SESSION_PROPERTY(ProxyPassword);
 }
 //---------------------------------------------------------------------
-AnsiString __fastcall TSessionData::GetProxyPassword()
+AnsiString __fastcall TSessionData::GetProxyPassword() const
 {
-  return FProxyPassword;
+  return DecryptPassword(FProxyPassword, ProxyUsername+ProxyHost);
 }
 //---------------------------------------------------------------------
 void __fastcall TSessionData::SetProxyTelnetCommand(AnsiString value)
@@ -1442,25 +1463,37 @@ void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage,
     {
       TSessionData *SessionData;
       AnsiString SessionName = UnMungeStr(SubKeys->Strings[Index]);
-      if (SessionName == FDefaultSettings->Name) SessionData = FDefaultSettings;
-        else SessionData = (TSessionData*)FindByName(SessionName);
-
-      if ((SessionData != FDefaultSettings) || !UseDefaults)
+      bool ValidName = true;
+      try
+      {
+        TSessionData::ValidateName(SessionName);
+      }
+      catch(...)
+      {
+        ValidName = false;
+      }
+      if (ValidName)
       {
-        if (!SessionData)
+        if (SessionName == FDefaultSettings->Name) SessionData = FDefaultSettings;
+          else SessionData = (TSessionData*)FindByName(SessionName);
+
+        if ((SessionData != FDefaultSettings) || !UseDefaults)
         {
-          SessionData = new TSessionData("");
-          if (UseDefaults)
+          if (!SessionData)
+          {
+            SessionData = new TSessionData("");
+            if (UseDefaults)
+            {
+              SessionData->Assign(DefaultSettings);
+            }
+            SessionData->Name = SessionName;
+            Add(SessionData);
+          }
+          SessionData->Load(Storage);
+          if (AsModified)
           {
-            SessionData->Assign(DefaultSettings);
+            SessionData->Modified = true;
           }
-          SessionData->Name = SessionName;
-          Add(SessionData);
-        }
-        SessionData->Load(Storage);
-        if (AsModified)
-        {
-          SessionData->Modified = true;
         }
       }
     }

+ 3 - 1
core/SessionData.h

@@ -43,6 +43,7 @@ extern const char FSProtocolNames[FSPROTOCOL_COUNT][11];
 class TSessionData : public TNamedObject
 {
 private:
+  static AnsiString FInvalidChars;
   AnsiString FHostName;
   int FPortNumber;
   AnsiString FUserName;
@@ -180,7 +181,7 @@ private:
   void __fastcall SetProxyTelnetCommand(AnsiString value);
   void __fastcall SetProxyDNS(TAutoSwitch value);
   void __fastcall SetProxyLocalhost(bool value);
-  AnsiString __fastcall GetProxyPassword();
+  AnsiString __fastcall GetProxyPassword() const;
   void __fastcall SetBug(TSshBug Bug, TAutoSwitch value);
   TAutoSwitch __fastcall GetBug(TSshBug Bug) const;
   AnsiString __fastcall GetSessionKey();
@@ -218,6 +219,7 @@ public:
     AnsiString * ConnectInfo, AnsiString * HostName, int * PortNumber,
     AnsiString * UserName, AnsiString * Password, AnsiString * Path,
     AnsiString * FileName);
+  static void __fastcall ValidateName(const AnsiString Name);
 
   __property AnsiString HostName  = { read=FHostName, write=SetHostName };
   __property int PortNumber  = { read=FPortNumber, write=SetPortNumber };

+ 267 - 164
core/SftpFileSystem.cpp

@@ -9,6 +9,7 @@
 #include "Interface.h"
 #include "Terminal.h"
 #include "TextsCore.h"
+#include "HelpCore.h"
 
 #include <memory>
 //---------------------------------------------------------------------------
@@ -119,13 +120,12 @@
 
 #define SFTP_MAX_PACKET_LEN   102400
 //---------------------------------------------------------------------------
-#define SFTP_EXT_WINSCP "[email protected]"
 #define SFTP_EXT_OWNER_GROUP "owner-group-query@generic-extensions"
 #define SFTP_EXT_OWNER_GROUP_REPLY "owner-group-query-reply@generic-extensions"
-#define SFTP_EXT_SOFTWARE "software@generic-extensions"
-#define SFTP_EXT_SOFTWARE_OS "Microsoft Windows"
 #define SFTP_EXT_NEWLINE "newline"
 #define SFTP_EXT_SUPPORTED "supported"
+#define SFTP_EXT_FSROOTS "[email protected]"
+#define SFTP_EXT_VENDOR_ID "vendor-id"
 //---------------------------------------------------------------------------
 #define OGQ_LIST_OWNERS 0x01
 #define OGQ_LIST_GROUPS 0x02
@@ -290,9 +290,10 @@ public:
     }
   }
 
-  inline void AddPathString(const AnsiString Value, int Version, bool Utf)
+  // now purposeless alias to AddString
+  inline void AddPathString(const AnsiString Value, bool Utf)
   {
-    AddString(Value, (Version >= 4) && Utf);
+    AddString(Value, Utf);
   }
 
   void AddProperties(unsigned short * Rights, AnsiString * Owner,
@@ -370,11 +371,13 @@ public:
   void AddProperties(const TRemoteProperties * Properties,
     unsigned short BaseRights, bool IsDirectory, int Version, bool Utf)
   {
-    enum { valNone = 0, valRights = 0x01, valOwner = 0x02, valGroup = 0x04 }
-      Valid = valNone;
+    enum { valNone = 0, valRights = 0x01, valOwner = 0x02, valGroup = 0x04,
+      valMTime = 0x08, valATime = 0x10 } Valid = valNone;
     unsigned short RightsNum = 0;
     AnsiString Owner;
     AnsiString Group;
+    __int64 MTime;
+    __int64 ATime;
 
     if (Properties != NULL)
     {
@@ -402,18 +405,32 @@ public:
         }
         RightsNum = Rights;
       }
+
+      if (Properties->Valid.Contains(vpLastAccess))
+      {
+        Valid |= valATime;
+        ATime = Properties->LastAccess;
+      }
+
+      if (Properties->Valid.Contains(vpModification))
+      {
+        Valid |= valMTime;
+        MTime = Properties->Modification;
+      }
     }
 
     AddProperties(
       Valid & valRights ? &RightsNum : NULL,
       Valid & valOwner ? &Owner : NULL,
       Valid & valGroup ? &Group : NULL,
-      NULL, NULL, NULL, IsDirectory, Version, Utf);
+      Valid & valMTime ? &MTime : NULL,
+      Valid & valATime ? &ATime : NULL,
+      NULL, IsDirectory, Version, Utf);
   }
 
   char GetByte()
   {
-    assert(FPosition <= FLength - sizeof(char));
+    Need(sizeof(char));
     char Result = FData[FPosition];
     FPosition++;
     return Result;
@@ -422,7 +439,7 @@ public:
   unsigned long GetCardinal()
   {
     unsigned long Result;
-    assert(FPosition <= FLength - sizeof(Result));
+    Need(sizeof(Result));
     Result = GET_32BIT(FData + FPosition);
     FPosition += sizeof(Result);
     return Result;
@@ -439,8 +456,10 @@ public:
   {
     AnsiString Result;
     unsigned long Len = GetCardinal();
+    Need(Len);
+    // cannot happen anyway as Need() would raise exception
+    assert(Len < SFTP_MAX_PACKET_LEN);
     Result.SetLength(Len);
-    assert(FLength >= Len && FPosition <= FLength - Len);
     memcpy(Result.c_str(), FData + FPosition, Len);
     FPosition += Len;
     return Result;
@@ -456,9 +475,10 @@ public:
     return (Utf ? GetUtfString() : GetString());
   }
 
-  inline AnsiString GetPathString(int Version, bool Utf)
+  // now purposeless alias to GetString
+  inline AnsiString GetPathString(bool Utf)
   {
-    return GetString((Version >= 4) && Utf);
+    return GetString(Utf);
   }
 
   void GetFile(TRemoteFile * File, int Version, bool ConsiderDST, bool Utf, bool SignedTS)
@@ -470,7 +490,7 @@ public:
     bool ParsingFailed = false;
     if (Type != SSH_FXP_ATTRS)
     {
-      File->FileName = GetPathString(Version, Utf);
+      File->FileName = GetPathString(Utf);
       if (Version < 4)
       {
         ListingStr = GetString();
@@ -493,9 +513,10 @@ public:
     {
       File->Size = GetInt64();
     }
-    if (Flags & SSH_FILEXFER_ATTR_UIDGID)
+    // SSH-2.0-3.2.0 F-SECURE SSH - Process Software MultiNet
+    // sets SSH_FILEXFER_ATTR_UIDGID for v4, but does not include the UID/GUID
+    if ((Flags & SSH_FILEXFER_ATTR_UIDGID) && (Version < 4))
     {
-      assert(Version < 4);
       GetCardinal(); // skip UID
       GetCardinal(); // skip GUID
     }
@@ -615,6 +636,15 @@ public:
     File->Complete();
   }
 
+  char * GetNextData(unsigned int Size = 0)
+  {
+    if (Size > 0)
+    {
+      Need(Size);
+    }
+    return FPosition < FLength ? FData + FPosition : NULL;
+  }
+
   void DataUpdated(int ALength)
   {
     FPosition = 0;
@@ -697,7 +727,6 @@ public:
 
   __property unsigned int Length = { read = FLength };
   __property char * Data = { read = FData };
-  __property char * NextData = { read = GetNextData };
   __property char * SendData = { read = GetSendData };
   __property unsigned int SendLength = { read = GetSendLength };
   __property unsigned int Capacity = { read = FCapacity, write = SetCapacity };
@@ -752,7 +781,7 @@ private:
     }
   }
 
-  void Add(const void * AData, int ALength)
+  inline void Add(const void * AData, int ALength)
   {
     if (Length + ALength > Capacity)
     {
@@ -823,11 +852,6 @@ private:
     }
   }
 
-  char * GetNextData()
-  {
-    return FPosition < FLength ? FData + FPosition : NULL;
-  }
-
   char * GetSendData() const
   {
     char * Result = FData - FSendPrefixLen;
@@ -840,6 +864,14 @@ private:
   {
     return FSendPrefixLen + Length;
   }
+
+  inline void Need(unsigned int Size)
+  {
+    if (FPosition + Size > FLength)
+    {
+      throw Exception(FMTLOAD(SFTP_PACKET_ERROR, (int(FPosition), int(Size), int(FLength))));
+    }
+  }
 };
 //---------------------------------------------------------------------------
 int TSFTPPacket::FMessageCounter = 0;
@@ -1254,6 +1286,7 @@ struct TOpenRemoteFileParams
   __int64 DestFileSize; // output
   AnsiString RemoteFileHandle; // output
   TOverwriteFileParams * FileParams;
+  bool Confirmed;
 };
 //---------------------------------------------------------------------------
 struct TSinkFileParams
@@ -1276,10 +1309,12 @@ __fastcall TSFTPFileSystem::TSFTPFileSystem(TTerminal * ATerminal):
   FBusy = 0;
   FAvoidBusy = false;
   FUtfStrings = false;
+  FUtfNever = false;
   FSignedTS = false;
   FSupport = new TSFTPSupport();
   FSupport->Extensions = new TStringList();
   FExtensions = new TStringList();
+  FFixedPaths = NULL;
 }
 //---------------------------------------------------------------------------
 __fastcall TSFTPFileSystem::~TSFTPFileSystem()
@@ -1293,6 +1328,7 @@ __fastcall TSFTPFileSystem::~TSFTPFileSystem()
   }
   delete FPacketReservations;
   delete FExtensions;
+  delete FFixedPaths;
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall TSFTPFileSystem::GetProtocolName() const
@@ -1311,9 +1347,11 @@ bool __fastcall TSFTPFileSystem::IsCapable(int Capability) const
 
     case fcModeChanging:
     case fcNewerOnlyUpload:
+    case fcTimestampChanging:
       return true;
 
     case fcRename:
+    case fcRemoteMove:
       return (FVersion >= 2);
 
     case fcSymbolicLink:
@@ -1346,7 +1384,7 @@ bool __fastcall TSFTPFileSystem::SupportsExtension(const AnsiString & Extension)
 void __fastcall TSFTPFileSystem::KeepAlive()
 {
   TSFTPPacket Packet(SSH_FXP_REALPATH);
-  Packet.AddPathString("/", FVersion, FUtfStrings);
+  Packet.AddPathString("/", FUtfStrings);
   SendPacketAndReceiveResponse(&Packet, &Packet);
 }
 //---------------------------------------------------------------------------
@@ -1648,11 +1686,12 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     {
       // message is in UTF only since SFTP specification 01 (specification 00 
       // is also version 3)
-      ServerMessage = Packet->GetString(FUtfStrings);
+      // (in other words, always use UTF unless server is know to be buggy)
+      ServerMessage = Packet->GetString(!FUtfNever);
       LanguageTag = Packet->GetString();
       if ((FVersion >= 5) && (Message == SFTP_STATUS_UNKNOWN_PRINCIPLE))
       {
-        while (Packet->NextData != NULL)
+        while (Packet->GetNextData() != NULL)
         {
           if (!AdditionalInfo.IsEmpty())
           {
@@ -1917,14 +1956,14 @@ AnsiString __fastcall TSFTPFileSystem::RealPath(const AnsiString Path)
       (Path)));
 
     TSFTPPacket Packet(SSH_FXP_REALPATH);
-    Packet.AddPathString(Path, FVersion, FUtfStrings);
+    Packet.AddPathString(Path, FUtfStrings);
     SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_NAME);
     if (Packet.GetCardinal() != 1)
     {
       FTerminal->FatalError(LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
     }
 
-    AnsiString RealDir = Packet.GetPathString(FVersion, FUtfStrings);
+    AnsiString RealDir = Packet.GetPathString(FUtfStrings);
     // ignore rest of SSH_FXP_NAME packet
 
     FTerminal->LogEvent(FORMAT("Real path is '%s'", (RealDir)));
@@ -2116,7 +2155,7 @@ void __fastcall TSFTPFileSystem::DoStartup()
     AnsiString ExtensionName;
     AnsiString ExtensionData;
     AnsiString ExtensionDisplayData;
-    while (Packet.NextData)
+    while (Packet.GetNextData() != NULL)
     {
       ExtensionName = Packet.GetString();
       ExtensionData = Packet.GetString();
@@ -2142,7 +2181,7 @@ void __fastcall TSFTPFileSystem::DoStartup()
         FSupport->MaxReadSize = SupportedStruct.GetCardinal();
         AnsiString Extension;
         FSupport->Extensions->Clear();
-        while (SupportedStruct.NextData)
+        while (SupportedStruct.GetNextData() != NULL)
         {
           Extension = SupportedStruct.GetString();
           FSupport->Extensions->Add(Extension);
@@ -2168,6 +2207,48 @@ void __fastcall TSFTPFileSystem::DoStartup()
           }
         }
       }
+      else if (ExtensionName == SFTP_EXT_VENDOR_ID)
+      {
+        TSFTPPacket VendorIdStruct(ExtensionData);
+        AnsiString VendorName(VendorIdStruct.GetString());
+        AnsiString ProductName(VendorIdStruct.GetString());
+        AnsiString ProductVersion(VendorIdStruct.GetString());
+        __int64 ProductBuildNumber = VendorIdStruct.GetInt64();
+        FTerminal->LogEvent(FORMAT("Server software: %s %s (%d) by %s",
+          (ProductName, ProductVersion, int(ProductBuildNumber), VendorName)));
+      }
+      else if (ExtensionName == SFTP_EXT_FSROOTS)
+      {
+        FTerminal->LogEvent("File system roots:\n");
+        assert(FFixedPaths == NULL);
+        FFixedPaths = new TStringList();
+        try
+        {
+          TSFTPPacket RootsPacket(ExtensionData);
+          while (RootsPacket.GetNextData() != NULL)
+          {
+            unsigned long Dummy = RootsPacket.GetCardinal();
+            if (Dummy != 1)
+            {
+              break;
+            }
+            else
+            {
+              char Drive = RootsPacket.GetByte();
+              char MaybeType = RootsPacket.GetByte();
+              FTerminal->LogEvent(FORMAT("  %s: (type %d)", (Drive, (int)MaybeType)));
+              FFixedPaths->Add(FORMAT("%s:", (Drive)));
+            }
+          }
+        }
+        catch(Exception & E)
+        {
+          FFixedPaths->Clear();
+          FTerminal->LogEvent(FORMAT("Failed to decode %s extension",
+            (SFTP_EXT_FSROOTS)));
+          FTerminal->HandleException(&E);
+        }
+      }
       else
       {
         FTerminal->LogEvent(FORMAT("Unknown server extension %s=%s",
@@ -2176,58 +2257,17 @@ void __fastcall TSFTPFileSystem::DoStartup()
       FExtensions->Values[ExtensionName] = ExtensionData;
     }
 
-    Packet.ChangeType(SSH_FXP_EXTENDED);
-    Packet.AddString(SFTP_EXT_WINSCP);
-    SendPacket(&Packet);
-    ReserveResponse(&Packet, &Packet);
-
-    TSFTPPacket PacketSoftware(SSH_FXP_EXTENDED);
-    bool SoftwareExt = SupportsExtension(SFTP_EXT_SOFTWARE);
-    if (SoftwareExt)
-    {
-      PacketSoftware.AddString(SFTP_EXT_SOFTWARE);
-      PacketSoftware.AddString(FTerminal->Configuration->ProductName);
-      PacketSoftware.AddString(FTerminal->Configuration->ProductVersion);
-      PacketSoftware.AddString(FTerminal->Configuration->CompanyName);
-      PacketSoftware.AddString(SFTP_EXT_SOFTWARE_OS);
-      PacketSoftware.AddString(FTerminal->Configuration->OSVersionStr);
-      SendPacket(&PacketSoftware);
-      ReserveResponse(&PacketSoftware, &PacketSoftware);
-    }
-
-    int Status = ReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS,
-      asOK | asOpUnsupported);
-    if (Status == SSH_FX_OK)
-    {
-      FTerminal->LogEvent("Server recognises WinSCP.");
-    }
-    else
+    if (SupportsExtension(SFTP_EXT_VENDOR_ID))
     {
-      FTerminal->LogEvent("Server does not recognise WinSCP.");
-    }
-
-    if (SoftwareExt)
-    {
-      ReceiveResponse(&PacketSoftware, &PacketSoftware, SSH_FXP_EXTENDED_REPLY,
-        asOpUnsupported);
-      if ((PacketSoftware.Type != SSH_FXP_EXTENDED_REPLY) ||
-          (PacketSoftware.GetString() != SFTP_EXT_SOFTWARE))
-      {
-        FTerminal->LogEvent(FORMAT("Invalid response to %s", (SFTP_EXT_SOFTWARE)));
-      }
-      else
-      {
-        AnsiString Software(PacketSoftware.GetString());
-        AnsiString Vendor(PacketSoftware.GetString());
-        AnsiString Version(PacketSoftware.GetString());
-        AnsiString OS(PacketSoftware.GetString());
-        AnsiString OSVersion(PacketSoftware.GetString());
-
-        FTerminal->LogEvent(FORMAT("Server software: %s %s by %s",
-          (Software, Version, Vendor)));
-        FTerminal->LogEvent(FORMAT("Server OS: %s %s",
-          (OS, OSVersion)));
-      }
+      TSFTPPacket Packet(SSH_FXP_EXTENDED);
+      Packet.AddString(SFTP_EXT_VENDOR_ID);
+      Packet.AddString(FTerminal->Configuration->CompanyName);
+      Packet.AddString(FTerminal->Configuration->ProductName);
+      Packet.AddString(FTerminal->Configuration->ProductVersion);
+      Packet.AddInt64(LOWORD(FTerminal->Configuration->FixedApplicationInfo->dwFileVersionLS));
+      SendPacket(&Packet);
+      // we are not interested in the response, do not wait for it
+      ReserveResponse(&Packet, NULL);
     }
   }
 
@@ -2242,23 +2282,26 @@ void __fastcall TSFTPFileSystem::DoStartup()
     }
   }
 
-  if (FVersion >= 4)
+  // use UTF when forced or ...
+  // when "auto" and version is at least 4 and the server is not know not to use UTF
+  FUtfNever = (FTerminal->SshImplementation.Pos("Foxit-WAC-Server") == 1) ||
+    (FTerminal->SessionData->SFTPBug[sbUtf] == asOn);
+  FUtfStrings =
+    (FTerminal->SessionData->SFTPBug[sbUtf] == asOff) ||
+    ((FTerminal->SessionData->SFTPBug[sbUtf] == asAuto) &&
+      (FVersion >= 4) && !FUtfNever);
+
+  if (FUtfStrings)
   {
-    FUtfStrings = (FTerminal->SessionData->SFTPBug[sbUtf] == asOff) ||
-      ((FTerminal->SessionData->SFTPBug[sbUtf] == asAuto) &&
-        (FTerminal->SshImplementation.Pos("Foxit-WAC-Server") != 1));
-    if (FUtfStrings)
-    {
-      FTerminal->LogEvent("We will use UTF-8 strings when appropriate");
-    }
-    else
-    {
-      FTerminal->LogEvent("We believe the server has SFTP UTF-8 bug");
-    }
+    FTerminal->LogEvent("We will use UTF-8 strings when appropriate");
+  }
+  else if (FUtfNever)
+  {
+    FTerminal->LogEvent("We will never use UTF-8 strings");
   }
   else
   {
-    FUtfStrings = false;
+    FTerminal->LogEvent("We will use UTF-8 strings for status messages only");
   }
 }
 //---------------------------------------------------------------------------
@@ -2319,7 +2362,7 @@ void __fastcall TSFTPFileSystem::LookupUsersGroups()
         List->Clear();
         for (unsigned long Item = 0; Item < Count; Item++)
         {
-          List->Add(Packet->GetUtfString());
+          List->Add(Packet->GetString(!FUtfNever));
         }
       }
       __finally
@@ -2353,8 +2396,25 @@ void __fastcall TSFTPFileSystem::TryOpenDirectory(const AnsiString Directory)
 {
   FTerminal->LogEvent(FORMAT("Trying to open directory \"%s\".", (Directory)));
   TRemoteFile * File;
-  ReadFile(Directory, File);
-  delete File;
+  CustomReadFile(Directory, File, SSH_FXP_LSTAT, NULL, asOpUnsupported);
+  if (File == NULL)
+  {
+    // File can be NULL only when server does not support SSH_FXP_LSTAT.
+    // Fallback to legacy solution, which in turn does not allow entering
+    // traverse-only (chmod 110) directories.
+    // This is workaround for http://www.ftpshell.com/
+    TSFTPPacket Packet(SSH_FXP_OPENDIR);
+    Packet.AddPathString(UnixExcludeTrailingBackslash(Directory), FUtfStrings);
+    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_HANDLE);
+    AnsiString Handle = Packet.GetString();
+    Packet.ChangeType(SSH_FXP_CLOSE);
+    Packet.AddString(Handle);
+    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS, asAll);
+  }
+  else
+  {
+    delete File;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::ChangeDirectory(const AnsiString Directory)
@@ -2394,7 +2454,7 @@ void __fastcall TSFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
 
   try
   {
-    Packet.AddPathString(Directory, FVersion, FUtfStrings);
+    Packet.AddPathString(Directory, FUtfStrings);
 
     SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_HANDLE);
 
@@ -2441,8 +2501,9 @@ void __fastcall TSFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
         {
           File = LoadFile(&ListingPacket, NULL, "");
           // security fix
+          // (LastDelimiter works for MBCS)
           if (((File->FileName.Length() > 2) && IsDots(File->FileName)) || 
-              (File->FileName.Pos("/") > 0) || (File->FileName.Pos("\\") > 0))
+              (File->FileName.LastDelimiter("/\\") > 0))
           {
             FTerminal->LogEvent(FORMAT("Ignored suspicious file '%s'", (File->FileName))); 
             delete File;
@@ -2539,14 +2600,14 @@ void __fastcall TSFTPFileSystem::ReadSymlink(TRemoteFile * SymlinkFile,
   AnsiString FileName = LocalCanonify(SymlinkFile->FileName);
 
   TSFTPPacket ReadLinkPacket(SSH_FXP_READLINK);
-  ReadLinkPacket.AddPathString(FileName, FVersion, FUtfStrings);
+  ReadLinkPacket.AddPathString(FileName, FUtfStrings);
   SendPacket(&ReadLinkPacket);
   ReserveResponse(&ReadLinkPacket, &ReadLinkPacket);
 
   // send second request before reading response to first one
   // (performance benefit)
   TSFTPPacket AttrsPacket(SSH_FXP_STAT);
-  AttrsPacket.AddPathString(FileName, FVersion, FUtfStrings);
+  AttrsPacket.AddPathString(FileName, FUtfStrings);
   if (FVersion >= 4)
   {
     AttrsPacket.AddCardinal(SSH_FILEXFER_ATTR_COMMON);
@@ -2559,7 +2620,7 @@ void __fastcall TSFTPFileSystem::ReadSymlink(TRemoteFile * SymlinkFile,
   {
     FTerminal->FatalError(LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
   }
-  SymlinkFile->LinkTo = ReadLinkPacket.GetPathString(FVersion, FUtfStrings);
+  SymlinkFile->LinkTo = ReadLinkPacket.GetPathString(FUtfStrings);
 
   ReceiveResponse(&AttrsPacket, &AttrsPacket, SSH_FXP_ATTRS);
   // SymlinkFile->FileName was used instead SymlinkFile->LinkTo before, why?
@@ -2580,7 +2641,7 @@ bool __fastcall TSFTPFileSystem::RemoteFileExists(const AnsiString FullPath,
   try
   {
     TRemoteFile * AFile;
-    CustomReadFile(FullPath, AFile, SSH_FXP_LSTAT, NULL, true);
+    CustomReadFile(FullPath, AFile, SSH_FXP_LSTAT, NULL, asNoSuchFile);
     Result = (AFile != NULL);
     if (Result)
     {
@@ -2607,11 +2668,11 @@ bool __fastcall TSFTPFileSystem::RemoteFileExists(const AnsiString FullPath,
 //---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::CustomReadFile(const AnsiString FileName,
   TRemoteFile *& File, char Type, TRemoteFile * ALinkedByFile,
-  bool AllowNonexistence)
+  int AllowStatus)
 {
   TSFTPPacket Packet(Type);
 
-  Packet.AddPathString(LocalCanonify(FileName), FVersion, FUtfStrings);
+  Packet.AddPathString(LocalCanonify(FileName), FUtfStrings);
   if (FVersion >= 4)
   {
     Packet.AddCardinal(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS |
@@ -2619,7 +2680,7 @@ void __fastcall TSFTPFileSystem::CustomReadFile(const AnsiString FileName,
       SSH_FILEXFER_ATTR_OWNERGROUP);
   }
   SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_ATTRS,
-    AllowNonexistence ? asNoSuchFile : -1);
+    AllowStatus);
 
   if (Packet.Type == SSH_FXP_ATTRS)
   {
@@ -2627,7 +2688,7 @@ void __fastcall TSFTPFileSystem::CustomReadFile(const AnsiString FileName,
   }
   else
   {
-    assert(AllowNonexistence);
+    assert(AllowStatus > 0);
     File = NULL;
   }
 }
@@ -2651,7 +2712,7 @@ void __fastcall TSFTPFileSystem::DeleteFile(const AnsiString FileName,
   }
 
   TSFTPPacket Packet(Type);
-  Packet.AddPathString(RealFileName, FVersion, FUtfStrings);
+  Packet.AddPathString(RealFileName, FUtfStrings);
   SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
 }
 //---------------------------------------------------------------------------
@@ -2660,7 +2721,7 @@ void __fastcall TSFTPFileSystem::RenameFile(const AnsiString FileName,
 {
   TSFTPPacket Packet(SSH_FXP_RENAME);
   AnsiString RealName = LocalCanonify(FileName);
-  Packet.AddPathString(RealName, FVersion, FUtfStrings);
+  Packet.AddPathString(RealName, FUtfStrings);
   AnsiString TargetName;
   if (UnixExtractFilePath(NewName).IsEmpty())
   {
@@ -2671,7 +2732,7 @@ void __fastcall TSFTPFileSystem::RenameFile(const AnsiString FileName,
   {
     TargetName = LocalCanonify(NewName);
   }
-  Packet.AddPathString(TargetName, FVersion, FUtfStrings);
+  Packet.AddPathString(TargetName, FUtfStrings);
   if (FVersion >= 5)
   {
     Packet.AddCardinal(0);
@@ -2690,14 +2751,14 @@ void __fastcall TSFTPFileSystem::CreateDirectory(const AnsiString DirName,
 {
   TSFTPPacket Packet(SSH_FXP_MKDIR);
   AnsiString CanonifiedName = Canonify(DirName);
-  Packet.AddPathString(CanonifiedName, FVersion, FUtfStrings);
+  Packet.AddPathString(CanonifiedName, FUtfStrings);
   Packet.AddProperties(NULL, 0, true, FVersion, FUtfStrings);
   SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
 
   // explicitly set permissions after directory creation,
   // permissions specified in SSH_FXP_MKDIR are ignored at least by OpenSSH
   Packet.ChangeType(SSH_FXP_SETSTAT);
-  Packet.AddPathString(CanonifiedName, FVersion, FUtfStrings);
+  Packet.AddPathString(CanonifiedName, FUtfStrings);
   Packet.AddProperties(Properties, 0, true, FVersion, FUtfStrings);
   SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
 }
@@ -2716,14 +2777,14 @@ void __fastcall TSFTPFileSystem::CreateLink(const AnsiString FileName,
 
   if (!Buggy)
   {
-    Packet.AddPathString(Canonify(FileName), FVersion, FUtfStrings);
-    Packet.AddPathString(PointTo, FVersion, FUtfStrings);
+    Packet.AddPathString(Canonify(FileName), FUtfStrings);
+    Packet.AddPathString(PointTo, FUtfStrings);
   }
   else
   {
     FTerminal->LogEvent("We believe the server has SFTP symlink bug");
-    Packet.AddPathString(PointTo, FVersion, FUtfStrings);
-    Packet.AddPathString(Canonify(FileName), FVersion, FUtfStrings);
+    Packet.AddPathString(PointTo, FUtfStrings);
+    Packet.AddPathString(Canonify(FileName), FUtfStrings);
   }
   SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
 }
@@ -2749,7 +2810,7 @@ void __fastcall TSFTPFileSystem::ChangeFileProperties(const AnsiString FileName,
     }
 
     TSFTPPacket Packet(SSH_FXP_SETSTAT);
-    Packet.AddPathString(RealFileName, FVersion, FUtfStrings);
+    Packet.AddPathString(RealFileName, FUtfStrings);
     Packet.AddProperties(Properties, *File->Rights, File->IsDirectory, FVersion, FUtfStrings);
     SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
   }
@@ -2778,6 +2839,11 @@ AnsiString __fastcall TSFTPFileSystem::FileUrl(const AnsiString FileName)
     (FileName[1] == '/' ? "" : "/") + FileName;
 }
 //---------------------------------------------------------------------------
+TStrings * __fastcall TSFTPFileSystem::GetFixedPaths()
+{
+  return FFixedPaths;
+}
+//---------------------------------------------------------------------------
 // transfer protocol
 //---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::CopyToRemote(TStrings * FilesToCopy,
@@ -2836,16 +2902,21 @@ void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(const AnsiString FileName,
   bool TargetBiggerThanSource, TFileOperationProgressType * OperationProgress,
   TSFTPOverwriteMode & OverwriteMode, const TOverwriteFileParams * FileParams)
 {
+  bool CanAlternateResume = !TargetBiggerThanSource && !OperationProgress->AsciiTransfer;
   if (OperationProgress->NoToAll)
   {
     THROW_SKIP_FILE_NULL;
   }
+  else if (CanAlternateResume && OperationProgress->AlternateResumeAlways)
+  {
+    OverwriteMode = omResume;
+  }
   else
   {
     int Answer;
     SUSPEND_OPERATION
     (
-      int Answers = qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll | qaAll;
+      int Answers = qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll;
       if ((FVersion < 4) || !OperationProgress->AsciiTransfer)
       {
         Answers |= qaRetry;
@@ -2866,29 +2937,42 @@ void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(const AnsiString FileName,
 
     if (Answer == qaRetry)
     {
-      if (TargetBiggerThanSource || OperationProgress->AsciiTransfer)
+      if (!CanAlternateResume)
       {
         OverwriteMode = omAppend;
       }
       else
       {
+        TQueryParams Params(0, HELP_APPEND_OR_RESUME);
         SUSPEND_OPERATION
         (
           Answer = FTerminal->DoQueryUser(FORMAT(LoadStr(APPEND_OR_RESUME), (FileName)),
-            qaYes | qaNo | qaCancel, 0);
+            qaYes | qaNo | qaNoToAll | qaCancel, &Params);
         );
 
-        if (Answer == qaCancel)
+        switch (Answer)
         {
-          if (!OperationProgress->Cancel)
-          {
-            OperationProgress->Cancel = csCancel;
-          }
-          Abort();
-        }
-        else
-        {
-          OverwriteMode = ((Answer == qaYes) ? omAppend : omResume);
+          case qaYes:
+            OverwriteMode = omAppend;
+            break;
+
+          case qaNo:
+            OverwriteMode = omResume;
+            break;
+
+          case qaNoToAll:
+            OverwriteMode = omResume;
+            OperationProgress->AlternateResumeAlways = true;
+            break;
+
+          default: assert(false); //fallthru
+          case qaCancel:
+            if (!OperationProgress->Cancel)
+            {
+              OperationProgress->Cancel = csCancel;
+            }
+            Abort();
+            break;
         }
       }
     }
@@ -2897,7 +2981,7 @@ void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(const AnsiString FileName,
       OverwriteMode = omOverwrite;
       switch (Answer)
       {
-        case qaAbort:
+        case qaCancel:
           if (!OperationProgress->Cancel)
           {
             OperationProgress->Cancel = csCancel;
@@ -2922,9 +3006,9 @@ bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
     int Answer;
     SUSPEND_OPERATION
     (
-      TQueryParams Params(qpAllowContinueOnError);
+      TQueryParams Params(qpAllowContinueOnError, HELP_PARTIAL_BIGGER_THAN_SOURCE);
       Answer = FTerminal->DoQueryUser(
-        FMTLOAD(PARTIAL_BIGGER_THAN_SOURCE, (DestFileName)), NULL,
+        FMTLOAD(PARTIAL_BIGGER_THAN_SOURCE, (DestFileName)),
           qaOK | qaAbort, &Params, qtWarning);
     )
 
@@ -2943,9 +3027,11 @@ bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
     int Answer;
     SUSPEND_OPERATION
     (
-      TQueryParams Params(qpAllowContinueOnError | qpNeverAskAgainCheck);
+      TQueryParams Params(qpAllowContinueOnError | qpNeverAskAgainCheck,
+        HELP_RESUME_TRANSFER);
+      // "abort" replaced with "cancel" to unify with "append/resume" query
       Answer = FTerminal->DoQueryUser(
-        FMTLOAD(RESUME_TRANSFER, (DestFileName)), qaYes | qaNo | qaAbort,
+        FMTLOAD(RESUME_TRANSFER, (DestFileName)), qaYes | qaNo | qaCancel,
         &Params);
     );
 
@@ -2960,7 +3046,7 @@ bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
         ResumeTransfer = false;
         break;
 
-      case qaAbort:
+      case qaCancel:
         if (!OperationProgress->Cancel)
         {
           OperationProgress->Cancel = csCancel;
@@ -2980,10 +3066,8 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
   const AnsiString TargetDir, const TCopyParamType * CopyParam, int Params,
   TFileOperationProgressType * OperationProgress, unsigned int Flags)
 {
-  AnsiString OnlyFileName = ExtractFileName(FileName);
-
   if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(OnlyFileName))
+      !CopyParam->AllowTransfer(FileName, osLocal))
   {
     FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
     THROW_SKIP_FILE_NULL;
@@ -3015,7 +3099,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
       // File is regular file (not directory)
       assert(File);
 
-      AnsiString DestFileName = CopyParam->ChangeFileName(OnlyFileName,
+      AnsiString DestFileName = CopyParam->ChangeFileName(ExtractFileName(FileName),
         osLocal, FLAGSET(Flags, tfFirstLevel));
       AnsiString DestFullName = LocalCanonify(TargetDir + DestFileName);
       AnsiString DestPartinalFullName;
@@ -3036,7 +3120,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
       OperationProgress->TransferingFile = false;
 
       // Will we use ASCII of BINARY file tranfer?
-      OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName));
+      OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName, osLocal));
       FTerminal->LogEvent(
         AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
           " transfer mode selected.");
@@ -3076,8 +3160,16 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
           delete File;
           File = NULL;
 
-          ResumeTransfer = SFTPConfirmResume(DestFileName,
-            ResumeOffset > OperationProgress->LocalSize, OperationProgress);
+          bool PartialBiggerThanSource = (ResumeOffset > OperationProgress->LocalSize);
+          if (FLAGCLEAR(Params, cpNoConfirmation))
+          {
+            ResumeTransfer = SFTPConfirmResume(DestFileName,
+              PartialBiggerThanSource, OperationProgress);
+          }
+          else
+          {
+            ResumeTransfer = !PartialBiggerThanSource;
+          }
 
           if (!ResumeTransfer)
           {
@@ -3101,15 +3193,27 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
               OperationProgress, OpenParams.OverwriteMode, &FileParams);
           }
         }
+
       }
 
       bool DoResume = (ResumeAllowed && (OpenParams.OverwriteMode == omOverwrite));
+
+      if (DoResume && DestFileExists)
+      {
+        FILE_OPERATION_LOOP(FMTLOAD(DELETE_BEFORE_RESUME_ERROR,
+            (UnixExtractFileName(DestFullName), DestFullName)),
+
+          DeleteFile(DestFullName);
+        );
+      }
+
       OpenParams.RemoteFileName = DoResume ? DestPartinalFullName : DestFullName;
       OpenParams.Resume = DoResume;
       OpenParams.OperationProgress = OperationProgress;
       OpenParams.CopyParam = CopyParam;
       OpenParams.Params = Params;
       OpenParams.FileParams = &FileParams;
+      OpenParams.Confirmed = false;
 
       FTerminal->LogEvent("Opening remote file.");
       FTerminal->FileOperationLoop(SFTPOpenRemote, OperationProgress, true,
@@ -3125,7 +3229,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
       TSFTPPacket PropertiesResponse;
       if (SetProperties)
       {
-        PropertiesRequest.AddPathString(DestFullName, FVersion, FUtfStrings);
+        PropertiesRequest.AddPathString(DestFullName, FUtfStrings);
         unsigned short Rights = 0;
         if (CopyParam->PreserveRights)
         {
@@ -3228,12 +3332,6 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
       {
         FILE_OPERATION_LOOP(FMTLOAD(RENAME_AFTER_RESUME_ERROR,
             (UnixExtractFileName(OpenParams.RemoteFileName), DestFileName)),
-
-          if (DestFileExists)
-          {
-            DeleteFile(DestFullName);
-            // DestFileExists used to be set to false here. Had it any reason?
-          }
           RenameFile(OpenParams.RemoteFileName, DestFileName);
         );
       }
@@ -3287,7 +3385,7 @@ AnsiString __fastcall TSFTPFileSystem::SFTPOpenRemoteFile(
 {
   TSFTPPacket Packet(SSH_FXP_OPEN);
 
-  Packet.AddPathString(FileName, FVersion, FUtfStrings);
+  Packet.AddPathString(FileName, FUtfStrings);
   if (FVersion < 5)
   {
     Packet.AddCardinal(OpenType);
@@ -3339,7 +3437,6 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
   TFileOperationProgressType * OperationProgress = OpenParams->OperationProgress;
 
   int OpenType;
-  bool Confirmed = false;
   bool Success = false;
 
   do
@@ -3348,7 +3445,7 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
     {
       OpenType = SSH_FXF_WRITE | SSH_FXF_CREAT;
       if (FTerminal->Configuration->ConfirmOverwriting &&
-          !Confirmed && !OperationProgress->YesToAll && !OpenParams->Resume &&
+          !OpenParams->Confirmed && !OperationProgress->YesToAll && !OpenParams->Resume &&
           !(OpenParams->Params & cpNoConfirmation) &&
           (OpenParams->OverwriteMode == omOverwrite))
       {
@@ -3370,7 +3467,7 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
     }
     catch(...)
     {
-      if (!Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
+      if (!OpenParams->Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
       {
         bool TargetBiggerThanSource;
         bool ThrowOriginal = false;
@@ -3415,7 +3512,7 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
         SFTPConfirmOverwrite(UnixExtractFileName(OpenParams->RemoteFileName),
           TargetBiggerThanSource, OperationProgress, OpenParams->OverwriteMode,
           OpenParams->FileParams);
-        Confirmed = true;
+        OpenParams->Confirmed = true;
 
         if (FTerminal->SessionData->OverwrittenToRecycleBin)
         {
@@ -3621,7 +3718,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
   AnsiString OnlyFileName = UnixExtractFileName(FileName);
 
   if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(OnlyFileName))
+      !CopyParam->AllowTransfer(FileName, osRemote))
   {
     FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
     THROW_SKIP_FILE_NULL;  
@@ -3683,7 +3780,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
     __int64 ResumeOffset;
 
     // Will we use ASCII of BINARY file tranfer?
-    OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(DestFileName));
+    OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName, osRemote));
     FTerminal->LogEvent(AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
       " transfer mode selected.");
 
@@ -3692,10 +3789,8 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
     OperationProgress->SetTransferSize(File->Size);
     OperationProgress->SetLocalSize(OperationProgress->TransferSize);
 
-    // resume has no sense for drag&drop downloads, because files are downloaded
-    // to temp directory, but anyway TTerminal does know this, so the condition
-    // placement is currently wrong.
-    ResumeAllowed = ((Params & cpDragDrop) == 0) &&
+    // resume has no sense for temporary downloads
+    ResumeAllowed = ((Params & cpTemporary) == 0) &&
       !OperationProgress->AsciiTransfer &&
       CopyParam->AllowResume(OperationProgress->TransferSize);
     OperationProgress->SetResumeStatus(ResumeAllowed ? rsEnabled : rsDisabled);
@@ -3727,8 +3822,16 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
           FTerminal->OpenLocalFile(DestPartinalFullName, GENERIC_WRITE,
             NULL, &LocalHandle, NULL, NULL, NULL, &ResumeOffset);
 
-          ResumeTransfer = SFTPConfirmResume(DestFileName,
-            ResumeOffset > OperationProgress->TransferSize, OperationProgress);
+          bool PartialBiggerThanSource = (ResumeOffset > OperationProgress->TransferSize);
+          if (FLAGCLEAR(Params, cpNoConfirmation))
+          {
+            ResumeTransfer = SFTPConfirmResume(DestFileName,
+              PartialBiggerThanSource, OperationProgress);
+          }
+          else
+          {
+            ResumeTransfer = !PartialBiggerThanSource;
+          }
 
           if (!ResumeTransfer)
           {
@@ -3924,7 +4027,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
             }
 
             assert(DataLen <= BlockSize);
-            BlockBuf.Insert(0, DataPacket.NextData, DataLen);
+            BlockBuf.Insert(0, DataPacket.GetNextData(DataLen), DataLen);
             OperationProgress->AddTransfered(DataLen);
 
             if (OperationProgress->AsciiTransfer)

+ 4 - 1
core/SftpFileSystem.h

@@ -59,6 +59,7 @@ public:
   virtual void __fastcall CopyFile(const AnsiString FileName,
     const AnsiString NewName);
   virtual AnsiString __fastcall FileUrl(const AnsiString FileName);
+  virtual TStrings * __fastcall GetFixedPaths();
 
 protected:
   int FVersion;
@@ -75,11 +76,13 @@ protected:
   TStrings * FExtensions;
   TSFTPSupport * FSupport;
   bool FUtfStrings;
+  bool FUtfNever;
   bool FSignedTS;
+  TStrings * FFixedPaths;
 
   void __fastcall CustomReadFile(const AnsiString FileName,
     TRemoteFile *& File, char Type, TRemoteFile * ALinkedByFile = NULL,
-    bool AllowNonexistence = false);
+    int AllowStatus = -1);
   virtual AnsiString __fastcall GetCurrentDirectory();
   AnsiString __fastcall GetHomeDirectory();
   unsigned long __fastcall GotStatusPacket(TSFTPPacket * Packet, int AllowStatus);

+ 350 - 75
core/Terminal.cpp

@@ -14,6 +14,7 @@
 #include "ScpFileSystem.h"
 #include "SftpFileSystem.h"
 #include "TextsCore.h"
+#include "HelpCore.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -35,6 +36,11 @@ struct TMoveFileParams
   AnsiString FileMask;
 };
 //---------------------------------------------------------------------------
+TSynchronizeStats::TSynchronizeStats()
+{
+  memset(this, 0, sizeof(*this));
+}
+//---------------------------------------------------------------------------
 __fastcall TTerminal::TTerminal(): TSecureShell()
 {
   FFiles = new TRemoteDirectory(this);
@@ -247,7 +253,11 @@ void __fastcall TTerminal::ReactOnCommand(int /*TFSCommand*/ Cmd)
     case fsChangeGroup:
     case fsChangeOwner:
     case fsChangeProperties:
+      ModifiesFiles = true;
+      break;
+
     case fsAnyCommand:
+      ChangesDirectory = true;
       ModifiesFiles = true;
       break;
   }
@@ -611,8 +621,32 @@ int __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg,
   }
   else
   {
-    TQueryParams Params(qpAllowContinueOnError);
-    Result = DoQueryUser(Msg, E, Answers, &Params);
+    // small hack to anable "skip to all" for COMMAND_ERROR_ARI
+    bool CanSkip = FLAGSET(Answers, qaSkip) && (OperationProgress != NULL);
+    if (CanSkip && OperationProgress->SkipToAll)
+    {
+      Result = qaSkip;
+    }
+    else
+    {
+      TQueryParams Params(qpAllowContinueOnError);
+      TQueryButtonAlias Aliases[1];
+      if (CanSkip)
+      {
+        Aliases[0].Button = qaAll;
+        Aliases[0].Alias = LoadStr(SKIP_ALL_BUTTON);
+        Params.Aliases = Aliases;
+        Params.AliasesCount = LENOF(Aliases);
+        Answers |= qaAll;
+      }
+      Result = DoQueryUser(Msg, E, Answers, &Params, qtError);
+      if (Result == qaAll)
+      {
+        assert(OperationProgress != NULL);
+        OperationProgress->SkipToAll = true;
+        Result = qaSkip;
+      }
+    }
   }
   return Result;
 }
@@ -644,7 +678,8 @@ int __fastcall TTerminal::ConfirmFileOverwrite(const AnsiString FileName,
 {
   int Answer;
   int AnswerForNewer =
-    (FileParams->SourceTimestamp > FileParams->DestTimestamp ? qaYes : qaNo);
+    (CompareFileTime(FileParams->SourceTimestamp, FileParams->DestTimestamp) > 0) ?
+    qaYes : qaNo;
   if (OperationProgress->YesToNewer)
   {
     Answer = AnswerForNewer;
@@ -822,6 +857,9 @@ void __fastcall TTerminal::ReadCurrentDirectory()
   assert(FFileSystem);
   try
   {
+    // reset flag is case we are called externally (like from console dialog)
+    FReadCurrentDirectoryPending = false;
+
     LogEvent("Getting current directory name.");
     AnsiString OldDirectory = FFileSystem->CurrentDirectory;
 
@@ -1056,7 +1094,7 @@ void __fastcall TTerminal::ReadFile(const AnsiString FileName,
 //---------------------------------------------------------------------------
 bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
   TFileOperation Operation, TProcessFileEvent ProcessFile, void * Param,
-  TOperationSide Side)
+  TOperationSide Side, bool Ex)
 {
   assert(FFileSystem);
   assert(FileList);
@@ -1088,7 +1126,15 @@ bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
           try
           {
             Success = false;
-            ProcessFile(FileName, (TRemoteFile *)FileList->Objects[Index], Param);
+            if (!Ex)
+            {
+              ProcessFile(FileName, (TRemoteFile *)FileList->Objects[Index], Param);
+            }
+            else
+            {
+              TProcessFileEventEx ProcessFileEx = (TProcessFileEventEx)ProcessFile;
+              ProcessFileEx(FileName, (TRemoteFile *)FileList->Objects[Index], Param, Index);
+            }
             Success = true;
           }
           __finally
@@ -1133,6 +1179,19 @@ bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TTerminal::ProcessFilesEx(TStrings * FileList, TFileOperation Operation,
+  TProcessFileEventEx ProcessFile, void * Param, TOperationSide Side)
+{
+  return ProcessFiles(FileList, Operation, TProcessFileEvent(ProcessFile),
+    Param, Side, true);
+}
+//---------------------------------------------------------------------------
+TStrings * __fastcall TTerminal::GetFixedPaths()
+{
+  assert(FFileSystem != NULL);
+  return FFileSystem->GetFixedPaths();
+}
+//---------------------------------------------------------------------------
 bool __fastcall TTerminal::IsRecycledFile(AnsiString FileName)
 {
   AnsiString Path = UnixExtractFilePath(FileName);
@@ -1212,6 +1271,9 @@ void __fastcall TTerminal::DoDeleteFile(const AnsiString FileName,
 //---------------------------------------------------------------------------
 bool __fastcall TTerminal::DeleteFiles(TStrings * FilesToDelete, bool * Recursive)
 {
+  // TODO: avoid resolving symlinks while reading subdirectories.
+  // Resolving does not work anyway for relative symlinks in subdirectories
+  // (at least for SFTP).
   return ProcessFiles(FilesToDelete, foDelete, DeleteFile, Recursive);
 }
 //---------------------------------------------------------------------------
@@ -1379,6 +1441,18 @@ void __fastcall TTerminal::ChangeFileProperties(AnsiString FileName,
     {
       LogEvent(FORMAT(" - owner: \"%s\"", (RProperties->Owner)));
     }
+    if (RProperties->Valid.Contains(vpModification))
+    {
+      LogEvent(FORMAT(" - modification: \"%s\"",
+        (FormatDateTime("dddddd tt",
+           UnixToDateTime(RProperties->Modification, SessionData->ConsiderDST)))));
+    }
+    if (RProperties->Valid.Contains(vpLastAccess))
+    {
+      LogEvent(FORMAT(" - last access: \"%s\"",
+        (FormatDateTime("dddddd tt",
+           UnixToDateTime(RProperties->LastAccess, SessionData->ConsiderDST)))));
+    }
   }
   if (File) FileModified(File, FileName);
   DoChangeFileProperties(FileName, File, RProperties);
@@ -1416,18 +1490,20 @@ void __fastcall TTerminal::CalculateFileSize(AnsiString FileName,
   assert(File);
   TCalculateSizeParams * AParams = static_cast<TCalculateSizeParams*>(Param);
 
-  if (FileName.IsEmpty() && File)
+  if (FileName.IsEmpty())
   {
     FileName = File->FileName;
   }
 
   if ((AParams->CopyParam == NULL) ||
-      AParams->CopyParam->AllowTransfer(UnixExtractFileName(FileName)))
+      AParams->CopyParam->AllowTransfer(UnixExcludeTrailingBackslash(File->FullFileName),
+        osRemote))
   {
     if (File->IsDirectory && !File->IsSymLink)
     {
       LogEvent(FORMAT("Getting size of directory \"%s\"", (FileName)));
-      DoCalculateDirectorySize(FileName, File, AParams);
+      // pass in full path so we get it back in file list for AllowTransfer() exclusion
+      DoCalculateDirectorySize(File->FullFileName, File, AParams);
     }
     else
     {
@@ -1791,7 +1867,10 @@ bool __fastcall TTerminal::AllowedAnyCommand(const AnsiString Command)
 //---------------------------------------------------------------------------
 bool __fastcall TTerminal::GetCommandSessionOpened()
 {
-  return (FCommandSession != NULL) && FCommandSession->Active;
+  // consider secodary terminal open in "ready" state only
+  // so we never do keepalives on it until it is completelly initialised
+  return (FCommandSession != NULL) &&
+    (FCommandSession->Status == sshReady);
 }
 //---------------------------------------------------------------------------
 TTerminal * __fastcall TTerminal::GetCommandSession()
@@ -1853,13 +1932,23 @@ void __fastcall TTerminal::AnyCommand(const AnsiString Command)
       assert(FCommandSession->FSProtocol == cfsSCP);
       LogEvent("Executing user defined command on command session.");
       FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
+      TLogAddLineEvent PrevLog = Log->OnAddLine;
       try
       {
         FCommandSession->CurrentDirectory = CurrentDirectory;
         FCommandSession->FFileSystem->AnyCommand(Command);
+
+        // ugly hack to prevent SFTP logging be displayed in console
+        Log->OnAddLine = NULL;
+
+        FCommandSession->FFileSystem->ReadCurrentDirectory();
+
+        // synchronize pwd (by purpose we lose transaction optimalisation here)
+        ChangeDirectory(FCommandSession->CurrentDirectory);
       }
       __finally
       {
+        Log->OnAddLine = PrevLog;
         FCommandSession->Log->OnAddLine = NULL;
       }
     }
@@ -1905,11 +1994,11 @@ bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
               (
                 Answer = DoQueryUser(
                   FMTLOAD(READ_ONLY_OVERWRITE, (FileName)),
-                  qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll, 0);
+                  qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll, 0);
               );
               switch (Answer) {
                 case qaYesToAll: OperationProgress->YesToAll = true; break;
-                case qaAbort: OperationProgress->Cancel = csCancel; // continue on next case
+                case qaCancel: OperationProgress->Cancel = csCancel; // continue on next case
                 case qaNoToAll: OperationProgress->NoToAll = true;
                 case qaNo: Result = false; break;
               }
@@ -2067,7 +2156,7 @@ void __fastcall TTerminal::CalculateLocalFileSize(const AnsiString FileName,
   TCalculateSizeParams * AParams = static_cast<TCalculateSizeParams*>(Params);
 
   if ((AParams->CopyParam == NULL) ||
-      AParams->CopyParam->AllowTransfer(ExtractFileName(FileName)))
+      AParams->CopyParam->AllowTransfer(FileName, osLocal))
   {
     if (FLAGCLEAR(Rec.Attr, faDirectory))
     {
@@ -2132,11 +2221,11 @@ struct TSynchronizeFileData
 {
   int Time;
   int Attr;
-  unsigned long SizeHigh;
-  unsigned long SizeLow;
+  __int64 Size;
   FILETIME LastWriteTime;
   bool Modified;
   bool New;
+  const TRemoteFile * MatchingRemoteFile;
 };
 //---------------------------------------------------------------------------
 struct TSynchronizeData
@@ -2146,16 +2235,18 @@ struct TSynchronizeData
   TTerminal::TSynchronizeMode Mode;
   int Params;
   TSynchronizeDirectory OnSynchronizeDirectory;
+  TSynchronizeStats * Stats;
   TStringList * LocalFileList;
   TStringList * ModifiedRemoteFileList;
   TStringList * NewRemoteFileList;
   const TCopyParamType * CopyParam;
+  TStringList * MatchingLocalFiles;
 };
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::Synchronize(const AnsiString LocalDirectory,
   const AnsiString RemoteDirectory, TSynchronizeMode Mode,
   const TCopyParamType * CopyParam, int Params,
-  TSynchronizeDirectory OnSynchronizeDirectory)
+  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats)
 {
   assert(CopyParam != NULL);
   TCopyParamType SyncCopyParam = *CopyParam;
@@ -2165,7 +2256,7 @@ void __fastcall TTerminal::Synchronize(const AnsiString LocalDirectory,
   try
   {
     DoSynchronizeDirectory(LocalDirectory, RemoteDirectory, Mode,
-      &SyncCopyParam, Params, OnSynchronizeDirectory);
+      &SyncCopyParam, Params, OnSynchronizeDirectory, Stats);
   }
   __finally
   {
@@ -2187,11 +2278,8 @@ void __fastcall TTerminal::DoSynchronizeProgress(const TSynchronizeData & Data)
 void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirectory,
   const AnsiString RemoteDirectory, TSynchronizeMode Mode,
   const TCopyParamType * CopyParam, int Params,
-  TSynchronizeDirectory OnSynchronizeDirectory)
+  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats)
 {
-  TSearchRec SearchRec;
-  bool Found;
-  bool Delete = (Params & spDelete) != 0;
   TSynchronizeData Data;
 
   Data.LocalDirectory = IncludeTrailingBackslash(LocalDirectory);
@@ -2203,6 +2291,7 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
   Data.NewRemoteFileList = NULL;
   Data.ModifiedRemoteFileList = NULL;
   Data.CopyParam = CopyParam;
+  Data.Stats = Stats;
   TStrings * LocalFileList = NULL;
 
   LogEvent(FORMAT("Synchronizing local directory '%s' with remote directory '%s', "
@@ -2216,11 +2305,14 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
 
   try
   {
+    bool Found;
+    TSearchRec SearchRec;
     Data.LocalFileList = new TStringList();
     Data.LocalFileList->Sorted = true;
     Data.LocalFileList->CaseSensitive = false;
     Data.NewRemoteFileList = new TStringList();
     Data.ModifiedRemoteFileList = new TStringList();
+    Data.MatchingLocalFiles = new TStringList();
     LocalFileList = new TStringList();
 
     FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (LocalDirectory)),
@@ -2236,19 +2328,35 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
         while (Found)
         {
           FileName = SearchRec.Name;
+          // add dirs for recursive mode or when we are interested in newly
+          // added subdirs
           if ((FileName != ".") && (FileName != "..") &&
               (FLAGCLEAR(SearchRec.Attr, faDirectory) ||
-               FLAGCLEAR(Params, spNoRecurse)) &&
-              CopyParam->AllowTransfer(FileName))
+               FLAGCLEAR(Params, spNoRecurse) || FLAGSET(Params, spSubDirs)) &&
+              CopyParam->AllowTransfer(Data.LocalDirectory + FileName, osLocal))
           {
             TSynchronizeFileData * FileData = new TSynchronizeFileData;
             FileData->Time = SearchRec.Time;
-            FileData->SizeHigh = SearchRec.FindData.nFileSizeHigh;
-            FileData->SizeLow = SearchRec.FindData.nFileSizeLow;
+            FileData->Size =
+              (static_cast<__int64>(SearchRec.FindData.nFileSizeHigh) << 32) +
+              SearchRec.FindData.nFileSizeLow;
             FileData->Attr = SearchRec.Attr;
             FileData->LastWriteTime = SearchRec.FindData.ftLastWriteTime;
-            FileData->New = true;
+            if (FLAGSET(SearchRec.Attr, faDirectory) && FLAGSET(Params, spSubDirs) &&
+                FLAGSET(Params, spExistingOnly))
+            {
+              // when "existing only" is set to "keep up to date" (non-recursive sync),
+              // we must still add local directory to list, so that the remote one
+              // is not deleted as obsolete, but must clear its "new" attribute
+              // not to upload it.
+              FileData->New = false;
+            }
+            else
+            {
+              FileData->New = true;
+            }
             FileData->Modified = false;
+            FileData->MatchingRemoteFile = NULL;
             Data.LocalFileList->AddObject(FileName,
               reinterpret_cast<TObject*>(FileData));
           }
@@ -2276,6 +2384,7 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
       ProcessDirectory(RemoteDirectory, SynchronizeFile, &Data,
         FLAGSET(Params, spUseCache));
 
+      int LocalDirs = 0;
       TSynchronizeFileData * FileData;
       for (int Index = 0; Index < Data.LocalFileList->Count; Index++)
       {
@@ -2285,9 +2394,18 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
         // (i.e. if it is updated or we want to upload even new files)
         // or if we are going to delete it (i.e. all "new"=obsolete files)
         if ((FileData->Modified && ((Mode == smBoth) || (Mode == smRemote))) ||
-            (FileData->New && ((Mode == smLocal) || FLAGCLEAR(Params, spExistingOnly))))
+            (FileData->New &&
+              ((Mode == smLocal) ||
+               (FLAGCLEAR(Params, spExistingOnly) && FLAGCLEAR(Params, spTimestamp)))))
         {
-          LocalFileList->Add(Data.LocalDirectory + Data.LocalFileList->Strings[Index]);
+          LocalFileList->AddObject(Data.LocalDirectory + Data.LocalFileList->Strings[Index],
+            (FLAGSET(Params, spTimestamp) ? reinterpret_cast<TObject*>(FileData) : NULL));
+
+          if (FLAGSET(FileData->Attr, faDirectory))
+          {
+            LocalDirs++;
+            assert(FileData->New && !FileData->Modified);
+          }
         }
       }
 
@@ -2297,16 +2415,27 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
         Data.ModifiedRemoteFileList->AddStrings(Data.NewRemoteFileList);
       }
 
-      int CopyParams = (Params & spNoConfirmation) != 0 ? cpNoConfirmation : 0;
+      int NewRemoteDirectories = 0;
+      if (Stats != NULL)
+      {
+        for (int Index = 0; Index < Data.NewRemoteFileList->Count; Index++)
+        {
+          if (static_cast<TRemoteFile*>(Data.NewRemoteFileList->Objects[Index])->IsDirectory)
+          {
+            NewRemoteDirectories++;
+          }
+        }
+      }
 
+      bool Delete = FLAGSET(Params, spDelete) && FLAGCLEAR(Params, spTimestamp);
       bool Upload = (LocalFileList->Count > 0) &&
         ((Mode == smBoth) || (Mode == smRemote));
       bool DeleteLocal = (LocalFileList->Count > 0) &&
         (Mode == smLocal) && Delete;
       bool Download = (Data.ModifiedRemoteFileList->Count > 0) &&
         ((Mode == smBoth) || (Mode == smLocal));
-      bool DeleteRemote = (Data.NewRemoteFileList->Count > 0) &&
-        (Mode == smRemote) && Delete;
+      bool ObsoleteRemote = (Data.NewRemoteFileList->Count > 0) && (Mode == smRemote);
+      bool DeleteRemote = ObsoleteRemote && Delete;
 
       if (Upload || DeleteLocal || Download || DeleteRemote)
       {
@@ -2315,42 +2444,85 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
           DoSynchronizeProgress(Data);
         }
 
-        if (Upload &&
-            ((FLAGSET(Params, spPreviewChanges) &&
-              (DoQueryUser(FMTLOAD(SYNCHRONIZE_UPLOAD, (LocalFileList->Count, LocalDirectory, RemoteDirectory)),
-                 LocalFileList, qaYes | qaNo, NULL) == qaNo)) ||
-             !CopyToRemote(LocalFileList, RemoteDirectory, CopyParam, CopyParams)))
+        if (FLAGSET(Params, spTimestamp))
         {
-          Abort();
-        }
+          assert(!DeleteLocal);
+          assert(!DeleteRemote);
 
-        if (DeleteLocal &&
-            ((FLAGSET(Params, spPreviewChanges) &&
-              (DoQueryUser(FMTLOAD(SYNCHRONIZE_LOCAL_DELETE, (LocalFileList->Count, LocalDirectory)),
-                 LocalFileList, qaYes | qaNo, NULL) == qaNo)) ||
-             !DeleteLocalFiles(LocalFileList)))
-        {
-          Abort();
-        }
+          if (Download)
+          {
+            assert(Data.ModifiedRemoteFileList->Count == Data.MatchingLocalFiles->Count);
+            ProcessFilesEx(Data.ModifiedRemoteFileList, foSetProperties,
+              SynchronizeLocalTimestamp, &Data, osLocal);
+          }
 
-        if (Download &&
-            ((FLAGSET(Params, spPreviewChanges) &&
-              (DoQueryUser(FMTLOAD(SYNCHRONIZE_DOWNLOAD, (Data.ModifiedRemoteFileList->Count, RemoteDirectory, LocalDirectory)),
-                 Data.ModifiedRemoteFileList, qaYes | qaNo, NULL) == qaNo)) ||
-             !CopyToLocal(Data.ModifiedRemoteFileList, LocalDirectory, CopyParam, CopyParams)))
-        {
-          Abort();
+          if (Upload)
+          {
+            ProcessFiles(LocalFileList, foSetProperties,
+              SynchronizeRemoteTimestamp, &Data);
+          }
         }
-
-        if (DeleteRemote &&
-            ((FLAGSET(Params, spPreviewChanges) &&
-              (DoQueryUser(FMTLOAD(SYNCHRONIZE_REMOTE_DELETE, (Data.NewRemoteFileList->Count, RemoteDirectory)),
-                 Data.NewRemoteFileList, qaYes | qaNo, NULL) == qaNo)) ||
-             !DeleteFiles(Data.NewRemoteFileList)))
+        else
         {
-          Abort();
+          int CopyParams = (Params & spNoConfirmation) != 0 ? cpNoConfirmation : 0;
+          TQueryParams QueryParams(0, HELP_SYNCHRONIZE);
+
+          if (Upload)
+          {
+            if ((FLAGSET(Params, spPreviewChanges) &&
+                 (DoQueryUser(FMTLOAD(SYNCHRONIZE_UPLOAD, (LocalFileList->Count, LocalDirectory, RemoteDirectory)),
+                    LocalFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
+                !CopyToRemote(LocalFileList, RemoteDirectory, CopyParam, CopyParams))
+            {
+              Abort();
+            }
+
+            if (Stats != NULL)
+            {
+              Stats->NewDirectories += LocalDirs;
+            }
+          }
+
+          if (DeleteLocal &&
+              ((FLAGSET(Params, spPreviewChanges) &&
+                (DoQueryUser(FMTLOAD(SYNCHRONIZE_LOCAL_DELETE, (LocalFileList->Count, LocalDirectory)),
+                   LocalFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
+               !DeleteLocalFiles(LocalFileList)))
+          {
+            Abort();
+          }
+
+          if (Download &&
+              ((FLAGSET(Params, spPreviewChanges) &&
+                (DoQueryUser(FMTLOAD(SYNCHRONIZE_DOWNLOAD, (Data.ModifiedRemoteFileList->Count, RemoteDirectory, LocalDirectory)),
+                   Data.ModifiedRemoteFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
+               !CopyToLocal(Data.ModifiedRemoteFileList, LocalDirectory, CopyParam, CopyParams)))
+          {
+            Abort();
+          }
+
+          if (DeleteRemote)
+          {
+            if (Stats != NULL)
+            {
+              Stats->RemovedDirectories += NewRemoteDirectories;
+            }
+
+            if ((FLAGSET(Params, spPreviewChanges) &&
+                 (DoQueryUser(FMTLOAD(SYNCHRONIZE_REMOTE_DELETE, (Data.NewRemoteFileList->Count, RemoteDirectory)),
+                    Data.NewRemoteFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
+                !DeleteFiles(Data.NewRemoteFileList))
+            {
+              Abort();
+            }
+          }
         }
       }
+
+      if (ObsoleteRemote && !DeleteRemote && (Stats != NULL))
+      {
+        Stats->ObsoleteDirectories += NewRemoteDirectories;
+      }
     }
   }
   __finally
@@ -2359,8 +2531,10 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
     {
       for (int Index = 0; Index < Data.LocalFileList->Count; Index++)
       {
-        delete reinterpret_cast<TSynchronizeFileData*>
+        TSynchronizeFileData * FileData = reinterpret_cast<TSynchronizeFileData*>
           (Data.LocalFileList->Objects[Index]);
+        delete FileData->MatchingRemoteFile;
+        delete FileData;
       }
       delete Data.LocalFileList;
     }
@@ -2380,23 +2554,66 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
     }
 
     delete LocalFileList;
+    delete Data.MatchingLocalFiles;
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::SynchronizeLocalTimestamp(const AnsiString FileName,
+  const TRemoteFile * File, void * Param, int Index)
+{
+  TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
+  assert(Data != NULL);
+  AnsiString LocalFile = Data->LocalDirectory + Data->MatchingLocalFiles->Strings[Index];
+
+  FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (LocalFile)),
+    HANDLE Handle;
+    OpenLocalFile(LocalFile, GENERIC_WRITE, NULL, &Handle,
+      NULL, NULL, NULL, NULL);
+    FILETIME WrTime = DateTimeToFileTime(File->Modification,
+      SessionData->ConsiderDST);
+    bool Result = SetFileTime(Handle, NULL, NULL, &WrTime);
+    CloseHandle(Handle);
+    if (!Result)
+    {
+      Abort();
+    }
+  );
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::SynchronizeRemoteTimestamp(const AnsiString FileName,
+  const TRemoteFile * File, void * Param)
+{
+  TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
+  assert(Data != NULL);
+  const TSynchronizeFileData * FileData = 
+    reinterpret_cast<const TSynchronizeFileData *>(File);
+  const TRemoteFile * RemoteFile = FileData->MatchingRemoteFile;
+  assert(RemoteFile != NULL);
+              
+  TRemoteProperties Properties;
+  Properties.Valid << vpModification;
+  Properties.Modification = ConvertTimestampToUnix(FileData->LastWriteTime, 
+    SessionData->ConsiderDST);
+
+  ChangeFileProperties(Data->RemoteDirectory + RemoteFile->FileName,
+    RemoteFile, &Properties);
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
   const TRemoteFile * File, /*TSynchronizeData*/ void * Param)
 {
   TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
 
-  if (Data->CopyParam->AllowTransfer(File->FileName))
+  if (Data->CopyParam->AllowTransfer(
+        UnixExcludeTrailingBackslash(File->FullFileName), osRemote))
   {
     bool Modified = false;
-    int Index = Data->LocalFileList->IndexOf(File->FileName);
-    bool New = (Index < 0);
+    int LocalIndex = Data->LocalFileList->IndexOf(File->FileName);
+    bool New = (LocalIndex < 0);
     if (!New)
     {
       TSynchronizeFileData * LocalData =
-        reinterpret_cast<TSynchronizeFileData *>(Data->LocalFileList->Objects[Index]);
+        reinterpret_cast<TSynchronizeFileData *>(Data->LocalFileList->Objects[LocalIndex]);
 
       LocalData->New = false;
 
@@ -2415,15 +2632,46 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
         TDateTime LocalTime = SystemTimeToDateTime(SystemLastWriteTime);
         TDateTime RemoteTime = File->Modification;
 
-        UnifyDateTimePrecision(LocalTime, RemoteTime);
+        ReduceDateTimePrecision(LocalTime, File->ModificationFmt);
 
-        if (LocalTime < RemoteTime)
+        bool LocalModified = false;
+        if (FLAGCLEAR(Data->Params, spNotByTime) &&
+            (CompareFileTime(LocalTime, RemoteTime) < 0))
+        {
+          if (FLAGCLEAR(Data->Params, spTimestamp) ||
+              (Data->Mode == smBoth) || (Data->Mode == smLocal))
+          {
+            Modified = true;
+          }
+          else
+          {
+            LocalModified = true;
+          }
+        }
+        else if (FLAGCLEAR(Data->Params, spNotByTime) &&
+                 (CompareFileTime(LocalTime, RemoteTime) > 0))
+        {
+          if (FLAGCLEAR(Data->Params, spTimestamp) ||
+              (Data->Mode == smBoth) || (Data->Mode == smRemote))
+          {
+            LocalModified = true;
+          }
+          else
+          {
+            Modified = true;
+          }
+        }
+        else if (FLAGSET(Data->Params, spBySize) &&
+                 (LocalData->Size != File->Size))
         {
           Modified = true;
+          LocalModified = true;
         }
-        else if (LocalTime > RemoteTime)
+
+        if (LocalModified)
         {
           LocalData->Modified = true;
+          LocalData->MatchingRemoteFile = const_cast<TRemoteFile *>(File)->Duplicate();
         }
       }
       else if (FLAGCLEAR(Data->Params, spNoRecurse))
@@ -2431,22 +2679,34 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
         DoSynchronizeDirectory(
           Data->LocalDirectory + File->FileName,
           Data->RemoteDirectory + File->FileName,
-          Data->Mode, Data->CopyParam, Data->Params, Data->OnSynchronizeDirectory);
+          Data->Mode, Data->CopyParam, Data->Params, Data->OnSynchronizeDirectory,
+          Data->Stats);
       }
     }
     else
     {
-      New = !File->IsDirectory || FLAGCLEAR(Data->Params, spNoRecurse);
+      New = !File->IsDirectory || FLAGCLEAR(Data->Params, spNoRecurse) ||
+        FLAGSET(Data->Params, spSubDirs);
     }
 
     if (New || Modified)
     {
       assert(!New || !Modified);
 
-      TStringList * FileList = New ? Data->NewRemoteFileList :
-        Data->ModifiedRemoteFileList;
-      FileList->AddObject(FileName,
-        const_cast<TRemoteFile *>(File)->Duplicate());
+      if (New)
+      {
+        Data->NewRemoteFileList->AddObject(FileName,
+          const_cast<TRemoteFile *>(File)->Duplicate());
+      }
+      else
+      {
+        Data->ModifiedRemoteFileList->AddObject(FileName,
+          const_cast<TRemoteFile *>(File)->Duplicate());
+        assert(LocalIndex >= 0);
+        Data->MatchingLocalFiles->AddObject(
+          Data->LocalFileList->Strings[LocalIndex],
+          Data->LocalFileList->Objects[LocalIndex]);
+      }
     }
   }
 }
@@ -2475,7 +2735,7 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
 
     TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
     OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osLocal,
-      FilesToCopy->Count, Params & cpDragDrop, TargetDir);
+      FilesToCopy->Count, Params & cpTemporary, TargetDir);
 
     OperationProgress.YesToNewer = FLAGSET(Params, cpNewerOnly);
 
@@ -2575,7 +2835,7 @@ bool __fastcall TTerminal::CopyToLocal(TStrings * FilesToCopy,
       }
 
       OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osRemote,
-        FilesToCopy->Count, Params & cpDragDrop, TargetDir);
+        FilesToCopy->Count, Params & cpTemporary, TargetDir);
 
       OperationProgress.YesToNewer = FLAGSET(Params, cpNewerOnly);
 
@@ -2736,6 +2996,21 @@ TTerminal * __fastcall TTerminalList::GetTerminal(int Index)
   return dynamic_cast<TTerminal *>(Items[Index]);
 }
 //---------------------------------------------------------------------------
+int __fastcall TTerminalList::GetActiveCount()
+{
+  int Result = 0;
+  TTerminal * Terminal;
+  for (int i = 0; i < Count; i++)
+  {
+    Terminal = Terminals[i];
+    if (Terminal->Active)
+    {
+      Result++;
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminalList::Idle()
 {
   TTerminal * Terminal;

+ 66 - 19
core/Terminal.h

@@ -16,12 +16,15 @@ class TCustomFileSystem;
 struct TCalculateSizeParams;
 struct TOverwriteFileParams;
 struct TSynchronizeData;
+struct TSynchronizeStats;
 typedef TStringList TUsersGroupsList;
 typedef void __fastcall (__closure *TReadDirectoryEvent)(System::TObject* Sender, Boolean ReloadOnly);
 typedef void __fastcall (__closure *TReadDirectoryProgressEvent)(
   System::TObject* Sender, int Progress);
 typedef void __fastcall (__closure *TProcessFileEvent)
   (const AnsiString FileName, const TRemoteFile * File, void * Param);
+typedef void __fastcall (__closure *TProcessFileEventEx)
+  (const AnsiString FileName, const TRemoteFile * File, void * Param, int Index);
 typedef int __fastcall (__closure *TFileOperationEvent)
   (void * Param1, void * Param2);
 typedef void __fastcall (__closure *TSynchronizeDirectory)
@@ -57,19 +60,37 @@ typedef int __fastcall (__closure *TDirectoryModifiedEvent)
     {                                                                       \
       throw;                                                                \
     }                                                                       \
-    catch (EFatal & E)                                                \
+    catch (EFatal & E)                                                      \
     {                                                                       \
       throw;                                                                \
     }                                                                       \
     catch (Exception & E)                                                   \
     { \
       TERMINAL->DoHandleExtendedException(&E); \
-      int Answers = qaRetry | qaAbort | ((ALLOW_SKIP) ? qaSkip : 0); \
-      int Answer; \
-      TQueryParams Params(qpAllowContinueOnError | (!(ALLOW_SKIP) ? qpFatalAbort : 0)); \
-      SUSPEND_OPERATION ( \
-        Answer = TERMINAL->DoQueryUser(MESSAGE, &E, Answers, &Params); \
-      ); \
+      int Answer;                                                           \
+      if ((ALLOW_SKIP) && OperationProgress->SkipToAll)                     \
+        Answer = qaSkip;                                                    \
+      else                                                                  \
+      {                                                                     \
+        int Answers = qaRetry | qaAbort | ((ALLOW_SKIP) ? (qaSkip | qaAll) : 0); \
+        TQueryParams Params(qpAllowContinueOnError | (!(ALLOW_SKIP) ? qpFatalAbort : 0)); \
+        TQueryButtonAlias Aliases[1];                                       \
+        if (FLAGSET(Answers, qaAll))                                        \
+        {                                                                   \
+          Aliases[0].Button = qaAll;                                        \
+          Aliases[0].Alias = LoadStr(SKIP_ALL_BUTTON);                      \
+          Params.Aliases = Aliases;                                         \
+          Params.AliasesCount = LENOF(Aliases);                             \
+        }                                                                   \
+        SUSPEND_OPERATION (                                                 \
+          Answer = TERMINAL->DoQueryUser(MESSAGE, &E, Answers, &Params, qtError); \
+        );                                                                  \
+        if (Answer == qaAll)                                                \
+        {                                                                   \
+          OperationProgress->SkipToAll = true;                              \
+          Answer = qaSkip;                                                  \
+        }                                                                   \
+      }                                                                     \
       DoRepeat = (Answer == qaRetry); \
       if (Answer == qaAbort) OperationProgress->Cancel = csCancel; \
       if (!DoRepeat && ALLOW_SKIP) THROW_SKIP_FILE(&E, MESSAGE); \
@@ -83,12 +104,12 @@ typedef int __fastcall (__closure *TDirectoryModifiedEvent)
 //---------------------------------------------------------------------------
 enum TFSCapability { fcUserGroupListing, fcModeChanging, fcGroupChanging,
   fcOwnerChanging, fcAnyCommand, fcHardLink, fcSymbolicLink, fcResolveSymlink,
-  fcTextMode, fcRename, fcNativeTextMode, fcNewerOnlyUpload, fcRemoteCopy };
+  fcTextMode, fcRename, fcNativeTextMode, fcNewerOnlyUpload, fcRemoteCopy,
+  fcTimestampChanging, fcRemoteMove };
 enum TCurrentFSProtocol { cfsUnknown, cfsSCP, cfsSFTP };
 //---------------------------------------------------------------------------
 const cpDelete = 0x01;
-const cpDragDrop = 0x04;
-const cpTemporary = 0x04; // alias to cpDragDrop
+const cpTemporary = 0x04;
 const cpNoConfirmation = 0x08;
 const cpNewerOnly = 0x10;
 //---------------------------------------------------------------------------
@@ -103,13 +124,17 @@ class TTerminal : public TSecureShell
 public:
   // TScript::SynchronizeProc relies on the order
   enum TSynchronizeMode { smRemote, smLocal, smBoth };
-  static const spDelete = 0x01;
-  static const spNoConfirmation = 0x02;
-  static const spExistingOnly = 0x04;
+  static const spDelete = 0x01; // cannot be combined with spTimestamp and spBySize
+  static const spNoConfirmation = 0x02; // has no effect for spTimestamp
+  static const spExistingOnly = 0x04; // is implicit for spTimestamp
   static const spNoRecurse = 0x08;
-  static const spUseCache = 0x10;
-  static const spDelayProgress = 0x20;
-  static const spPreviewChanges = 0x40;
+  static const spUseCache = 0x10; // cannot be combined with spTimestamp
+  static const spDelayProgress = 0x20; // cannot be combined with spTimestamp
+  static const spPreviewChanges = 0x40; // has no effect for spTimestamp
+  static const spSubDirs = 0x80; // cannot be combined with spTimestamp
+  static const spTimestamp = 0x100;
+  static const spNotByTime = 0x200; // cannot be combined with spTimestamp and smBoth
+  static const spBySize = 0x400; // cannot be combined with spTimestamp and smBoth
 
 // for TranslateLockedPath()
 friend class TRemoteFile;
@@ -193,7 +218,10 @@ protected:
     const AnsiString Message, void * Param1 = NULL, void * Param2 = NULL);
   bool __fastcall GetIsCapable(TFSCapability Capability) const;
   bool __fastcall ProcessFiles(TStrings * FileList, TFileOperation Operation,
-    TProcessFileEvent ProcessFile, void * Param = NULL, TOperationSide Side = osRemote);
+    TProcessFileEvent ProcessFile, void * Param = NULL, TOperationSide Side = osRemote,
+    bool Ex = false);
+  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);
   AnsiString __fastcall TranslateLockedPath(AnsiString Path, bool Lock);
@@ -221,14 +249,19 @@ protected:
   void __fastcall DoSynchronizeDirectory(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
     const TCopyParamType * CopyParam, int Params,
-    TSynchronizeDirectory OnSynchronizeDirectory);
+    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats);
   void __fastcall SynchronizeFile(const AnsiString FileName,
     const TRemoteFile * File, /*TSynchronizeData*/ void * Param);
+  void __fastcall SynchronizeRemoteTimestamp(const AnsiString FileName,
+    const TRemoteFile * File, void * Param);
+  void __fastcall SynchronizeLocalTimestamp(const AnsiString FileName,
+    const TRemoteFile * File, void * Param, int Index);
   void __fastcall DoSynchronizeProgress(const TSynchronizeData & Data);
   void __fastcall DeleteLocalFile(AnsiString FileName,
     const TRemoteFile * File, void * Param);
   void __fastcall RecycleFile(AnsiString FileName, const TRemoteFile * File);
   bool __fastcall IsRecycledFile(AnsiString FileName);
+  TStrings * __fastcall GetFixedPaths();
 
   __property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
 
@@ -293,7 +326,7 @@ public:
   void __fastcall Synchronize(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
     const TCopyParamType * CopyParam, int Params,
-    TSynchronizeDirectory OnSynchronizeDirectory);
+    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats);
   bool __fastcall DirectoryFileList(const AnsiString Path,
     TRemoteFileList *& FileList, bool CanLoad);
   void __fastcall MakeLocalFileList(const AnsiString FileName, 
@@ -327,6 +360,7 @@ public:
   __property bool CommandSessionOpened = { read = GetCommandSessionOpened };
   __property TTerminal * CommandSession = { read = GetCommandSession };
   __property bool AutoReadDirectory = { read = FAutoReadDirectory, write = FAutoReadDirectory };
+  __property TStrings * FixedPaths = { read = GetFixedPaths };
 };
 //---------------------------------------------------------------------------
 class TSecondaryTerminal : public TTerminal
@@ -359,11 +393,13 @@ public:
   virtual void __fastcall Idle();
 
   __property TTerminal * Terminals[int Index]  = { read=GetTerminal };
+  __property int ActiveCount = { read = GetActiveCount };
 
 private:
   TConfiguration * FConfiguration;
 
   TTerminal * __fastcall GetTerminal(int Index);
+  int __fastcall GetActiveCount();
 };
 //---------------------------------------------------------------------------
 struct TCustomCommandParams
@@ -395,4 +431,15 @@ struct TMakeLocalFileListParams
   bool Recursive;
 };
 //---------------------------------------------------------------------------
+struct TSynchronizeStats
+{
+  TSynchronizeStats();
+
+  // currently we do not need any other stats
+  // (these are for keep remote directory up to date)
+  int NewDirectories;
+  int RemovedDirectories;
+  int ObsoleteDirectories;
+};
+//---------------------------------------------------------------------------
 #endif

+ 12 - 10
forms/About.cpp

@@ -6,15 +6,16 @@
 //---------------------------------------------------------------------
 #include <VCLCommon.h>
 #include <Common.h>
-#include "About.h"
+#include <Tools.h>
 #include "WinInterface.h"
+#include "About.h"
 #include "TextsCore.h"
 #include "TextsWin.h"
 //---------------------------------------------------------------------
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------------
 __fastcall TAboutDialog::TAboutDialog(TComponent* AOwner)
-	: TForm(AOwner)
+  : TForm(AOwner)
 {
   ThirdPartyBox->VertScrollBar->Position = 0;
   UseSystemSettings(this);
@@ -22,6 +23,8 @@ __fastcall TAboutDialog::TAboutDialog(TComponent* AOwner)
   LinkLabel(ForumUrlLabel);
   LinkLabel(PuttyLicenceLabel);
   LinkLabel(PuttyHomepageLabel);
+  LinkLabel(Toolbar2000HomepageLabel);
+  LinkLabel(TBXHomepageLabel);
   ApplicationLabel->Caption = AppName;
   HomepageLabel->Caption = LoadStr(HOMEPAGE_URL);
   ForumUrlLabel->Caption = LoadStr(FORUM_URL);
@@ -46,20 +49,14 @@ void __fastcall TAboutDialog::LoadData()
   VersionLabel->Caption = Configuration->VersionStr;
 }
 //---------------------------------------------------------------------------
-void __fastcall TAboutDialog::OpenAddress(const AnsiString Address)
-{
-  ShellExecute(Handle, "open", Address.c_str(),
-    NULL, NULL, SW_SHOWNORMAL);
-} 
-//---------------------------------------------------------------------------
 void __fastcall TAboutDialog::HomepageLabelClick(TObject * Sender)
 {
-  OpenAddress(((TLabel*)Sender)->Caption);
+  OpenBrowser(((TLabel*)Sender)->Caption);
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::EmailLabelClick(TObject * Sender)
 {
-  OpenAddress("mailto:" + ((TLabel*)Sender)->Caption);
+  OpenBrowser("mailto:" + ((TLabel*)Sender)->Caption);
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::DisplayLicence(TObject * Sender)
@@ -82,4 +79,9 @@ void __fastcall TAboutDialog::SetAllowLicence(bool value)
   LicenceButton->Visible = value;
 }
 //---------------------------------------------------------------------------
+void __fastcall TAboutDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 84 - 12
forms/About.dfm

@@ -52,10 +52,10 @@ object AboutDialog: TAboutDialog
   object HomepageLabel: TLabel
     Left = 72
     Top = 72
-    Width = 188
+    Width = 129
     Height = 13
     Cursor = crHandPoint
-    Caption = 'http://XXXXXXwinscp.sourceforge.net/'
+    Caption = 'http://XXXXXXwinscp.net/'
     Color = clBtnFace
     Font.Charset = DEFAULT_CHARSET
     Font.Color = clBlue
@@ -76,10 +76,10 @@ object AboutDialog: TAboutDialog
   object ForumUrlLabel: TLabel
     Left = 72
     Top = 112
-    Width = 205
+    Width = 146
     Height = 13
     Cursor = crHandPoint
-    Caption = 'http://XXXXwinscp.sourceforge.net/forum/'
+    Caption = 'http://XXXXwinscp.net/forum/'
     Font.Charset = DEFAULT_CHARSET
     Font.Color = clBlue
     Font.Height = -11
@@ -103,7 +103,7 @@ object AboutDialog: TAboutDialog
     Anchors = [akLeft, akTop, akBottom]
     BevelOuter = bvNone
     Color = clTeal
-    TabOrder = 2
+    TabOrder = 3
     DesignSize = (
       49
       318)
@@ -229,7 +229,7 @@ object AboutDialog: TAboutDialog
     HorzScrollBar.Visible = False
     VertScrollBar.Smooth = True
     Anchors = [akLeft, akRight, akBottom]
-    TabOrder = 3
+    TabOrder = 4
     DesignSize = (
       285
       137)
@@ -285,21 +285,21 @@ object AboutDialog: TAboutDialog
     end
     object Label8: TLabel
       Left = 8
-      Top = 152
+      Top = 264
       Width = 181
       Height = 13
       Caption = 'Filemanager Toolset library Version 2.6'
     end
     object Label10: TLabel
       Left = 8
-      Top = 168
+      Top = 280
       Width = 137
       Height = 13
       Caption = 'Copyright '#169' 1999 Ingo Eckel'
     end
     object ProlongBoxLabel: TLabel
       Left = 8
-      Top = 184
+      Top = 296
       Width = 63
       Height = 13
       Caption = '                     '
@@ -323,9 +323,71 @@ object AboutDialog: TAboutDialog
       ParentFont = False
       OnClick = DisplayLicence
     end
+    object Label1: TLabel
+      Left = 8
+      Top = 152
+      Width = 117
+      Height = 13
+      Caption = 'Toolbar2000 library 2.1.5'
+    end
+    object Label2: TLabel
+      Left = 8
+      Top = 168
+      Width = 182
+      Height = 13
+      Caption = 'Copyright '#169' 1998-2005 Jordan Russell'
+    end
+    object Toolbar2000HomepageLabel: TLabel
+      Left = 8
+      Top = 184
+      Width = 176
+      Height = 13
+      Cursor = crHandPoint
+      Caption = 'http://www.jrsoftware.org/tb2kdl.php'
+      Color = clBtnFace
+      Font.Charset = DEFAULT_CHARSET
+      Font.Color = clBlue
+      Font.Height = -11
+      Font.Name = 'MS Sans Serif'
+      Font.Style = [fsUnderline]
+      ParentColor = False
+      ParentFont = False
+      OnClick = HomepageLabelClick
+    end
+    object Label5: TLabel
+      Left = 8
+      Top = 208
+      Width = 69
+      Height = 13
+      Caption = 'TBX library 2.1'
+    end
+    object Label6: TLabel
+      Left = 8
+      Top = 224
+      Width = 188
+      Height = 13
+      Caption = 'Copyright '#169' 2001-2005 Alex A. Denisov'
+    end
+    object TBXHomepageLabel: TLabel
+      Left = 8
+      Top = 240
+      Width = 118
+      Height = 13
+      Cursor = crHandPoint
+      Caption = 'http://www.g32.org/tbx/'
+      Color = clBtnFace
+      Font.Charset = DEFAULT_CHARSET
+      Font.Color = clBlue
+      Font.Height = -11
+      Font.Name = 'MS Sans Serif'
+      Font.Style = [fsUnderline]
+      ParentColor = False
+      ParentFont = False
+      OnClick = HomepageLabelClick
+    end
   end
   object OKButton: TButton
-    Left = 303
+    Left = 221
     Top = 303
     Width = 75
     Height = 25
@@ -337,13 +399,23 @@ object AboutDialog: TAboutDialog
     TabOrder = 0
   end
   object LicenceButton: TButton
-    Left = 215
+    Left = 72
     Top = 303
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Caption = '&Licence...'
-    TabOrder = 1
+    TabOrder = 2
     OnClick = LicenceButtonClick
   end
+  object HelpButton: TButton
+    Left = 303
+    Top = 303
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 1
+    OnClick = HelpButtonClick
+  end
 end

+ 8 - 2
forms/About.h

@@ -40,11 +40,19 @@ __published:
   TButton *LicenceButton;
   TLabel *PuttyLicenceLabel;
   TLabel *TranslatorLabel;
+  TLabel *Label1;
+  TLabel *Label2;
+  TLabel *Toolbar2000HomepageLabel;
+  TLabel *Label5;
+  TLabel *Label6;
+  TLabel *TBXHomepageLabel;
+  TButton *HelpButton;
   void __fastcall HomepageLabelClick(TObject *Sender);
   void __fastcall EmailLabelClick(TObject *Sender);
   void __fastcall DisplayLicence(TObject *Sender);
   void __fastcall LicenceButtonClick(TObject *Sender);
   bool __fastcall GetAllowLicence();
+  void __fastcall HelpButtonClick(TObject *Sender);
 private:
   TConfiguration * FConfiguration;
   void __fastcall SetConfiguration(TConfiguration * value);
@@ -54,8 +62,6 @@ public:
   void __fastcall LoadData();
   __property TConfiguration * Configuration  = { read=FConfiguration, write=SetConfiguration };
   __property bool AllowLicence = { read=GetAllowLicence, write=SetAllowLicence };
-protected:
-  void __fastcall OpenAddress(const AnsiString Address);
 };
 //----------------------------------------------------------------------------
 #endif

+ 6 - 0
forms/Cleanup.cpp

@@ -168,3 +168,9 @@ Boolean __fastcall TCleanupDialog::GetCleanupData(TWinSCPData Data)
   }
   return False;
 }
+//---------------------------------------------------------------------------
+void __fastcall TCleanupDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------

+ 12 - 2
forms/Cleanup.dfm

@@ -34,7 +34,7 @@ object CleanupDialog: TCleanupDialog
     WordWrap = True
   end
   object OKButton: TButton
-    Left = 329
+    Left = 249
     Top = 268
     Width = 75
     Height = 25
@@ -45,7 +45,7 @@ object CleanupDialog: TCleanupDialog
     TabOrder = 2
   end
   object CancelButton: TButton
-    Left = 409
+    Left = 329
     Top = 268
     Width = 75
     Height = 25
@@ -103,4 +103,14 @@ object CleanupDialog: TCleanupDialog
     TabOrder = 1
     OnClick = CheckAllButtonClick
   end
+  object HelpButton: TButton
+    Left = 409
+    Top = 268
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 4
+    OnClick = HelpButtonClick
+  end
 end

+ 2 - 0
forms/Cleanup.h

@@ -29,6 +29,7 @@ __published:
   TListView *DataListView;
   TLabel *Label1;
   TButton *CheckAllButton;
+  TButton *HelpButton;
   void __fastcall DataListViewMouseDown(TObject *Sender,
     TMouseButton Button, TShiftState Shift, int X, int Y);
   void __fastcall DataListViewKeyUp(TObject *Sender, WORD &Key,
@@ -37,6 +38,7 @@ __published:
   void __fastcall CheckAllButtonClick(TObject *Sender);
   void __fastcall DataListViewInfoTip(TObject *Sender,
     TListItem *Item, AnsiString &InfoTip);
+  void __fastcall HelpButtonClick(TObject *Sender);
 private:
   TStoredSessionList *FSessionList;
   TConfiguration * FConfiguration;

+ 30 - 8
forms/ComboInput.cpp

@@ -30,10 +30,7 @@ bool __fastcall DoComboInputDialog(
     ComboInputDialog->Items = Items;
     ComboInputDialog->AllowEmpty = AllowEmpty;
     ComboInputDialog->OnCloseQuery = OnCloseQuery;
-    if (!HelpKeyword.IsEmpty())
-    {
-      ComboInputDialog->HelpKeyword = HelpKeyword;
-    }
+    ComboInputDialog->HelpKeyword = HelpKeyword;
     Result = (ComboInputDialog->ShowModal() == mrOk);
     if (Result)
     {
@@ -74,7 +71,7 @@ AnsiString __fastcall DoSaveSessionDialog(
     SaveSessionDialog->Caption = LoadStr(SAVE_SESSION_CAPTION);
     SaveSessionDialog->Prompt = LoadStr(SAVE_SESSION_PROMPT);
     SaveSessionDialog->OnCloseQuery = SaveSessionDialog->StoredSessionsCloseQuery;
-    SaveSessionDialog->HelpKeyword = HELP_UI_LOGIN_SAVE;
+    SaveSessionDialog->HelpKeyword = HELP_SESSION_SAVE;
     if (SaveSessionDialog->ShowModal() == mrOk)
     {
       Result = SaveSessionDialog->Text;
@@ -89,7 +86,7 @@ AnsiString __fastcall DoSaveSessionDialog(
 }
 //---------------------------------------------------------------------
 __fastcall TComboInputDialog::TComboInputDialog(TComponent* AOwner)
-	: TForm(AOwner)
+  : TForm(AOwner)
 {
   FAllowEmpty = false;
   UseSystemSettings(this);
@@ -143,20 +140,45 @@ void __fastcall TComboInputDialog::StoredSessionsCloseQuery(TObject * /*Sender*/
   CanClose = true;
   if (ModalResult == mrOk)
   {
+    TSessionData::ValidateName(Text);
+
     assert(StoredSessions);
     TSessionData * Data = (TSessionData *)StoredSessions->FindByName(Text);
     if (Data && Data->Special)
     {
       MessageDialog(FMTLOAD(CANNOT_OVERWRITE_SPECIAL_SESSION, (Text)),
-        qtError, qaOK, 0);
+        qtError, qaOK, HELP_NONE);
       CanClose = false;
     }
     else if (Data &&
       MessageDialog(FMTLOAD(CONFIRM_OVERWRITE_SESSION, (Text)),
-        qtConfirmation, qaYes | qaNo, 0) != qaYes)
+        qtConfirmation, qaYes | qaNo, HELP_SESSION_SAVE_OVERWRITE) != qaYes)
     {
       CanClose = false;
     }
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TComboInputDialog::FormShow(TObject * /*Sender*/)
+{
+  TBorderIcons BI = BorderIcons;
+  if (HelpKeyword.IsEmpty())
+  {
+    BI >> biHelp;
+
+    OKButton->Left = CancelButton->Left;
+    CancelButton->Left = HelpButton->Left;
+    HelpButton->Visible = false;
+  }
+  else
+  {
+    BI << biHelp;
+  }
+  BorderIcons = BI;
+}
+//---------------------------------------------------------------------------
+void __fastcall TComboInputDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------

+ 16 - 5
forms/ComboInput.dfm

@@ -5,13 +5,14 @@ object ComboInputDialog: TComboInputDialog
   BorderStyle = bsDialog
   Caption = 'Save session as'
   ClientHeight = 86
-  ClientWidth = 269
+  ClientWidth = 326
   Color = clBtnFace
   ParentFont = True
   OldCreateOrder = True
   Position = poOwnerFormCenter
+  OnShow = FormShow
   DesignSize = (
-    269
+    326
     86)
   PixelsPerInch = 96
   TextHeight = 13
@@ -24,7 +25,7 @@ object ComboInputDialog: TComboInputDialog
     FocusControl = InputCombo
   end
   object OKButton: TButton
-    Left = 100
+    Left = 69
     Top = 54
     Width = 75
     Height = 25
@@ -35,7 +36,7 @@ object ComboInputDialog: TComboInputDialog
     TabOrder = 1
   end
   object CancelButton: TButton
-    Left = 188
+    Left = 157
     Top = 54
     Width = 75
     Height = 25
@@ -48,7 +49,7 @@ object ComboInputDialog: TComboInputDialog
   object InputCombo: TComboBox
     Left = 8
     Top = 24
-    Width = 255
+    Width = 312
     Height = 21
     Anchors = [akLeft, akTop, akRight]
     ItemHeight = 13
@@ -56,4 +57,14 @@ object ComboInputDialog: TComboInputDialog
     TabOrder = 0
     OnChange = InputComboChange
   end
+  object HelpButton: TButton
+    Left = 244
+    Top = 54
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 3
+    OnClick = HelpButtonClick
+  end
 end

+ 3 - 0
forms/ComboInput.h

@@ -22,7 +22,10 @@ __published:
   TButton * CancelButton;
   TComboBox * InputCombo;
   TLabel *InputLabel;
+  TButton *HelpButton;
   void __fastcall InputComboChange(TObject * Sender);
+  void __fastcall FormShow(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
 
 public:
   virtual __fastcall TComboInputDialog(TComponent * AOwner);

+ 11 - 6
forms/Console.cpp

@@ -4,6 +4,7 @@
 
 #include <Common.h>
 
+#include "WinInterface.h"
 #include "Console.h"
 
 #include <TextsWin.h>
@@ -132,7 +133,7 @@ bool __fastcall TConsoleDialog::Execute(const AnsiString Command,
         TStrings * ALog = const_cast<TStrings *>(Log);
         for (int i = 0; i < ALog->Count; i++)
         {
-          AddLine(ALog->Strings[i]);
+          AddLine(llOutput, ALog->Strings[i]);
         }
       }
       __finally
@@ -219,20 +220,19 @@ void __fastcall TConsoleDialog::CommandEditChange(TObject * /*Sender*/)
 }
 //---------------------------------------------------------------------------
 void __fastcall TConsoleDialog::DoLogAddLine(TObject* /*Sender*/,
-  const AnsiString AddedLine)
+  TLogLineType Type, const AnsiString AddedLine)
 {
   if (FAddOutput)
   {
-    AddLine(AddedLine);
+    AddLine(Type, AddedLine);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleDialog::AddLine(AnsiString Line)
+void __fastcall TConsoleDialog::AddLine(TLogLineType Type, AnsiString Line)
 {
-  if (!Line.IsEmpty() && (Line[1] == '<' || Line[1] == '!'))
+  if (!Line.IsEmpty() && (Type == llOutput || Type == llStdError))
   {
     int ReturnCode;
-    Line.Delete(1, 2);
     if (!TSCPFileSystem::RemoveLastLine(Line, ReturnCode) ||
         !Line.IsEmpty())
     {
@@ -247,4 +247,9 @@ void __fastcall TConsoleDialog::CreateParams(TCreateParams & Params)
   Params.Style = Params.Style & ~WS_SYSMENU;
 }
 //---------------------------------------------------------------------------
+void __fastcall TConsoleDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 25 - 13
forms/Console.dfm

@@ -3,7 +3,9 @@ object ConsoleDialog: TConsoleDialog
   Top = 169
   Width = 567
   Height = 431
-  BorderIcons = [biSystemMenu, biMaximize]
+  HelpType = htKeyword
+  HelpKeyword = 'ui_console'
+  BorderIcons = [biSystemMenu, biMaximize, biHelp]
   Caption = 'Console'
   Color = clBtnFace
   Constraints.MinHeight = 250
@@ -20,13 +22,13 @@ object ConsoleDialog: TConsoleDialog
     Left = 0
     Top = 0
     Width = 559
-    Height = 81
+    Height = 78
     Align = alTop
     Shape = bsBottomLine
   end
   object Label1: TLabel
     Left = 13
-    Top = 31
+    Top = 13
     Width = 74
     Height = 13
     Caption = 'Enter &command'
@@ -34,22 +36,22 @@ object ConsoleDialog: TConsoleDialog
   end
   object Label2: TLabel
     Left = 13
-    Top = 55
+    Top = 56
     Width = 80
     Height = 13
     Caption = 'Current directory:'
   end
   object Label4: TLabel
     Left = 13
-    Top = 7
+    Top = 34
     Width = 286
     Height = 13
     Caption = 'Warning: Do NOT execute commands that require user-input'
   end
   object DirectoryLabel: TPathLabel
     Left = 120
-    Top = 55
-    Width = 426
+    Top = 56
+    Width = 337
     Height = 13
     IndentHorizontal = 0
     IndentVertical = 0
@@ -59,20 +61,20 @@ object ConsoleDialog: TConsoleDialog
   end
   object OutputMemo: TMemo
     Left = 0
-    Top = 81
+    Top = 78
     Width = 559
-    Height = 316
+    Height = 319
     TabStop = False
     Align = alClient
     Color = clBtnFace
     ReadOnly = True
     ScrollBars = ssBoth
-    TabOrder = 3
+    TabOrder = 4
     WantReturns = False
   end
   object CancelBtn: TButton
     Left = 472
-    Top = 24
+    Top = 7
     Width = 75
     Height = 25
     Anchors = [akTop, akRight]
@@ -83,7 +85,7 @@ object ConsoleDialog: TConsoleDialog
   end
   object CommandEdit: THistoryComboBox
     Left = 120
-    Top = 26
+    Top = 9
     Width = 251
     Height = 21
     AutoComplete = False
@@ -95,7 +97,7 @@ object ConsoleDialog: TConsoleDialog
   end
   object ExecuteButton: TButton
     Left = 384
-    Top = 24
+    Top = 7
     Width = 75
     Height = 25
     Anchors = [akTop, akRight]
@@ -104,4 +106,14 @@ object ConsoleDialog: TConsoleDialog
     TabOrder = 1
     OnClick = ExecuteButtonClick
   end
+  object HelpButton: TButton
+    Left = 472
+    Top = 42
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 3
+    OnClick = HelpButtonClick
+  end
 end

+ 5 - 2
forms/Console.h

@@ -30,8 +30,10 @@ __published:
   THistoryComboBox *CommandEdit;
   TButton *ExecuteButton;
   TPathLabel *DirectoryLabel;
+  TButton *HelpButton;
   void __fastcall ExecuteButtonClick(TObject *Sender);
   void __fastcall CommandEditChange(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
   
 private:
   TTerminal * FTerminal;
@@ -45,11 +47,12 @@ private:
   void __fastcall ExecuteCommand();
   void __fastcall SetTerminal(TTerminal * value);
   void __fastcall TerminalClose(TObject * Sender);
-  void __fastcall AddLine(AnsiString Line);
+  void __fastcall AddLine(TLogLineType Type, AnsiString Line);
 
 protected:
   void __fastcall DoChangeDirectory(TObject * Sender);
-  void __fastcall DoLogAddLine(System::TObject* Sender, const AnsiString AddedLine);
+  void __fastcall DoLogAddLine(System::TObject* Sender, TLogLineType Type,
+    const AnsiString AddedLine);
   void __fastcall UpdateControls();
   virtual void __fastcall CreateParams(TCreateParams & Params);
 

+ 75 - 19
forms/Copy.cpp

@@ -20,7 +20,7 @@
 //---------------------------------------------------------------------------
 bool __fastcall DoCopyDialog(bool ToRemote,
   bool Move, TStrings * FileList, AnsiString & TargetDirectory,
-  TGUICopyParamType * Params, int Options)
+  TGUICopyParamType * Params, int Options, int * OutputOptions)
 {
   bool Result;
   TCopyDialog *CopyDialog = new TCopyDialog(Application);
@@ -38,11 +38,19 @@ bool __fastcall DoCopyDialog(bool ToRemote,
     CopyDialog->FileList = FileList;
     CopyDialog->Params = *Params;
     CopyDialog->Move = Move;
+    if (OutputOptions != NULL)
+    {
+      CopyDialog->OutputOptions = *OutputOptions;
+    }
     Result = CopyDialog->Execute();
     if (Result)
     {
       TargetDirectory = CopyDialog->Directory;
       *Params = CopyDialog->Params;
+      if (OutputOptions != NULL)
+      {
+        *OutputOptions = CopyDialog->OutputOptions;
+      }
     }
   }
   __finally
@@ -62,15 +70,26 @@ __fastcall TCopyDialog::TCopyDialog(TComponent* Owner)
   ToRemote = true;
   Move = false;
   FOptions = 0;
+  FOutputOptions = 0;
+  FPresetsMenu = new TPopupMenu(this);
 
   UseSystemSettings(this);
 }
 //---------------------------------------------------------------------------
+__fastcall TCopyDialog::~TCopyDialog()
+{
+  delete FPresetsMenu;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCopyDialog::AdjustControls()
 {
+  if (FLAGSET(Options, coDoNotShowAgain))
+  {
+    SaveSettingsCheck->Caption = LoadStr(NEVER_SHOW_DIALOG_AGAIN);
+  }
   RemoteDirectoryEdit->Visible = false;
   LocalDirectoryEdit->Visible = false;
-  DirectoryEdit->Visible = FLAGCLEAR(Options, coDragDropTemp);
+  DirectoryEdit->Visible = FLAGCLEAR(Options, coTemp);
   EnableControl(DirectoryEdit, FLAGCLEAR(Options, coDisableDirectory));
   EnableControl(DirectoryLabel, DirectoryEdit->Enabled);
   EnableControl(LocalDirectoryBrowseButton, DirectoryEdit->Enabled);
@@ -85,8 +104,10 @@ void __fastcall TCopyDialog::AdjustControls()
   if (FileList && FileList->Count)
   {
     AnsiString TransferStr = LoadStr(!Move ? COPY_COPY : COPY_MOVE);
+    // currently the copy dialog is shown when downloading to temp folder
+    // only for drag&drop downloads, for we dare to display d&d specific prompt
     AnsiString DirectionStr =
-      LoadStr(((Options & coDragDropTemp) != 0) ? COPY_TODROP :
+      LoadStr(((Options & coTemp) != 0) ? COPY_TODROP :
         (ToRemote ? COPY_TOREMOTE : COPY_TOLOCAL));
 
     if (FileList->Count == 1)
@@ -116,7 +137,7 @@ void __fastcall TCopyDialog::AdjustControls()
   }
 
   LocalDirectoryBrowseButton->Visible = !ToRemote &&
-    ((Options & coDragDropTemp) == 0);
+    ((Options & coTemp) == 0);
 
   UpdateControls();
 }
@@ -143,6 +164,21 @@ void __fastcall TCopyDialog::SetOptions(int value)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCopyDialog::SetOutputOptions(int value)
+{
+  if (OutputOptions != value)
+  {
+    SaveSettingsCheck->Checked = FLAGSET(FOutputOptions, cooDoNotShowAgain);
+    FOutputOptions = (value & ~cooDoNotShowAgain);
+  }
+}
+//---------------------------------------------------------------------------
+int __fastcall TCopyDialog::GetOutputOptions()
+{
+  return FOutputOptions |
+    FLAGMASK(SaveSettingsCheck->Checked, cooDoNotShowAgain);
+}
+//---------------------------------------------------------------------------
 THistoryComboBox * __fastcall TCopyDialog::GetDirectoryEdit()
 {
   return ToRemote ? RemoteDirectoryEdit : LocalDirectoryEdit;
@@ -221,10 +257,11 @@ void __fastcall TCopyDialog::SetFileList(TStrings * value)
 void __fastcall TCopyDialog::UpdateControls()
 {
   EnableControl(QueueCheck,
-    (Options & (coDisableQueue | coDragDropTemp)) == 0);
+    (Options & (coDisableQueue | coTemp)) == 0);
   EnableControl(QueueNoConfirmationCheck,
-    ((Options & coDragDropTemp) == 0) && QueueCheck->Checked);
+    ((Options & coTemp) == 0) && QueueCheck->Checked);
   QueueNoConfirmationCheck->Visible = MoreButton->Expanded;
+  EnableControl(SaveSettingsCheck, FLAGCLEAR(Options, coDisableSaveSettings));
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyDialog::SetMove(bool value)
@@ -253,13 +290,17 @@ void __fastcall TCopyDialog::FormShow(TObject * /*Sender*/)
     CopyButton->SetFocus();
   }
   UpdateControls();
+
+  InstallPathWordBreakProc(RemoteDirectoryEdit);
+  InstallPathWordBreakProc(LocalDirectoryEdit);
 }
 //---------------------------------------------------------------------------
 bool __fastcall TCopyDialog::Execute()
 {
+  // at start assume that copy param is current preset
+  FPreset = GUIConfiguration->CopyParamCurrent;
   DirectoryEdit->Items = CustomWinConfiguration->History[
     ToRemote ? "RemoteTarget" : "LocalTarget"];
-  SaveSettingsCheck->Checked = false;
   MoreButton->Expanded = GUIConfiguration->CopyParamDialogExpanded;
   CopyParamsFrame->BeforeExecute();
   bool Result = (ShowModal() == mrOk);
@@ -270,9 +311,10 @@ bool __fastcall TCopyDialog::Execute()
     try
     {
       GUIConfiguration->CopyParamDialogExpanded = MoreButton->Expanded;
-      if (SaveSettingsCheck->Checked)
+      if (FLAGSET(OutputOptions, cooSaveSettings) &&
+          FLAGCLEAR(Options, coDisableSaveSettings))
       {
-        GUIConfiguration->CopyParam = Params;
+        GUIConfiguration->DefaultCopyParam = Params;
       }
       DirectoryEdit->SaveToHistory();
       CustomWinConfiguration->History[ToRemote ?
@@ -291,14 +333,14 @@ void __fastcall TCopyDialog::FormCloseQuery(TObject * /*Sender*/,
 {
   if (ModalResult != mrCancel)
   {
-    if (!ToRemote && ((Options & coDragDropTemp) == 0))
+    if (!ToRemote && ((Options & coTemp) == 0))
     {
       AnsiString Dir = Directory;
       AnsiString Drive = ExtractFileDrive(Dir);
       if (!DirectoryExists(Dir))
       {
         if (MessageDialog(FMTLOAD(CREATE_LOCAL_DIRECTORY, (Dir)),
-              qtConfirmation, qaOK | qaCancel, 0) != qaCancel)
+              qtConfirmation, qaOK | qaCancel, HELP_NONE) != qaCancel)
         {
           if (!ForceDirectories(Dir))
           {
@@ -337,17 +379,31 @@ void __fastcall TCopyDialog::LocalDirectoryBrowseButtonClick(
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TCopyDialog::DirectoryEditKeyDown(TObject * Sender,
-  WORD & Key, TShiftState Shift)
+void __fastcall TCopyDialog::ControlChange(TObject * /*Sender*/)
 {
-  PathComboBoxKeyDown(dynamic_cast<TCustomComboBox*>(Sender), Key, Shift,
-    (Sender == RemoteDirectoryEdit));
+  UpdateControls();
+  ResetSystemSettings(this);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCopyDialog::ControlChange(TObject * /*Sender*/)
+void __fastcall TCopyDialog::PresetsButtonClick(TObject * /*Sender*/)
 {
-  UpdateControls();
+  TCopyParamType Param = Params;
+  CopyParamListPopup(
+    PresetsButton->ClientToScreen(TPoint(0, PresetsButton->Height)),
+    FPresetsMenu, Params, FPreset, CopyParamClick, cplNone);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyDialog::CopyParamClick(TObject * Sender)
+{
+  TCopyParamType Param = Params;
+  if (CopyParamListPopupClick(Sender, Param, FPreset))
+  {
+    Params = Param;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
 }
 //---------------------------------------------------------------------------
-
-

+ 26 - 8
forms/Copy.dfm

@@ -39,7 +39,6 @@ object CopyDialog: TCopyDialog
     ItemHeight = 13
     TabOrder = 0
     Text = 'LocalDirectoryEdit'
-    OnKeyDown = DirectoryEditKeyDown
   end
   object RemoteDirectoryEdit: THistoryComboBox
     Left = 8
@@ -52,22 +51,21 @@ object CopyDialog: TCopyDialog
     MaxLength = 1000
     TabOrder = 2
     Text = 'RemoteDirectoryEdit'
-    OnKeyDown = DirectoryEditKeyDown
   end
   object MoreButton: TMoreButton
-    Left = 251
+    Left = 176
     Top = 329
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Caption = '<< &Less'
-    TabOrder = 6
+    TabOrder = 7
     OnChange = ControlChange
     Panel = MorePanel
     RepositionForm = True
   end
   object CopyButton: TButton
-    Left = 339
+    Left = 260
     Top = 329
     Width = 75
     Height = 25
@@ -75,10 +73,10 @@ object CopyDialog: TCopyDialog
     Caption = 'Copy'
     Default = True
     ModalResult = 1
-    TabOrder = 7
+    TabOrder = 8
   end
   object CancelButton: TButton
-    Left = 427
+    Left = 343
     Top = 329
     Width = 75
     Height = 25
@@ -86,7 +84,7 @@ object CopyDialog: TCopyDialog
     Cancel = True
     Caption = 'Cancel'
     ModalResult = 2
-    TabOrder = 8
+    TabOrder = 9
   end
   object MorePanel: TPanel
     Left = 0
@@ -149,4 +147,24 @@ object CopyDialog: TCopyDialog
     TabOrder = 5
     OnClick = ControlChange
   end
+  object PresetsButton: TButton
+    Left = 8
+    Top = 329
+    Width = 97
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Presets...'
+    TabOrder = 6
+    OnClick = PresetsButtonClick
+  end
+  object HelpButton: TButton
+    Left = 427
+    Top = 329
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'Help'
+    TabOrder = 10
+    OnClick = HelpButtonClick
+  end
 end

+ 13 - 2
forms/Copy.h

@@ -13,6 +13,7 @@
 
 #include "Rights.h"
 #include "CopyParams.h"
+#include <Menus.hpp>
 //---------------------------------------------------------------------------
 class TCopyDialog : public TForm
 {
@@ -30,18 +31,23 @@ __published:
   TCheckBox *QueueCheck;
   TCheckBox *QueueNoConfirmationCheck;
   TCheckBox *NewerOnlyCheck;
+  TButton *PresetsButton;
+  TButton *HelpButton;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
   void __fastcall LocalDirectoryBrowseButtonClick(TObject *Sender);
-  void __fastcall DirectoryEditKeyDown(TObject *Sender, WORD &Key,
-    TShiftState Shift);
   void __fastcall ControlChange(TObject *Sender);
+  void __fastcall PresetsButtonClick(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
 private:
   bool FToRemote;
   TStrings * FFileList;
   bool FMove;
   int FOptions;
+  int FOutputOptions;
   TGUICopyParamType FParams;
+  TPopupMenu * FPresetsMenu;
+  AnsiString FPreset;
   AnsiString __fastcall GetDirectory();
   void __fastcall SetToRemote(bool value);
   THistoryComboBox * __fastcall GetDirectoryEdit();
@@ -52,11 +58,15 @@ private:
   void __fastcall SetMove(bool value);
   AnsiString __fastcall GetFileMask();
   void __fastcall SetOptions(int value);
+  void __fastcall SetOutputOptions(int value);
+  int __fastcall GetOutputOptions();
+  void __fastcall CopyParamClick(TObject * Sender);
 protected:
   void __fastcall UpdateControls();
   void __fastcall AdjustControls();
 public:
   __fastcall TCopyDialog(TComponent* Owner);
+  virtual __fastcall ~TCopyDialog();
   bool __fastcall Execute();
 
   __property bool ToRemote = { read = FToRemote, write = SetToRemote };
@@ -66,6 +76,7 @@ public:
   __property TGUICopyParamType Params = { read = GetParams, write = SetParams };
   __property bool Move = { read = FMove, write = SetMove };
   __property int Options = { read = FOptions, write = SetOptions };
+  __property int OutputOptions = { read = GetOutputOptions, write = SetOutputOptions };
 };
 //---------------------------------------------------------------------------
 #endif

+ 76 - 0
forms/CopyParamCustom.cpp

@@ -0,0 +1,76 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include <Common.h>
+#include <TextsWin.h>
+#include <GUIConfiguration.h>
+#include <GUITools.h>
+#include "CopyParamCustom.h"
+#include "VCLCommon.h"
+//---------------------------------------------------------------------------
+#pragma package(smart_init)
+#pragma link "XPThemes"
+#pragma link "CopyParams"
+#pragma resource "*.dfm"
+//---------------------------------------------------------------------------
+bool __fastcall DoCopyParamCustomDialog(TCopyParamType & CopyParam, int Options)
+{
+  bool Result;
+  TCopyParamCustomDialog * Dialog = new TCopyParamCustomDialog(
+    Application, Options, 0);
+  try
+  {
+    Result = Dialog->Execute(CopyParam);
+  }
+  __finally
+  {
+    delete Dialog;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+// Dummy parameter distinguishes the constructor from Object Pascals TForm::CreateNew
+__fastcall TCopyParamCustomDialog::TCopyParamCustomDialog(TComponent * Owner,
+  int Options, int /*Dummy*/)
+  : TForm(Owner)
+{
+  UseSystemSettings(this);
+  CopyParamsFrame->Direction = pdAll;
+  if (Options >= 0)
+  {
+    CopyParamsFrame->Options = Options;
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TCopyParamCustomDialog::Execute(TCopyParamType & CopyParam)
+{
+  CopyParamsFrame->Params = CopyParam;
+
+  CopyParamsFrame->BeforeExecute();
+  bool Result = (ShowModal() == mrOk);
+
+  if (Result)
+  {
+    CopyParamsFrame->AfterExecute();
+
+    CopyParam = CopyParamsFrame->Params;
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamCustomDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & /*CanClose*/)
+{
+  if (ModalResult != mrCancel)
+  {
+    CopyParamsFrame->Validate();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamCustomDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------

+ 156 - 0
forms/CopyParamCustom.dfm

@@ -0,0 +1,156 @@
+object CopyParamCustomDialog: TCopyParamCustomDialog
+  Left = 264
+  Top = 122
+  HelpType = htKeyword
+  HelpKeyword = 'ui_transfer_preset'
+  BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
+  BorderStyle = bsDialog
+  Caption = 'Transfer settings'
+  ClientHeight = 387
+  ClientWidth = 377
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'MS Sans Serif'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
+  DesignSize = (
+    377
+    387)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object OkButton: TButton
+    Left = 125
+    Top = 354
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'OK'
+    Default = True
+    ModalResult = 1
+    TabOrder = 1
+  end
+  object CancelButton: TButton
+    Left = 209
+    Top = 354
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Cancel = True
+    Caption = 'Cancel'
+    ModalResult = 2
+    TabOrder = 2
+  end
+  inline CopyParamsFrame: TCopyParamsFrame
+    Left = 0
+    Top = 0
+    Width = 377
+    Height = 345
+    HelpType = htKeyword
+    TabOrder = 0
+    inherited CommonPropertiesGroup: TXPGroupBox
+      Left = 197
+      Top = 209
+      Height = 73
+      Caption = 'Common options'
+      DesignSize = (
+        173
+        73)
+      inherited CommonPreserveTimestampCheck: TCheckBox
+        Top = 19
+      end
+    end
+    inherited LocalPropertiesGroup: TXPGroupBox
+      Left = 197
+      Top = 156
+      Height = 48
+      Caption = 'Download options'
+      DesignSize = (
+        173
+        48)
+      inherited PreserveReadOnlyCheck: TCheckBox
+        Top = 20
+      end
+      inherited LocalPreserveTimeCheck: TCheckBox
+        Top = 92
+      end
+    end
+    inherited RemotePropertiesGroup: TXPGroupBox
+      Left = 8
+      Top = 156
+      Width = 182
+      Height = 126
+      Caption = 'Upload options'
+      inherited RemotePreserveTimeCheck: TCheckBox
+        Top = 161
+      end
+    end
+    inherited ChangeCaseGroup: TXPGroupBox
+      Left = 247
+      Top = 8
+      Width = 123
+      DesignSize = (
+        123
+        146)
+      inherited CCLowerCaseShortButton: TRadioButton
+        Width = 110
+      end
+      inherited CCNoChangeButton: TRadioButton
+        Width = 110
+      end
+      inherited CCUpperCaseButton: TRadioButton
+        Width = 110
+      end
+      inherited CCLowerCaseButton: TRadioButton
+        Width = 110
+      end
+      inherited CCFirstUpperCaseButton: TRadioButton
+        Width = 110
+      end
+    end
+    inherited TransferModeGroup: TXPGroupBox
+      Left = 8
+      Top = 8
+      Width = 231
+      DesignSize = (
+        231
+        146)
+      inherited TMTextButton: TRadioButton
+        Width = 219
+      end
+      inherited TMBinaryButton: TRadioButton
+        Width = 219
+      end
+      inherited TMAutomaticButton: TRadioButton
+        Width = 219
+      end
+      inherited AsciiFileMaskCombo: THistoryComboBox
+        Width = 213
+      end
+    end
+    inherited OtherGroup: TXPGroupBox
+      Left = 8
+      Top = 284
+      Width = 362
+      DesignSize = (
+        362
+        61)
+      inherited ExcludeFileMaskCombo: THistoryComboBox
+        Width = 217
+      end
+    end
+  end
+  object HelpButton: TButton
+    Left = 293
+    Top = 354
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'Help'
+    TabOrder = 3
+    OnClick = HelpButtonClick
+  end
+end

+ 30 - 0
forms/CopyParamCustom.h

@@ -0,0 +1,30 @@
+//---------------------------------------------------------------------------
+#ifndef CopyParamCustomH
+#define CopyParamCustomH
+//---------------------------------------------------------------------------
+#include <Classes.hpp>
+#include <Controls.hpp>
+#include <StdCtrls.hpp>
+#include <Forms.hpp>
+#include "XPThemes.hpp"
+#include "CopyParams.h"
+//---------------------------------------------------------------------------
+class TCustomCommands;
+//---------------------------------------------------------------------------
+class TCopyParamCustomDialog : public TForm
+{
+__published:
+  TButton *OkButton;
+  TButton *CancelButton;
+  TCopyParamsFrame *CopyParamsFrame;
+  TButton *HelpButton;
+  void __fastcall FormCloseQuery(TObject * Sender, bool & CanClose);
+  void __fastcall HelpButtonClick(TObject *Sender);
+
+public:
+  __fastcall TCopyParamCustomDialog(TComponent * Owner, int Options, int Dummy);
+
+  bool __fastcall Execute(TCopyParamType & CopyParam);
+};
+//---------------------------------------------------------------------------
+#endif

+ 211 - 0
forms/CopyParamPreset.cpp

@@ -0,0 +1,211 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include <Common.h>
+#include <TextsWin.h>
+#include <GUIConfiguration.h>
+#include <GUITools.h>
+#include "CopyParamPreset.h"
+#include "VCLCommon.h"
+//---------------------------------------------------------------------------
+#pragma package(smart_init)
+#pragma link "XPThemes"
+#pragma link "CopyParams"
+#pragma resource "*.dfm"
+//---------------------------------------------------------------------------
+bool __fastcall DoCopyParamPresetDialog(TCopyParamList * CopyParamList,
+  int & Index, TCopyParamPresetMode Mode, TCopyParamRuleData * CurrentRuleData)
+{
+  bool Result;
+  TCopyParamPresetDialog * Dialog = new TCopyParamPresetDialog(Application, Mode, CurrentRuleData);
+  try
+  {
+    Result = Dialog->Execute(CopyParamList, Index);
+  }
+  __finally
+  {
+    delete Dialog;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+__fastcall TCopyParamPresetDialog::TCopyParamPresetDialog(TComponent * Owner,
+  TCopyParamPresetMode Mode, TCopyParamRuleData * CurrentRuleData)
+  : TForm(Owner)
+{
+  UseSystemSettings(this);
+  CopyParamsFrame->Direction = pdAll;
+  FMode = Mode;
+  FCurrentRuleData = CurrentRuleData;
+  Caption = LoadStr(Mode == cpmEdit ? COPY_PARAM_EDIT : COPY_PARAM_ADD);
+  InstallPathWordBreakProc(HostNameEdit);
+  InstallPathWordBreakProc(UserNameEdit);
+  InstallPathWordBreakProc(RemoteDirectoryEdit);
+  InstallPathWordBreakProc(LocalDirectoryEdit);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::UpdateControls()
+{
+  EnableControl(OkButton, !DescriptionEdit->Text.IsEmpty());
+  EnableControl(RuleGroup, HasRuleCheck->Checked);
+  CurrentRuleButton->Visible = (FCurrentRuleData != NULL);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::ControlChange(TObject * /*Sender*/)
+{
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+bool __fastcall TCopyParamPresetDialog::Execute(TCopyParamList * CopyParamList,
+  int & Index)
+{
+  FCopyParamList = CopyParamList;
+  if ((FMode == cpmEdit) || (FMode == cpmDuplicate))
+  {
+    CopyParamsFrame->Params = *CopyParamList->CopyParams[Index];
+    const TCopyParamRule * Rule = CopyParamList->Rules[Index];
+
+    if (FMode == cpmEdit)
+    {
+      DescriptionEdit->Text = CopyParamList->Names[Index];
+      FIndex = Index;
+    }
+    else
+    {
+      DescriptionEdit->Text = "";
+      FIndex = -1; // never used
+      Index = FCopyParamList->Count;
+    }
+    HasRuleCheck->Checked = (Rule != NULL);
+
+    if (Rule != NULL)
+    {
+      SetRuleData(Rule->Data);
+    }
+  }
+  else
+  {
+    DescriptionEdit->Text = "";
+    TCopyParamType Default;
+    CopyParamsFrame->Params = Default;
+    HasRuleCheck->Checked = false;
+    FIndex = -1; // never used
+    if (Index < 0)
+    {
+      Index = FCopyParamList->Count;
+    }
+  }
+
+  CopyParamsFrame->BeforeExecute();
+  bool Result = (ShowModal() == mrOk);
+
+  if (Result)
+  {
+    CopyParamsFrame->AfterExecute();
+
+    AnsiString Name;
+    TCopyParamType * CopyParam = NULL;
+    TCopyParamRule * Rule = NULL;
+    try
+    {
+      Name = DescriptionEdit->Text;
+      CopyParam = new TCopyParamType(CopyParamsFrame->Params);
+      Rule = GetRule();
+    }
+    catch(...)
+    {
+      delete CopyParam;
+      delete Rule;
+      throw;
+    }
+
+    if (FMode == cpmEdit)
+    {
+      FCopyParamList->Change(Index, Name, CopyParam, Rule);
+    }
+    else
+    {
+      FCopyParamList->Insert(Index, Name, CopyParam, Rule);
+    }
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::SetRuleData(const TCopyParamRuleData & Data)
+{
+  HostNameEdit->Text = Data.HostName;
+  UserNameEdit->Text = Data.UserName;
+  RemoteDirectoryEdit->Text = Data.RemoteDirectory;
+  LocalDirectoryEdit->Text = Data.LocalDirectory;
+}
+//---------------------------------------------------------------------------
+TCopyParamRule * __fastcall TCopyParamPresetDialog::GetRule()
+{
+  TCopyParamRule * Rule = NULL;
+  if (HasRuleCheck->Checked)
+  {
+    TCopyParamRuleData Data;
+    Data.HostName = HostNameEdit->Text;
+    Data.UserName = UserNameEdit->Text;
+    Data.RemoteDirectory = RemoteDirectoryEdit->Text;
+    Data.LocalDirectory = LocalDirectoryEdit->Text;
+    Rule = new TCopyParamRule(Data);
+  }
+  return Rule;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::FormShow(TObject * /*Sender*/)
+{
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & /*CanClose*/)
+{
+  if (ModalResult != mrCancel)
+  {
+    CopyParamsFrame->Validate();
+
+    AnsiString Description = DescriptionEdit->Text;
+    TCopyParamList::ValidateName(Description);
+
+    TCopyParamRule * Rule = GetRule();
+    if (Rule != NULL)
+    {
+      try
+      {
+        if (Rule->IsEmpty)
+        {
+          throw Exception(LoadStr(COPY_PARAM_NO_RULE));
+        }
+      }
+      __finally
+      {
+        delete Rule;
+      }
+    }
+
+    int Index = FCopyParamList->IndexOfName(Description);
+    if (((FMode == cpmEdit) && (Index >= 0) && (Index != FIndex)) ||
+        (((FMode == cpmAdd) || (FMode == cpmDuplicate)) && (Index >= 0)))
+    {
+      DescriptionEdit->SetFocus();
+      throw Exception(FMTLOAD(COPY_PARAM_DUPLICATE, (Description)));
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::CurrentRuleButtonClick(
+  TObject * /*Sender*/)
+{
+  assert(FCurrentRuleData != NULL);
+  SetRuleData(*FCurrentRuleData);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------

+ 276 - 0
forms/CopyParamPreset.dfm

@@ -0,0 +1,276 @@
+object CopyParamPresetDialog: TCopyParamPresetDialog
+  Left = 264
+  Top = 122
+  HelpType = htKeyword
+  HelpKeyword = 'ui_transfer_preset'
+  BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
+  BorderStyle = bsDialog
+  Caption = 'CopyParamPresetDialog'
+  ClientHeight = 438
+  ClientWidth = 632
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'MS Sans Serif'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
+  OnShow = FormShow
+  DesignSize = (
+    632
+    438)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object Label1: TLabel
+    Left = 11
+    Top = 13
+    Width = 84
+    Height = 13
+    Caption = 'Preset &description'
+    FocusControl = DescriptionEdit
+  end
+  object OkButton: TButton
+    Left = 380
+    Top = 405
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'OK'
+    Default = True
+    ModalResult = 1
+    TabOrder = 4
+  end
+  object CancelButton: TButton
+    Left = 464
+    Top = 405
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Cancel = True
+    Caption = 'Cancel'
+    ModalResult = 2
+    TabOrder = 5
+  end
+  object DescriptionEdit: TEdit
+    Left = 11
+    Top = 29
+    Width = 366
+    Height = 21
+    MaxLength = 250
+    TabOrder = 0
+    OnChange = ControlChange
+  end
+  inline CopyParamsFrame: TCopyParamsFrame
+    Left = 8
+    Top = 51
+    Width = 377
+    Height = 345
+    TabOrder = 1
+    inherited CommonPropertiesGroup: TXPGroupBox
+      Left = 197
+      Top = 209
+      Height = 73
+      Caption = 'Common options'
+      DesignSize = (
+        173
+        73)
+      inherited CommonPreserveTimestampCheck: TCheckBox
+        Top = 19
+      end
+    end
+    inherited LocalPropertiesGroup: TXPGroupBox
+      Left = 197
+      Top = 156
+      Height = 48
+      Caption = 'Download options'
+      DesignSize = (
+        173
+        48)
+      inherited PreserveReadOnlyCheck: TCheckBox
+        Top = 20
+      end
+      inherited LocalPreserveTimeCheck: TCheckBox
+        Top = 92
+      end
+    end
+    inherited RemotePropertiesGroup: TXPGroupBox
+      Left = 8
+      Top = 156
+      Width = 182
+      Height = 126
+      Caption = 'Upload options'
+      inherited RemotePreserveTimeCheck: TCheckBox
+        Top = 161
+      end
+    end
+    inherited ChangeCaseGroup: TXPGroupBox
+      Left = 247
+      Top = 8
+      Width = 123
+      DesignSize = (
+        123
+        146)
+      inherited CCLowerCaseShortButton: TRadioButton
+        Width = 110
+      end
+      inherited CCNoChangeButton: TRadioButton
+        Width = 110
+      end
+      inherited CCUpperCaseButton: TRadioButton
+        Width = 110
+      end
+      inherited CCLowerCaseButton: TRadioButton
+        Width = 110
+      end
+      inherited CCFirstUpperCaseButton: TRadioButton
+        Width = 110
+      end
+    end
+    inherited TransferModeGroup: TXPGroupBox
+      Left = 8
+      Top = 8
+      Width = 231
+      DesignSize = (
+        231
+        146)
+      inherited TMTextButton: TRadioButton
+        Width = 219
+      end
+      inherited TMBinaryButton: TRadioButton
+        Width = 219
+      end
+      inherited TMAutomaticButton: TRadioButton
+        Width = 219
+      end
+      inherited AsciiFileMaskCombo: THistoryComboBox
+        Width = 213
+      end
+    end
+    inherited OtherGroup: TXPGroupBox
+      Left = 8
+      Top = 284
+      Width = 362
+      DesignSize = (
+        362
+        61)
+      inherited ExcludeFileMaskCombo: THistoryComboBox
+        Width = 217
+      end
+    end
+  end
+  object RuleGroup: TXPGroupBox
+    Left = 388
+    Top = 91
+    Width = 235
+    Height = 305
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Autoselection rule'
+    TabOrder = 3
+    DesignSize = (
+      235
+      305)
+    object Label2: TLabel
+      Left = 10
+      Top = 20
+      Width = 76
+      Height = 13
+      Caption = 'Hostna&me mask'
+      FocusControl = HostNameEdit
+    end
+    object Label3: TLabel
+      Left = 10
+      Top = 68
+      Width = 76
+      Height = 13
+      Caption = 'Us&ername mask'
+      FocusControl = UserNameEdit
+    end
+    object Label4: TLabel
+      Left = 10
+      Top = 116
+      Width = 108
+      Height = 13
+      Caption = 'Remote director&y mask'
+      FocusControl = RemoteDirectoryEdit
+    end
+    object Label5: TLabel
+      Left = 10
+      Top = 164
+      Width = 97
+      Height = 13
+      Caption = '&Local directory mask'
+      FocusControl = LocalDirectoryEdit
+    end
+    object HostNameEdit: TEdit
+      Left = 10
+      Top = 36
+      Width = 215
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      MaxLength = 250
+      TabOrder = 0
+      OnChange = ControlChange
+    end
+    object UserNameEdit: TEdit
+      Left = 10
+      Top = 84
+      Width = 215
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      MaxLength = 250
+      TabOrder = 1
+      OnChange = ControlChange
+    end
+    object RemoteDirectoryEdit: TEdit
+      Left = 10
+      Top = 132
+      Width = 215
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      MaxLength = 250
+      TabOrder = 2
+      OnChange = ControlChange
+    end
+    object LocalDirectoryEdit: TEdit
+      Left = 10
+      Top = 180
+      Width = 215
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      MaxLength = 250
+      TabOrder = 3
+      OnChange = ControlChange
+    end
+    object CurrentRuleButton: TButton
+      Left = 10
+      Top = 208
+      Width = 75
+      Height = 25
+      Caption = 'Current'
+      TabOrder = 4
+      OnClick = CurrentRuleButtonClick
+    end
+  end
+  object HasRuleCheck: TCheckBox
+    Left = 395
+    Top = 66
+    Width = 212
+    Height = 17
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Automatically select this preset when'
+    TabOrder = 2
+    OnClick = ControlChange
+  end
+  object HelpButton: TButton
+    Left = 548
+    Top = 405
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'Help'
+    TabOrder = 6
+    OnClick = HelpButtonClick
+  end
+end

+ 58 - 0
forms/CopyParamPreset.h

@@ -0,0 +1,58 @@
+//---------------------------------------------------------------------------
+#ifndef CopyParamPresetH
+#define CopyParamPresetH
+//---------------------------------------------------------------------------
+#include <Classes.hpp>
+#include <Controls.hpp>
+#include <StdCtrls.hpp>
+#include <Forms.hpp>
+#include "XPThemes.hpp"
+#include "CopyParams.h"
+//---------------------------------------------------------------------------
+class TCustomCommands;
+//---------------------------------------------------------------------------
+class TCopyParamPresetDialog : public TForm
+{
+__published:
+  TButton *OkButton;
+  TButton *CancelButton;
+  TLabel *Label1;
+  TEdit *DescriptionEdit;
+  TCopyParamsFrame *CopyParamsFrame;
+  TXPGroupBox *RuleGroup;
+  TLabel *Label2;
+  TEdit *HostNameEdit;
+  TCheckBox *HasRuleCheck;
+  TLabel *Label3;
+  TEdit *UserNameEdit;
+  TLabel *Label4;
+  TEdit *RemoteDirectoryEdit;
+  TLabel *Label5;
+  TEdit *LocalDirectoryEdit;
+  TButton *CurrentRuleButton;
+  TButton *HelpButton;
+  void __fastcall ControlChange(TObject *Sender);
+  void __fastcall FormShow(TObject *Sender);
+  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
+  void __fastcall CurrentRuleButtonClick(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
+
+private:
+  TCopyParamPresetMode FMode;
+  TCopyParamList * FCopyParamList;
+  int FIndex;
+  TCopyParamRuleData * FCurrentRuleData;
+
+protected:
+  void __fastcall UpdateControls();
+  TCopyParamRule * __fastcall GetRule();
+  void __fastcall SetRuleData(const TCopyParamRuleData & Data);
+
+public:
+  __fastcall TCopyParamPresetDialog(TComponent * Owner,
+    TCopyParamPresetMode Mode, TCopyParamRuleData * CurrentRuleData);
+
+  bool __fastcall Execute(TCopyParamList * CopyParamList, int & Index);
+};
+//---------------------------------------------------------------------------
+#endif

+ 9 - 1
forms/CopyParams.cpp

@@ -30,6 +30,9 @@ __fastcall TCopyParamsFrame::TCopyParamsFrame(TComponent* Owner)
   FParams = new TCopyParamType();
   TCopyParamType DefParams;
   Params = DefParams;
+
+  InstallPathWordBreakProc(AsciiFileMaskCombo);
+  InstallPathWordBreakProc(ExcludeFileMaskCombo);
 }
 //---------------------------------------------------------------------------
 __fastcall TCopyParamsFrame::~TCopyParamsFrame()
@@ -69,6 +72,7 @@ void __fastcall TCopyParamsFrame::SetParams(TCopyParamType value)
 
   CommonCalculateSizeCheck->Checked = value.CalculateSize;
 
+  NegativeExcludeCombo->ItemIndex = (value.NegativeExclude ? 1 : 0);
   ExcludeFileMaskCombo->Text = value.ExcludeFileMask.Masks;
   ClearArchiveCheck->Checked = value.ClearArchive;
 
@@ -112,6 +116,8 @@ TCopyParamType __fastcall TCopyParamsFrame::GetParams()
   Result.CalculateSize = CommonCalculateSizeCheck->Checked;
 
   Result.ExcludeFileMask.Masks = ExcludeFileMaskCombo->Text;
+  Result.NegativeExclude = (NegativeExcludeCombo->ItemIndex == 1);
+
   Result.ClearArchive = ClearArchiveCheck->Checked;
 
   return Result;
@@ -137,8 +143,10 @@ void __fastcall TCopyParamsFrame::UpdateControls()
     FLAGSET(Options, cfAllowTransferMode) && TMAutomaticButton->Checked && Enabled);
   EnableControl(RightsFrame, PreserveRightsCheck->Checked && Enabled);
   EnableControl(ExcludeFileMaskCombo, FLAGSET(Options, cfAllowExcludeMask));
-  EnableControl(ExcludeFileMaskLabel, ExcludeFileMaskCombo->Enabled);
+  EnableControl(ExclusionFileMaskLabel, ExcludeFileMaskCombo->Enabled);
+  EnableControl(NegativeExcludeCombo, ExcludeFileMaskCombo->Enabled);
   EnableControl(ClearArchiveCheck, FLAGSET(Options, cfAllowClearArchive));
+  EnableControl(PreserveTimeCheck, FLAGCLEAR(Options, cfDisablePreserveTime));
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyParamsFrame::SetDirection(TParamsForDirection value)

+ 23 - 11
forms/CopyParams.dfm

@@ -246,33 +246,45 @@ object CopyParamsFrame: TCopyParamsFrame
     DesignSize = (
       501
       61)
-    object ExcludeFileMaskLabel: TLabel
-      Left = 10
-      Top = 17
-      Width = 66
+    object ExclusionFileMaskLabel: TLabel
+      Left = 90
+      Top = 18
+      Width = 25
       Height = 13
-      Caption = 'Exclude mas&k'
+      Caption = 'mas&k'
       FocusControl = ExcludeFileMaskCombo
     end
     object ExcludeFileMaskCombo: THistoryComboBox
-      Left = 101
-      Top = 12
-      Width = 393
+      Left = 136
+      Top = 14
+      Width = 358
       Height = 21
       Anchors = [akLeft, akTop, akRight]
       ItemHeight = 13
       MaxLength = 3000
-      TabOrder = 0
+      TabOrder = 1
       Text = 'ExcludeFileMaskCombo'
       OnExit = ValidateMaskComboExit
     end
     object ClearArchiveCheck: TCheckBox
       Left = 10
-      Top = 37
+      Top = 39
       Width = 231
       Height = 17
       Caption = 'Clear source file '#39'Archi&ve'#39' attribute'
-      TabOrder = 1
+      TabOrder = 2
+    end
+    object NegativeExcludeCombo: TComboBox
+      Left = 10
+      Top = 14
+      Width = 76
+      Height = 21
+      Style = csDropDownList
+      ItemHeight = 13
+      TabOrder = 0
+      Items.Strings = (
+        'Exclude'
+        'Include')
     end
   end
 end

+ 3 - 1
forms/CopyParams.h

@@ -17,6 +17,7 @@
 const int cfAllowTransferMode = 0x01;
 const int cfAllowExcludeMask =  0x02;
 const int cfAllowClearArchive = 0x04;
+const int cfDisablePreserveTime = 0x08;
 //---------------------------------------------------------------------------
 class TCopyParamsFrame : public TFrame
 {
@@ -44,10 +45,11 @@ __published:
   TCheckBox *ReplaceInvalidCharsCheck;
   TCheckBox *CommonCalculateSizeCheck;
   TXPGroupBox *OtherGroup;
-  TLabel *ExcludeFileMaskLabel;
+  TLabel *ExclusionFileMaskLabel;
   THistoryComboBox *ExcludeFileMaskCombo;
   TRadioButton *CCLowerCaseShortButton;
   TCheckBox *ClearArchiveCheck;
+  TComboBox *NegativeExcludeCombo;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall ValidateMaskComboExit(TObject *Sender);
 private:

+ 89 - 21
forms/CustomCommand.cpp

@@ -6,16 +6,20 @@
 #include <Terminal.h>
 #include <TextsWin.h>
 #include <WinConfiguration.h>
+#include <WinInterface.h>
 #include <GUITools.h>
+#include <ScpMain.h>
 #include "CustomCommand.h"
 #include "VCLCommon.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma link "XPThemes"
+#pragma link "HistoryComboBox"
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------------
 bool __fastcall DoCustomCommandDialog(AnsiString & Description,
-  AnsiString & Command, int & Params, const TCustomCommands * CustomCommands, bool Edit)
+  AnsiString & Command, int & Params, const TCustomCommands * CustomCommands,
+  TCustomCommandsMode Mode, TCustomCommandValidate OnValidate)
 {
   bool Result;
   TCustomCommandDialog * Dialog = new TCustomCommandDialog(Application);
@@ -25,7 +29,8 @@ bool __fastcall DoCustomCommandDialog(AnsiString & Description,
     Dialog->Command = Command;
     Dialog->Params = Params;
     Dialog->CustomCommands = CustomCommands;
-    Dialog->Edit = Edit;
+    Dialog->Mode = Mode;
+    Dialog->OnValidate = OnValidate;
     Result = Dialog->Execute();
     if (Result)
     {
@@ -46,7 +51,9 @@ __fastcall TCustomCommandDialog::TCustomCommandDialog(TComponent* Owner)
 {
   UseSystemSettings(this);
   FCustomCommands = NULL;
-  FEdit = true;
+  FMode = ccmEdit;
+  FOnValidate = NULL;
+  InstallPathWordBreakProc(CommandEdit);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::UpdateControls()
@@ -55,6 +62,7 @@ void __fastcall TCustomCommandDialog::UpdateControls()
 
   bool RemoteCommand = RemoteCommandButton->Checked;
   bool AllowRecursive = true;
+  bool AllowApplyToDirectories = true;
   try
   {
     TRemoteCustomCommand RemoteCustomCommand;
@@ -64,17 +72,20 @@ void __fastcall TCustomCommandDialog::UpdateControls()
 
     TInteractiveCustomCommand InteractiveCustomCommand(FileCustomCommand);
     AnsiString Cmd = InteractiveCustomCommand.Complete(Command, false);
-    AllowRecursive = !FileCustomCommand->IsFileListCommand(Cmd);
+    bool FileCommand = FileCustomCommand->IsFileCommand(Cmd);
+    AllowRecursive = FileCommand && !FileCustomCommand->IsFileListCommand(Cmd);
     if (AllowRecursive && !RemoteCommand)
     {
       AllowRecursive = !LocalCustomCommand.HasLocalFileName(Cmd);
     }
+    AllowApplyToDirectories = FileCommand;
   }
   catch(...)
   {
   }
 
   EnableControl(RecursiveCheck, AllowRecursive);
+  EnableControl(ApplyToDirectoriesCheck, AllowApplyToDirectories);
   EnableControl(ShowResultsCheck, RemoteCommand);
 }
 //---------------------------------------------------------------------------
@@ -125,32 +136,86 @@ void __fastcall TCustomCommandDialog::ControlChange(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 bool __fastcall TCustomCommandDialog::Execute()
 {
-  return (ShowModal() == mrOk);
+  CommandEdit->Items = CustomWinConfiguration->History["CustomCommand"];
+  if (CommandEdit->Items->Count == 0)
+  {
+    TCustomCommands * CustomCommands = const_cast<TCustomCommands*>(FCustomCommands);
+    for (int i = 0; i < CustomCommands->Count; i++)
+    {
+      CommandEdit->Items->Add(CustomCommands->Values[CustomCommands->Names[i]]);
+    }
+  }
+  bool Result = (ShowModal() == mrOk);
+  if (Result)
+  {
+    CustomWinConfiguration->History["CustomCommand"] = CommandEdit->Items;
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::FormShow(TObject * /*Sender*/)
 {
-  Caption = LoadStr(Edit ? CUSTOM_COMMAND_EDIT : CUSTOM_COMMAND_ADD);
+  int CaptionRes;
+  switch (Mode)
+  {
+    case ccmAdd:
+      CaptionRes = CUSTOM_COMMAND_ADD;
+      break;
+    case ccmEdit:
+      CaptionRes = CUSTOM_COMMAND_EDIT;
+      break;
+    case ccmAdHoc:
+    default:
+      CaptionRes = CUSTOM_COMMAND_AD_HOC;
+      break;
+  }
+  Caption = LoadStr(CaptionRes);
+
+  if (Mode == ccmAdHoc)
+  {
+    int Shift = CommandEdit->Top - DescriptionEdit->Top;
+
+    DescriptionLabel->Visible = false;
+    DescriptionEdit->Visible = false;
+    for (int i = 0; i < Group->ControlCount; i++)
+    {
+      TControl * Control = Group->Controls[i];
+      if (Control->Visible)
+      {
+        if (Control->Top > DescriptionLabel->Top)
+        {
+          Control->Top = Control->Top - Shift;
+        }
+      }
+    }
+
+    ClientHeight = ClientHeight - Shift;
+  }
+
   UpdateControls();
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomCommandDialog::PathEditsKeyDown(TObject * /*Sender*/,
-  WORD & Key, TShiftState Shift)
-{
-  PathEditKeyDown(CommandEdit, Key, Shift, RemoteCommandButton->Checked);
-}
-//---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::FormCloseQuery(TObject * /*Sender*/,
   bool & /*CanClose*/)
 {
   if (ModalResult != mrCancel)
   {
-    AnsiString Desc = Description;
-    
-    if (Desc.Pos("=") > 0)
+    if ((Mode == ccmAdd) || (Mode == ccmEdit))
     {
-      DescriptionEdit->SetFocus();
-      throw Exception(FMTLOAD(CUSTOM_COMMAND_INVALID, ("=")));
+      AnsiString Desc = Description;
+    
+      if (Desc.Pos("=") > 0)
+      {
+        DescriptionEdit->SetFocus();
+        throw Exception(FMTLOAD(CUSTOM_COMMAND_INVALID, ("=")));
+      }
+
+      if (((Mode == ccmAdd) || ((Mode == ccmEdit) && (Desc != FOrigDescription))) &&
+          (const_cast<TCustomCommands*>(FCustomCommands)->IndexOfName(Desc) >= 0))
+      {
+        DescriptionEdit->SetFocus();
+        throw Exception(FMTLOAD(CUSTOM_COMMAND_DUPLICATE, (Desc)));
+      }
     }
 
     try
@@ -175,13 +240,16 @@ void __fastcall TCustomCommandDialog::FormCloseQuery(TObject * /*Sender*/,
       throw;
     }
 
-    if ((!Edit || (Desc != FOrigDescription)) &&
-        (const_cast<TCustomCommands*>(FCustomCommands)->IndexOfName(Desc) >= 0))
+    if (FOnValidate)
     {
-      DescriptionEdit->SetFocus();
-      throw Exception(FMTLOAD(CUSTOM_COMMAND_DUPLICATE, (Desc)));
+      FOnValidate(Command, Params);
     }
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomCommandDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 16 - 6
forms/CustomCommand.dfm

@@ -33,7 +33,7 @@ object CustomCommandDialog: TCustomCommandDialog
     DesignSize = (
       380
       255)
-    object FileNameLabel: TLabel
+    object DescriptionLabel: TLabel
       Left = 11
       Top = 16
       Width = 56
@@ -56,7 +56,7 @@ object CustomCommandDialog: TCustomCommandDialog
       Top = 189
       Width = 353
       Height = 61
-      Anchors = [akLeft, akTop, akRight, akBottom]
+      Anchors = [akLeft, akTop, akRight]
       AutoSize = False
       Caption = 
         'Patterns:'#160'!!'#160'-'#160'exclamation mark; !'#160'-'#160'file name; !?prompt?default' +
@@ -75,16 +75,16 @@ object CustomCommandDialog: TCustomCommandDialog
       TabOrder = 0
       OnChange = ControlChange
     end
-    object CommandEdit: TEdit
+    object CommandEdit: THistoryComboBox
       Left = 11
       Top = 80
       Width = 358
       Height = 21
       Anchors = [akLeft, akTop, akRight]
+      ItemHeight = 13
       MaxLength = 250
       TabOrder = 1
       OnChange = ControlChange
-      OnKeyDown = PathEditsKeyDown
     end
     object ApplyToDirectoriesCheck: TCheckBox
       Left = 16
@@ -133,7 +133,7 @@ object CustomCommandDialog: TCustomCommandDialog
     end
   end
   object OkButton: TButton
-    Left = 228
+    Left = 144
     Top = 272
     Width = 75
     Height = 25
@@ -144,7 +144,7 @@ object CustomCommandDialog: TCustomCommandDialog
     TabOrder = 1
   end
   object CancelButton: TButton
-    Left = 312
+    Left = 228
     Top = 272
     Width = 75
     Height = 25
@@ -154,4 +154,14 @@ object CustomCommandDialog: TCustomCommandDialog
     ModalResult = 2
     TabOrder = 2
   end
+  object HelpButton: TButton
+    Left = 312
+    Top = 272
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 3
+    OnClick = HelpButtonClick
+  end
 end

+ 9 - 6
forms/CustomCommand.h

@@ -7,6 +7,7 @@
 #include <StdCtrls.hpp>
 #include <Forms.hpp>
 #include "XPThemes.hpp"
+#include "HistoryComboBox.hpp"
 //---------------------------------------------------------------------------
 class TCustomCommands;
 //---------------------------------------------------------------------------
@@ -16,27 +17,28 @@ __published:
   TXPGroupBox *Group;
   TButton *OkButton;
   TButton *CancelButton;
-  TLabel *FileNameLabel;
+  TLabel *DescriptionLabel;
   TEdit *DescriptionEdit;
   TLabel *Label1;
-  TEdit *CommandEdit;
+  THistoryComboBox *CommandEdit;
   TCheckBox *ApplyToDirectoriesCheck;
   TCheckBox *RecursiveCheck;
   TLabel *CustomCommandsPatternsLabel;
   TRadioButton *LocalCommandButton;
   TRadioButton *RemoteCommandButton;
   TCheckBox *ShowResultsCheck;
+  TButton *HelpButton;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
-  void __fastcall PathEditsKeyDown(TObject *Sender, WORD &Key,
-    TShiftState Shift);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
+  void __fastcall HelpButtonClick(TObject *Sender);
 
 private:
-  bool FEdit;
+  TCustomCommandsMode FMode;
   int FParams;
   AnsiString FOrigDescription;
   const TCustomCommands * FCustomCommands;
+  TCustomCommandValidate FOnValidate;
 
   void __fastcall SetCommand(AnsiString value);
   AnsiString __fastcall GetCommand();
@@ -56,8 +58,9 @@ public:
   __property AnsiString Command = { read = GetCommand, write = SetCommand };
   __property AnsiString Description = { read = GetDescription, write = SetDescription };
   __property int Params = { read = GetParams, write = SetParams };
-  __property bool Edit = { read = FEdit, write = FEdit };
+  __property TCustomCommandsMode Mode = { read = FMode, write = FMode };
   __property const TCustomCommands * CustomCommands = { read = FCustomCommands, write = FCustomCommands };
+  __property TCustomCommandValidate OnValidate = { read = FOnValidate, write = FOnValidate };
 };
 //---------------------------------------------------------------------------
 #endif

File diff suppressed because it is too large
+ 396 - 188
forms/CustomScpExplorer.cpp


+ 34 - 91
forms/CustomScpExplorer.dfm

@@ -1,5 +1,5 @@
 object CustomScpExplorerForm: TCustomScpExplorerForm
-  Left = 328
+  Left = 304
   Top = 166
   Width = 636
   Height = 470
@@ -24,21 +24,18 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     ResizeStyle = rsUpdate
     OnCanResize = QueueSplitterCanResize
   end
-  object TopCoolBar: TCoolBar
+  object TopDock: TTBXDock
     Left = 0
     Top = 0
     Width = 628
-    Height = 41
-    AutoSize = True
-    BandMaximize = bmDblClick
-    Bands = <>
-    FixedSize = True
+    Height = 9
+    FixAlign = True
   end
   object RemotePanel: TPanel
     Left = 0
-    Top = 41
+    Top = 9
     Width = 628
-    Height = 252
+    Height = 284
     Align = alClient
     BevelOuter = bvNone
     TabOrder = 0
@@ -46,40 +43,31 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       Left = 169
       Top = 0
       Width = 3
-      Height = 233
+      Height = 265
       Cursor = crHSplit
       AutoSnap = False
       MinSize = 70
       ResizeStyle = rsUpdate
     end
-    object RemoteStatusBar: TAssociatedStatusBar
+    object RemoteStatusBar: TTBXStatusBar
       Left = 0
-      Top = 233
+      Top = 265
       Width = 628
       Height = 19
-      Hint = '1'
-      Panels = <
-        item
-          Text = '0 b of 0 b in 0 of 0'
-          Width = 50
-        end>
-      ParentFont = True
+      Panels = <>
       ParentShowHint = False
       ShowHint = True
-      SimplePanel = False
       UseSystemFont = False
-      OnResize = StatusBarResize
-      FocusControl = RemoteDirView
+      OnClick = RemoteStatusBarClick
     end
     object RemoteDirView: TUnixDirView
       Left = 172
       Top = 0
       Width = 456
-      Height = 233
+      Height = 265
       Align = alClient
       FullDrag = True
       HideSelection = False
-      ParentFont = False
       PopupMenu = NonVisualDataModule.RemoteDirViewPopup
       TabOrder = 1
       ViewStyle = vsReport
@@ -87,10 +75,9 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnEnter = RemoteDirViewEnter
       NortonLike = False
       UnixColProperties.ExtWidth = 20
-      UnixColProperties.ExtVisible = False
       OnDDDragFileName = RemoteFileControlDDDragFileName
-      StatusBar = RemoteStatusBar
       OnGetSelectFilter = RemoteDirViewGetSelectFilter
+      OnLoaded = DirViewLoaded
       OnExecFile = DirViewExecFile
       OnMatchMask = DirViewMatchMask
       OnGetOverlay = RemoteDirViewGetOverlay
@@ -105,13 +92,14 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnDDFileOperation = RemoteFileControlDDFileOperation
       OnDDCreateDataObject = RemoteFileControlDDCreateDataObject
       OnContextPopup = RemoteDirViewContextPopup
+      OnHistoryChange = DirViewHistoryChange
       OnDisplayProperties = RemoteDirViewDisplayProperties
     end
     object RemoteDriveView: TUnixDriveView
       Left = 0
       Top = 0
       Width = 169
-      Height = 233
+      Height = 265
       DirView = RemoteDirView
       OnDDDragFileName = RemoteFileControlDDDragFileName
       OnDDEnd = RemoteFileControlDDEnd
@@ -140,7 +128,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     Height = 140
     Align = alBottom
     BevelOuter = bvNone
-    TabOrder = 2
+    TabOrder = 1
     object QueueView: TListView
       Left = 0
       Top = 26
@@ -180,8 +168,8 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       ReadOnly = True
       RowSelect = True
       PopupMenu = NonVisualDataModule.QueuePopup
-      SmallImages = NonVisualDataModule.QueueImages
-      StateImages = NonVisualDataModule.QueueImages
+      SmallImages = GlyphsModule.QueueImages
+      StateImages = GlyphsModule.QueueImages
       TabOrder = 0
       ViewStyle = vsReport
       OnContextPopup = QueueViewContextPopup
@@ -192,91 +180,46 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnSelectItem = QueueViewSelectItem
       OnStartDrag = QueueViewStartDrag
     end
-    object QueueCoolBar: TCoolBar
+    object QueueDock: TTBXDock
       Left = 0
       Top = 0
       Width = 628
       Height = 26
-      AutoSize = True
-      BandMaximize = bmDblClick
-      Bands = <
-        item
-          Control = QueueToolBar
-          ImageIndex = -1
-          MinHeight = 22
-          Width = 624
-        end>
-      FixedSize = True
-      object QueueToolBar: TToolBar
-        Left = 9
+      AllowDrag = False
+      object QueueToolbar: TTBXToolbar
+        Left = 0
         Top = 0
-        Width = 611
-        Height = 22
-        Hint = '|E'
-        Caption = 'QueueToolBar'
-        EdgeBorders = []
-        Flat = True
-        Images = NonVisualDataModule.ExplorerImages
+        Caption = 'QueueToolbar'
+        Images = GlyphsModule.ExplorerImages
         ParentShowHint = False
         ShowHint = True
         TabOrder = 0
-        Transparent = True
-        object ToolButton52: TToolButton
-          Left = 0
-          Top = 0
+        object TBXItem201: TTBXItem
           Action = NonVisualDataModule.QueueItemQueryAction
         end
-        object ToolButton54: TToolButton
-          Left = 23
-          Top = 0
+        object TBXItem202: TTBXItem
           Action = NonVisualDataModule.QueueItemErrorAction
         end
-        object ToolButton53: TToolButton
-          Left = 46
-          Top = 0
+        object TBXItem203: TTBXItem
           Action = NonVisualDataModule.QueueItemPromptAction
         end
-        object ToolButton55: TToolButton
-          Left = 69
-          Top = 0
+        object TBXItem204: TTBXItem
           Action = NonVisualDataModule.QueueItemExecuteAction
         end
-        object ToolButton63: TToolButton
-          Left = 92
-          Top = 0
+        object TBXItem205: TTBXItem
           Action = NonVisualDataModule.QueueItemDeleteAction
         end
-        object ToolButton56: TToolButton
-          Left = 115
-          Top = 0
-          Width = 8
-          Hint = 'E'
-          Caption = 'ToolButton56'
-          ImageIndex = 71
-          Style = tbsSeparator
+        object TBXSeparatorItem201: TTBXSeparatorItem
         end
-        object ToolButton64: TToolButton
-          Left = 123
-          Top = 0
+        object TBXItem206: TTBXItem
           Action = NonVisualDataModule.QueueItemUpAction
         end
-        object ToolButton65: TToolButton
-          Left = 146
-          Top = 0
+        object TBXItem207: TTBXItem
           Action = NonVisualDataModule.QueueItemDownAction
         end
-        object ToolButton66: TToolButton
-          Left = 169
-          Top = 0
-          Width = 8
-          Hint = 'E'
-          Caption = 'ToolButton66'
-          ImageIndex = 74
-          Style = tbsSeparator
+        object TBXSeparatorItem202: TTBXSeparatorItem
         end
-        object ToolButton67: TToolButton
-          Left = 177
-          Top = 0
+        object TBXItem208: TTBXItem
           Action = NonVisualDataModule.QueuePreferencesAction
         end
       end

+ 80 - 49
forms/CustomScpExplorer.h

@@ -13,7 +13,6 @@
 #include <UnixDirView.h>
 #include <ComCtrls.hpp>
 #include <ExtCtrls.hpp>
-#include <AssociatedStatusBar.hpp>
 #include <ToolWin.hpp>
 
 #include <WinInterface.h>
@@ -21,6 +20,12 @@
 #include "QueueController.h"
 #include "UnixDriveView.h"
 #include "CustomDriveView.hpp"
+#include "TBX.hpp"
+#include "TB2Dock.hpp"
+#include "TBXExtItems.hpp"
+#include "TBXStatusBars.hpp"
+#include "TB2Item.hpp"
+#include "TB2Toolbar.hpp"
 //---------------------------------------------------------------------------
 class TProgressForm;
 class TSynchronizeProgressForm;
@@ -43,37 +48,32 @@ class TCustomScpExplorerForm : public TForm
 {
 __published:
   TPanel *RemotePanel;
-  TAssociatedStatusBar *RemoteStatusBar;
+  TTBXStatusBar *RemoteStatusBar;
   TUnixDirView *RemoteDirView;
-  TCoolBar *TopCoolBar;
+  TTBXDock *TopDock;
   TListView *QueueView;
   TPanel *QueuePanel;
   TSplitter *QueueSplitter;
-  TToolBar *QueueToolBar;
-  TToolButton *ToolButton52;
-  TToolButton *ToolButton54;
-  TToolButton *ToolButton53;
-  TToolButton *ToolButton55;
-  TToolButton *ToolButton63;
-  TToolButton *ToolButton56;
-  TToolButton *ToolButton64;
-  TToolButton *ToolButton65;
-  TCoolBar *QueueCoolBar;
-  TToolButton *ToolButton66;
-  TToolButton *ToolButton67;
+  TTBXToolbar *QueueToolbar;
+  TTBXDock *QueueDock;
+  TTBXItem *TBXItem201;
+  TTBXItem *TBXItem202;
+  TTBXItem *TBXItem203;
+  TTBXItem *TBXItem204;
+  TTBXItem *TBXItem205;
+  TTBXSeparatorItem *TBXSeparatorItem201;
+  TTBXItem *TBXItem206;
+  TTBXItem *TBXItem207;
+  TTBXSeparatorItem *TBXSeparatorItem202;
+  TTBXItem *TBXItem208;
   TUnixDriveView *RemoteDriveView;
   TSplitter *RemotePanelSplitter;
   void __fastcall RemoteDirViewContextPopup(TObject *Sender,
     const TPoint &MousePos, bool &Handled);
   void __fastcall RemoteDirViewGetSelectFilter(
     TCustomDirView *Sender, bool Select, TFileFilter &Filter);
-  void __fastcall SessionStatusBarDrawPanel(TStatusBar *StatusBar,
-    TStatusPanel *Panel, const TRect &Rect);
-  void __fastcall SessionStatusBarMouseMove(TObject *Sender,
-    TShiftState Shift, int X, int Y);
   void __fastcall ApplicationHint(TObject *Sender);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
-  void __fastcall DropDownButtonMenu(TObject *Sender);
   void __fastcall RemoteDirViewDisplayProperties(TObject *Sender);
   void __fastcall DirViewColumnRightClick(TObject *Sender,
     TListColumn *Column, TPoint &Point);
@@ -92,7 +92,6 @@ __published:
     int dwEffect, HRESULT &Result);
   void __fastcall QueueSplitterCanResize(TObject *Sender, int &NewSize,
     bool &Accept);
-  void __fastcall StatusBarResize(TObject *Sender);
   void __fastcall QueueViewContextPopup(TObject *Sender, TPoint &MousePos,
     bool &Handled);
   void __fastcall QueueViewDeletion(TObject *Sender, TListItem *Item);
@@ -123,15 +122,15 @@ __published:
           AnsiString FileName, AnsiString Masks, bool &Matches);
   void __fastcall RemoteDirViewGetOverlay(TObject *Sender, TListItem *Item,
           WORD &Indexes);
-  void __fastcall SessionComboResizerCanResize(TObject *Sender, int &NewSize, 
-    bool &Accept);
-  void __fastcall SessionComboResizerMoved(TObject *Sender);
-  void __fastcall SessionComboResizerDblClick(TObject * Sender);
+  void __fastcall DirViewHistoryChange(TCustomDirView *Sender);
+  void __fastcall RemoteStatusBarClick(TObject *Sender);
+  void __fastcall DirViewLoaded(TObject *Sender);
+  void __fastcall AddressToolbarGetBaseSize(TTBCustomToolbar * Toolbar, TPoint & ASize);
 
 private:
   TTerminal * FTerminal;
   TTerminalQueue * FQueue;
-  TTerminalQueueStatus * FQueueStatus; 
+  TTerminalQueueStatus * FQueueStatus;
   TCriticalSection * FQueueStatusSection;
   bool FQueueStatusInvalidated;
   bool FQueueItemInvalidated;
@@ -160,18 +159,28 @@ private:
   bool FPendingTempSpaceWarn;
   TEditorManager * FEditorManager;
   TStrings * FCapturedLog;
+  bool FDragDropOperation;
+  AnsiString FCopyParamDefault;
+  AnsiString FCopyParamAutoSelected;
+  bool FEditingFocusedAdHocCommand;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side);
   void __fastcall SetTerminal(TTerminal * value);
   void __fastcall SetQueue(TTerminalQueue * value);
-  void __fastcall SessionComboDropDown(TObject * Sender);
-  void __fastcall SessionComboDrawItem(TWinControl * Control, int Index,
-    const TRect & Rect, TOwnerDrawState State);
-  void __fastcall SessionComboChange(TObject * Sender);
+  void __fastcall SessionComboPopup(TTBCustomItem * Sender, bool FromLink);
+  void __fastcall SessionComboDrawItem(TTBXCustomList * Sender, TCanvas * ACanvas,
+    const TRect & ARect, int AIndex, int AHoverIndex, bool & DrawDefault);
+  void __fastcall SessionComboChange(TObject * Sender, const AnsiString Text);
+  void __fastcall TransferComboChange(TObject * Sender, const AnsiString Text);
   void __fastcall CloseInternalEditor(TObject * Sender);
   void __fastcall ForceCloseInternalEditor(TObject * Sender);
-  void __fastcall TerminalCaptureLog(TObject* Sender, const AnsiString AddedLine);
+  void __fastcall TerminalCaptureLog(TObject* Sender, TLogLineType Type,
+    const AnsiString AddedLine);
+  void __fastcall HistoryItemClick(System::TObject* Sender);
+  void __fastcall UpdateHistoryMenu(TOperationSide Side, bool Back);
+  void __fastcall AdHocCustomCommandValidate(const AnsiString & Command,
+    int Params);
 
 protected:
   TOperationSide FCurrentSide;
@@ -182,9 +191,11 @@ protected:
   bool FDDMoveSlipped;
   TTimer * FUserActionTimer;
   TQueueItemProxy * FPendingQueueActionItem;
+  TTBXPopupMenu * FHistoryMenu[2][2];
+  bool FNoTransferPresetAutoSelect;
 
   virtual bool __fastcall CopyParamDialog(TTransferDirection Direction,
-    TTransferType Type, bool DragDrop, TStrings * FileList,
+    TTransferType Type, bool Temp, TStrings * FileList,
     AnsiString & TargetDirectory, TGUICopyParamType & CopyParam, bool Confirm);
   virtual bool __fastcall RemoteTransferDialog(TStrings * FileList,
     AnsiString & Target, AnsiString & FileMask, bool NoConfirmation, bool Move);
@@ -193,7 +204,6 @@ protected:
   void __fastcall RemoteTransferFiles(TStrings * FileList, bool NoConfirmation, bool Move);
   virtual void __fastcall DoDirViewExecFile(TObject * Sender, TListItem * Item, bool & AllowExec);
   virtual TControl * __fastcall GetComponent(Byte Component);
-  virtual TCoolBand * __fastcall GetCoolBand(TCoolBar * Coolbar, int ID);
   bool __fastcall GetComponentVisible(Word Component);
   virtual Boolean __fastcall GetHasDirView(TOperationSide Side);
   DYNAMIC void __fastcall KeyDown(Word & Key, Classes::TShiftState Shift);
@@ -202,13 +212,15 @@ protected:
   virtual void __fastcall SetComponentVisible(Word Component, bool value);
   virtual void __fastcall FixControlsPlacement();
   void __fastcall SetProperties(TOperationSide Side, TStrings * FileList);
-  void __fastcall CustomCommand(TStrings * FileList, const AnsiString & Name);
+  void __fastcall CustomCommand(TStrings * FileList, AnsiString Name,
+    AnsiString Command, int Params);
   virtual void __fastcall TerminalChanging();
   virtual void __fastcall TerminalChanged();
   virtual void __fastcall QueueChanged();
+  void __fastcall InitStatusBar();
   void __fastcall UpdateStatusBar();
   virtual void __fastcall DoOperationFinished(TFileOperation Operation,
-    TOperationSide Side, bool DragDrop, const AnsiString FileName, bool Success,
+    TOperationSide Side, bool Temp, const AnsiString FileName, bool Success,
     bool & DisconnectWhenFinished);
   virtual void __fastcall DoOpenDirectoryDialog(TOpenDirectoryMode Mode, TOperationSide Side);
   virtual void __fastcall FileOperationProgress(
@@ -229,12 +241,13 @@ protected:
     const AnsiString RemoteDirectory, bool & Continue);
   void __fastcall DoSynchronize(TSynchronizeController * Sender,
     const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
-    const TSynchronizeParamType & Params);
+    const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
+    TSynchronizeStats * Stats, bool Full);
   void __fastcall DoSynchronizeInvalid(TSynchronizeController * Sender,
-    const AnsiString Directory);
+    const AnsiString Directory, const AnsiString ErrorStr);
   void __fastcall Synchronize(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
-    const TCopyParamType & CopyParam, int Params);
+    const TCopyParamType & CopyParam, int Params, TSynchronizeStats * Stats);
   virtual void __fastcall BatchStart(void *& Storage);
   virtual void __fastcall BatchEnd(void * Storage);
   void __fastcall ExecuteFileOperation(TFileOperation Operation, TOperationSide Side,
@@ -276,6 +289,15 @@ protected:
   bool __fastcall RemoteExecuteForceText(TExecuteFileBy ExecuteFileBy);
   void __fastcall TemporarilyDownloadFiles(TStrings * FileList, bool ForceText,
     AnsiString & TempDir, bool AllFiles, bool GetTargetNames);
+  TTBXPopupMenu * __fastcall HistoryMenu(TOperationSide Side, bool Back);
+  void __fastcall UpdateFileStatusBar(TTBXStatusBar * StatusBar,
+    const TStatusFileInfo & FileInfo, int Panel);
+  virtual void __fastcall DoDirViewLoaded(TCustomDirView * Sender);
+  virtual void __fastcall UpdateControls();
+  void __fastcall StartUpdates();
+  void __fastcall TransferPresetAutoSelect();
+  virtual void __fastcall GetTransferPresetAutoSelectData(TCopyParamRuleData & Data);
+  int __fastcall CustomCommandState(const AnsiString & Command, int Params, bool OnFocused);
 
   #pragma warn -inl
   BEGIN_MESSAGE_MAP
@@ -294,10 +316,11 @@ public:
   void __fastcall CreateDirectory(TOperationSide Side);
   void __fastcall ExecuteFileOperation(TFileOperation Operation, TOperationSide Side,
     bool OnFocused, bool NoConfirmation = false, void * Param = NULL);
+  void __fastcall AdHocCustomCommand(bool OnFocused);
   virtual TCustomDirView * __fastcall DirView(TOperationSide Side);
   virtual void __fastcall ChangePath(TOperationSide Side) = 0;
   virtual void __fastcall StoreParams();
-  bool __fastcall EnableCustomCommand(const AnsiString & Description);
+  int __fastcall CustomCommandState(const AnsiString & Description, bool OnFocused);
   void __fastcall NewSession();
   void __fastcall CloseSession();
   void __fastcall OpenDirectory(TOperationSide Side);
@@ -327,16 +350,20 @@ public:
   void __fastcall TerminalListChanged(TObject * Sender);
   int __fastcall MoreMessageDialog(const AnsiString Message,
     TStrings * MoreMessages, TQueryType Type, int Answers,
-    int HelpCtx, const TMessageParams * Params = NULL);
+    AnsiString HelpKeyword, const TMessageParams * Params = NULL);
   void __fastcall OperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool DragDrop, const AnsiString FileName, bool Success, bool & DisconnectWhenFinished);
+    bool Temp, const AnsiString FileName, bool Success, bool & DisconnectWhenFinished);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData, TCancelStatus & Cancel);
   bool __fastcall DoSynchronizeDirectories(AnsiString & LocalDirectory,
     AnsiString & RemoteDirectory);
   bool __fastcall DoFullSynchronizeDirectories(AnsiString & LocalDirectory,
-    AnsiString & RemoteDirectory, TSynchronizeMode & Mode);
+    AnsiString & RemoteDirectory, TSynchronizeMode & Mode, bool & SaveMode);
   bool __fastcall CanPasteFromClipBoard();
   void __fastcall PasteFromClipBoard();
+  void __fastcall ToggleQueueVisibility();
+  virtual AnsiString __fastcall PathForCaption();
+  void __fastcall FileListFromClipboard();
+  void __fastcall PreferencesDialog(TPreferencesMode APreferencesMode);
 
   __property bool ComponentVisible[Word Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation };
@@ -349,14 +376,18 @@ public:
 class TExporerState : public TObject
 {
 public:
-  struct TDirViewState
-  {
-    AnsiString SortStr;
-    AnsiString FocusedItem;
-  };
+  __fastcall TExporerState();
+  virtual __fastcall ~TExporerState();
 
-  TDirViewState Remote;
-  TDirViewState Local;
+  TObject * Remote;
+  TObject * Local;
+};
+//---------------------------------------------------------------------------
+struct TCustomCommandParam
+{
+  AnsiString Name;
+  AnsiString Command;
+  int Params;
 };
 //---------------------------------------------------------------------------
 #endif

+ 19 - 7
forms/Editor.cpp

@@ -12,6 +12,11 @@
 #include "WinConfiguration.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
+#pragma link "TB2Dock"
+#pragma link "TBX"
+#pragma link "TB2Item"
+#pragma link "TB2Toolbar"
+#pragma link "TBXStatusBars"
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------------
 TForm * __fastcall ShowEditorForm(const AnsiString FileName, TCustomForm * ParentForm,
@@ -125,7 +130,8 @@ void __fastcall TEditorForm::EditorActionsUpdate(TBasicAction *Action,
       FLastFindDialog != NULL || !FindDialog->FindText.IsEmpty();
   }
   else if (Action == PreferencesAction || Action == CloseAction ||
-    Action == FindAction || Action == ReplaceAction || Action == GoToLineAction)
+    Action == FindAction || Action == ReplaceAction || Action == GoToLineAction ||
+    Action == HelpAction)
   {
     ((TAction *)Action)->Enabled = true;
   }
@@ -185,6 +191,10 @@ void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
     SendMessage(EditorMemo->Handle, EM_PASTESPECIAL, CF_TEXT,
       (LPARAM)&RepasteSpecial);
   }
+  else if (Action == HelpAction)
+  {
+    FormHelp(this);
+  }
   else
   {
     Handled = false;
@@ -251,9 +261,9 @@ void __fastcall TEditorForm::UpdateControls()
   {
     FCaretPos = ACaretPos;
     int Count = EditorMemo->Lines->Count;
-    StatusBar->Panels->Items[0]->Text = FMTLOAD(EDITOR_LINE_STATUS,
+    StatusBar->Panels->Items[0]->Caption = FMTLOAD(EDITOR_LINE_STATUS,
       ((int)FCaretPos.y+1, Count));
-    StatusBar->Panels->Items[1]->Text = FMTLOAD(EDITOR_COLUMN_STATUS,
+    StatusBar->Panels->Items[1]->Caption = FMTLOAD(EDITOR_COLUMN_STATUS,
       ((int)FCaretPos.x+1));
     AnsiString Character;
     if (FCaretPos.y >= 0 && FCaretPos.y < EditorMemo->Lines->Count)
@@ -265,9 +275,9 @@ void __fastcall TEditorForm::UpdateControls()
           (int((unsigned char)Line[FCaretPos.x+1]), int((unsigned char)Line[FCaretPos.x+1])));
       }
     }
-    StatusBar->Panels->Items[2]->Text = Character;
+    StatusBar->Panels->Items[2]->Caption = Character;
   }
-  StatusBar->Panels->Items[3]->Text =
+  StatusBar->Panels->Items[3]->Caption =
     (EditorMemo->Modified ? LoadStr(EDITOR_MODIFIED) : AnsiString(""));
   EditorActions->UpdateAction(SaveAction);  
 }
@@ -345,11 +355,11 @@ void __fastcall TEditorForm::Find()
     {
       if (!Replacements)
       {
-        MessageDialog(FMTLOAD(EDITOR_FIND_END, (FLastFindDialog->FindText)), qtInformation, qaOK, 0);
+        MessageDialog(FMTLOAD(EDITOR_FIND_END, (FLastFindDialog->FindText)), qtInformation, qaOK, HELP_NONE);
       }
       else
       {
-        MessageDialog(FMTLOAD(EDITOR_REPLACE_END, (Replacements)), qtInformation, qaOK, 0);
+        MessageDialog(FMTLOAD(EDITOR_REPLACE_END, (Replacements)), qtInformation, qaOK, HELP_NONE);
       }
     }
   }
@@ -360,6 +370,8 @@ void __fastcall TEditorForm::Find()
 void __fastcall TEditorForm::FormShow(TObject * /*Sender*/)
 {
   LoadFile();
+
+  CutFormToDesktop(this);
 }
 //---------------------------------------------------------------------------
 void __fastcall TEditorForm::LoadFile()

+ 234 - 156
forms/Editor.dfm

@@ -1,5 +1,5 @@
 object EditorForm: TEditorForm
-  Left = 353
+  Left = 315
   Top = 186
   Width = 625
   Height = 419
@@ -32,173 +32,117 @@ object EditorForm: TEditorForm
   OnShow = FormShow
   PixelsPerInch = 96
   TextHeight = 13
-  object TopCoolBar: TCoolBar
+  object TopDock: TTBXDock
     Left = 0
     Top = 0
     Width = 617
     Height = 26
-    AutoSize = True
-    Bands = <
-      item
-        Control = ToolBar
-        ImageIndex = -1
-        MinHeight = 22
-        Width = 613
-      end>
-    object ToolBar: TToolBar
-      Left = 9
+    AllowDrag = False
+    object ToolBar: TTBXToolbar
+      Left = 0
       Top = 0
-      Width = 331
-      Height = 22
-      Align = alLeft
-      AutoSize = True
       Caption = 'ToolBar'
-      EdgeBorders = []
-      Flat = True
       Images = EditorImages
       ParentShowHint = False
       ShowHint = True
       TabOrder = 0
-      Transparent = True
-      Wrapable = False
-      object ToolButton12: TToolButton
-        Left = 0
-        Top = 0
+      object TBXItem1: TTBXItem
         Action = CloseAction
       end
-      object ToolButton1: TToolButton
-        Left = 23
-        Top = 0
+      object TBXItem2: TTBXItem
         Action = SaveAction
       end
-      object ToolButton2: TToolButton
-        Left = 46
-        Top = 0
-        Width = 8
-        Caption = 'ToolButton2'
-        ImageIndex = 1
-        Style = tbsSeparator
+      object TBXSeparatorItem1: TTBXSeparatorItem
       end
-      object ToolButton3: TToolButton
-        Left = 54
-        Top = 0
+      object TBXItem3: TTBXItem
         Action = EditCopy
       end
-      object ToolButton4: TToolButton
-        Left = 77
-        Top = 0
+      object TBXItem4: TTBXItem
         Action = EditCut
       end
-      object ToolButton5: TToolButton
-        Left = 100
-        Top = 0
+      object TBXItem5: TTBXItem
         Action = EditPaste
       end
-      object ToolButton6: TToolButton
-        Left = 123
-        Top = 0
+      object TBXItem6: TTBXItem
         Action = EditDelete
       end
-      object ToolButton8: TToolButton
-        Left = 146
-        Top = 0
+      object TBXItem7: TTBXItem
         Action = EditSelectAll
       end
-      object ToolButton9: TToolButton
-        Left = 169
-        Top = 0
-        Width = 8
-        Caption = 'ToolButton9'
-        ImageIndex = 5
-        Style = tbsSeparator
+      object TBXSeparatorItem2: TTBXSeparatorItem
       end
-      object ToolButton7: TToolButton
-        Left = 177
-        Top = 0
+      object TBXItem8: TTBXItem
         Action = EditUndo
       end
-      object ToolButton10: TToolButton
-        Left = 200
-        Top = 0
-        Width = 8
-        Caption = 'ToolButton10'
-        ImageIndex = 5
-        Style = tbsSeparator
+      object TBXSeparatorItem3: TTBXSeparatorItem
       end
-      object ToolButton13: TToolButton
-        Left = 208
-        Top = 0
+      object TBXItem9: TTBXItem
         Action = FindAction
       end
-      object ToolButton14: TToolButton
-        Left = 231
-        Top = 0
+      object TBXItem10: TTBXItem
         Action = ReplaceAction
       end
-      object ToolButton15: TToolButton
-        Left = 254
-        Top = 0
+      object TBXItem11: TTBXItem
         Action = FindNextAction
       end
-      object ToolButton16: TToolButton
-        Left = 277
-        Top = 0
+      object TBXItem12: TTBXItem
         Action = GoToLineAction
       end
-      object ToolButton17: TToolButton
-        Left = 300
-        Top = 0
-        Width = 8
-        Caption = 'ToolButton17'
-        ImageIndex = 8
-        Style = tbsSeparator
+      object TBXSeparatorItem4: TTBXSeparatorItem
       end
-      object ToolButton11: TToolButton
-        Left = 308
-        Top = 0
+      object TBXItem13: TTBXItem
         Action = PreferencesAction
       end
+      object TBXSeparatorItem5: TTBXSeparatorItem
+      end
+      object TBXItem14: TTBXItem
+        Action = HelpAction
+      end
     end
   end
   object EditorMemo: TRichEdit
     Left = 0
     Top = 26
     Width = 617
-    Height = 340
+    Height = 337
     Align = alClient
     HideSelection = False
     PlainText = True
     PopupMenu = EditorPopup
     ScrollBars = ssBoth
-    TabOrder = 1
+    TabOrder = 0
     WantTabs = True
     OnChange = EditorMemoChange
     OnKeyUp = EditorMemoKeyUp
     OnMouseUp = EditorMemoMouseUp
   end
-  object StatusBar: TStatusBar
+  object StatusBar: TTBXStatusBar
     Left = 0
-    Top = 366
+    Top = 363
     Width = 617
-    Height = 19
     Panels = <
       item
-        Text = 'Line: 2000/20000'
-        Width = 130
+        Caption = 'Line: 2000/20000'
+        Size = 130
+        Tag = 0
       end
       item
-        Text = 'Column: 20'
-        Width = 150
+        Caption = 'Column: 20'
+        ViewPriority = 80
+        Size = 150
+        Tag = 0
       end
       item
-        Text = 'Character: 132 (0x56)'
-        Width = 150
+        Caption = 'Character: 132 (0x56)'
+        ViewPriority = 90
+        Size = 150
+        Tag = 0
       end
       item
-        Width = 50
+        ViewPriority = 70
+        StretchPriority = 100
+        Tag = 0
       end>
-    ParentFont = True
-    SimplePanel = False
     UseSystemFont = False
   end
   object EditorActions: TActionList
@@ -297,13 +241,68 @@ object EditorForm: TEditorForm
       SecondaryShortCuts.Strings = (
         'Alt+F8')
     end
+    object HelpAction: TAction
+      Caption = '&Help'
+      Hint = 'Editor help'
+      ImageIndex = 13
+    end
   end
   object EditorImages: TImageList
     Left = 536
     Top = 56
     Bitmap = {
-      494C01010D000E00040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
-      0000000000003600000028000000400000004000000001002000000000000040
+      494C01010F001300040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      0000000000003600000028000000400000005000000001002000000000000050
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -313,7 +312,6 @@ object EditorForm: TEditorForm
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000848484000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -321,113 +319,192 @@ object EditorForm: TEditorForm
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      000084848400FFFFFF0084848400848484008484840084848400848484008484
-      84008484840084848400FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000000000000000000000000
-      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000084000000000000000000
-      000084848400FFFFFF0084848400848484008484840084848400848484008484
-      84008484840084848400FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000084000000840000000000
-      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
-      FF0000FFFF0000FFFF0000FFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000084000000840000000000
-      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
-      FF0000FFFF0000FFFF0000FFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000084000000000000000000
-      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
-      FF0000FFFF0000FFFF0000FFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000008400000000000000000000000000
-      000084848400FFFFFF0084848400848484008484840084848400848484008484
-      84008484840084848400FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      000084848400FFFFFF0084848400848484008484840084848400FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000660066000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000848484000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000066006600660066009300D6008000800000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
       000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00000000000000000000000000000000000000000000000000000000000000
+      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000006600
+      6600660066009300D600CC66CC00D6E7E700CBCBCB0080008000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      000084848400FFFFFF0084848400848484008484840084848400848484008484
+      84008484840084848400FFFFFF00000000000000000066006600660066009300
+      D600CC66CC00D6E7E700D6E7E700D6E7E700CBCBCB00CBCBCB00800080000000
       0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000840000008400
+      0000840000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000000000000000000000000
       000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF0000000000FFFFFF0000000000000000000000000000000000000000000000
+      FF00FFFFFF00FFFFFF00FFFFFF0000000000800080009300D600CC66CC00D6E7
+      E700D6E7E700CBCBCB00CBCBCB00800080009999990090A9AD00CBCBCB008000
+      8000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000840000008400
+      0000840000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000084000000000000000000
+      000084848400FFFFFF0084848400848484008484840084848400848484008484
+      84008484840084848400FFFFFF00000000008000800099999900D6E7E700CBCB
+      CB0090A9AD00800080008000800099009900000000008686860099999900CBCB
+      CB00800080000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000084000000840000000000
+      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
+      FF0000FFFF0000FFFF0000FFFF00000000008000800099999900CBCBCB008000
+      800080008000CC00CC00CC00CC00CC00CC009900990000000000666666009999
+      990090A9AD008000800000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000840000008400
+      0000840000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000084000000840000000000
+      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
+      FF0000FFFF0000FFFF0000FFFF0000000000800080008000800080008000CC00
+      CC00CC00CC009900990099009900CC00CC00CC00CC0099009900000000006666
+      6600999999008686860080008000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000840000008400
+      0000840000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000084000000000000000000
+      00008484840000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FF
+      FF0000FFFF0000FFFF0000FFFF000000000080008000FF99FF00CC00CC00CC00
+      CC00CC00CC0000FFFF0033CCFF00336699009900990080008000990099000000
+      0000666666009999990080008000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000008400
+      0000840000008400000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000000000000000000000000
+      000084848400FFFFFF0084848400848484008484840084848400848484008484
+      84008484840084848400FFFFFF00000000000000000080008000FF99FF00CC00
+      CC00CC00CC00CC00CC00CC00CC0000FFFF0000FFFF0033CCFF00336699008000
+      8000000000006666660080008000D8E9EC000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000840000008400000084000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF0000000000000000000000000080008000FF99
+      FF00CC00CC00CC00CC00CC00CC00CC00CC00CC00CC0033CCFF0000FFFF006600
+      6600800080000000000080008000D8E9EC000000000000000000000000000000
+      0000000000000000000000000000840000008400000084000000000000000000
+      0000000000008400000084000000840000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000084848400FFFFFF0084848400848484008484840084848400FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000008000
+      8000FF99FF00CC00CC00CC00CC0000FFFF0000FFFF0000FFFF003399CC006600
+      6600800080008000800000000000000000000000000000000000000000000000
+      0000000000000000000000000000840000008400000084000000000000000000
+      0000000000008400000084000000840000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
       FF00000000000000000000000000000000000000000000000000000000000000
+      000080008000FF99FF00CC00CC00CC00CC003366990033669900660066008000
+      8000800080006600660000000000000000000000000000000000000000000000
+      0000000000000000000000000000840000008400000084000000000000000000
+      0000000000008400000084000000840000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF0000000000FFFFFF0000000000000000000000000000000000000000000000
+      00000000000080008000FF99FF00CC00CC00CC00CC00CC00CC00990099006600
+      6600000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000008400000084000000840000008400
+      0000840000008400000084000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
+      000084848400FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000080008000FF99FF00CC00CC0066006600000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000084000000840000008400
+      0000840000008400000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000848484008484840084848400848484008484840084848400848484008484
       8400000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000800080000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -817,12 +894,16 @@ object EditorForm: TEditorForm
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       000000000000000000000000000000000000424D3E000000000000003E000000
-      2800000040000000400000000100010000000000000200000000000000000000
-      000000000000000000000000FFFFFF00FFFF000000000000F000000000000000
-      F000000000000000F00000000000000070000000000000003000000000000000
-      1000000000000000100000000000000030000000000000007000000000000000
-      F000000000000000F000000000000000F000000000000000F001000000000000
-      F003000000000000F007000000000000FFFFFFFFB6E7FFFF0015FFFFB76BFE49
+      2800000040000000500000000100010000000000800200000000000000000000
+      000000000000000000000000FFFFFF0000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000FFFFFE7FFFFF0000F000F83FF9FF0000
+      F000E01FF9FF0000F000800FF3C700007000000773C700003000000327FF0000
+      1000000107C700001000000000C700003000000001E300007000800003F10000
+      F000C00006380000F000E0000E380000F000F0011E380000F001F8073F010000
+      F003FC1F7F830000F007FE7FFFFF0000FFFFFFFFB6E7FFFF0015FFFFB76BFE49
       000007C18427FE49000007C1B76BFFFFE00707C1CEE7FFFFE0070101FFFFC7C7
       E0070001C7C7C7C7E0070001C7C7C387E0070001C387C007E0078003C007C007
       E007C107C007C007E007C107C007C007FFFFE38FC007C007F81FE38FC007F39F
@@ -850,47 +931,44 @@ object EditorForm: TEditorForm
     Left = 536
     Top = 96
   end
-  object EditorPopup: TPopupMenu
+  object EditorPopup: TTBXPopupMenu
     Images = EditorImages
     Left = 480
     Top = 136
-    object Undo1: TMenuItem
+    object Undo1: TTBXItem
       Action = EditUndo
     end
-    object N1: TMenuItem
-      Caption = '-'
+    object N1: TTBXSeparatorItem
     end
-    object Cut1: TMenuItem
+    object Cut1: TTBXItem
       Action = EditCut
     end
-    object Copy1: TMenuItem
+    object Copy1: TTBXItem
       Action = EditCopy
     end
-    object Paste1: TMenuItem
+    object Paste1: TTBXItem
       Action = EditPaste
     end
-    object Delete1: TMenuItem
+    object Delete1: TTBXItem
       Action = EditDelete
     end
-    object N2: TMenuItem
-      Caption = '-'
+    object N2: TTBXSeparatorItem
     end
-    object SelectAll1: TMenuItem
+    object SelectAll1: TTBXItem
       Action = EditSelectAll
     end
-    object N3: TMenuItem
-      Caption = '-'
+    object N3: TTBXSeparatorItem
     end
-    object Find1: TMenuItem
+    object Find1: TTBXItem
       Action = FindAction
     end
-    object Replace1: TMenuItem
+    object Replace1: TTBXItem
       Action = ReplaceAction
     end
-    object Findnext1: TMenuItem
+    object Findnext1: TTBXItem
       Action = FindNextAction
     end
-    object Gotolinenumber1: TMenuItem
+    object Gotolinenumber1: TTBXItem
       Action = GoToLineAction
     end
   end

+ 42 - 34
forms/Editor.h

@@ -14,6 +14,11 @@
 #include <StdActns.hpp>
 #include <Dialogs.hpp>
 #include <Menus.hpp>
+#include "TB2Dock.hpp"
+#include "TBX.hpp"
+#include "TB2Item.hpp"
+#include "TB2Toolbar.hpp"
+#include "TBXStatusBars.hpp"
 //---------------------------------------------------------------------------
 class TEditorForm : public TForm
 {
@@ -21,55 +26,58 @@ __published:
   TActionList *EditorActions;
   TImageList *EditorImages;
   TAction *SaveAction;
-  TCoolBar *TopCoolBar;
-  TToolBar *ToolBar;
-  TToolButton *ToolButton1;
+  TTBXDock *TopDock;
+  TTBXToolbar *ToolBar;
   TRichEdit *EditorMemo;
-  TStatusBar *StatusBar;
-  TToolButton *ToolButton2;
-  TToolButton *ToolButton3;
-  TToolButton *ToolButton4;
-  TToolButton *ToolButton5;
-  TToolButton *ToolButton6;
-  TToolButton *ToolButton7;
-  TToolButton *ToolButton8;
+  TTBXStatusBar *StatusBar;
   TEditCut *EditCut;
   TEditCopy *EditCopy;
   TEditPaste *EditPaste;
   TEditSelectAll *EditSelectAll;
   TEditUndo *EditUndo;
   TEditDelete *EditDelete;
-  TToolButton *ToolButton9;
-  TToolButton *ToolButton10;
   TAction *PreferencesAction;
-  TToolButton *ToolButton11;
   TAction *CloseAction;
-  TToolButton *ToolButton12;
   TAction *FindAction;
   TAction *ReplaceAction;
   TAction *FindNextAction;
   TAction *GoToLineAction;
-  TToolButton *ToolButton13;
-  TToolButton *ToolButton14;
-  TToolButton *ToolButton15;
-  TToolButton *ToolButton16;
-  TToolButton *ToolButton17;
+  TTBXItem *TBXItem1;
+  TTBXItem *TBXItem2;
+  TTBXSeparatorItem *TBXSeparatorItem1;
+  TTBXItem *TBXItem3;
+  TTBXItem *TBXItem4;
+  TTBXItem *TBXItem5;
+  TTBXItem *TBXItem6;
+  TTBXItem *TBXItem7;
+  TTBXItem *TBXItem8;
+  TTBXSeparatorItem *TBXSeparatorItem2;
+  TTBXItem *TBXItem9;
+  TTBXSeparatorItem *TBXSeparatorItem3;
+  TTBXItem *TBXItem10;
+  TTBXItem *TBXItem11;
+  TTBXItem *TBXItem12;
+  TTBXSeparatorItem *TBXSeparatorItem4;
+  TTBXItem *TBXItem13;
   TFindDialog *FindDialog;
   TReplaceDialog *ReplaceDialog;
-  TPopupMenu *EditorPopup;
-  TMenuItem *Undo1;
-  TMenuItem *N1;
-  TMenuItem *Cut1;
-  TMenuItem *Copy1;
-  TMenuItem *Paste1;
-  TMenuItem *Delete1;
-  TMenuItem *N2;
-  TMenuItem *SelectAll1;
-  TMenuItem *N3;
-  TMenuItem *Find1;
-  TMenuItem *Replace1;
-  TMenuItem *Findnext1;
-  TMenuItem *Gotolinenumber1;
+  TTBXPopupMenu *EditorPopup;
+  TTBXItem *Undo1;
+  TTBXSeparatorItem *N1;
+  TTBXItem *Cut1;
+  TTBXItem *Copy1;
+  TTBXItem *Paste1;
+  TTBXItem *Delete1;
+  TTBXSeparatorItem *N2;
+  TTBXItem *SelectAll1;
+  TTBXSeparatorItem *N3;
+  TTBXItem *Find1;
+  TTBXItem *Replace1;
+  TTBXItem *Findnext1;
+  TTBXItem *Gotolinenumber1;
+  TTBXSeparatorItem *TBXSeparatorItem5;
+  TTBXItem *TBXItem14;
+  TAction *HelpAction;
   void __fastcall EditorActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall EditorActionsExecute(TBasicAction *Action,
           bool &Handled);

+ 8 - 2
forms/FileSystemInfo.cpp

@@ -5,6 +5,7 @@
 #include <Common.h>
 #include <Terminal.h>
 #include <VCLCommon.h>
+#include "WinInterface.h"
 #include "FileSystemInfo.h"
 //---------------------------------------------------------------------
 #pragma link "XPThemes"
@@ -25,7 +26,7 @@ void __fastcall DoFileSystemInfoDialog(TTerminal * Terminal)
 } 
 //---------------------------------------------------------------------
 __fastcall TFileSystemInfoDialog::TFileSystemInfoDialog(TComponent* AOwner)
-	: TForm(AOwner)
+    : TForm(AOwner)
 {
   UseSystemSettings(this);
 }
@@ -46,7 +47,7 @@ void __fastcall TFileSystemInfoDialog::UpdateControls()
 {
   assert(Terminal);
 
-  SshVersionEdit->Text = IntToStr(Terminal->SshVersion);
+  SshVersionEdit->Text = FORMAT("SSH-%d", (Terminal->SshVersion));
   SshImplementationEdit->Text = Terminal->SshImplementation;
 
   AnsiString Str = CipherNames[Terminal->CSCipher];
@@ -87,4 +88,9 @@ void __fastcall TFileSystemInfoDialog::SetTerminal(TTerminal * value)
   }
 }
 //---------------------------------------------------------------------
+void __fastcall TFileSystemInfoDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 11 - 1
forms/FileSystemInfo.dfm

@@ -18,7 +18,7 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
   PixelsPerInch = 96
   TextHeight = 13
   object CloseButton: TButton
-    Left = 283
+    Left = 200
     Top = 396
     Width = 75
     Height = 25
@@ -334,4 +334,14 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
       Text = 'HostKeyFingerprintEdit'
     end
   end
+  object HelpButton: TButton
+    Left = 283
+    Top = 396
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 4
+    OnClick = HelpButtonClick
+  end
 end

+ 2 - 0
forms/FileSystemInfo.h

@@ -49,6 +49,8 @@ __published:
   TEdit *RemoteCopyEdit;
   TXPGroupBox *HostKeyGroup;
   TEdit *HostKeyFingerprintEdit;
+  TButton *HelpButton;
+  void __fastcall HelpButtonClick(TObject *Sender);
 public:
 	virtual __fastcall TFileSystemInfoDialog(TComponent* AOwner);
 

+ 146 - 13
forms/FullSynchronize.cpp

@@ -4,12 +4,14 @@
 
 #include <Common.h>
 
+#include "WinInterface.h"
 #include "FullSynchronize.h"
 #include "VCLCommon.h"
 
 #include <ScpMain.h>
 #include <Configuration.h>
 #include <TextsWin.h>
+#include <HelpWin.h>
 #include <CustomWinConfiguration.h>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
@@ -18,17 +20,21 @@
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------------
 bool __fastcall DoFullSynchronizeDialog(TSynchronizeMode & Mode, int & Params,
-  AnsiString & LocalDirectory, AnsiString & RemoteDirectory, bool & SaveSettings)
+  AnsiString & LocalDirectory, AnsiString & RemoteDirectory,
+  TCopyParamType * CopyParams, bool & SaveSettings, bool & SaveMode, int Options)
 {
   bool Result;
   TFullSynchronizeDialog * Dialog = new TFullSynchronizeDialog(Application);
   try
   {
     Dialog->Mode = Mode;
+    Dialog->Options = Options;
     Dialog->Params = Params;
     Dialog->LocalDirectory = LocalDirectory;
     Dialog->RemoteDirectory = RemoteDirectory;
+    Dialog->CopyParams = *CopyParams;
     Dialog->SaveSettings = SaveSettings;
+    Dialog->SaveMode = SaveMode;
     Result = Dialog->Execute();
     if (Result)
     {
@@ -36,7 +42,9 @@ bool __fastcall DoFullSynchronizeDialog(TSynchronizeMode & Mode, int & Params,
       Params = Dialog->Params;
       LocalDirectory = Dialog->LocalDirectory;
       RemoteDirectory = Dialog->RemoteDirectory;
+      *CopyParams = Dialog->CopyParams;
       SaveSettings = Dialog->SaveSettings;
+      SaveMode = Dialog->SaveMode;
     }
   }
   __finally
@@ -51,13 +59,50 @@ __fastcall TFullSynchronizeDialog::TFullSynchronizeDialog(TComponent* Owner)
 {
   UseSystemSettings(this);
   FParams = 0;
+  FSaveMode = false;
+  FOptions = 0;
+  FPresetsMenu = new TPopupMenu(this);
+  InstallPathWordBreakProc(LocalDirectoryEdit);
+  InstallPathWordBreakProc(RemoteDirectoryEdit);
+}
+//---------------------------------------------------------------------------
+__fastcall TFullSynchronizeDialog::~TFullSynchronizeDialog()
+{
+  delete FPresetsMenu;
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::UpdateControls()
 {
-  EnableControl(SynchronizeDeleteCheck, !SynchronizeBothButton->Checked);
+  EnableControl(SynchronizeTimestampCheck, FLAGCLEAR(Options, fsoDisableTimestamp));
+  if (SynchronizeTimestampCheck->Checked)
+  {
+    SynchronizeExistingOnlyCheck->Checked = true;
+    SynchronizePreviewChangesCheck->Checked = false;
+    SynchronizeNoConfirmationCheck->Checked = false;
+  }
+  if (SynchronizeTimestampCheck->Checked || SynchronizeBothButton->Checked)
+  {
+    SynchronizeDeleteCheck->Checked = false;
+    SynchronizeByTimeCheck->Checked = true;
+    SynchronizeBySizeCheck->Checked = false;
+  }
+  EnableControl(SynchronizeDeleteCheck, !SynchronizeBothButton->Checked && 
+    !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizeExistingOnlyCheck, !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizePreviewChangesCheck, !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizeNoConfirmationCheck, !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizeByTimeCheck, !SynchronizeBothButton->Checked && 
+    !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizeBySizeCheck, !SynchronizeBothButton->Checked && 
+    !SynchronizeTimestampCheck->Checked);
   EnableControl(OkButton, !LocalDirectoryEdit->Text.IsEmpty() &&
     !RemoteDirectoryEdit->Text.IsEmpty());
+
+  AnsiString InfoStr = FCopyParams.GetInfoStr("; ");
+  CopyParamLabel->Caption = InfoStr;
+  CopyParamLabel->Hint = InfoStr;
+  CopyParamLabel->ShowHint =
+    (CopyParamLabel->Canvas->TextWidth(InfoStr) > (CopyParamLabel->Width * 3 / 2));
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::ControlChange(TObject * /*Sender*/)
@@ -67,6 +112,8 @@ void __fastcall TFullSynchronizeDialog::ControlChange(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 bool __fastcall TFullSynchronizeDialog::Execute()
 {
+  // at start assume that copy param is current preset
+  FPreset = GUIConfiguration->CopyParamCurrent;
   LocalDirectoryEdit->Items = CustomWinConfiguration->History["LocalDirectory"];
   RemoteDirectoryEdit->Items = CustomWinConfiguration->History["RemoteDirectory"];
   bool Result = (ShowModal() == mrOk);
@@ -102,6 +149,7 @@ AnsiString __fastcall TFullSynchronizeDialog::GetLocalDirectory()
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::SetMode(TSynchronizeMode value)
 {
+  FOrigMode = value;
   switch (value)
   {
     case smRemote:
@@ -140,20 +188,30 @@ TSynchronizeMode __fastcall TFullSynchronizeDialog::GetMode()
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::SetParams(int value)
 {
-  FParams = value & ~(spDelete | spNoConfirmation | spExistingOnly | spPreviewChanges);
+  FParams = value & ~(spDelete | spNoConfirmation | spExistingOnly |
+    spPreviewChanges | spTimestamp | spNotByTime | spBySize);
   SynchronizeDeleteCheck->Checked = FLAGSET(value, spDelete);
   SynchronizeNoConfirmationCheck->Checked = FLAGSET(value, spNoConfirmation);
   SynchronizeExistingOnlyCheck->Checked = FLAGSET(value, spExistingOnly);
   SynchronizePreviewChangesCheck->Checked = FLAGSET(value, spPreviewChanges);
+  SynchronizeTimestampCheck->Checked = FLAGSET(value, spTimestamp) &&
+    FLAGCLEAR(Options, fsoDisableTimestamp);
+  SynchronizeByTimeCheck->Checked = FLAGCLEAR(value, spNotByTime);
+  SynchronizeBySizeCheck->Checked = FLAGSET(value, spBySize);
+  UpdateControls();
 }
 //---------------------------------------------------------------------------
 int __fastcall TFullSynchronizeDialog::GetParams()
 {
   return FParams |
-    (SynchronizeDeleteCheck->Checked ? spDelete : 0) |
-    (SynchronizeNoConfirmationCheck->Checked ? spNoConfirmation : 0) |
-    (SynchronizeExistingOnlyCheck->Checked ? spExistingOnly : 0) |
-    (SynchronizePreviewChangesCheck->Checked ? spPreviewChanges : 0);
+    FLAGMASK(SynchronizeDeleteCheck->Checked, spDelete) |
+    FLAGMASK(SynchronizeNoConfirmationCheck->Checked, spNoConfirmation) |
+    FLAGMASK(SynchronizeExistingOnlyCheck->Checked, spExistingOnly) |
+    FLAGMASK(SynchronizePreviewChangesCheck->Checked, spPreviewChanges) |
+    FLAGMASK(SynchronizeTimestampCheck->Checked && FLAGCLEAR(Options, fsoDisableTimestamp),
+      spTimestamp) |
+    FLAGMASK(!SynchronizeByTimeCheck->Checked, spNotByTime) |
+    FLAGMASK(SynchronizeBySizeCheck->Checked, spBySize);
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::LocalDirectoryBrowseButtonClick(
@@ -176,17 +234,33 @@ bool __fastcall TFullSynchronizeDialog::GetSaveSettings()
   return SaveSettingsCheck->Checked;
 }
 //---------------------------------------------------------------------------
-void __fastcall TFullSynchronizeDialog::DirectoryEditKeyDown(
-  TObject * Sender, WORD & Key, TShiftState Shift)
+void __fastcall TFullSynchronizeDialog::SetOptions(int value)
 {
-  PathComboBoxKeyDown(dynamic_cast<TCustomComboBox*>(Sender), Key, Shift,
-    (Sender == RemoteDirectoryEdit));
+  if (Options != value)
+  {
+    FOptions = value;
+    if (FLAGSET(Options, fsoDisableTimestamp))
+    {
+      SynchronizeTimestampCheck->Checked = false;
+    }
+    UpdateControls();
+  }
 }
 //---------------------------------------------------------------------------
-void __fastcall TFullSynchronizeDialog::TransferPreferencesButtonClick(
+void __fastcall TFullSynchronizeDialog::TransferSettingsButtonClick(
   TObject * /*Sender*/)
 {
-  DoPreferencesDialog(pmTransfer);
+  CopyParamListPopup(
+    TransferSettingsButton->ClientToScreen(TPoint(0, TransferSettingsButton->Height)),
+    FPresetsMenu, FCopyParams, FPreset, CopyParamClick, cplCustomize);
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::CopyParamClick(TObject * Sender)
+{
+  if (CopyParamListPopupClick(Sender, FCopyParams, FPreset))
+  {
+    UpdateControls();
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::FormShow(TObject * /*Sender*/)
@@ -194,4 +268,63 @@ void __fastcall TFullSynchronizeDialog::FormShow(TObject * /*Sender*/)
   UpdateControls();
 }
 //---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & CanClose)
+{
+  if ((ModalResult != mrCancel) &&
+      SaveSettings && (FOrigMode != Mode) && !FSaveMode)
+  {
+    switch (MessageDialog(LoadStr(SAVE_SYNCHRONIZE_MODE),
+          qtConfirmation, qaYes | qaNo | qaCancel, HELP_SYNCHRONIZE_SAVE_MODE))
+    {
+      case qaYes:
+        FSaveMode = true;
+        break;
+
+      case qaCancel:
+        CanClose = false;
+        break;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::SynchronizeByTimeSizeCheckClick(
+  TObject * Sender)
+{
+  if (!dynamic_cast<TCheckBox*>(Sender)->Checked)
+  {
+    (Sender == SynchronizeByTimeCheck ? SynchronizeBySizeCheck : SynchronizeByTimeCheck)->
+      Checked = true;
+  }
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::SetCopyParams(const TCopyParamType & value)
+{
+  FCopyParams = value;
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::CopyParamGroupContextPopup(
+  TObject * /*Sender*/, TPoint & MousePos, bool & Handled)
+{
+  CopyParamListPopup(CopyParamGroup->ClientToScreen(MousePos), FPresetsMenu,
+    FCopyParams, FPreset, CopyParamClick, cplCustomize | cplCustomizeDefault);
+  Handled = true;
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::CopyParamGroupDblClick(
+  TObject * /*Sender*/)
+{
+  if (DoCopyParamCustomDialog(FCopyParams))
+  {
+    UpdateControls();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TFullSynchronizeDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 121 - 53
forms/FullSynchronize.dfm

@@ -6,8 +6,8 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'Synchronize'
-  ClientHeight = 299
-  ClientWidth = 396
+  ClientHeight = 399
+  ClientWidth = 433
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
   Font.Color = clWindowText
@@ -16,22 +16,23 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
   Font.Style = []
   OldCreateOrder = False
   Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
   OnShow = FormShow
   DesignSize = (
-    396
-    299)
+    433
+    399)
   PixelsPerInch = 96
   TextHeight = 13
   object DirectoriesGroup: TXPGroupBox
     Left = 8
     Top = 6
-    Width = 380
+    Width = 417
     Height = 119
     Anchors = [akLeft, akTop, akRight]
     Caption = 'Directories'
     TabOrder = 0
     DesignSize = (
-      380
+      417
       119)
     object LocalDirectoryLabel: TLabel
       Left = 11
@@ -54,7 +55,7 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
     object RemoteDirectoryEdit: THistoryComboBox
       Left = 11
       Top = 84
-      Width = 358
+      Width = 395
       Height = 21
       AutoComplete = False
       Anchors = [akLeft, akTop, akRight]
@@ -63,12 +64,11 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       TabOrder = 2
       Text = 'RemoteDirectoryEdit'
       OnChange = ControlChange
-      OnKeyDown = DirectoryEditKeyDown
     end
     object LocalDirectoryEdit: THistoryComboBox
       Left = 11
       Top = 35
-      Width = 275
+      Width = 312
       Height = 21
       AutoComplete = False
       Anchors = [akLeft, akTop, akRight]
@@ -77,111 +77,111 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       TabOrder = 0
       Text = 'LocalDirectoryEdit'
       OnChange = ControlChange
-      OnKeyDown = DirectoryEditKeyDown
     end
     object LocalDirectoryBrowseButton: TButton
-      Left = 293
+      Left = 330
       Top = 33
       Width = 75
       Height = 25
-      Caption = '&Browse...'
+      Anchors = [akTop, akRight]
+      Caption = 'Bro&wse...'
       TabOrder = 1
       OnClick = LocalDirectoryBrowseButtonClick
     end
   end
   object OkButton: TButton
-    Left = 228
-    Top = 266
+    Left = 185
+    Top = 366
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Caption = 'OK'
     Default = True
     ModalResult = 1
-    TabOrder = 3
+    TabOrder = 7
   end
   object CancelButton: TButton
-    Left = 312
-    Top = 266
+    Left = 267
+    Top = 366
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
     Cancel = True
     Caption = 'Cancel'
     ModalResult = 2
-    TabOrder = 4
+    TabOrder = 8
   end
   object OptionsGroup: TXPGroupBox
     Left = 8
     Top = 184
-    Width = 380
-    Height = 73
-    Anchors = [akLeft, akTop, akRight]
+    Width = 268
+    Height = 97
     Caption = 'Synchronize options'
-    TabOrder = 1
+    TabOrder = 2
     object SynchronizeDeleteCheck: TCheckBox
       Left = 11
-      Top = 20
-      Width = 97
+      Top = 44
+      Width = 126
       Height = 17
       Caption = '&Delete files'
-      TabOrder = 0
+      TabOrder = 1
       OnClick = ControlChange
     end
     object SynchronizeNoConfirmationCheck: TCheckBox
-      Left = 235
-      Top = 20
-      Width = 135
+      Left = 139
+      Top = 68
+      Width = 123
       Height = 17
       Caption = '&No confirmations'
-      TabOrder = 2
-      OnClick = ControlChange
-    end
-    object SaveSettingsCheck: TCheckBox
-      Left = 123
-      Top = 44
-      Width = 249
-      Height = 17
-      Caption = 'Use &same options next time'
       TabOrder = 4
+      OnClick = ControlChange
     end
     object SynchronizeExistingOnlyCheck: TCheckBox
-      Left = 123
-      Top = 20
-      Width = 107
+      Left = 139
+      Top = 44
+      Width = 123
       Height = 17
       Caption = '&Existing files only'
-      TabOrder = 1
+      TabOrder = 2
       OnClick = ControlChange
     end
     object SynchronizePreviewChangesCheck: TCheckBox
       Left = 11
-      Top = 44
-      Width = 112
+      Top = 68
+      Width = 126
       Height = 17
       Caption = 'Pre&view changes'
       TabOrder = 3
       OnClick = ControlChange
     end
+    object SynchronizeTimestampCheck: TCheckBox
+      Left = 11
+      Top = 20
+      Width = 246
+      Height = 17
+      Caption = 'Synchronize timestamps &only, not files'
+      TabOrder = 0
+      OnClick = ControlChange
+    end
   end
-  object TransferPreferencesButton: TButton
+  object TransferSettingsButton: TButton
     Left = 8
-    Top = 266
-    Width = 137
+    Top = 366
+    Width = 129
     Height = 25
     Anchors = [akRight, akBottom]
-    Caption = 'Transfer &preferences...'
-    TabOrder = 2
-    OnClick = TransferPreferencesButtonClick
+    Caption = 'Transfer settin&gs...'
+    TabOrder = 6
+    OnClick = TransferSettingsButtonClick
   end
   object DirectionGroup: TXPGroupBox
     Left = 8
     Top = 130
-    Width = 380
+    Width = 417
     Height = 49
     Anchors = [akLeft, akTop, akRight]
     Caption = 'Direction'
-    TabOrder = 5
+    TabOrder = 1
     object SynchronizeBothButton: TRadioButton
       Left = 11
       Top = 20
@@ -192,7 +192,7 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       OnClick = ControlChange
     end
     object SynchronizeRemoteButton: TRadioButton
-      Left = 123
+      Left = 139
       Top = 20
       Width = 102
       Height = 17
@@ -201,7 +201,7 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       OnClick = ControlChange
     end
     object SynchronizeLocalButton: TRadioButton
-      Left = 235
+      Left = 288
       Top = 20
       Width = 110
       Height = 17
@@ -210,4 +210,72 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       OnClick = ControlChange
     end
   end
+  object CompareCriterionsGroup: TXPGroupBox
+    Left = 285
+    Top = 184
+    Width = 140
+    Height = 97
+    Caption = 'Compare criterions'
+    TabOrder = 3
+    object SynchronizeByTimeCheck: TCheckBox
+      Left = 11
+      Top = 20
+      Width = 121
+      Height = 17
+      Caption = '&Modification time'
+      TabOrder = 0
+      OnClick = SynchronizeByTimeSizeCheckClick
+    end
+    object SynchronizeBySizeCheck: TCheckBox
+      Left = 11
+      Top = 44
+      Width = 121
+      Height = 17
+      Caption = '&File size'
+      TabOrder = 1
+      OnClick = SynchronizeByTimeSizeCheckClick
+    end
+  end
+  object SaveSettingsCheck: TCheckBox
+    Left = 19
+    Top = 342
+    Width = 246
+    Height = 17
+    Caption = 'Use &same options next time'
+    TabOrder = 5
+  end
+  object CopyParamGroup: TXPGroupBox
+    Left = 8
+    Top = 286
+    Width = 417
+    Height = 50
+    Caption = 'Transfer settings'
+    TabOrder = 4
+    OnContextPopup = CopyParamGroupContextPopup
+    OnDblClick = CopyParamGroupDblClick
+    DesignSize = (
+      417
+      50)
+    object CopyParamLabel: TLabel
+      Left = 7
+      Top = 15
+      Width = 403
+      Height = 26
+      Anchors = [akLeft, akTop, akRight, akBottom]
+      AutoSize = False
+      Caption = 'CopyParamLabel'
+      WordWrap = True
+      OnDblClick = CopyParamGroupDblClick
+    end
+  end
+  object HelpButton: TButton
+    Left = 349
+    Top = 366
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 9
+    OnClick = HelpButtonClick
+  end
 end

+ 30 - 5
forms/FullSynchronize.h

@@ -25,23 +25,40 @@ __published:
   TCheckBox *SynchronizeDeleteCheck;
   TCheckBox *SynchronizeNoConfirmationCheck;
   TButton *LocalDirectoryBrowseButton;
-  TCheckBox *SaveSettingsCheck;
   TCheckBox *SynchronizeExistingOnlyCheck;
-  TButton *TransferPreferencesButton;
+  TButton *TransferSettingsButton;
   TCheckBox *SynchronizePreviewChangesCheck;
   TXPGroupBox *DirectionGroup;
   TRadioButton *SynchronizeBothButton;
   TRadioButton *SynchronizeRemoteButton;
   TRadioButton *SynchronizeLocalButton;
+  TCheckBox *SynchronizeTimestampCheck;
+  TXPGroupBox *CompareCriterionsGroup;
+  TCheckBox *SynchronizeByTimeCheck;
+  TCheckBox *SynchronizeBySizeCheck;
+  TCheckBox *SaveSettingsCheck;
+  TXPGroupBox *CopyParamGroup;
+  TLabel *CopyParamLabel;
+  TButton *HelpButton;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall LocalDirectoryBrowseButtonClick(TObject *Sender);
-  void __fastcall DirectoryEditKeyDown(TObject *Sender, WORD &Key,
-    TShiftState Shift);
-  void __fastcall TransferPreferencesButtonClick(TObject *Sender);
+  void __fastcall TransferSettingsButtonClick(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
+  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
+  void __fastcall SynchronizeByTimeSizeCheckClick(TObject *Sender);
+  void __fastcall CopyParamGroupContextPopup(TObject *Sender,
+          TPoint &MousePos, bool &Handled);
+  void __fastcall CopyParamGroupDblClick(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
   
 private:
   int FParams;
+  bool FSaveMode;
+  TSynchronizeMode FOrigMode;
+  int FOptions;
+  TCopyParamType FCopyParams;
+  TPopupMenu * FPresetsMenu;
+  AnsiString FPreset;
   void __fastcall SetRemoteDirectory(const AnsiString value);
   AnsiString __fastcall GetRemoteDirectory();
   void __fastcall SetLocalDirectory(const AnsiString value);
@@ -52,9 +69,14 @@ private:
   int __fastcall GetParams();
   void __fastcall SetSaveSettings(bool value);
   bool __fastcall GetSaveSettings();
+  void __fastcall SetOptions(int value);
+  void __fastcall SetCopyParams(const TCopyParamType & value);
+  TCopyParamType __fastcall GetCopyParams();
+  void __fastcall CopyParamClick(TObject * Sender);
 
 public:
   __fastcall TFullSynchronizeDialog(TComponent* Owner);
+  virtual __fastcall ~TFullSynchronizeDialog();
 
   bool __fastcall Execute();
 
@@ -63,6 +85,9 @@ public:
   __property int Params = { read = GetParams, write = SetParams };
   __property TSynchronizeMode Mode = { read = GetMode, write = SetMode };
   __property bool SaveSettings = { read = GetSaveSettings, write = SetSaveSettings };
+  __property bool SaveMode = { read = FSaveMode, write = FSaveMode };
+  __property int Options = { read = FOptions, write = SetOptions };
+  __property TCopyParamType CopyParams = { read = FCopyParams, write = SetCopyParams };
 
 protected:
   void __fastcall UpdateControls();

+ 2 - 0
forms/Glyphs.cpp

@@ -7,6 +7,8 @@
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------------
+TGlyphsModule * GlyphsModule;
+//---------------------------------------------------------------------------
 __fastcall TGlyphsModule::TGlyphsModule(TComponent* Owner)
   : TDataModule(Owner)
 {

File diff suppressed because it is too large
+ 123 - 2906
forms/Glyphs.dfm


+ 2 - 2
forms/Glyphs.h

@@ -10,15 +10,15 @@ class TGlyphsModule : public TDataModule
 {
 __published:
   TImageList *ExplorerImages;
-  TImageList *ExplorerDisabledImages;
   TImageList *SessionImages;
   TImageList *QueueImages;
   TImageList *LogImages;
-  TImageList *LogDisabledImages;
   TImageList *ArrowImages;
   
 public:
   __fastcall TGlyphsModule(TComponent * Owner);
 };
 //---------------------------------------------------------------------------
+extern PACKAGE TGlyphsModule * GlyphsModule;
+//---------------------------------------------------------------------------
 #endif

+ 8 - 1
forms/ImportSessions.cpp

@@ -10,6 +10,7 @@
 #include <ScpMain.h>
 
 #include <VCLCommon.h>
+#include <WinInterface.h>
 //---------------------------------------------------------------------
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------
@@ -47,7 +48,7 @@ Boolean __fastcall DoImportSessionsDialog(TStoredSessionList *SessionList)
 }
 //---------------------------------------------------------------------
 __fastcall TImportSessionsDialog::TImportSessionsDialog(TComponent* AOwner)
-	: TForm(AOwner)
+  : TForm(AOwner)
 {
   UseSystemSettings(this);
 }
@@ -135,4 +136,10 @@ bool __fastcall TImportSessionsDialog::GetImportKeys()
 {
   return ImportKeysCheck->Checked;
 }
+//---------------------------------------------------------------------------
+void __fastcall TImportSessionsDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

+ 18 - 8
forms/ImportSessions.dfm

@@ -7,7 +7,7 @@ object ImportSessionsDialog: TImportSessionsDialog
   BorderStyle = bsDialog
   Caption = 'Import sessions from PuTTY'
   ClientHeight = 293
-  ClientWidth = 351
+  ClientWidth = 375
   Color = clBtnFace
   ParentFont = True
   OldCreateOrder = True
@@ -15,14 +15,14 @@ object ImportSessionsDialog: TImportSessionsDialog
   OnClose = FormClose
   OnShow = FormShow
   DesignSize = (
-    351
+    375
     293)
   PixelsPerInch = 96
   TextHeight = 13
   object Label1: TLabel
     Left = 8
     Top = 8
-    Width = 337
+    Width = 361
     Height = 45
     Anchors = [akLeft, akTop, akRight]
     AutoSize = False
@@ -34,14 +34,14 @@ object ImportSessionsDialog: TImportSessionsDialog
   object Label2: TLabel
     Left = 8
     Top = 52
-    Width = 290
+    Width = 314
     Height = 13
     Anchors = [akLeft, akTop, akRight]
     Caption = 'Notice: This application always connects using SSH protocol.'
     WordWrap = True
   end
   object OKButton: TButton
-    Left = 191
+    Left = 135
     Top = 262
     Width = 75
     Height = 25
@@ -52,7 +52,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     TabOrder = 3
   end
   object CancelButton: TButton
-    Left = 271
+    Left = 215
     Top = 262
     Width = 75
     Height = 25
@@ -65,7 +65,7 @@ object ImportSessionsDialog: TImportSessionsDialog
   object SessionListView: TListView
     Left = 8
     Top = 72
-    Width = 337
+    Width = 361
     Height = 160
     Anchors = [akLeft, akTop, akRight, akBottom]
     Checkboxes = True
@@ -102,9 +102,19 @@ object ImportSessionsDialog: TImportSessionsDialog
   object ImportKeysCheck: TCheckBox
     Left = 16
     Top = 238
-    Width = 329
+    Width = 345
     Height = 17
     Caption = 'Import cached host &keys for checked sessions'
     TabOrder = 1
   end
+  object HelpButton: TButton
+    Left = 294
+    Top = 262
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 5
+    OnClick = HelpButtonClick
+  end
 end

+ 2 - 0
forms/ImportSessions.h

@@ -26,6 +26,7 @@ __published:
   TLabel *Label2;
   TButton *CheckAllButton;
   TCheckBox *ImportKeysCheck;
+  TButton *HelpButton;
   void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
   void __fastcall SessionListViewInfoTip(TObject *Sender,
     TListItem *Item, AnsiString &InfoTip);
@@ -35,6 +36,7 @@ __published:
     TShiftState Shift);
   void __fastcall FormShow(TObject *Sender);
   void __fastcall CheckAllButtonClick(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
 private:
   TStoredSessionList *FSessionList;
   void __fastcall UpdateControls();

+ 48 - 6
forms/InputDlg.cpp

@@ -9,8 +9,15 @@
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
+void __fastcall InputDialogHelp(void * /*Data*/, TObject * Sender)
+{
+  TControl * Control = dynamic_cast<TControl *>(Sender);
+  Application->HelpKeyword(Control->Parent->HelpKeyword);
+}
+//---------------------------------------------------------------------------
 bool __fastcall InputDialog(const AnsiString ACaption,
-  const AnsiString APrompt, AnsiString & Value, TStrings * History)
+  const AnsiString APrompt, AnsiString & Value, AnsiString HelpKeyword,
+  TStrings * History, bool PathInput)
 {
   TForm * Form;
   TLabel * Prompt;
@@ -22,7 +29,8 @@ bool __fastcall InputDialog(const AnsiString ACaption,
   Form = new TForm(Application);
   try
   {
-    UseSystemSettings(Form);
+    SetCorrectFormParent(Form);
+    UseSystemSettingsPre(Form);
     Form->Canvas->Font = Form->Font;
     DialogUnits = GetAveCharSize(Form->Canvas);
     Form->BorderStyle = bsDialog;
@@ -30,6 +38,12 @@ bool __fastcall InputDialog(const AnsiString ACaption,
     Form->ClientWidth = MulDiv(220, DialogUnits.x, 4);
     Form->ClientHeight = MulDiv(63, DialogUnits.y, 8);
     Form->Position = poMainFormCenter;
+    if (!HelpKeyword.IsEmpty())
+    {
+      Form->HelpKeyword = HelpKeyword;
+
+      Form->BorderIcons = TBorderIcons(Form->BorderIcons) << biHelp;
+    }
 
     Prompt = new TLabel(Form);
     Prompt->Parent = Form;
@@ -61,12 +75,26 @@ bool __fastcall InputDialog(const AnsiString ACaption,
     EditControl->Left = Prompt->Left;
     EditControl->Top = MulDiv(19, DialogUnits.y, 8);
     EditControl->Width = MulDiv(204, DialogUnits.x, 4);
+    if (PathInput)
+    {
+      InstallPathWordBreakProc(EditControl);
+    }
 
     Prompt->FocusControl = EditControl;
 
     ButtonTop = MulDiv(41, DialogUnits.y, 8);
     ButtonWidth = MulDiv(50, DialogUnits.x, 4);
     ButtonHeight = MulDiv(14, DialogUnits.y, 8);
+    int ButtonSpace = MulDiv(5, DialogUnits.x, 4);
+    int ButtonsStart;
+    if (HelpKeyword.IsEmpty())
+    {
+      ButtonsStart = (Form->ClientWidth / 2) - ButtonWidth - (ButtonSpace / 2);
+    }
+    else
+    {
+      ButtonsStart = (Form->ClientWidth / 2) - (3 * ButtonWidth / 2) - ButtonSpace;
+    }
 
     TButton * Button;
     Button = new TButton(Form);
@@ -74,16 +102,30 @@ bool __fastcall InputDialog(const AnsiString ACaption,
     Button->Caption = Consts_SMsgDlgOK;
     Button->ModalResult = mrOk;
     Button->Default = True;
-    Button->SetBounds(MulDiv(58, DialogUnits.x, 4), ButtonTop, ButtonWidth,
-      ButtonHeight);
+    Button->SetBounds(ButtonsStart, ButtonTop, ButtonWidth, ButtonHeight);
 
     Button = new TButton(Form);
     Button->Parent = Form;
     Button->Caption = Consts_SMsgDlgCancel;
     Button->ModalResult = mrCancel;
     Button->Cancel = True;
-    Button->SetBounds(MulDiv(112, DialogUnits.x, 4), ButtonTop, ButtonWidth,
-      ButtonHeight);
+    Button->SetBounds(ButtonsStart + ButtonWidth + ButtonSpace, ButtonTop,
+      ButtonWidth, ButtonHeight);
+
+    if (!HelpKeyword.IsEmpty())
+    {
+      Button = new TButton(Form);
+      Button->Parent = Form;
+      Button->Caption = Consts_SMsgDlgHelp;
+      Button->ModalResult = mrNone;
+      Button->SetBounds(ButtonsStart + 2 * (ButtonWidth + ButtonSpace), ButtonTop,
+        ButtonWidth, ButtonHeight);
+      TNotifyEvent OnClick;  
+      ((TMethod*)&OnClick)->Code = InputDialogHelp;
+      Button->OnClick = OnClick;
+    }
+
+    UseSystemSettingsPost(Form);
 
     if (Form->ShowModal() == mrOk)
     {

+ 1 - 0
forms/Licence.cpp

@@ -6,6 +6,7 @@
 #include <TextsWin.h>
 
 #include <VCLCommon.h>
+#include "WinInterface.h"
 #include "Licence.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)

+ 20 - 13
forms/LocationProfiles.cpp

@@ -7,6 +7,7 @@
 #include <RemoteFiles.h>
 #include <VCLCommon.h>
 #include <TextsWin.h>
+#include <HelpWin.h>
 #include <Common.h>
 
 #include "LocationProfiles.h"
@@ -61,6 +62,9 @@ __fastcall TLocationProfilesDialog::TLocationProfilesDialog(TComponent * AOwner)
   FFolders->Duplicates = dupIgnore;
 
   UseSystemSettings(this);
+
+  InstallPathWordBreakProc(LocalDirectoryEdit);
+  InstallPathWordBreakProc(RemoteDirectoryEdit);
 }
 //---------------------------------------------------------------------
 __fastcall TLocationProfilesDialog::~TLocationProfilesDialog()
@@ -205,10 +209,13 @@ void __fastcall TLocationProfilesDialog::LoadBookmarks()
 bool __fastcall TLocationProfilesDialog::Execute()
 {
   bool Result;
+  AnsiString SessionKey;
   if (Terminal)
   {
     TBookmarkList * BookmarkList;
-    BookmarkList = WinConfiguration->Bookmarks[Terminal->SessionData->SessionKey];
+    // cache session key, in case terminal is closed while the window is open
+    SessionKey = Terminal->SessionData->SessionKey;
+    BookmarkList = WinConfiguration->Bookmarks[SessionKey];
     if (BookmarkList)
     {
       FBookmarkList->Assign(BookmarkList);
@@ -222,7 +229,7 @@ bool __fastcall TLocationProfilesDialog::Execute()
   Result = ((Mode != odAddBookmark) || AddAsBookmark()) && (ShowModal() == mrOk);
   if (Terminal)
   {
-    WinConfiguration->Bookmarks[Terminal->SessionData->SessionKey] = FBookmarkList;
+    WinConfiguration->Bookmarks[SessionKey] = FBookmarkList;
   }
   return Result;
 }
@@ -242,7 +249,8 @@ bool __fastcall TLocationProfilesDialog::AddAsBookmark()
   {
     BookmarkName = RemoteDirectory;
   }
-  Result = InputDialog(LoadStr(ADD_BOOKMARK_CAPTION), LoadStr(ADD_BOOKMARK_PROMPT), BookmarkName);
+  Result = InputDialog(LoadStr(ADD_BOOKMARK_CAPTION), LoadStr(ADD_BOOKMARK_PROMPT),
+    BookmarkName, HELP_LOCATION_PROFILE_ADD);
   if (Result)
   {
     if (BookmarkName.IsEmpty() || (StrToIntDef(BookmarkName, -123) != -123))
@@ -306,7 +314,7 @@ void __fastcall TLocationProfilesDialog::RemoveBookmarkButtonClick(TObject * /*S
   else
   {
     if (MessageDialog(LoadStr(DELETE_BOOKMARK_FOLDER), qtConfirmation,
-          qaYes | qaNo, 0) == qaYes)
+          qaYes | qaNo, HELP_LOCATION_PROFILE_DELETE) == qaYes)
     {
       assert(Node->Count);
       for (int i = 0; i < Node->Count; i++)
@@ -492,7 +500,8 @@ void __fastcall TLocationProfilesDialog::MoveToButtonClick(TObject * /*Sender*/)
   TBookmark * Bookmark = (TBookmark *)ProfilesView->Selected->Data;
   AnsiString Name = Bookmark->Node;
   if (DoComboInputDialog(LoadStr(MOVE_BOOKMARK_CAPTION), LoadStr(MOVE_BOOKMARK_PROMPT),
-      Name, FFolders, NULL, true) && Name != Bookmark->Node)
+      Name, FFolders, NULL, true, HELP_LOCATION_PROFILE_MOVE) &&
+      (Name != Bookmark->Node))
   {
     if (Name.Pos("\\"))
     {
@@ -543,7 +552,7 @@ void __fastcall TLocationProfilesDialog::RenameButtonClick(TObject * /*Sender*/)
   TTreeNode * Node = ProfilesView->Selected;
   AnsiString Name = Node->Text;
   if (InputDialog(LoadStr(RENAME_BOOKMARK_CAPTION), LoadStr(RENAME_BOOKMARK_PROMPT),
-      Name) && (Name != Node->Text))
+        Name, HELP_LOCATION_PROFILE_RENAME) && (Name != Node->Text))
   {
     if (Node->Data)
     {
@@ -614,13 +623,6 @@ void __fastcall TLocationProfilesDialog::ProfilesViewGetSelectedIndex(
   Node->SelectedIndex = Node->Data ? 0 : (Node->Expanded ? 1 : 2);
 }
 //---------------------------------------------------------------------------
-void __fastcall TLocationProfilesDialog::DirectoryEditKeyDown(
-  TObject * Sender, WORD & Key, TShiftState Shift)
-{
-  PathComboBoxKeyDown(dynamic_cast<TCustomComboBox*>(Sender), Key, Shift,
-    (Sender == RemoteDirectoryEdit));
-}
-//---------------------------------------------------------------------------
 void __fastcall TLocationProfilesDialog::LocalDirectoryBrowseButtonClick(
   TObject * /*Sender*/)
 {
@@ -637,4 +639,9 @@ void __fastcall TLocationProfilesDialog::SwitchButtonClick(TObject * /*Sender*/)
   WinConfiguration->UseLocationProfiles = false;
 }
 //---------------------------------------------------------------------------
+void __fastcall TLocationProfilesDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
 

Some files were not shown because too many files changed in this diff