Martin Prikryl 22 years ago
parent
commit
f5a3083934
100 changed files with 3774 additions and 1372 deletions
  1. 1 1
      ScpForms.bpr
  2. 12 9
      WinSCP3.bpr
  3. BIN
      WinSCP3.res
  4. 3 2
      core/Common.h
  5. 69 3
      core/Configuration.cpp
  6. 21 4
      core/Configuration.h
  7. 2 1
      core/FileOperationProgress.h
  8. 2 0
      core/FileSystems.h
  9. 71 5
      core/HierarchicalStorage.cpp
  10. 7 3
      core/HierarchicalStorage.h
  11. 6 14
      core/Interface.h
  12. 85 3
      core/RemoteFiles.cpp
  13. 6 6
      core/RemoteFiles.h
  14. 42 3
      core/ScpFileSystem.cpp
  15. 2 0
      core/ScpFileSystem.h
  16. 0 2
      core/ScpMain.cpp
  17. 14 1
      core/SecureShell.cpp
  18. 8 6
      core/SecureShell.h
  19. 60 29
      core/SessionData.cpp
  20. 6 8
      core/SessionData.h
  21. 49 28
      core/SftpFileSystem.cpp
  22. 2 0
      core/SftpFileSystem.h
  23. 48 13
      core/Terminal.cpp
  24. 12 9
      core/Terminal.h
  25. 1 1
      forms/About.dfm
  26. 1 1
      forms/CopyParams.dfm
  27. 149 55
      forms/CustomScpExplorer.cpp
  28. 7 1
      forms/CustomScpExplorer.h
  29. 1 1
      forms/Editor.cpp
  30. 32 23
      forms/Login.cpp
  31. 36 3
      forms/Login.dfm
  32. 3 0
      forms/Login.h
  33. 72 2
      forms/NonVisual.cpp
  34. 670 104
      forms/NonVisual.dfm
  35. 24 3
      forms/NonVisual.h
  36. 196 6
      forms/Preferences.cpp
  37. 219 28
      forms/Preferences.dfm
  38. 42 1
      forms/Preferences.h
  39. 48 24
      forms/Progress.cpp
  40. 2 2
      forms/Properties.dfm
  41. 29 9
      forms/Rights.cpp
  42. 1 0
      forms/Rights.dfm
  43. 2 0
      forms/Rights.h
  44. 44 22
      forms/ScpCommander.cpp
  45. 15 5
      forms/ScpCommander.dfm
  46. 2 0
      forms/ScpCommander.h
  47. 14 14
      forms/SelectMask.cpp
  48. 4 4
      forms/SelectMask.h
  49. 1 1
      general/moje komponenty/ToolbarPanel.pas
  50. 16 10
      putty/MAKEFILE.BOR
  51. 24 18
      putty/MAKEFILE.CYG
  52. 18 11
      putty/MAKEFILE.VC
  53. 6 3
      putty/MISC.C
  54. 2 2
      putty/MKFILES.PL
  55. 5 0
      putty/NETWORK.H
  56. 9 8
      putty/PAGEANT.C
  57. 11 0
      putty/PLINK.C
  58. 3 2
      putty/PORTFWD.C
  59. 50 26
      putty/PRINTING.C
  60. 2 3
      putty/PROXY.C
  61. 31 96
      putty/PSFTP.C
  62. 150 0
      putty/PSFTP.H
  63. 2 2
      putty/PUTTYGEN.C
  64. 3 3
      putty/RAW.C
  65. 6 2
      putty/RECIPE
  66. 3 3
      putty/RLOGIN.C
  67. 102 341
      putty/SCP.C
  68. 5 0
      putty/SETTINGS.C
  69. 3 2
      putty/SFTP.C
  70. 16 8
      putty/SSH.C
  71. 5 4
      putty/SSH.H
  72. 2 0
      putty/SSHBN.C
  73. 80 24
      putty/SSHPUBK.C
  74. 3 3
      putty/TELNET.C
  75. 4 0
      putty/WINCFG.C
  76. 39 11
      putty/WINDOW.C
  77. 1 0
      putty/WINHELP.H
  78. 24 0
      putty/WINMISC.C
  79. 3 0
      putty/WINNET.C
  80. 533 0
      putty/WINSFTP.C
  81. 3 0
      putty/WINSTUFF.H
  82. 3 2
      putty/X11FWD.C
  83. 0 19
      putty/putty.h
  84. 3 0
      putty/putty.org.h
  85. 0 7
      putty/puttyps.bak.h
  86. 1 0
      resource/TextsCore.h
  87. BIN
      resource/TextsCore.res
  88. 2 0
      resource/TextsCore1.rc
  89. 184 169
      resource/TextsWin.h
  90. BIN
      resource/TextsWin.res
  91. 37 7
      resource/TextsWin1.rc
  92. 22 19
      windows/Bookmarks.cpp
  93. 1 1
      windows/Bookmarks.h
  94. 35 33
      windows/TerminalManager.cpp
  95. 12 0
      windows/TerminalManager.h
  96. 6 2
      windows/VCLCommon.cpp
  97. 88 31
      windows/WinConfiguration.cpp
  98. 18 2
      windows/WinConfiguration.h
  99. 49 36
      windows/WinInterface.cpp
  100. 6 2
      windows/WinInterface.h

+ 1 - 1
ScpForms.bpr

@@ -38,7 +38,7 @@
     <PATHRC value=".;"/>
     <PATHASM value=".;"/>
     <LINKER value="TLib"/>
-    <USERDEFINES value="SSH_DEFAULT;OLD_DND;_DEBUG"/>
+    <USERDEFINES value="OLD_DND;_DEBUG"/>
     <SYSDEFINES value="NO_STRICT"/>
     <MAINSOURCE value="ScpForms.bpf"/>
     <INCLUDEPATH value="forms;core;components;windows;resource;packages\filemng;packages\dragndrop;packages\my;packages\my\filemng;$(BCB)\include;$(BCB)\include\vcl"/>

+ 12 - 9
WinSCP3.bpr

@@ -14,8 +14,10 @@
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES) forms\CustomScpExplorer.dfm forms\NonVisual.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 vclx.lib rtl.lib vcl.lib"/>
-    <SPARELIBS value="vcl.lib rtl.lib vclx.lib Moje_B5.lib DriveDir_B5.lib DragDrop_B5.lib"/>
+    <LIBRARIES value="nmfast.lib DragDrop_B5.lib DriveDir_B5.lib Moje_B5.lib vclx.lib rtl.lib 
+      vcl.lib"/>
+    <SPARELIBS value="vcl.lib rtl.lib vclx.lib Moje_B5.lib DriveDir_B5.lib DragDrop_B5.lib 
+      nmfast.lib"/>
     <PACKAGES value="vcl.bpi rtl.bpi vclx.bpi bcbsmp.bpi dbrtl.bpi adortl.bpi vcldb.bpi 
       qrpt.bpi bdertl.bpi vcldbx.bpi dsnap.bpi cds.bpi bdecds.bpi teeui.bpi 
       teedb.bpi tee.bpi teeqr.bpi ibxpress.bpi vclie.bpi xmlrtl.bpi inet.bpi 
@@ -29,7 +31,7 @@
     <DEBUGLIBPATH value="$(BCB)\lib\debug"/>
     <RELEASELIBPATH value="$(BCB)\lib\release"/>
     <LINKER value="ilink32"/>
-    <USERDEFINES value="SSH_DEFAULT;OLD_DND;_DEBUG"/>
+    <USERDEFINES value="OLD_DND;_DEBUG"/>
     <SYSDEFINES value="NO_STRICT"/>
     <MAINSOURCE value="WinSCP3.cpp"/>
     <INCLUDEPATH value="core;forms;windows;resource;components;packages\filemng;packages\dragndrop;packages\my;packages\my\filemng;$(BCB)\include;$(BCB)\include\vcl"/>
@@ -87,9 +89,9 @@
 IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=3
-MinorVer=2
-Release=1
-Build=174
+MinorVer=3
+Release=0
+Build=178
 Debug=0
 PreRelease=0
 Special=0
@@ -101,13 +103,14 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Windows SCP/SFTP client
-FileVersion=3.2.1.174
+FileVersion=3.3.0.178
 InternalName=winscp3
 LegalCopyright=(c) 2000-2003 Martin Prikryl
 LegalTrademarks=
-OriginalFilename=winscp321.exe
+OriginalFilename=winscp330.exe
 ProductName=WinSCP
-ProductVersion=3.2.1.0
+ProductVersion=3.3.0.0
+WWW=http://winscp.sourceforge.net/
 
 [Compiler]
 ShowInfoMsgs=1

BIN
WinSCP3.res


+ 3 - 2
core/Common.h

@@ -41,9 +41,10 @@ AnsiString __fastcall FormatCommand(AnsiString Program, AnsiString Params);
 //---------------------------------------------------------------------------
 #endif
 //---------------------------------------------------------------------------
+#include <assert.h>
 #ifndef _DEBUG
-#define NDEBUG
+#undef assert
+#define assert(p)   ((void)0)
 #endif
-#include <assert.h>
 //---------------------------------------------------------------------------
 #endif

+ 69 - 3
core/Configuration.cpp

@@ -14,6 +14,8 @@
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
+const char CustomCommandFileNamePattern[] = "!";
+//---------------------------------------------------------------------------
 bool SpecialFolderLocation(int PathID, AnsiString & Path)
 {
   LPITEMIDLIST Pidl;
@@ -54,6 +56,8 @@ void __fastcall TConfiguration::Default()
   FLogFileAppend = true;
   FLogWindowLines = 100;
 
+  FDisablePasswordStoring = false;
+
   Changed();
 }
 //---------------------------------------------------------------------------
@@ -136,6 +140,11 @@ void __fastcall TConfiguration::LoadSpecial(THierarchicalStorage * /*Storage*/)
 {
 }
 //---------------------------------------------------------------------------
+void __fastcall TConfiguration::LoadAdmin(THierarchicalStorage * Storage)
+{
+  FDisablePasswordStoring = Storage->ReadBool("DisablePasswordStoring", FDisablePasswordStoring);
+}
+//---------------------------------------------------------------------------
 void __fastcall TConfiguration::Load()
 {
   #define KEY(TYPE, VAR) VAR = Storage->Read ## TYPE(LASTELEM(AnsiString(#VAR)), VAR)
@@ -143,6 +152,21 @@ void __fastcall TConfiguration::Load()
   REGCONFIG(smRead, false, LoadSpecial);
   #pragma warn +eas
   #undef KEY
+
+  TRegistryStorage * AdminStorage;
+  AdminStorage = new TRegistryStorage(RegistryStorageKey, HKEY_LOCAL_MACHINE);
+  try
+  {
+    if (AdminStorage->OpenRootKey(false))
+    {
+      LoadAdmin(AdminStorage);
+      AdminStorage->CloseSubKey();
+    }
+  }
+  __finally
+  {
+    delete AdminStorage;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::Changed()
@@ -263,15 +287,34 @@ TVSFixedFileInfo *__fastcall TConfiguration::GetFixedApplicationInfo()
   return GetFixedFileInfo(ApplicationInfo);
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::ModuleFileName()
+{
+  return ParamStr(0);
+}
+//---------------------------------------------------------------------------
 void * __fastcall TConfiguration::GetApplicationInfo()
 {
   if (!FApplicationInfo)
   {
-    FApplicationInfo = CreateFileInfo(ParamStr(0));
+    FApplicationInfo = CreateFileInfo(ModuleFileName());
   }
   return FApplicationInfo;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetProductVersion()
+{
+  return TrimVersion(FileInfoString["ProductVersion"]);
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::TrimVersion(AnsiString Version)
+{
+  while (Version.SubString(Version.Length() - 1, 2) == ".0")
+  {
+    Version.SetLength(Version.Length() - 2);
+  }
+  return Version;
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::GetVersionStr()
 {
   try
@@ -293,10 +336,10 @@ AnsiString __fastcall TConfiguration::GetVersion()
   try
   {
     AnsiString Result;
-    Result = FORMAT("%d.%d.%d.%d", (HIWORD(FixedApplicationInfo->dwFileVersionMS),
+    Result = TrimVersion(FORMAT("%d.%d.%d.%d", (HIWORD(FixedApplicationInfo->dwFileVersionMS),
       LOWORD(FixedApplicationInfo->dwFileVersionMS),
       HIWORD(FixedApplicationInfo->dwFileVersionLS),
-      LOWORD(FixedApplicationInfo->dwFileVersionLS)));
+      LOWORD(FixedApplicationInfo->dwFileVersionLS))));
     return Result;
   }
   catch (Exception &E)
@@ -305,6 +348,24 @@ AnsiString __fastcall TConfiguration::GetVersion()
   }
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetFileInfoString(const AnsiString Key)
+{
+  AnsiString Result;
+  if (GetTranslationCount(ApplicationInfo) > 0)
+  {
+    TTranslation Translation;
+    Translation = GetTranslation(ApplicationInfo, 0);
+    Result = ::GetFileInfoString(ApplicationInfo,
+      Translation, Key);
+    PackStr(Result);
+  }
+  else
+  {
+    assert(false);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::GetRegistryStorageKey()
 {
   return GetRegistryKey();
@@ -470,6 +531,11 @@ void __fastcall TConfiguration::SetConfirmOverwriting(bool value)
   SET_CONFIG_PROPERTY(ConfirmOverwriting);
 }
 //---------------------------------------------------------------------------
+bool __fastcall TConfiguration::GetConfirmOverwriting()
+{
+  return FConfirmOverwriting;
+}
+//---------------------------------------------------------------------------
 void __fastcall TConfiguration::SetDefaultDirIsHome(bool value)
 {
   SET_CONFIG_PROPERTY(DefaultDirIsHome);

+ 21 - 4
core/Configuration.h

@@ -9,6 +9,8 @@
 #define SET_CONFIG_PROPERTY(PROPERTY) \
   if (PROPERTY != value) { F ## PROPERTY = value; Changed(); }
 //---------------------------------------------------------------------------
+extern const char CustomCommandFileNamePattern[];
+//---------------------------------------------------------------------------
 class TConfiguration : public TObject
 {
 private:
@@ -29,10 +31,14 @@ private:
   bool FConfirmOverwriting;
   AnsiString FIniFileStorageName;
 
+  bool FDisablePasswordStoring;
+
   TVSFixedFileInfo *__fastcall GetFixedApplicationInfo();
   void * __fastcall GetApplicationInfo();
   virtual AnsiString __fastcall GetVersionStr();
   virtual AnsiString __fastcall GetVersion();
+  AnsiString __fastcall GetProductVersion();
+  AnsiString __fastcall TrimVersion(AnsiString Version);
   AnsiString __fastcall GetStoredSessionsSubKey();
   AnsiString __fastcall GetPuttySessionsKey();
   AnsiString __fastcall GetPuttyRegistryStorageKey();
@@ -56,12 +62,13 @@ private:
   AnsiString __fastcall GetDefaultLogFileName();
   AnsiString __fastcall GetTimeFormat();
   void __fastcall SetIgnoreCancelBeforeFinish(TDateTime value);
-  void __fastcall SetConfirmOverwriting(bool value);
   void __fastcall SetStorage(TStorage value);
   AnsiString __fastcall GetRegistryStorageKey();
   AnsiString __fastcall GetIniFileStorageName();
   void __fastcall SetIniFileStorageName(AnsiString value);
   AnsiString __fastcall GetPartialExt() const;
+  AnsiString __fastcall GetFileInfoString(const AnsiString Key);
+  
 protected:
   TStorage FStorage;
 
@@ -69,9 +76,15 @@ protected:
   virtual void __fastcall Changed();
   virtual void __fastcall SaveSpecial(THierarchicalStorage * Storage);
   virtual void __fastcall LoadSpecial(THierarchicalStorage * Storage);
+  virtual void __fastcall LoadAdmin(THierarchicalStorage * Storage);
   virtual AnsiString __fastcall GetDefaultKeyFile();
   virtual void __fastcall ModifyAll();
 
+  virtual bool __fastcall GetConfirmOverwriting();
+  virtual void __fastcall SetConfirmOverwriting(bool value);
+
+  virtual AnsiString __fastcall ModuleFileName();
+
 public:
   __fastcall TConfiguration();
   __fastcall ~TConfiguration();
@@ -100,8 +113,10 @@ public:
   __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 };
+  __property AnsiString VersionStr = { read=GetVersionStr };
+  __property AnsiString Version = { read=GetVersion };
+  __property AnsiString ProductVersion = { read=GetProductVersion };
+  __property AnsiString FileInfoString[AnsiString Key] = { read = GetFileInfoString };
   __property bool Logging  = { read=FLogging, write=SetLogging };
   __property AnsiString LogFileName  = { read=FLogFileName, write=SetLogFileName };
   __property bool LogToFile  = { read=GetLogToFile, write=SetLogToFile };
@@ -111,7 +126,7 @@ public:
   __property AnsiString DefaultLogFileName  = { read=GetDefaultLogFileName };
   __property TDateTime IgnoreCancelBeforeFinish = { read = FIgnoreCancelBeforeFinish, write = SetIgnoreCancelBeforeFinish };
   __property TNotifyEvent OnChange = { read = FOnChange, write = FOnChange };
-  __property bool ConfirmOverwriting = { read = FConfirmOverwriting, write = SetConfirmOverwriting};
+  __property bool ConfirmOverwriting = { read = GetConfirmOverwriting, write = SetConfirmOverwriting};
   __property AnsiString PartialExt = {read=GetPartialExt};
 
   __property AnsiString TimeFormat = { read = GetTimeFormat };
@@ -119,6 +134,8 @@ public:
   __property AnsiString RegistryStorageKey  = { read=GetRegistryStorageKey };
   __property AnsiString IniFileStorageName  = { read=GetIniFileStorageName, write=SetIniFileStorageName };
   __property AnsiString DefaultKeyFile = { read = GetDefaultKeyFile };
+
+  __property bool DisablePasswordStoring = { read = FDisablePasswordStoring }; 
 };
 //---------------------------------------------------------------------------
 bool SpecialFolderLocation(int PathID, AnsiString & Path);

+ 2 - 1
core/FileOperationProgress.h

@@ -5,7 +5,8 @@
 #include "Configuration.h"
 //---------------------------------------------------------------------------
 class TFileOperationProgressType;
-enum TFileOperation { foNone, foCopy, foMove, foDelete, foSetProperties, foRename };
+enum TFileOperation { foNone, foCopy, foMove, foDelete, foSetProperties,
+  foRename, foCustomCommand };
 enum TCancelStatus { csContinue = 0, csCancel, csCancelTransfer, csRemoteAbort };
 enum TResumeStatus { rsNotAvailable, rsEnabled, rsDisabled };
 typedef void __fastcall (__closure *TFileOperationProgressEvent)

+ 2 - 0
core/FileSystems.h

@@ -39,6 +39,8 @@ public:
   virtual void __fastcall CreateLink(const AnsiString FileName, const AnsiString PointTo, bool Symbolic) = 0;
   virtual void __fastcall DeleteFile(const AnsiString FileName,
     const TRemoteFile * File, bool Recursive) = 0;
+  virtual void __fastcall CustomCommandOnFile(const AnsiString FileName,
+    const TRemoteFile * File, AnsiString Command) = 0;
   virtual void __fastcall DoStartup() = 0;
   virtual void __fastcall HomeDirectory() = 0;
   virtual bool __fastcall IsCapable(int Capability) const = 0;

+ 71 - 5
core/HierarchicalStorage.cpp

@@ -117,7 +117,8 @@ bool __fastcall THierarchicalStorage::HasSubKeys()
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall THierarchicalStorage::ReadValues(Classes::TStrings* Strings)
+void __fastcall THierarchicalStorage::ReadValues(Classes::TStrings* Strings,
+  bool MaintainKeys)
 {
   TStrings * Names = new TStringList();
   try
@@ -125,7 +126,15 @@ void __fastcall THierarchicalStorage::ReadValues(Classes::TStrings* Strings)
     GetValueNames(Names);
     for (int Index = 0; Index < Names->Count; Index++)
     {
-      Strings->Add(ReadString(Names->Strings[Index], ""));
+      if (MaintainKeys)
+      {
+        Strings->Add(FORMAT("%s=%s", (Names->Strings[Index],
+          ReadString(Names->Strings[Index], ""))));
+      }
+      else
+      {
+        Strings->Add(ReadString(Names->Strings[Index], ""));
+      }
     }
   }
   __finally
@@ -134,7 +143,8 @@ void __fastcall THierarchicalStorage::ReadValues(Classes::TStrings* Strings)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall THierarchicalStorage::WriteValues(Classes::TStrings* Strings)
+void __fastcall THierarchicalStorage::WriteValues(Classes::TStrings * Strings,
+  bool MaintainKeys)
 {
   TStrings * Names = new TStringList();
   try
@@ -154,7 +164,15 @@ void __fastcall THierarchicalStorage::WriteValues(Classes::TStrings* Strings)
   {
     for (int Index = 0; Index < Strings->Count; Index++)
     {
-      WriteString(IntToStr(Index), Strings->Strings[Index]);
+      if (MaintainKeys)
+      {
+        assert(Strings->Strings[Index].Pos("=") > 1);
+        WriteString(Strings->Names[Index], Strings->Values[Strings->Names[Index]]);
+      }
+      else
+      {
+        WriteString(IntToStr(Index), Strings->Strings[Index]);
+      }
     }
   }
 }
@@ -181,11 +199,23 @@ AnsiString __fastcall THierarchicalStorage::ExcludeTrailingBackslash(const AnsiS
 //===========================================================================
 __fastcall TRegistryStorage::TRegistryStorage(const AnsiString AStorage):
   THierarchicalStorage(IncludeTrailingBackslash(AStorage))
+{
+  Init();
+};
+//---------------------------------------------------------------------------
+__fastcall TRegistryStorage::TRegistryStorage(const AnsiString AStorage, HKEY ARootKey):
+  THierarchicalStorage(IncludeTrailingBackslash(AStorage))
+{
+  Init();
+  FRegistry->RootKey = ARootKey;
+}
+//---------------------------------------------------------------------------
+void __fastcall TRegistryStorage::Init()
 {
   FFailed = 0;
   FRegistry = new TRegistry();
   FRegistry->Access = KEY_READ;
-};
+}
 //---------------------------------------------------------------------------
 __fastcall TRegistryStorage::~TRegistryStorage()
 {
@@ -364,6 +394,42 @@ AnsiString __fastcall TIniFileStorage::GetCurrentSection()
   return ExcludeTrailingBackslash(CurrentSubKey);
 }
 //---------------------------------------------------------------------------
+bool __fastcall TIniFileStorage::OpenSubKey(const AnsiString SubKey, bool CanCreate)
+{
+  bool Result = CanCreate;
+
+  if (!Result)
+  {
+    TStringList * Sections = new TStringList();
+    try
+    {
+      Sections->Sorted = true;
+      FIniFile->ReadSections(Sections);
+      AnsiString NewKey = ExcludeTrailingBackslash(CurrentSubKey+SubKey);
+      int Index = -1;
+      if (Sections->Count)
+      {
+        Result = Sections->Find(NewKey, Index);
+        if (!Result && Index < Sections->Count &&
+            Sections->Strings[Index].SubString(1, NewKey.Length()+1) == NewKey + "\\")
+        {
+          Result = true;
+        }
+      }
+    }
+    __finally
+    {
+      delete Sections;
+    }
+  }
+  
+  if (Result)
+  {
+    Result = THierarchicalStorage::OpenSubKey(SubKey, CanCreate);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 bool __fastcall TIniFileStorage::DeleteSubKey(const AnsiString SubKey)
 {
   bool Result;

+ 7 - 3
core/HierarchicalStorage.h

@@ -21,8 +21,8 @@ public:
   virtual void __fastcall GetValueNames(Classes::TStrings* Strings) = 0;
   bool __fastcall HasSubKeys();
   virtual void __fastcall RecursiveDeleteSubKey(const AnsiString Key);
-  virtual void __fastcall ReadValues(Classes::TStrings* Strings);
-  virtual void __fastcall WriteValues(Classes::TStrings* Strings);
+  virtual void __fastcall ReadValues(Classes::TStrings* Strings, bool MaintainKeys = false);
+  virtual void __fastcall WriteValues(Classes::TStrings* Strings, bool MaintainKeys = false);
   virtual bool __fastcall DeleteValue(const AnsiString Name) = 0;
 
   virtual bool __fastcall ReadBool(const AnsiString Name, bool Default) = 0;
@@ -61,7 +61,8 @@ protected:
 class TRegistryStorage : public THierarchicalStorage
 {
 public:
-  __fastcall TRegistryStorage(const AnsiString aRoot);
+  __fastcall TRegistryStorage(const AnsiString AStorage, HKEY ARootKey);
+  __fastcall TRegistryStorage(const AnsiString AStorage);
   virtual __fastcall ~TRegistryStorage();
   virtual bool __fastcall OpenSubKey(const AnsiString SubKey, bool CanCreate);
   virtual bool __fastcall CreateSubKey(const AnsiString SubKey);
@@ -94,6 +95,8 @@ protected:
 private:
   TRegistry * FRegistry;
   int FFailed;
+
+  void __fastcall Init();
 };
 //---------------------------------------------------------------------------
 class TIniFileStorage : public THierarchicalStorage
@@ -102,6 +105,7 @@ public:
   __fastcall TIniFileStorage(const AnsiString FileName);
   virtual __fastcall ~TIniFileStorage();
 
+  virtual bool __fastcall OpenSubKey(const AnsiString SubKey, bool CanCreate);
   virtual bool __fastcall DeleteSubKey(const AnsiString SubKey);
   virtual bool __fastcall DeleteValue(const AnsiString Name);
   virtual void __fastcall GetSubKeyNames(Classes::TStrings* Strings);

+ 6 - 14
core/Interface.h

@@ -2,7 +2,6 @@
 #ifndef InterfaceH
 #define InterfaceH
 //---------------------------------------------------------------------------
-//!!!#include <Classes.hpp>
 #include "Configuration.h"
 #include "SessionData.h"
 //---------------------------------------------------------------------------
@@ -28,23 +27,16 @@ const int qaNoToAll =  0x0100;
 const int qaYesToAll = 0x0200;
 const int qaHelp =     0x0400;
 const int qaSkip =     0x0800;
-const int qaResume =   0x1000;
-const int qaCustom =   0x2000; // reserved for "More" button in VCL interface
+const int qaPrev =     0x1000;
+const int qaNext =     0x2000;
+const int qaCustom =   0x4000; // reserved for "More" button in VCL interface
 
 const int qaNeverAskAgain = 0x8000;
 
-const int qpFatalAbort =         0x01;
-const int qpNeverAskAgainCheck = 0x02;
+const int qpFatalAbort =           0x01;
+const int qpNeverAskAgainCheck =   0x02;
+const int qpAllowContinueOnError = 0x04;
 
 enum TQueryType { qtConfirmation, qtWarning, qtError, qtInformation };
-
-/*#define mb_YesNoCancel (TMsgDlgButtons() << mbYes << mbNo << mbCancel)
-#define mb_OKCancel (TMsgDlgButtons() << mbOK << mbCancel)
-#define mb_YesNo (TMsgDlgButtons() << mbYes << mbNo)
-#define mb_RetryAbort (TMsgDlgButtons() << mbRetry << mbAbort)
-#define mb_OKAbort (TMsgDlgButtons() << mbOK << mbAbort)*/
-//#define mbSkip mbIgnore
-//#define mbResume mbHelp
-//#define mrNeverAskAgain 100
 //---------------------------------------------------------------------------
 #endif

+ 85 - 3
core/RemoteFiles.cpp

@@ -284,6 +284,18 @@ bool __fastcall TRemoteFile::GetBrokenLink()
   // "!FLinkTo.IsEmpty()" removed because it does not work with SFTP
 }
 //---------------------------------------------------------------------------
+void __fastcall TRemoteFile::ShiftTime(const TDateTime & Difference)
+{
+  double D = double(Difference);
+  if ((D != 0) && (FModificationFmt != mfMDY))
+  {
+    assert(int(FModification) != 0);
+    FModification = double(FModification) + D;
+    assert(int(FLastAccess) != 0);
+    FLastAccess = double(FLastAccess) + D;
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TRemoteFile::SetModification(const TDateTime & value)
 {
   if (FModification != value)
@@ -442,6 +454,10 @@ void __fastcall TRemoteFile::SetListingStr(AnsiString value)
 
       FModification = AdjustDateTimeFromUnix(
         EncodeDate(Year, Month, Day) + EncodeTime(Hour, Min, 0, 0));
+      if (double(FLastAccess) == 0)
+      {
+        FLastAccess = FModification;
+      }
 
       // separating space is already deleted, other spaces are treated as part of name
 
@@ -894,9 +910,10 @@ void __fastcall TRights::SetNumber(Word value)
 AnsiString __fastcall TRights::GetOctal() const
 {
   AnsiString Result;
-  Result += (char)('0' + ((Number & 0700) >> 6));
-  Result += (char)('0' + ((Number & 0070) >> 3));
-  Result += (char)('0' + ((Number & 0007) >> 0));
+  Word N = NumberSet; // used to be "Number"
+  Result += (char)('0' + ((N & 0700) >> 6));
+  Result += (char)('0' + ((N & 0070) >> 3));
+  Result += (char)('0' + ((N & 0007) >> 0));
 
   return Result;
 }
@@ -1217,5 +1234,70 @@ bool __fastcall TRemoteProperties::operator !=(const TRemoteProperties & rhp) co
 {
   return !(*this == rhp);
 }
+//---------------------------------------------------------------------------
+TRemoteProperties __fastcall TRemoteProperties::CommonProperties(TStrings * FileList)
+{
+  TRemoteProperties CommonProperties;
+  for (int Index = 0; Index < FileList->Count; Index++)
+  {
+    TRemoteFile * File = (TRemoteFile *)(FileList->Objects[Index]);
+    assert(File);
+    if (!Index)
+    {
+      CommonProperties.Rights = *(File->Rights);
+      CommonProperties.Rights.AllowUndef = File->IsDirectory || File->Rights->IsUndef;
+      CommonProperties.Valid << vpRights;
+      if (!File->Owner.IsEmpty())
+      {
+        CommonProperties.Owner = File->Owner;
+        CommonProperties.Valid << vpOwner;
+      }
+      if (!File->Group.IsEmpty())
+      {
+        CommonProperties.Group = File->Group;
+        CommonProperties.Valid << vpGroup;
+      }
+    }
+    else
+    {
+      CommonProperties.Rights.AllowUndef = True;
+      CommonProperties.Rights &= *File->Rights;
+      if (CommonProperties.Owner != File->Owner)
+      {
+        CommonProperties.Owner = "";
+        CommonProperties.Valid >> vpOwner;
+      };
+      if (CommonProperties.Group != File->Group)
+      {
+        CommonProperties.Group = "";
+        CommonProperties.Valid >> vpGroup;
+      };
+    }
+  }
+  return CommonProperties;
+}
+//---------------------------------------------------------------------------
+TRemoteProperties __fastcall TRemoteProperties::ChangedProperties(
+  const TRemoteProperties & OriginalProperties, TRemoteProperties NewProperties)
+{
+  if (!NewProperties.Recursive)
+  {
+    if (NewProperties.Rights == OriginalProperties.Rights &&
+        !NewProperties.AddXToDirectories)
+    {
+      NewProperties.Valid >> vpRights;
+    }
+
+    if (NewProperties.Group == OriginalProperties.Group)
+    {
+      NewProperties.Valid >> vpGroup;
+    }
 
+    if (NewProperties.Owner == OriginalProperties.Owner)
+    {
+      NewProperties.Valid >> vpOwner;
+    }
+  }
+  return NewProperties;
+}
 

+ 6 - 6
core/RemoteFiles.h

@@ -105,7 +105,8 @@ public:
   virtual __fastcall ~TRemoteFile();
   TRemoteFile * __fastcall Duplicate();
 
-  //void __fastcall SetProperties(const TRemoteProperties & Properties);
+  void __fastcall ShiftTime(const TDateTime & Difference);
+
   __property int Attr = { read = GetAttr };
   __property bool BrokenLink = { read = GetBrokenLink };
   __property TRemoteFileList * Directory = { read = FDirectory, write = FDirectory };
@@ -122,9 +123,7 @@ public:
   __property bool IsSymLink = { read = FIsSymLink };
   __property bool IsDirectory = { read = GetIsDirectory };
   __property TRemoteFile * LinkedFile = { read = GetLinkedFile, write = SetLinkedFile };
-  //__property const TRemoteFile * LinkedByFile = { read = FLinkedByFile, write = FLinkedByFile };
   __property AnsiString LinkTo = { read = FLinkTo, write = FLinkTo };
-  //__property bool CyclicLink = { read = FCyclicLink, write = FCyclicLink };
   __property AnsiString ListingStr = { read = GetListingStr, write = SetListingStr };
   __property TRights * Rights = { read = FRights, write = SetRights };
   __property TTerminal * Terminal = { read = FTerminal, write = SetTerminal };
@@ -288,11 +287,12 @@ public:
   AnsiString Owner;
 
   __fastcall TRemoteProperties();
-/*  __fastcall TRemoteProperties(const TRemoteProperties & Source);
-  void __fastcall Clear();
-  void __fastcall operator =(const TRemoteProperties &rhp);*/
   bool __fastcall operator ==(const TRemoteProperties & rhp) const;
   bool __fastcall operator !=(const TRemoteProperties & rhp) const;
+
+  static TRemoteProperties __fastcall CommonProperties(TStrings * FileList);
+  static TRemoteProperties __fastcall ChangedProperties(
+    const TRemoteProperties & OriginalProperties, TRemoteProperties NewProperties);
 };
 //---------------------------------------------------------------------------
 AnsiString __fastcall UnixIncludeTrailingBackslash(const AnsiString Path);

+ 42 - 3
core/ScpFileSystem.cpp

@@ -725,6 +725,7 @@ void __fastcall TSCPFileSystem::ReadDirectory(TRemoteFileList * FileList)
         File = new TRemoteFile();
         File->Terminal = FTerminal;
         File->ListingStr = OutputCopy->Strings[Index];
+        File->ShiftTime(FTerminal->SessionData->TimeDifference);
         FileList->AddFile(File);
       }
     }
@@ -854,6 +855,44 @@ void __fastcall TSCPFileSystem::ChangeFileProperties(const AnsiString FileName,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TSCPFileSystem::CustomCommandOnFile(const AnsiString FileName,
+    const TRemoteFile * File, AnsiString Command)
+{
+  assert(File);
+  if (File->IsDirectory && !File->IsSymLink)
+  {
+    FTerminal->ProcessDirectory(FileName, FTerminal->CustomCommandOnFile, &Command);
+  }
+  else
+  {
+    AnsiString Cmd = Command;
+
+    int LastP = 0;
+    int P;
+    do
+    {
+      P = Cmd.SubString(LastP + 1, Cmd.Length() - LastP).Pos(CustomCommandFileNamePattern);
+      if (P)
+      {
+        LastP += P;
+        if ((Cmd.Length() > LastP) && (Cmd[LastP + 1] == '!'))
+        {
+          LastP++;
+        }
+        else
+        {
+          Cmd.Delete(LastP, strlen(CustomCommandFileNamePattern));
+          Cmd.Insert(FileName, LastP);
+          LastP += FileName.Length() - strlen(CustomCommandFileNamePattern);
+        }
+      }
+    }
+    while (P);
+
+    AnyCommand(Cmd);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::AnyCommand(const AnsiString Command)
 {
   ExecCommand(fsAnyCommand, ARRAYOFCONST((Command)));
@@ -1009,7 +1048,7 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
         {
           SUSPEND_OPERATION (
             if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (FileName)), E.Message,
-              qaOK | qaAbort, 0) == qaAbort)
+              qaOK | qaAbort, qpAllowContinueOnError) == qaAbort)
             {
               OperationProgress->Cancel = csCancel;
             }
@@ -1327,7 +1366,7 @@ void __fastcall TSCPFileSystem::SCPDirectorySource(const AnsiString DirectoryNam
         // If ESkipFile occurs, just log it and continue with next file
         SUSPEND_OPERATION (
           if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (FileName)), E.Message,
-                qaOK | qaAbort, 0) == qaAbort)
+                qaOK | qaAbort, qpAllowContinueOnError) == qaAbort)
           {
             OperationProgress->Cancel = csCancel;
           }
@@ -1813,7 +1852,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
       {
         SUSPEND_OPERATION (
           if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (OperationProgress->FileName)),
-            E.Message, qaOK | qaAbort, 0) == qaAbort)
+            E.Message, qaOK | qaAbort, qpAllowContinueOnError) == qaAbort)
           {
             OperationProgress->Cancel = csCancel;
           }

+ 2 - 0
core/ScpFileSystem.h

@@ -29,6 +29,8 @@ public:
   virtual void __fastcall CreateLink(const AnsiString FileName, const AnsiString PointTo, bool Symbolic);
   virtual void __fastcall DeleteFile(const AnsiString FileName,
     const TRemoteFile * File, bool Recursive);
+  virtual void __fastcall CustomCommandOnFile(const AnsiString FileName,
+    const TRemoteFile * File, AnsiString Command);
   virtual void __fastcall DoStartup();
   virtual void __fastcall HomeDirectory();
   virtual bool __fastcall IsCapable(int Capability) const;

+ 0 - 2
core/ScpMain.cpp

@@ -22,8 +22,6 @@ TStoredSessionList *StoredSessions;
 //---------------------------------------------------------------------------
 #ifdef SCP_CONSOLE
 /* TODO 1 : Won't be needed (while debuggin we hang TSessionLog::OnAddLine to it) */
-//class TCallExceptionClass;
-//extern TCallExceptionClass *CallExceptionClass;
 class TCallExceptionClass : public TObject
 {
 public:

+ 14 - 1
core/SecureShell.cpp

@@ -47,6 +47,7 @@ __fastcall TSecureShell::~TSecureShell()
     SAFE_DESTROY(FLog);
     delete FConfig;
     FConfig = NULL;
+    UserObject = NULL;
   }
   __finally
   {
@@ -507,7 +508,7 @@ void __fastcall TSecureShell::WaitForData()
     {
       LogEvent("Waiting for data timed out, asking user what to do.");
       if (DoQueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT, (FSessionData->Timeout)),
-            qaRetry | qaAbort, qpFatalAbort) != qaRetry)
+            qaRetry | qaAbort, qpFatalAbort | qpAllowContinueOnError) != qaRetry)
       {
         FatalError(LoadStr(USER_TERMINATED));
       }
@@ -783,6 +784,18 @@ void __fastcall TSecureShell::UpdateStatus(Integer Value)
     if (FOnUpdateStatus) FOnUpdateStatus(this);
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::SetUserObject(TObject * value)
+{
+  if (UserObject != value)
+  {
+    if (UserObject)
+    {
+      delete UserObject;
+    }
+    FUserObject = value;
+  }
+}
 //=== TSessionLog -----------------------------------------------------------
 const char *LogLineMarks = "<>!.*";
 __fastcall TSessionLog::TSessionLog(): TStringList()

+ 8 - 6
core/SecureShell.h

@@ -111,7 +111,6 @@ private:
   Backend * FBackend;
   void * FBackendHandle;
   Config * FConfig;
-  //void * FLoggingContext;
 
   unsigned PendLen;
   unsigned PendSize;
@@ -127,6 +126,7 @@ private:
   int FReachedStatus;
   AnsiString FStdErrorTemp;
   AnsiString FAuthenticationLog;
+  TObject * FUserObject;
 
   TCipher FCSCipher;
   TCipher FSCCipher;
@@ -138,18 +138,19 @@ private:
   void __fastcall SetSocket(SOCKET value);
   void __fastcall SetSessionData(TSessionData * value);
   void __fastcall SetActive(Boolean value);
-  Boolean __fastcall GetActive();
+  bool __fastcall GetActive();
   TCipher __fastcall GetCSCipher();
   TCompressionType __fastcall GetCSCompression();
   TDateTime __fastcall GetDuration();
   TCipher __fastcall GetSCCipher();
   TCompressionType __fastcall GetSCCompression();
-  Integer __fastcall GetSshVersion();
-  Integer __fastcall GetStatus();
+  int __fastcall GetSshVersion();
+  int __fastcall GetStatus();
   void inline __fastcall CheckConnection(int Message = -1);
   void __fastcall WaitForData();
   void __fastcall SetLog(TSessionLog * value);
-  void __fastcall SetConfiguration(TConfiguration *value);
+  void __fastcall SetConfiguration(TConfiguration * value);
+  void __fastcall SetUserObject(TObject * value);
 
 protected:
   AnsiString StdError;
@@ -195,7 +196,7 @@ public:
   {
     return Configuration->Logging || Log->OnAddLine;
   }
-  void __fastcall inline LogEvent(const AnsiString Str)
+  void __fastcall inline LogEvent(const AnsiString & Str)
   {
     if (IsLogging()) Log->Add(llMessage, Str);
   }
@@ -219,6 +220,7 @@ public:
   __property TNotifyEvent OnUpdateStatus = { read = FOnUpdateStatus, write = FOnUpdateStatus };
   __property TNotifyEvent OnClose = { read = FOnClose, write = FOnClose };
   __property int Status = { read = GetStatus };
+  __property TObject * UserObject = { read = FUserObject, write = SetUserObject };
 };
 //---------------------------------------------------------------------------
 #endif

+ 60 - 29
core/SessionData.cpp

@@ -89,6 +89,7 @@ void __fastcall TSessionData::Default()
   AliasGroupList = false;
   IgnoreLsWarnings = true;
   Scp1Compatibility = false;
+  TimeDifference = 0;
 
   CustomParam1 = "";
   CustomParam2 = "";
@@ -137,6 +138,7 @@ void __fastcall TSessionData::Assign(TPersistent * Source)
     DUPL(UnsetNationalVars);
     DUPL(AliasGroupList);
     DUPL(IgnoreLsWarnings);
+    DUPL(TimeDifference);
     // new in 53b
     DUPL(TcpNoDelay);
     DUPL(AuthKI);
@@ -285,7 +287,10 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     HostName = Storage->ReadString("HostName", HostName);
     PortNumber = Storage->ReadInteger("PortNumber", PortNumber);
     UserName = Storage->ReadString("UserName", UserName);
-    FPassword = Storage->ReadString("Password", FPassword);
+    if (!Configuration->DisablePasswordStoring)
+    {
+      FPassword = Storage->ReadString("Password", FPassword);
+    }
     PingInterval =
       Storage->ReadInteger("PingInterval", PingInterval/60)*60 +
       Storage->ReadInteger("PingIntervalSec", PingInterval%60);
@@ -313,6 +318,7 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     AliasGroupList = Storage->ReadBool("AliasGroupList", AliasGroupList);
     IgnoreLsWarnings = Storage->ReadBool("IgnoreLsWarnings", IgnoreLsWarnings);
     Scp1Compatibility = Storage->ReadBool("Scp1Compatibility", Scp1Compatibility);
+    TimeDifference = Storage->ReadFloat("TimeDifference", TimeDifference);
 
     ReturnVar = Storage->ReadString("ReturnVar", ReturnVar);
     LookupUserGroups = Storage->ReadBool("LookupUserGroups", LookupUserGroups);
@@ -381,14 +387,17 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
   FModified = false;
 }
 //---------------------------------------------------------------------
-void __fastcall TSessionData::Save(THierarchicalStorage * Storage)
+void __fastcall TSessionData::Save(THierarchicalStorage * Storage, bool PuttyExport)
 {
   if (Modified && Storage->OpenSubKey(MungeStr(Name), true))
   {
     Storage->WriteString("HostName", HostName);
     Storage->WriteInteger("PortNumber", PortNumber);
     Storage->WriteString("UserName", UserName);
-    Storage->WriteString("Password", FPassword);
+    if (!Configuration->DisablePasswordStoring && !PuttyExport)
+    {
+      Storage->WriteString("Password", FPassword);
+    }
     Storage->WriteInteger("PingInterval", PingInterval/60);
     Storage->WriteInteger("PingIntervalSec", PingInterval%60);
     Storage->WriteInteger("Timeout", Timeout);
@@ -401,29 +410,33 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage)
     Storage->WriteString("Cipher", CipherList);
     Storage->WriteString("PublicKeyFile", PublicKeyFile);
 
-    Storage->WriteInteger("FSProtocol", FSProtocol);
-    Storage->WriteString("LocalDirectory", LocalDirectory);
-    Storage->WriteString("RemoteDirectory", RemoteDirectory);
-    Storage->WriteBool("UpdateDirectories", UpdateDirectories);
-    Storage->WriteBool("CacheDirectories", CacheDirectories);
-    Storage->WriteBool("ResolveSymlinks", ResolveSymlinks);
-    Storage->WriteBool("LockInHome", LockInHome);
-    // Special is never stored (if it would, login dialog must be modified not to
-    // duplicate Special parameter when Special session is loaded and then stored
-    // under different name)
-    // Storage->WriteBool("Special", Special);
-    Storage->WriteString("Shell", Shell);
-    Storage->WriteBool("ClearAliases", ClearAliases);
-    Storage->WriteBool("UnsetNationalVars", UnsetNationalVars);
-    Storage->WriteBool("AliasGroupList", AliasGroupList);
-    Storage->WriteBool("IgnoreLsWarnings", IgnoreLsWarnings);
-    Storage->WriteBool("Scp1Compatibility", Scp1Compatibility);
+    Storage->WriteBool("TcpNoDelay", TcpNoDelay);
 
-    Storage->WriteString("ReturnVar", ReturnVar);
-    Storage->WriteBool("LookupUserGroups", LookupUserGroups);
-    Storage->WriteInteger("EOLType", EOLType);
+    if (!PuttyExport)
+    {
+      Storage->WriteInteger("FSProtocol", FSProtocol);
+      Storage->WriteString("LocalDirectory", LocalDirectory);
+      Storage->WriteString("RemoteDirectory", RemoteDirectory);
+      Storage->WriteBool("UpdateDirectories", UpdateDirectories);
+      Storage->WriteBool("CacheDirectories", CacheDirectories);
+      Storage->WriteBool("ResolveSymlinks", ResolveSymlinks);
+      Storage->WriteBool("LockInHome", LockInHome);
+      // Special is never stored (if it would, login dialog must be modified not to
+      // duplicate Special parameter when Special session is loaded and then stored
+      // under different name)
+      // Storage->WriteBool("Special", Special);
+      Storage->WriteString("Shell", Shell);
+      Storage->WriteBool("ClearAliases", ClearAliases);
+      Storage->WriteBool("UnsetNationalVars", UnsetNationalVars);
+      Storage->WriteBool("AliasGroupList", AliasGroupList);
+      Storage->WriteBool("IgnoreLsWarnings", IgnoreLsWarnings);
+      Storage->WriteBool("Scp1Compatibility", Scp1Compatibility);
+      Storage->WriteFloat("TimeDifference", TimeDifference);
 
-    Storage->WriteBool("TcpNoDelay", TcpNoDelay);
+      Storage->WriteString("ReturnVar", ReturnVar);
+      Storage->WriteBool("LookupUserGroups", LookupUserGroups);
+      Storage->WriteInteger("EOLType", EOLType);
+    }
 
     Storage->WriteInteger("ProxyMethod", ProxyMethod);
     Storage->WriteString("ProxyHost", ProxyHost);
@@ -445,8 +458,16 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage)
     WRITE_BUG(PKSessID2);
     #undef WRITE_BUG
 
-    Storage->WriteString("CustomParam1", CustomParam1);
-    Storage->WriteString("CustomParam2", CustomParam2);
+    if (PuttyExport)
+    {
+      Storage->WriteString("Protocol", ProtocolStr);
+    }
+  
+    if (!PuttyExport)
+    {
+      Storage->WriteString("CustomParam1", CustomParam1);
+      Storage->WriteString("CustomParam2", CustomParam2);
+    }
 
     Storage->CloseSubKey();
     FModified = false;
@@ -754,6 +775,11 @@ AnsiString __fastcall TSessionData::GetSessionName()
     else return "session";
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::SetTimeDifference(TDateTime value)
+{
+  SET_SESSION_PROPERTY(TimeDifference);
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::SetLocalDirectory(AnsiString value)
 {
   SET_SESSION_PROPERTY(LocalDirectory);
@@ -893,7 +919,7 @@ __fastcall TStoredSessionList::~TStoredSessionList()
   delete FDefaultSettings;
 }
 //---------------------------------------------------------------------
-void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage)
+void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage, bool AsModified)
 {
   TStringList *SubKeys = new TStringList();
   try {
@@ -911,6 +937,10 @@ void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage)
         Add(SessionData);
       }
       SessionData->Load(Storage);
+      if (AsModified)
+      {
+        SessionData->Modified = true;
+      }
     }
   } __finally {
     delete SubKeys;
@@ -1055,7 +1085,7 @@ TSessionData * __fastcall TStoredSessionList::NewSession(
     DuplicateSession->Assign(Session);
     DuplicateSession->Name = SessionName;
     // make sure, that new stored session is saved to registry
-    DuplicateSession->Modified = True;
+    DuplicateSession->Modified = true;
     Add(DuplicateSession);
   }
     else
@@ -1064,7 +1094,8 @@ TSessionData * __fastcall TStoredSessionList::NewSession(
     DuplicateSession->Name = SessionName;
     DuplicateSession->Modified = true;
   }
-  Save();
+  // list was saved here before to default storage, but it would not allow
+  // to work with special lists (export/import) not using default storage 
   return DuplicateSession;
 }
 //---------------------------------------------------------------------------

+ 6 - 8
core/SessionData.h

@@ -71,13 +71,13 @@ private:
   AnsiString FProxyUsername;
   AnsiString FProxyPassword;
   AnsiString FProxyTelnetCommand;
-  //int FProxySOCKSVersion;
   TAutoSwitch FProxyDNS;
   bool FProxyLocalhost;
   TAutoSwitch FBugs[BUG_COUNT];
   AnsiString FCustomParam1;
   AnsiString FCustomParam2;
   bool FResolveSymlinks;
+  TDateTime FTimeDifference;
 
   void __fastcall SetHostName(AnsiString value);
   void __fastcall SetPortNumber(int value);
@@ -100,6 +100,7 @@ private:
   bool __fastcall GetCanLogin();
   void __fastcall SetPingIntervalDT(TDateTime value);
   TDateTime __fastcall GetPingIntervalDT();
+  void __fastcall SetTimeDifference(TDateTime value);
   void __fastcall SetPingEnabled(bool value);
   bool __fastcall GetPingEnabled();
   AnsiString __fastcall GetSessionName();
@@ -137,7 +138,6 @@ private:
   void __fastcall SetProxyUsername(AnsiString value);
   void __fastcall SetProxyPassword(AnsiString value);
   void __fastcall SetProxyTelnetCommand(AnsiString value);
-  //void __fastcall SetProxySOCKSVersion(int value);
   void __fastcall SetProxyDNS(TAutoSwitch value);
   void __fastcall SetProxyLocalhost(bool value);
   AnsiString __fastcall GetProxyPassword();
@@ -153,7 +153,7 @@ public:
   void __fastcall Default();
   virtual void __fastcall StoreToConfig(void * config);
   void __fastcall Load(THierarchicalStorage * Storage);
-  void __fastcall Save(THierarchicalStorage * Storage);
+  void __fastcall Save(THierarchicalStorage * Storage, bool PuttyExport = false);
   void __fastcall Remove();
   virtual void __fastcall Assign(TPersistent * Source);
 
@@ -179,6 +179,7 @@ public:
   __property bool CanLogin  = { read=GetCanLogin };
   __property bool ClearAliases = { read = FClearAliases, write = SetClearAliases };
   __property TDateTime PingIntervalDT = { read = GetPingIntervalDT, write = SetPingIntervalDT };
+  __property TDateTime TimeDifference = { read = FTimeDifference, write = SetTimeDifference };
   __property bool PingEnabled = { read = GetPingEnabled, write = SetPingEnabled };
   __property AnsiString SessionName  = { read=GetSessionName };
   __property AnsiString LocalDirectory  = { read=FLocalDirectory, write=SetLocalDirectory };
@@ -209,7 +210,6 @@ public:
   __property AnsiString ProxyUsername  = { read=FProxyUsername, write=SetProxyUsername };
   __property AnsiString ProxyPassword  = { read=GetProxyPassword, write=SetProxyPassword };
   __property AnsiString ProxyTelnetCommand  = { read=FProxyTelnetCommand, write=SetProxyTelnetCommand };
-  //__property int ProxySOCKSVersion  = { read=FProxySOCKSVersion, write=SetProxySOCKSVersion };
   __property TAutoSwitch ProxyDNS  = { read=FProxyDNS, write=SetProxyDNS };
   __property bool ProxyLocalhost  = { read=FProxyLocalhost, write=SetProxyLocalhost };
   __property TAutoSwitch Bug[TSshBug Bug]  = { read=GetBug, write=SetBug };
@@ -227,6 +227,8 @@ public:
   void __fastcall Load();
   void __fastcall Save(AnsiString aKey);
   void __fastcall Save();
+  void __fastcall Load(THierarchicalStorage * Storage, bool AsModified = false);
+  void __fastcall Save(THierarchicalStorage * Storage);
   void __fastcall SelectAll(bool Select);
   void __fastcall Import(TStoredSessionList * From, bool OnlySelected);
   TSessionData * __fastcall AtSession(int Index)
@@ -239,10 +241,6 @@ public:
   __property TSessionData * Sessions[int Index]  = { read=AtSession };
   __property TSessionData * DefaultSettings  = { read=FDefaultSettings, write=SetDefaultSettings };
 
-protected:
-  void __fastcall Load(THierarchicalStorage * Storage);
-  void __fastcall Save(THierarchicalStorage * Storage);
-
 private:
   TStorage LastStorage;
   TSessionData * FDefaultSettings;

+ 49 - 28
core/SftpFileSystem.cpp

@@ -213,6 +213,8 @@ public:
     assert(File);
     unsigned int Flags;
     AnsiString ListingStr;
+    unsigned long Permissions;
+    bool ParsingFailed = false;
     if (Type != SSH_FXP_ATTRS)
     {
       File->FileName = GetString();
@@ -250,15 +252,7 @@ public:
     }
     if (Flags & SSH_FILEXFER_ATTR_PERMISSIONS)
     {
-      unsigned long Permissions = GetCardinal();
-      if (Type == SSH_FXP_ATTRS || Version >= 4)
-      {
-        File->Rights->Number = (unsigned short)(Permissions & 0777);
-        if (Version < 4)
-        {
-          File->Type = (Permissions & 0040000 ? FILETYPE_DIRECTORY : '-');
-        }
-      }
+      Permissions = GetCardinal();
     }
     if (Version < 4)
     {
@@ -298,9 +292,27 @@ public:
 
     if ((Version < 4) && (Type != SSH_FXP_ATTRS))
     {
-      // update permissions and user/group name
-      // modification time and filename is ignored
-      File->ListingStr = ListingStr;
+      try
+      {
+        // update permissions and user/group name
+        // modification time and filename is ignored
+        File->ListingStr = ListingStr;
+      }
+      catch(...)
+      {
+        // ignore any error while parsing listing line,
+        // SFTP specification do not recommend to parse it
+        ParsingFailed = true;
+      }
+    }
+
+    if (Type == SSH_FXP_ATTRS || Version >= 4 || ParsingFailed)
+    {
+      File->Rights->Number = (unsigned short)(Permissions & 0777);
+      if (Version < 4)
+      {
+        File->Type = (Permissions & 0040000 ? FILETYPE_DIRECTORY : '-');
+      }
     }
 
     // TODO: read extended attributes (Flags & SSH_FILEXFER_ATTR_EXTENDED)
@@ -615,7 +627,7 @@ void __fastcall TSFTPFileSystem::SendPacket(const TSFTPPacket * Packet)
       {
         if (FNotLoggedPackets)
         {
-          FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ and SSH_FXP_STATUS packets.",
+          FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ, SSH_FXP_DATA and SSH_FXP_STATUS packets.",
             (FNotLoggedPackets)));
           FNotLoggedPackets = 0;
         }
@@ -661,7 +673,6 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     SFTP_STATUS_NO_MEDIA,
   };
   int Message;
-  FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d", ((int)Code)));
   if ((AllowStatus & (0x01 << Code)) == 0)
   {
     if (Code >= sizeof(Messages) / sizeof(*Messages))
@@ -689,7 +700,7 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     }
     if (FTerminal->IsLogging())
     {
-      FTerminal->LogEvent(FORMAT("Error status: %d, Message: %d, Server: %s, Language: %s ",
+      FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d, Message: %d, Server: %s, Language: %s ",
         (int(Code), (int)Packet->MessageNumber, ServerMessage, LanguageTag)));
     }
     FTerminal->TerminalError(NULL, FMTLOAD(SFTP_ERROR_FORMAT,
@@ -698,6 +709,10 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
   }
   else
   {
+    if (!FNotLoggedPackets || Code)
+    {
+      FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d", ((int)Code)));
+    }
     return Code;
   }
 }
@@ -744,11 +759,11 @@ int __fastcall TSFTPFileSystem::ReceivePacket(TSFTPPacket * Packet,
         {
           if ((FPreviousLoggedPacket != SSH_FXP_READ &&
                FPreviousLoggedPacket != SSH_FXP_WRITE) ||
-              (Packet->Type != SSH_FXP_STATUS))
+              (Packet->Type != SSH_FXP_STATUS && Packet->Type != SSH_FXP_DATA))
           {
             if (FNotLoggedPackets)
             {
-              FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ and SSH_FXP_STATUS packets.",
+              FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ, SSH_FXP_DATA and SSH_FXP_STATUS packets.",
                 (FNotLoggedPackets)));
               FNotLoggedPackets = 0;
             }
@@ -1448,6 +1463,12 @@ void __fastcall TSFTPFileSystem::ChangeFileProperties(const AnsiString FileName,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TSFTPFileSystem::CustomCommandOnFile(const AnsiString /*FileName*/,
+    const TRemoteFile * /*File*/, AnsiString /*Command*/)
+{
+  assert(false);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::AnyCommand(const AnsiString /*Command*/)
 {
   assert(false);
@@ -1566,7 +1587,7 @@ bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
     (
       Answer = FTerminal->DoQueryUser(
         FMTLOAD(PARTIAL_BIGGER_THAN_SOURCE, (DestFileName)), NULL,
-          qaOK | qaAbort, 0, qtWarning);
+          qaOK | qaAbort, qpAllowContinueOnError, qtWarning);
     )
 
     if (Answer == qaAbort)
@@ -1585,7 +1606,8 @@ bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
     SUSPEND_OPERATION
     (
       Answer = FTerminal->DoQueryUser(
-        FMTLOAD(RESUME_TRANSFER, (DestFileName)), qaYes | qaNo | qaAbort, 0);
+        FMTLOAD(RESUME_TRANSFER, (DestFileName)), qaYes | qaNo | qaAbort,
+        qpAllowContinueOnError);
     );
 
     switch (Answer) {
@@ -1848,24 +1870,22 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
   TFileOperationProgressType * OperationProgress = OpenParams->OperationProgress;
 
   TSFTPPacket OpenRequest;
-  int OpenType = 0;
+  int OpenType;
+  bool Confirmed = false;
   bool Success = false;
 
   do
   {
     try
     {
+      OpenType = 0;
       OpenRequest.ChangeType(SSH_FXP_OPEN);
       if (FTerminal->Configuration->ConfirmOverwriting &&
-          !OpenType && !OperationProgress->YesToAll && !OpenParams->Resume &&
+          !Confirmed && !OperationProgress->YesToAll && !OpenParams->Resume &&
           !(OpenParams->Params & cpNoConfirmation))
       {
         OpenType |= SSH_FXF_EXCL;
       }
-      else
-      {
-        OpenType &= ~SSH_FXF_EXCL;
-      }
       if (!OpenParams->Resume)
       {
         OpenType |= SSH_FXF_TRUNC;
@@ -1895,10 +1915,10 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
     }
     catch(Exception & E)
     {
-      if (OpenType && FTerminal->Active)
+      if (!Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
       {
         // When exclusive opening of file fails, try to detect if file exists.
-        // When file exists, failure was probably caused by 'permission denied'
+        // When file does not exist, failure was probably caused by 'permission denied'
         // or similar error. In this case throw original exception.
         try
         {
@@ -1924,6 +1944,7 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
         // confirmation duplicated in SFTPSource for resumable file transfers.
         SFTPConfirmOverwrite(UnixExtractFileName(OpenParams->RemoteFileName),
           OperationProgress);
+        Confirmed = true; 
       }
       else
       {
@@ -1994,7 +2015,7 @@ void __fastcall TSFTPFileSystem::SFTPDirectorySource(const AnsiString DirectoryN
       // If ESkipFile occurs, just log it and continue with next file
       SUSPEND_OPERATION (
         if (FTerminal->DoQueryUser(FMTLOAD(COPY_ERROR, (FileName)), E.Message,
-              qaOK | qaAbort, 0) == qaAbort)
+              qaOK | qaAbort, qpAllowContinueOnError) == qaAbort)
         {
           OperationProgress->Cancel = csCancel;
         }

+ 2 - 0
core/SftpFileSystem.h

@@ -30,6 +30,8 @@ public:
   virtual void __fastcall CreateLink(const AnsiString FileName, const AnsiString PointTo, bool Symbolic);
   virtual void __fastcall DeleteFile(const AnsiString FileName,
     const TRemoteFile * File = NULL, bool Recursive = false);
+  virtual void __fastcall CustomCommandOnFile(const AnsiString FileName,
+    const TRemoteFile * File, AnsiString Command);
   virtual void __fastcall DoStartup();
   virtual void __fastcall HomeDirectory();
   virtual bool __fastcall IsCapable(int Capability) const;

+ 48 - 13
core/Terminal.cpp

@@ -315,7 +315,7 @@ int __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg,
   }
   else
   {
-    Result = DoQueryUser(Msg, E, Answers, 0);
+    Result = DoQueryUser(Msg, E, Answers, qpAllowContinueOnError);
   }
   return Result;
 }
@@ -344,13 +344,12 @@ void __fastcall TTerminal::CloseOnCompletion(const AnsiString Message)
 void __fastcall TTerminal::FileModified(const TRemoteFile * File)
 {
   assert(File);
-  if (SessionData->CacheDirectories && File->Directory
-      /*!!! && File->Directory == FFiles*/)
+  if (SessionData->CacheDirectories && File->Directory)
   {
     if (File->IsDirectory)
     {
+      // do not use UnixIncludeTrailingBackslash(CurrentDirectory) 
       FDirectoryCache->ClearFileList(
-        //!!!UnixIncludeTrailingBackslash(CurrentDirectory)+
         File->Directory->FullDirectory + File->FileName, true);
     }
     FDirectoryCache->ClearFileList(File->Directory->Directory, false);
@@ -701,7 +700,7 @@ void __fastcall TTerminal::DoDeleteFile(const AnsiString FileName,
   {
     assert(FFileSystem);
     // 'File' parameter: SFTPFileSystem needs to know if file is file or directory
-    FFileSystem->DeleteFile(FileName, File, 
+    FFileSystem->DeleteFile(FileName, File,
       Recursive ? *((bool*)Recursive) : true);
   }
   catch(Exception & E)
@@ -719,6 +718,49 @@ void __fastcall TTerminal::DeleteFiles(TStrings * FilesToDelete, bool * Recursiv
   ProcessFiles(FilesToDelete, foDelete, DeleteFile, Recursive);
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::CustomCommandOnFile(AnsiString FileName,
+  const TRemoteFile * File, void * ACommand)
+{
+  AnsiString Command = *((AnsiString *)ACommand);
+  if (FileName.IsEmpty() && File)
+  {
+    FileName = File->FileName;
+  }
+  if (OperationProgress && OperationProgress->Operation == foCustomCommand)
+  {
+    if (OperationProgress->Cancel != csContinue) Abort();
+    OperationProgress->SetFile(FileName);
+  }
+  LogEvent(FORMAT("Executing custom command \"%s\" on file \"%s\".",
+    (Command, FileName)));
+  if (File) FileModified(File);
+  DoCustomCommandOnFile(FileName, File, Command);
+  ReactOnCommand(fsAnyCommand);
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::DoCustomCommandOnFile(AnsiString FileName,
+  const TRemoteFile * File, AnsiString Command)
+{
+  try
+  {
+    assert(FFileSystem);
+    FFileSystem->CustomCommandOnFile(FileName, File, Command);
+  }
+  catch(Exception & E)
+  {
+    COMMAND_ERROR_ARI
+    (
+      FMTLOAD(CUSTOM_COMMAND_ERROR, (Command, FileName)),
+      DoCustomCommandOnFile(FileName, File, Command)
+    );
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::CustomCommandOnFiles(AnsiString Command, TStrings * Files)
+{
+  ProcessFiles(Files, foCustomCommand, CustomCommandOnFile, &Command);
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminal::ChangeFileProperties(AnsiString FileName,
   const TRemoteFile * File, /*const TRemoteProperties*/ void * Properties)
 {
@@ -756,14 +798,6 @@ void __fastcall TTerminal::ChangeFileProperties(AnsiString FileName,
   ReactOnCommand(fsChangeProperties);
 }
 //---------------------------------------------------------------------------
-/*void __fastcall TTerminal::ChangeFileProperties(const TRemoteFile * File,
-  const TRemoteProperties * Properties)
-{
-  assert(File);
-  FileModified(File);
-  ChangeFileProperties(File->FileName, Properties, File);
-} */
-//---------------------------------------------------------------------------
 void __fastcall TTerminal::DoChangeFileProperties(const AnsiString FileName,
   const TRemoteFile * File, const TRemoteProperties * Properties)
 {
@@ -981,6 +1015,7 @@ void __fastcall TTerminal::AnyCommand(const AnsiString Command)
   assert(FFileSystem);
   try
   {
+    DirectoryModified(CurrentDirectory, false);
     LogEvent("Executing used defined command.");
     FFileSystem->AnyCommand(Command);
     ReactOnCommand(fsAnyCommand);

+ 12 - 9
core/Terminal.h

@@ -59,7 +59,7 @@ typedef int __fastcall (__closure *TFileOperationEvent)
       HandleExtendedException(&E, this); \
       int Answers = qaRetry | qaAbort | ((ALLOW_SKIP) ? qaSkip : 0); \
       int Answer; \
-      int Params = !(ALLOW_SKIP) ? qpFatalAbort : 0; \
+      int Params = qpAllowContinueOnError | (!(ALLOW_SKIP) ? qpFatalAbort : 0); \
       SUSPEND_OPERATION ( \
         Answer = FTerminal->DoQueryUser(MESSAGE, &E, Answers, Params); \
       ); \
@@ -79,9 +79,8 @@ enum TFSCapability { fcUserGroupListing, fcModeChanging, fcGroupChanging,
   fcTextMode };
 //---------------------------------------------------------------------------
 const cpDelete = 1;
-//const cpCheckExistence = 2; !!! don't use
 const cpDragDrop = 4;
-const cpTemporary = 4; // alis to cpDragDrop
+const cpTemporary = 4; // alias to cpDragDrop
 const cpNoConfirmation = 8;
 //---------------------------------------------------------------------------
 class TTerminal : public TSecureShell
@@ -96,17 +95,17 @@ private:
   AnsiString FLockDirectory;
   Integer FExceptionOnFail;
   TRemoteDirectory * FFiles;
-  Integer FInTransaction;
+  int FInTransaction;
   TNotifyEvent FOnChangeDirectory;
   TReadDirectoryEvent FOnReadDirectory;
   bool FReadCurrentDirectoryPending;
   bool FReadDirectoryPending;
   TUserGroupsList * FUserGroups;
-  Boolean FUserGroupsLookedup;
+  bool FUserGroupsLookedup;
   TFileOperationProgressEvent FOnProgress;
   TFileOperationFinished FOnFinished;
   TFileOperationProgressType * FOperationProgress;
-  Boolean FUseBusyCursor;
+  bool FUseBusyCursor;
   TRemoteDirectoryCache * FDirectoryCache;
   TCustomFileSystem * FFileSystem;
   void __fastcall CommandError(Exception * E, const AnsiString Msg);
@@ -125,6 +124,8 @@ protected:
     const TRemoteProperties * Properties);
   void __fastcall DoDeleteFile(const AnsiString FileName,
     const TRemoteFile * File, void * Param);
+  void __fastcall DoCustomCommandOnFile(AnsiString FileName,
+    const TRemoteFile * File, AnsiString Command);
   void __fastcall DoRenameFile(const AnsiString FileName, const AnsiString NewName);
   void __fastcall DoChangeFileProperties(const AnsiString FileName,
     const TRemoteFile * File, const TRemoteProperties * Properties);
@@ -174,8 +175,10 @@ public:
   void __fastcall CreateLink(const AnsiString FileName, const AnsiString PointTo, bool Symbolic);
   void __fastcall DeleteFile(AnsiString FileName,
     const TRemoteFile * File = NULL, void * Recursive = NULL);
-  //void __fastcall DeleteFile(const TRemoteFile * File);
   void __fastcall DeleteFiles(TStrings * FilesToDelete, bool * Recursive = NULL);
+  void __fastcall CustomCommandOnFile(AnsiString FileName,
+    const TRemoteFile * File, void * ACommand);
+  void __fastcall CustomCommandOnFiles(AnsiString Command, TStrings * Files);
   void __fastcall ChangeDirectory(const AnsiString Directory);
   void __fastcall DoStartup();
   void __fastcall EndTransaction();
@@ -190,7 +193,7 @@ public:
   void __fastcall RenameFile(const AnsiString FileName, const AnsiString NewName);
   void __fastcall RenameFile(const TRemoteFile * File, const AnsiString NewName, bool CheckExistence);
   __property AnsiString CurrentDirectory = { read = GetCurrentDirectory, write = SetCurrentDirectory };
-  __property Boolean ExceptionOnFail = { read = GetExceptionOnFail, write = SetExceptionOnFail };
+  __property bool ExceptionOnFail = { read = GetExceptionOnFail, write = SetExceptionOnFail };
   __property TRemoteDirectory * Files = { read = FFiles };
   __property TNotifyEvent OnChangeDirectory = { read = FOnChangeDirectory, write = FOnChangeDirectory };
   __property TReadDirectoryEvent OnReadDirectory = { read = FOnReadDirectory, write = FOnReadDirectory };
@@ -198,7 +201,7 @@ public:
   __property TFileOperationProgressEvent OnProgress  = { read=FOnProgress, write=FOnProgress };
   __property TFileOperationFinished OnFinished  = { read=FOnFinished, write=FOnFinished };
   __property AnsiString ProtocolName = { read = GetProtocolName };
-  __property Boolean UseBusyCursor = { read = FUseBusyCursor, write = FUseBusyCursor };
+  __property bool UseBusyCursor = { read = FUseBusyCursor, write = FUseBusyCursor };
   __property AnsiString UserName  = { read=GetUserName };
   __property bool IsCapable[TFSCapability Capability] = { read = GetIsCapable };
 };

+ 1 - 1
forms/About.dfm

@@ -247,7 +247,7 @@ object AboutDialog: TAboutDialog
       Top = 72
       Width = 241
       Height = 13
-      Caption = 'SSH and SCP code based on PuTTY (2003-07-17)'
+      Caption = 'SSH and SCP code based on PuTTY (2003-09-12)'
     end
     object Label6: TLabel
       Left = 8

+ 1 - 1
forms/CopyParams.dfm

@@ -179,7 +179,7 @@ object CopyParamsFrame: TCopyParamsFrame
       Width = 164
       Height = 13
       Anchors = [akLeft, akTop, akRight]
-      Caption = 'Transfer following files in text &mode'
+      Caption = 'Transfer following &files in text mode'
       FocusControl = AsciiFileMaskCombo
     end
     object TMTextButton: TRadioButton

+ 149 - 55
forms/CustomScpExplorer.cpp

@@ -66,6 +66,7 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FForceExecution = false;
   FShowStatusBarHint = false;
   FIgnoreNextSysCommand = false;
+  FErrorList = NULL;
 
   UseSystemFont(this);
 
@@ -82,11 +83,12 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   assert(MenuToolBar->ControlCount);
   MenuToolBar->Height = MenuToolBar->Controls[0]->Height;
 
-  RemoteDirView->Font = Screen->IconFont; 
+  RemoteDirView->Font = Screen->IconFont;
 }
 //---------------------------------------------------------------------------
 __fastcall TCustomScpExplorerForm::~TCustomScpExplorerForm()
 {
+  assert(!FErrorList);
   StoreParams();
   Terminal = NULL;
   assert(NonVisualDataModule && (NonVisualDataModule->ScpExplorer == this));
@@ -221,7 +223,7 @@ void __fastcall TCustomScpExplorerForm::FileOperationProgress(
       FProgressForm = NULL;
     }
   }
-  
+
   if (FProgressForm)
   {
     FProgressForm->SetProgressData(ProgressData);
@@ -299,14 +301,14 @@ void __fastcall TCustomScpExplorerForm::RemoteDirViewContextPopup(
     NonVisualDataModule->CurrentCopyMenuItem->Default = WinConfiguration->CopyOnDoubleClick;
     NonVisualDataModule->CurrentOpenMenuItem->Visible = WinConfiguration->ExpertMode;
     NonVisualDataModule->CurentEditMenuItem->Visible = WinConfiguration->ExpertMode;
-    
+
     NonVisualDataModule->RemoteDirViewPopup->Popup(ScreenPoint.x, ScreenPoint.y);
   }
   Handled = true;
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::ExecuteFileOperation(TFileOperation Operation,
-  TOperationSide Side, bool OnFocused, bool NoConfirmation)
+  TOperationSide Side, bool OnFocused, bool NoConfirmation, void * Param)
 {
   if (Side == osCurrent)
   {
@@ -320,11 +322,17 @@ void __fastcall TCustomScpExplorerForm::ExecuteFileOperation(TFileOperation Oper
       Side = osLocal;
     }
   }
-  bool PrevWatchForChanges = (HasDirView[osLocal] ? DirView(osLocal)->WatchForChanges : false);
 
-  TStrings *FileList = DirView(Side)->CreateFileList(OnFocused, (Side == osLocal), NULL);
+  bool PrevWatchForChanges = (HasDirView[osLocal] ? DirView(osLocal)->WatchForChanges : false);
+  TStrings * FileList = NULL;
   try
   {
+    if (WinConfiguration->ContinueOnError)
+    {
+      FErrorList = new TStringList();
+    }
+    FileList = DirView(Side)->CreateFileList(OnFocused, (Side == osLocal), NULL);
+
     if ((Operation == foCopy) || (Operation == foMove))
     {
       TTransferDirection Direction = (Side == osLocal ? tdToRemote : tdToLocal);
@@ -426,6 +434,12 @@ void __fastcall TCustomScpExplorerForm::ExecuteFileOperation(TFileOperation Oper
     {
       SetProperties(Side, FileList);
     }
+    else if (Operation == foCustomCommand)
+    {
+      assert(Param);
+      assert(Side == osRemote);
+      Terminal->CustomCommandOnFiles(*((AnsiString *)Param), FileList);
+    }
     else
     {
       assert(false);
@@ -433,11 +447,60 @@ void __fastcall TCustomScpExplorerForm::ExecuteFileOperation(TFileOperation Oper
   }
   __finally
   {
-    delete FileList;
+    if (FErrorList)
+    {
+      HandleErrorList(FErrorList);
+    }
     if (HasDirView[osLocal])
     {
       DirView(osLocal)->WatchForChanges = PrevWatchForChanges;
     }
+    delete FileList;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::HandleErrorList(TStringList *& ErrorList)
+{
+  try
+  {
+    if (ErrorList->Count)
+    {
+      if (MessageDialog(FMTLOAD(ERROR_LIST_COUNT, (ErrorList->Count)), qtError,
+          qaOK | qaCancel, 0) == qaOK)
+      {
+        int Answer;
+        int Index = 0;
+        do
+        {
+          assert(Index >= 0 && Index < ErrorList->Count);
+          Answer = MoreMessageDialog(
+            FMTLOAD(ERROR_LIST_NUMBER, (Index+1, ErrorList->Count, ErrorList->Strings[Index])),
+            dynamic_cast<TStrings *>(ErrorList->Objects[Index]), qtError,
+            (Index ? qaPrev : 0) | (Index < ErrorList->Count - 1 ? qaNext : 0) |
+            qaOK, 0);
+
+          if (Answer == qaNext)
+          {
+            Index++;
+          }
+          if (Answer == qaPrev)
+          {
+            Index--;
+          }
+        }
+        while (Answer != qaOK);
+      }
+    }
+  }
+  __finally
+  {
+    TStrings * List = ErrorList;
+    ErrorList = NULL;
+    for (int i = 0; i < List->Count; i++)
+    {
+      delete List->Objects[i];
+    }
+    delete List;
   }
 }
 //---------------------------------------------------------------------------
@@ -728,7 +791,7 @@ void __fastcall TCustomScpExplorerForm::DeleteFiles(TOperationSide Side,
           }
           catch (Exception & E)
           {
-            int Result = ExceptionMessageDialog(&E, qtError, qaRetry | qaIgnore | qaAbort);
+            int Result = ExceptionMessageDialog(&E, qtError, qaRetry | qaSkip | qaAbort);
             if (Result == qaRetry)
             {
               Index--;
@@ -790,42 +853,7 @@ void __fastcall TCustomScpExplorerForm::SetProperties(TOperationSide Side, TStri
   {
     TRemoteProperties CurrentProperties;
 
-    for (Integer Index = 0; Index < FileList->Count; Index++)
-    {
-      TRemoteFile * File = (TRemoteFile *)(FileList->Objects[Index]);
-      assert(File);
-      if (!Index)
-      {
-        CurrentProperties.Rights = *(File->Rights);
-        CurrentProperties.Rights.AllowUndef = File->IsDirectory || File->Rights->IsUndef;
-        CurrentProperties.Valid << vpRights;
-        if (!File->Owner.IsEmpty())
-        {
-          CurrentProperties.Owner = File->Owner;
-          CurrentProperties.Valid << vpOwner;
-        }
-        if (!File->Group.IsEmpty())
-        {
-          CurrentProperties.Group = File->Group;
-          CurrentProperties.Valid << vpGroup;
-        }
-      }
-      else
-      {
-        CurrentProperties.Rights.AllowUndef = True;
-        CurrentProperties.Rights &= *File->Rights;
-        if (CurrentProperties.Owner != File->Owner)
-        {
-          CurrentProperties.Owner = "";
-          CurrentProperties.Valid >> vpOwner;
-        };
-        if (CurrentProperties.Group != File->Group)
-        {
-          CurrentProperties.Group = "";
-          CurrentProperties.Valid >> vpGroup;
-        };
-      }
-    }
+    CurrentProperties = TRemoteProperties::CommonProperties(FileList);
 
     int Flags = 0;
     if (Terminal->IsCapable[fcModeChanging]) Flags |= cpMode;
@@ -836,16 +864,7 @@ void __fastcall TCustomScpExplorerForm::SetProperties(TOperationSide Side, TStri
     if (DoPropertiesDialog(FileList, RemoteDirView->PathName,
         Terminal->UserGroups, &NewProperties, Flags))
     {
-      if (!NewProperties.Recursive)
-      {
-        if (NewProperties.Rights == CurrentProperties.Rights &&
-            !NewProperties.AddXToDirectories) NewProperties.Valid >> vpRights;
-        if (NewProperties.Group == CurrentProperties.Group)
-          NewProperties.Valid >> vpGroup;
-        if (NewProperties.Owner == CurrentProperties.Owner)
-          NewProperties.Valid >> vpOwner;
-      }
-
+      NewProperties = TRemoteProperties::ChangedProperties(CurrentProperties, NewProperties);
       Terminal->ChangeFilesProperties(FileList, &NewProperties);
     }
   }
@@ -914,6 +933,7 @@ void __fastcall TCustomScpExplorerForm::UpdateStatusBar()
     SessionStatusBar->Panels->Items[Index + 6]->Text =
       FormatDateTime(Configuration->TimeFormat, Terminal->Duration);
   }
+  SessionStatusBar->Invalidate();
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::SessionStatusBarDrawPanel(
@@ -1001,7 +1021,7 @@ void __fastcall TCustomScpExplorerForm::SessionStatusBarMouseMove(
               (BooleanToStr(Terminal->CSCompression), BooleanToStr(Terminal->SCCompression)));
           }
           break;
-              
+
         case 4:
           if (Terminal->CSCipher == Terminal->SCCipher)
           {
@@ -1260,6 +1280,7 @@ void __fastcall TCustomScpExplorerForm::SaveCurrentSession()
       SessionData->Assign(Terminal->SessionData);
       UpdateSessionData(SessionData);
       StoredSessions->NewSession(SessionName, SessionData);
+      StoredSessions->Save();
     }
     __finally
     {
@@ -1383,6 +1404,44 @@ void __fastcall TCustomScpExplorerForm::DoOpenDirectoryDialog(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::OpenInPutty()
+{
+  if (FileExists(WinConfiguration->PuttyPath))
+  {
+    THierarchicalStorage * Storage = NULL;
+    TSessionData * ExportData = NULL;
+    try
+    {
+      ExportData = new TSessionData("");
+      ExportData->Assign(Terminal->SessionData);
+      ExportData->Modified = true;
+      ExportData->Name = WinConfiguration->PuttySession;
+      ExportData->Password = "";
+      Storage = new TRegistryStorage(Configuration->PuttySessionsKey);
+      Storage->AccessMode = smReadWrite;
+      if (Storage->OpenRootKey(true))
+      {
+        ExportData->Save(Storage, true);
+      }
+    }
+    __finally
+    {
+      delete Storage;
+      delete ExportData;
+    }
+
+    if (!ExecuteShell(WinConfiguration->PuttyPath,
+          FORMAT("-load \"%s\"", (WinConfiguration->PuttySession))))
+    {
+      throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (WinConfiguration->PuttyPath)));
+    }
+  }
+  else
+  {
+    throw Exception(FMTLOAD(FILE_NOT_FOUND, (WinConfiguration->PuttyPath)));
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::OpenConsole()
 {
   DoConsoleDialog();
@@ -1578,3 +1637,38 @@ void __fastcall TCustomScpExplorerForm::DoShow()
 
   TForm::DoShow();
 }
+//---------------------------------------------------------------------------
+int __fastcall TCustomScpExplorerForm::MoreMessageDialog(const AnsiString Message,
+    TStrings * MoreMessages, TQueryType Type, int Answers,
+    int HelpCtx, int Params)
+{
+  if (WinConfiguration->ContinueOnError && (Params & mpAllowContinueOnError) &&
+      FErrorList)
+  {
+    TStringList * MoreMessagesCopy = NULL;
+    if (MoreMessages)
+    {
+      MoreMessagesCopy = new TStringList();
+      MoreMessagesCopy->Assign(MoreMessages);
+    }
+    FErrorList->AddObject(Message, MoreMessagesCopy);
+    if (Answers & qaSkip) return qaSkip;
+      else
+    if (Answers & qaIgnore) return qaIgnore;
+      else
+    if (Answers & qaOK) return qaOK;
+      else
+    if (Answers & qaYes) return qaYes;
+      else
+    if (Answers & qaRetry) return qaRetry;
+      else
+    {
+      assert(false);
+      return qaYes;
+    }
+  }
+  else
+  {
+    return ::MoreMessageDialog(Message, MoreMessages, Type, Answers, HelpCtx, Params);
+  }
+}

+ 7 - 1
forms/CustomScpExplorer.h

@@ -69,6 +69,7 @@ private:
   bool FShowStatusBarHint;
   AnsiString FStatusBarHint;
   bool FIgnoreNextSysCommand;
+  TStringList * FErrorList;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side);
@@ -111,6 +112,7 @@ protected:
   void __fastcall CMAppSysCommand(TMessage & Message);
   DYNAMIC void __fastcall DoShow();
   TStrings * __fastcall CreateVisitedDirectories(TOperationSide Side);
+  void __fastcall HandleErrorList(TStringList *& ErrorList);
 
   #pragma warn -inl
   BEGIN_MESSAGE_MAP
@@ -126,7 +128,7 @@ public:
   virtual void __fastcall ConfigurationChanged();
   void __fastcall CreateDirectory(TOperationSide Side);
   void __fastcall ExecuteFileOperation(TFileOperation Operation, TOperationSide Side,
-    bool OnFocused, bool NoConfirmation = False);
+    bool OnFocused, bool NoConfirmation = false, void * Param = NULL);
   virtual TCustomDirView * __fastcall DirView(TOperationSide Side);
   virtual void __fastcall ChangePath(TOperationSide Side) = 0;
   virtual void __fastcall StoreParams();
@@ -140,12 +142,16 @@ public:
   virtual void __fastcall CompareDirectories();
   void __fastcall ExecuteCurrentFile();
   void __fastcall OpenConsole();
+  void __fastcall OpenInPutty();
   virtual void __fastcall UpdateSessionData(TSessionData * Data = NULL);
   virtual void __fastcall SynchronizeDirectories();
   virtual void __fastcall ExploreLocalDirectory();
   void __fastcall ExecuteFile(TOperationSide Side, TExecuteFileBy ExecuteFileBy);
   void __fastcall LastTerminalClosed(TObject * Sender);
   void __fastcall TerminalListChanged(TObject * Sender);
+  int __fastcall MoreMessageDialog(const AnsiString Message,
+    TStrings * MoreMessages, TQueryType Type, int Answers,
+    int HelpCtx, int Params = 0);
 
   __property bool ComponentVisible[Word Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation };

+ 1 - 1
forms/Editor.cpp

@@ -248,7 +248,7 @@ void __fastcall TEditorForm::UpdateControls()
       if (FCaretPos.x+1 <= Line.Length())
       {
         Character = FMTLOAD(EDITOR_CHARACTER_STATUS,
-          ((int)Line[FCaretPos.x+1], (int)Line[FCaretPos.x+1]));
+          (int((unsigned char)Line[FCaretPos.x+1]), int((unsigned char)Line[FCaretPos.x+1])));
       }
     }
     StatusBar->Panels->Items[2]->Text = Character;

+ 32 - 23
forms/Login.cpp

@@ -45,7 +45,7 @@ bool __fastcall DoLoginDialog(TStoredSessionList *SessionList,
 }
 //---------------------------------------------------------------------
 __fastcall TLoginDialog::TLoginDialog(TComponent* AOwner)
-	: TForm(AOwner)
+        : TForm(AOwner)
 {
   FSessionData = new TSessionData("");
   Default();
@@ -116,16 +116,33 @@ void __fastcall TLoginDialog::LoadSession(TSessionData * aSessionData)
       case fsSCPonly: SCPonlyButton->Checked = true; break;
       case fsSFTP: SFTPButton->Checked = true; break;
       case fsSFTPonly:
-      default: SFTPonlyButton->Checked = fsSFTPonly; break;
+      default: SFTPonlyButton->Checked = true; break;
+    }
+
+    // Environment tab
+    LocalDirectoryEdit->Text = aSessionData->LocalDirectory;
+    RemoteDirectoryEdit->Text = aSessionData->RemoteDirectory;
+    UpdateDirectoriesCheck->Checked = aSessionData->UpdateDirectories;
+    CacheDirectoriesCheck->Checked = aSessionData->CacheDirectories;
+    ResolveSymlinksCheck->Checked = aSessionData->ResolveSymlinks;
+
+    if (aSessionData->EOLType == eolLF)
+    {
+      EOLTypeLFButton->Checked = true;
+    }
+    else
+    {
+      EOLTypeCRLFButton->Checked = true;
     }
 
-    // SSH tab
     AuthTISCheck->Checked = aSessionData->AuthTIS;
     AuthKICheck->Checked = aSessionData->AuthKI;
     AgentFwdCheck->Checked = aSessionData->AgentFwd;
-    CompressionCheck->Checked = aSessionData->Compression;
     Ssh2DESCheck->Checked = aSessionData->Ssh2DES;
 
+    // SSH tab
+    CompressionCheck->Checked = aSessionData->Compression;
+
     switch (aSessionData->SshProt) {
       case ssh1only:  SshProt1onlyButton->Checked = true; break;
       case ssh1:      SshProt1Button->Checked = true; break;
@@ -149,13 +166,6 @@ void __fastcall TLoginDialog::LoadSession(TSessionData * aSessionData)
       PingIntervalSecEdit->AsInteger = 60;
     TimeoutEdit->AsInteger = aSessionData->Timeout;
 
-    // Directories tab
-    LocalDirectoryEdit->Text = aSessionData->LocalDirectory;
-    RemoteDirectoryEdit->Text = aSessionData->RemoteDirectory;
-    UpdateDirectoriesCheck->Checked = aSessionData->UpdateDirectories;
-    CacheDirectoriesCheck->Checked = aSessionData->CacheDirectories;
-    ResolveSymlinksCheck->Checked = aSessionData->ResolveSymlinks;
-
     // Shell tab
     if (aSessionData->DefaultShell)
       DefaultShellButton->Checked = true;
@@ -172,10 +182,9 @@ void __fastcall TLoginDialog::LoadSession(TSessionData * aSessionData)
     ClearAliasesCheck->Checked = aSessionData->ClearAliases;
     IgnoreLsWarningsCheck->Checked = aSessionData->IgnoreLsWarnings;
     Scp1CompatibilityCheck->Checked = aSessionData->Scp1Compatibility;
-    if (aSessionData->EOLType == eolLF) EOLTypeLFButton->Checked = true;
-      else EOLTypeCRLFButton->Checked = true;
     UnsetNationalVarsCheck->Checked = aSessionData->UnsetNationalVars;
     AliasGroupListCheck->Checked = aSessionData->AliasGroupList;
+    TimeDifferenceEdit->AsInteger = int(aSessionData->TimeDifference) * 24;
 
     // Proxy tab
     switch (aSessionData->ProxyMethod) {
@@ -232,9 +241,6 @@ void __fastcall TLoginDialog::SaveSession(TSessionData * aSessionData)
     else aSessionData->FSProtocol = fsSFTPonly;
 
   // SSH tab
-  aSessionData->AuthTIS = AuthTISCheck->Checked;
-  aSessionData->AuthKI = AuthKICheck->Checked;
-  aSessionData->AgentFwd = AgentFwdCheck->Checked;
   aSessionData->Compression = CompressionCheck->Checked;
   aSessionData->Ssh2DES = Ssh2DESCheck->Checked;
 
@@ -250,13 +256,18 @@ void __fastcall TLoginDialog::SaveSession(TSessionData * aSessionData)
     aSessionData->Cipher[Index] = (TCipher)CipherListBox->Items->Objects[Index];
   }
 
+  // Authentication tab
+  aSessionData->AuthTIS = AuthTISCheck->Checked;
+  aSessionData->AuthKI = AuthKICheck->Checked;
+  aSessionData->AgentFwd = AgentFwdCheck->Checked;
+
   // Connection tab
   aSessionData->PingEnabled = PingIntervalCheck->Checked;
   if (PingIntervalCheck->Checked)
     aSessionData->PingInterval = PingIntervalSecEdit->AsInteger;
   aSessionData->Timeout = TimeoutEdit->AsInteger;
 
-  // Directories tab
+  // Environment tab
   aSessionData->LocalDirectory = LocalDirectoryEdit->Text;
   aSessionData->RemoteDirectory = RemoteDirectoryEdit->Text;
   aSessionData->UpdateDirectories = UpdateDirectoriesCheck->Checked;
@@ -278,6 +289,7 @@ void __fastcall TLoginDialog::SaveSession(TSessionData * aSessionData)
     else aSessionData->EOLType = eolCRLF;
   aSessionData->UnsetNationalVars = UnsetNationalVarsCheck->Checked;
   aSessionData->AliasGroupList = AliasGroupListCheck->Checked;
+  aSessionData->TimeDifference = double(TimeDifferenceEdit->AsInteger) / 24;
 
   // proxy
   if (ProxyHTTPButton->Checked) aSessionData->ProxyMethod = pmHTTP;
@@ -295,7 +307,7 @@ void __fastcall TLoginDialog::SaveSession(TSessionData * aSessionData)
   aSessionData->ProxyPassword = ProxyPasswordEdit->Text;
   aSessionData->ProxyTelnetCommand = ProxyTelnetCommandEdit->Text;
   aSessionData->ProxyLocalhost = ProxyLocalhostCheck->Checked;
-   
+
   if (ProxyDNSOnButton->Checked) aSessionData->ProxyDNS = asOn;
     else
   if (ProxyDNSOffButton->Checked) aSessionData->ProxyDNS = asOff;
@@ -392,7 +404,7 @@ void __fastcall TLoginDialog::PrepareNavigationTree(TTreeView * Tree)
       else
         i++;
     }
-    ToolsMenuButton->Visible = false;    
+    ToolsMenuButton->Visible = false;
   }
 }
 //---------------------------------------------------------------------------
@@ -551,6 +563,7 @@ void __fastcall TLoginDialog::SaveSessionActionExecute(TObject * /*Sender*/)
     TListItem * Item;
     TSessionData *NewSession =
       StoredSessions->NewSession(SessionName, FSessionData);
+    StoredSessions->Save();
     // by now list must contais same number of items or one less
     assert(StoredSessions->Count == SessionListView->Items->Count ||
       StoredSessions->Count == SessionListView->Items->Count+1);
@@ -866,7 +879,3 @@ void __fastcall TLoginDialog::SessionListViewCustomDrawItem(
   DefaultDraw = true;
 }
 //---------------------------------------------------------------------------
-
-
-
-

+ 36 - 3
forms/Login.dfm

@@ -414,7 +414,7 @@ object LoginDialog: TLoginDialog
             Top = 19
             Width = 160
             Height = 13
-            Caption = 'Encryption cipher &selection policy:'
+            Caption = '&Encryption cipher selection policy:'
             FocusControl = CipherListBox
           end
           object CipherListBox: TListBox
@@ -539,7 +539,7 @@ object LoginDialog: TLoginDialog
             Width = 321
             Height = 17
             Anchors = [akLeft, akTop, akRight]
-            Caption = '&Cache visited remote directories'
+            Caption = 'Cache &visited remote directories'
             TabOrder = 1
           end
           object ResolveSymlinksCheck: TCheckBox
@@ -591,10 +591,29 @@ object LoginDialog: TLoginDialog
           Left = 0
           Top = 108
           Width = 345
-          Height = 86
+          Height = 113
           Anchors = [akLeft, akTop, akRight]
           Caption = 'Other options'
           TabOrder = 2
+          DesignSize = (
+            345
+            113)
+          object Label29: TLabel
+            Left = 13
+            Top = 88
+            Width = 105
+            Height = 13
+            Caption = 'Server time&zone offset'
+            FocusControl = TimeDifferenceEdit
+          end
+          object Label30: TLabel
+            Left = 204
+            Top = 88
+            Width = 26
+            Height = 13
+            Caption = 'hours'
+            FocusControl = TimeDifferenceEdit
+          end
           object LookupUserGroupsCheck: TCheckBox
             Left = 12
             Top = 18
@@ -649,6 +668,20 @@ object LoginDialog: TLoginDialog
             TabOrder = 5
             OnClick = DataChange
           end
+          object TimeDifferenceEdit: TUpDownEdit
+            Left = 137
+            Top = 83
+            Width = 61
+            Height = 21
+            Alignment = taRightJustify
+            Decimal = 1
+            MaxValue = 12
+            MinValue = -12
+            Value = 1
+            Anchors = [akTop, akRight]
+            TabOrder = 6
+            OnChange = DataChange
+          end
         end
         object ReturnVarGroup: TXPGroupBox
           Left = 0

+ 3 - 0
forms/Login.h

@@ -185,6 +185,9 @@ __published:
   TRadioButton *ProxyDNSOffButton;
   TRadioButton *ProxyDNSAutoButton;
   TRadioButton *ProxyDNSOnButton;
+  TUpDownEdit *TimeDifferenceEdit;
+  TLabel *Label29;
+  TLabel *Label30;
   void __fastcall DataChange(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
   void __fastcall SessionListViewSelectItem(TObject *Sender,

+ 72 - 2
forms/NonVisual.cpp

@@ -221,6 +221,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPD(HistoryPageAction, true)
   UPD(RequirementsPageAction, true)
   UPD(ForumPageAction, true)
+  UPD(CheckForUpdatesAction, true)
 
   // VIEW
   UPDCOMP(StatusBar)
@@ -312,10 +313,17 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPD(CompareDirectoriesAction, true)
   UPD(SynchronizeAction, true)
   UPD(ConsoleAction, ScpExplorer->Terminal && ScpExplorer->Terminal->IsCapable[fcAnyCommand])
+  UPD(PuttyAction, true)
   UPD(SynchorizeBrowsingAction, true)
   UPD(CloseApplicationAction, true)
 
-  ;
+  // CUSTOM COMMANDS
+  UPD(CustomCommandsAction,
+    (ScpExplorer->DirView(osCurrent) == ScpExplorer->DirView(osRemote)) &&
+    ScpExplorer->Terminal && ScpExplorer->Terminal->IsCapable[fcAnyCommand])
+  UPD(CustomCommandsCustomizeAction, true)
+
+  ;            
 }
 //---------------------------------------------------------------------------
 void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
@@ -385,6 +393,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
   EXE(HistoryPageAction, OpenBrowser("http://winscp.sourceforge.net/eng/history.php"))
   EXE(RequirementsPageAction, OpenBrowser("http://winscp.sourceforge.net/eng/requirements.php"))
   EXE(ForumPageAction, OpenBrowser("http://winscp.sourceforge.net/eng/forum.php"))
+  EXE(CheckForUpdatesAction, CheckForUpdates())
 
   // VIEW
   EXECOMP(StatusBar)
@@ -474,9 +483,13 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
   EXE(CompareDirectoriesAction, ScpExplorer->CompareDirectories())
   EXE(SynchronizeAction, ScpExplorer->SynchronizeDirectories())
   EXE(ConsoleAction, ScpExplorer->OpenConsole())
+  EXE(PuttyAction, ScpExplorer->OpenInPutty())
   EXE(SynchorizeBrowsingAction, )
   EXE(CloseApplicationAction, ScpExplorer->Close())
 
+  // CUSTOM COMMANDS
+  EXE(CustomCommandsAction, CreateCustomCommandsMenu(CustomCommandsAction))
+  EXE(CustomCommandsCustomizeAction, DoPreferencesDialog(pmCustomCommands))
   ;
 }
 //---------------------------------------------------------------------------
@@ -583,6 +596,61 @@ void __fastcall TNonVisualDataModule::SessionIdleTimerTimer(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TNonVisualDataModule::CreateCustomCommandsMenu(TAction * Action)
+{
+  assert(Action);
+  TMenuItem * Menu = dynamic_cast<TMenuItem *>(Action->ActionComponent);
+  if (Menu)
+  {
+    int PrevCount = Menu->Count;
+    for (int Index = 0; Index < WinConfiguration->CustomCommands->Count; Index++)
+    {
+      AnsiString Description = WinConfiguration->CustomCommands->Names[Index];
+      AnsiString Command = WinConfiguration->CustomCommands->Values[Description];
+
+      TMenuItem * Item = new TMenuItem(Menu);
+      Item->Caption = Description;
+      Item->Tag = Index;
+      if (Menu == RemoteDirViewCustomCommandsMenu)
+      {
+        Item->Tag = Item->Tag | 0x0100; 
+      }
+      Item->Hint = FMTLOAD(CUSTOM_COMMAND_HINT,
+        (StringReplace(Description, "&", "", TReplaceFlags() << rfReplaceAll)));
+      Item->OnClick = CustomCommandClick;
+      Menu->Add(Item);
+    }
+
+    TMenuItem * Item;
+    if (WinConfiguration->CustomCommands->Count)
+    {
+      Item = new TMenuItem(Menu);
+      Item->Caption = "-";
+      Item->Hint = "E";
+      Menu->Add(Item);
+    }
+
+    Item = new TMenuItem(Menu);
+    Item->Action = CustomCommandsCustomizeAction;
+    Menu->Add(Item);
+
+    for (int Index = 0; Index < PrevCount; Index++)
+    {
+      Menu->Delete(0);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TNonVisualDataModule::CustomCommandClick(TObject * Sender)
+{
+  TMenuItem * Item = dynamic_cast<TMenuItem *>(Sender);
+  assert(Item);
+  AnsiString Command = WinConfiguration->CustomCommands->Values[
+    WinConfiguration->CustomCommands->Names[Item->Tag & 0x00FF]];
+  ScpExplorer->ExecuteFileOperation(foCustomCommand, osRemote,
+    (Item->Tag & 0xFF00) != 0, false, &Command);
+}
+//---------------------------------------------------------------------------
 void __fastcall TNonVisualDataModule::CreateSessionListMenu()
 {
   int PrevCount = SavedSessionsMenu->Count;
@@ -590,7 +658,7 @@ void __fastcall TNonVisualDataModule::CreateSessionListMenu()
   for (int Index = 0; Index < StoredSessions->Count; Index++)
   {
     TSessionData * Data = StoredSessions->Sessions[Index];
-    TMenuItem * Item = new TMenuItem(this);
+    TMenuItem * Item = new TMenuItem(SavedSessionsMenu);
     Item->Caption = Data->Name;
     Item->Tag = Index;
     Item->Hint = FMTLOAD(SAVEDSESSION_HINT, (Data->Name));
@@ -655,4 +723,6 @@ void __fastcall TNonVisualDataModule::OpenBrowser(AnsiString URL)
 {
   ShellExecute(Application->Handle, "open", URL.c_str(), NULL, NULL, SW_SHOWNORMAL);
 }
+//---------------------------------------------------------------------------
+
 

File diff suppressed because it is too large
+ 670 - 104
forms/NonVisual.dfm


+ 24 - 3
forms/NonVisual.h

@@ -192,7 +192,7 @@ __published:	// IDE-managed Components
   TMenuItem *SelectAll2;
   TMenuItem *UnselectFiles1;
   TMenuItem *SelectFiles1;
-  TMenuItem *CommanderCommandsMenu;
+  TMenuItem *CommanderFilesMenu;
   TMenuItem *Copyto3;
   TMenuItem *Moveto3;
   TMenuItem *Delete3;
@@ -421,7 +421,6 @@ __published:	// IDE-managed Components
   TAction *HideColumnAction;
   TMenuItem *Hidecolumn1;
   TMenuItem *Hidecolumn2;
-  TMenuItem *N39;
   TAction *CompareDirectoriesAction;
   TMenuItem *Comparedirectories1;
   TAction *CommanderCommandsBandAction;
@@ -469,6 +468,25 @@ __published:	// IDE-managed Components
   TAction *CloseApplicationAction;
   TAction *OpenedSessionsAction;
   TMenuItem *OpenedSessionsMenu;
+  TAction *CustomCommandsAction;
+  TMenuItem *CustomCommandsMenu;
+  TMenuItem *N43;
+  TMenuItem *CommanderCommandsMenu;
+  TMenuItem *N39;
+  TMenuItem *CustomCommands1;
+  TMenuItem *RemoteDirViewCustomCommandsMenu;
+  TMenuItem *N45;
+  TAction *CustomCommandsCustomizeAction;
+  TAction *CheckForUpdatesAction;
+  TMenuItem *N44;
+  TMenuItem *CheckForUpdates1;
+  TMenuItem *N46;
+  TMenuItem *Quit1;
+  TMenuItem *N47;
+  TMenuItem *Quit2;
+  TAction *PuttyAction;
+  TMenuItem *OpeninPuTTY1;
+  TMenuItem *OpeninPuTTY2;
   void __fastcall LogActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall LogActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall RightsActionsExecute(TBasicAction *Action, bool &Handled);
@@ -485,15 +503,18 @@ private:
   void __fastcall SetScpExplorer(TCustomScpExplorerForm * value);
 protected:
   void __fastcall CreateSessionListMenu();
+  void __fastcall CreateCustomCommandsMenu(TAction * Action);
   TCustomDirView * __fastcall DirView(TOperationSide Side) { return ScpExplorer->DirView(Side); }
-  void __fastcall OpenBrowser(AnsiString URL);
   void __fastcall SessionItemClick(TObject * Sender);
   void __fastcall OpenedSessionItemClick(TObject * Sender);
+  void __fastcall CustomCommandClick(TObject * Sender);
 public:
   void __fastcall CommanderShortcuts();
   void __fastcall ExplorerShortcuts();
   void __fastcall CreateOpenedSessionListMenu();
   TShortCut __fastcall OpenSessionShortCut(int Index);
+  void __fastcall OpenBrowser(AnsiString URL);
+
   __fastcall TNonVisualDataModule(TComponent * Owner);
   __property TListColumn * ListColumn = { read = FListColumn, write = FListColumn };
   __property TRightsFrame * RightsFrame = { read = FRightsFrame, write = FRightsFrame };

+ 196 - 6
forms/Preferences.cpp

@@ -3,8 +3,8 @@
 #pragma hdrstop
 
 #include "Preferences.h"
-    
-#include <Common.h>                                    
+
+#include <Common.h>
 #include <ScpMain.h>
 
 #include "VCLCommon.h"
@@ -18,6 +18,7 @@
 #pragma link "XPGroupBox"
 #pragma link "CopyParams"
 #pragma link "UpDownEdit"
+#pragma link "IEComboBox"
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------
 bool __fastcall DoPreferencesDialog(TPreferencesMode APreferencesMode)
@@ -44,6 +45,9 @@ __fastcall TPreferencesDialog::TPreferencesDialog(TComponent* AOwner)
   CopyParamsFrame->Direction = pdAll;
   FEditorFont = new TFont();
   FAfterExternalEditorDialog = false;
+  FCustomCommands = new TStringList();
+  FCustomCommandChanging = false;
+  FCustomCommandDragDest = -1;
   UseSystemFont(this);
 }
 //---------------------------------------------------------------------------
@@ -51,6 +55,7 @@ __fastcall TPreferencesDialog::~TPreferencesDialog()
 {
   LoggingFrame->OnGetDefaultLogFileName = NULL;
   delete FEditorFont;
+  delete FCustomCommands;
 }
 //---------------------------------------------------------------------
 bool __fastcall TPreferencesDialog::Execute()
@@ -88,6 +93,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
   BOOLPROP(ConfirmDeleting);
   BOOLPROP(ConfirmClosingSession);
   BOOLPROP(UseLocationProfiles);
+  BOOLPROP(ContinueOnError);
   #undef BOOLPROP
 
   if (WinConfiguration->DDTemporaryDirectory.IsEmpty())
@@ -103,6 +109,8 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
 
   ExplorerStyleSelectionCheck->Checked =
     WinConfiguration->ScpCommander.ExplorerStyleSelection;
+  PreserveLocalDirectoryCheck->Checked =
+    WinConfiguration->ScpCommander.PreserveLocalDirectory;
   ShowFullAddressCheck->Checked =
     WinConfiguration->ScpExplorer.ShowFullAddress;
   RegistryStorageButton->Checked = (Configuration->Storage == stRegistry);
@@ -143,6 +151,13 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
   RandomSeedFileLabel->Visible = WinConfiguration->ExpertMode;
   RandomSeedFileEdit->Visible = WinConfiguration->ExpertMode;
 
+  FCustomCommands->Assign(WinConfiguration->CustomCommands);
+  CustomCommandDescEdit->Text = "";
+  CustomCommandEdit->Text = "";
+  UpdateCustomCommandsView();
+
+  PuttyPathEdit->FileName = WinConfiguration->PuttyPath;
+
   UpdateControls();
 }
 //---------------------------------------------------------------------------
@@ -170,6 +185,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
     BOOLPROP(ConfirmDeleting);
     BOOLPROP(ConfirmClosingSession);
     BOOLPROP(UseLocationProfiles);
+    BOOLPROP(ContinueOnError);
     #undef BOOLPROP
 
     if (DDSystemTemporaryDirectoryButton->Checked)
@@ -185,6 +201,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
 
     TScpCommanderConfiguration ScpCommander = WinConfiguration->ScpCommander;
     ScpCommander.ExplorerStyleSelection = ExplorerStyleSelectionCheck->Checked;
+    ScpCommander.PreserveLocalDirectory = PreserveLocalDirectoryCheck->Checked;
     WinConfiguration->ScpCommander = ScpCommander;
 
     TScpExplorerConfiguration ScpExplorer = WinConfiguration->ScpExplorer;
@@ -210,6 +227,10 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
     if (ResumeOffButton->Checked) CopyParam.ResumeSupport = rsOff;
     CopyParam.ResumeThreshold = ResumeThresholdEdit->Value * 1024;
     Configuration->CopyParam = CopyParam;
+
+    WinConfiguration->CustomCommands = FCustomCommands;
+
+    WinConfiguration->PuttyPath = PuttyPathEdit->FileName;
   }
   __finally
   {
@@ -230,9 +251,11 @@ void __fastcall TPreferencesDialog::SetPreferencesMode(TPreferencesMode value)
 //---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::FormShow(TObject * /*Sender*/)
 {
-  PageControl->ActivePage =
-    (PreferencesMode == pmEditor) ? EditorSheet : PreferencesSheet;
-  //DefaultDirIsHomeCheck->SetFocus();
+  switch (PreferencesMode) {
+    case pmEditor: PageControl->ActivePage = EditorSheet; break;
+    case pmCustomCommands: PageControl->ActivePage = CustomCommandsSheet; break;
+    default: PageControl->ActivePage = PreferencesSheet; break;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::ControlChange(TObject * /*Sender*/)
@@ -247,7 +270,20 @@ void __fastcall TPreferencesDialog::UpdateControls()
   EnableControl(ResumeThresholdEdit, ResumeSmartButton->Checked);
 
   EditorFontLabel->Caption = FORMAT("%s, %d pt",
-    (FEditorFont->Name, FEditorFont->Size));  
+    (FEditorFont->Name, FEditorFont->Size));
+
+  bool CommandComplete = !CustomCommandDescEdit->Text.IsEmpty() &&
+    !CustomCommandEdit->Text.IsEmpty();
+  EnableControl(AddCommandButton, CommandComplete);
+  EnableControl(SaveCommandButton, CommandComplete &&
+    CustomCommandsView->Selected &&
+    (CustomCommandDescEdit->Text != FCustomCommands->Names[CustomCommandsView->ItemIndex] ||
+     CustomCommandEdit->Text != FCustomCommands->Values[
+      FCustomCommands->Names[CustomCommandsView->ItemIndex]]));
+  EnableControl(RemoveCommandButton, CustomCommandsView->Selected);
+  EnableControl(UpCommandButton, CustomCommandsView->ItemIndex > 0);
+  EnableControl(DownCommandButton, CustomCommandsView->ItemIndex >= 0 &&
+    CustomCommandsView->ItemIndex < CustomCommandsView->Items->Count - 1);
 }
 //---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::EditorFontButtonClick(TObject * /*Sender*/)
@@ -347,4 +383,158 @@ void __fastcall TPreferencesDialog::IconButtonClick(TObject *Sender)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewData(TObject * /*Sender*/,
+      TListItem * Item)
+{
+  assert(FCustomCommands);
+  int Index = Item->Index;
+  assert(Index >= 0 && Index <= FCustomCommands->Count);
+  Item->Caption = StringReplace(FCustomCommands->Names[Index], "&", "",
+    TReplaceFlags() << rfReplaceAll);
+  assert(!Item->SubItems->Count);
+  Item->SubItems->Add(FCustomCommands->Values[FCustomCommands->Names[Index]]);
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewSelectItem(
+      TObject * /*Sender*/, TListItem * Item, bool Selected)
+{
+  if (Item && Selected)
+  {
+    assert(Item);
+    int Index = Item->Index;
+    assert(Index >= 0 && Index <= FCustomCommands->Count);
+
+    CustomCommandDescEdit->Text = FCustomCommands->Names[Index];
+    CustomCommandEdit->Text = FCustomCommands->Values[FCustomCommands->Names[Index]];
+  }
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::UpdateCustomCommandsView()
+{
+  CustomCommandsView->Items->Count = FCustomCommands->Count;
+  AdjustListColumnsWidth(CustomCommandsView);
+  CustomCommandsView->Invalidate();
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewKeyDown(
+      TObject * /*Sender*/, WORD & Key, TShiftState /*Shift*/)
+{
+  if (RemoveCommandButton->Enabled && (Key == VK_DELETE))
+  {
+    RemoveCommandButtonClick(NULL);
+  }
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TPreferencesDialog::CustomCommandString(int Index)
+{
+  if (CustomCommandDescEdit->Text.Pos("="))
+  {
+    throw Exception(FMTLOAD(CUSTOM_COMMAND_INVALID, ("=")));
+  }
+  int I = FCustomCommands->IndexOfName(CustomCommandDescEdit->Text);
+  if (I >= 0 && (Index < 0 || I != Index))
+  {
+    throw Exception(FMTLOAD(CUSTOM_COMMAND_DUPLICATE, (CustomCommandDescEdit->Text)));
+  }
+  return FORMAT("%s=%s", (CustomCommandDescEdit->Text, CustomCommandEdit->Text));
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::AddCommandButtonClick(TObject * /*Sender*/)
+{
+  int Index;
+  if (CustomCommandsView->ItemIndex >= 0)
+  {
+    FCustomCommands->Insert(CustomCommandsView->ItemIndex, CustomCommandString());
+    Index = CustomCommandsView->ItemIndex;
+  }
+  else
+  {
+    Index = FCustomCommands->Add(CustomCommandString());
+  }
+  CustomCommandsView->ItemIndex = Index;
+  UpdateCustomCommandsView();
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::SaveCommandButtonClick(TObject * /*Sender*/)
+{
+  assert(CustomCommandsView->ItemIndex >= 0 &&
+    CustomCommandsView->ItemIndex < FCustomCommands->Count);
+  FCustomCommands->Strings[CustomCommandsView->ItemIndex] =
+    CustomCommandString(CustomCommandsView->ItemIndex);
+  UpdateCustomCommandsView();
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::RemoveCommandButtonClick(
+      TObject * /*Sender*/)
+{
+  assert(CustomCommandsView->ItemIndex >= 0 &&
+    CustomCommandsView->ItemIndex < FCustomCommands->Count);
+  FCustomCommands->Delete(CustomCommandsView->ItemIndex);
+  UpdateCustomCommandsView();
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandMove(int Source, int Dest)
+{
+  if (Source >= 0 && Source < FCustomCommands->Count &&
+      Dest >= 0 && Dest < FCustomCommands->Count)
+  {
+    FCustomCommands->Move(Source, Dest);
+    // workaround for bug in VCL
+    CustomCommandsView->ItemIndex = -1;
+    CustomCommandsView->ItemFocused = CustomCommandsView->Selected;
+    CustomCommandsView->ItemIndex = Dest;
+    UpdateCustomCommandsView();
+    UpdateControls();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::UpDownCommandButtonClick(TObject * Sender)
+{
+  CustomCommandMove(CustomCommandsView->ItemIndex,
+    CustomCommandsView->ItemIndex + (Sender == UpCommandButton ? -1 : 1));
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewStartDrag(
+      TObject * /*Sender*/, TDragObject *& /*DragObject*/)
+{
+  FCustomCommandDragSource = CustomCommandsView->ItemIndex;
+  FCustomCommandDragDest = -1;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TPreferencesDialog::AllowCustomCommandsDrag(int X, int Y)
+{
+  TListItem * Item = CustomCommandsView->GetItemAt(X, Y);
+  FCustomCommandDragDest = Item ? Item->Index : -1;
+  return (FCustomCommandDragDest >= 0) && (FCustomCommandDragDest != FCustomCommandDragSource);
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewDragDrop(
+      TObject * /*Sender*/, TObject * Source, int X, int Y)
+{
+  if (Source == CustomCommandsView)
+  {
+    if (AllowCustomCommandsDrag(X, Y))
+    {
+      CustomCommandMove(FCustomCommandDragSource, FCustomCommandDragDest);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::CustomCommandsViewDragOver(
+      TObject * /*Sender*/, TObject * Source, int /*X*/, int /*Y*/,
+      TDragState /*State*/, bool & Accept)
+{
+  if (Source == CustomCommandsView)
+  {
+    // cannot use AllowCustomCommandsDrag(X, Y) because of bug in VCL
+    // (when dropped on item itself, when it was dragged over another item before,
+    // that another item remains highlighted forever)
+    Accept = true;
+  }
+}
+//---------------------------------------------------------------------------
 

+ 219 - 28
forms/Preferences.dfm

@@ -1,6 +1,6 @@
 object PreferencesDialog: TPreferencesDialog
-  Left = 379
-  Top = 133
+  Left = 380
+  Top = 111
   BorderStyle = bsDialog
   Caption = 'Preferences'
   ClientHeight = 425
@@ -21,11 +21,11 @@ object PreferencesDialog: TPreferencesDialog
     Top = 0
     Width = 386
     Height = 385
-    ActivePage = CommanderSheet
+    ActivePage = IntegrationSheet
     Align = alTop
     Anchors = [akLeft, akTop, akRight, akBottom]
     MultiLine = True
-    TabIndex = 4
+    TabIndex = 8
     TabOrder = 0
     object PreferencesSheet: TTabSheet
       Caption = 'General'
@@ -35,7 +35,7 @@ object PreferencesDialog: TPreferencesDialog
         339)
       object RandomSeedFileLabel: TLabel
         Left = 16
-        Top = 258
+        Top = 276
         Width = 82
         Height = 13
         Caption = '&Random seed file'
@@ -44,13 +44,13 @@ object PreferencesDialog: TPreferencesDialog
         Left = 8
         Top = 8
         Width = 362
-        Height = 161
+        Height = 181
         Anchors = [akLeft, akTop, akRight]
         Caption = 'Confirmations'
         TabOrder = 0
         DesignSize = (
           362
-          161)
+          181)
         object CopyOnDoubleClickCheck: TCheckBox
           Left = 16
           Top = 109
@@ -111,10 +111,20 @@ object PreferencesDialog: TPreferencesDialog
           TabOrder = 3
           OnClick = ControlChange
         end
+        object ContinueOnErrorCheck: TCheckBox
+          Left = 16
+          Top = 153
+          Width = 330
+          Height = 17
+          Anchors = [akLeft, akTop, akRight]
+          Caption = 'Continue on &error (advanced users)'
+          TabOrder = 6
+          OnClick = ControlChange
+        end
       end
       object RandomSeedFileEdit: TFilenameEdit
         Left = 128
-        Top = 254
+        Top = 272
         Width = 242
         Height = 21
         AcceptFiles = True
@@ -130,7 +140,7 @@ object PreferencesDialog: TPreferencesDialog
       end
       object StorageGroup: TXPGroupBox
         Left = 8
-        Top = 176
+        Top = 195
         Width = 362
         Height = 68
         Anchors = [akLeft, akTop, akRight]
@@ -195,12 +205,12 @@ object PreferencesDialog: TPreferencesDialog
       object Label1: TLabel
         Left = 8
         Top = 226
-        Width = 320
+        Width = 361
         Height = 33
         AutoSize = False
         Caption = 
           'Note: a change to this setting will only take effect the next ti' +
-          'me you open session in WinSCP.'
+          'me you start the application.'
         WordWrap = True
       end
       inline GeneralSettingsFrame: TGeneralSettingsFrame
@@ -377,13 +387,13 @@ object PreferencesDialog: TPreferencesDialog
         Left = 8
         Top = 38
         Width = 362
-        Height = 76
+        Height = 99
         Anchors = [akLeft, akTop, akRight]
         Caption = 'Panels'
         TabOrder = 0
         DesignSize = (
           362
-          76)
+          99)
         object DeleteToRecycleBinCheck: TCheckBox
           Left = 16
           Top = 21
@@ -404,10 +414,20 @@ object PreferencesDialog: TPreferencesDialog
           TabOrder = 1
           OnClick = ControlChange
         end
+        object PreserveLocalDirectoryCheck: TCheckBox
+          Left = 16
+          Top = 69
+          Width = 330
+          Height = 17
+          Anchors = [akLeft, akTop, akRight]
+          Caption = 'Do not &change local directory when switching sessions'
+          TabOrder = 2
+          OnClick = ControlChange
+        end
       end
       object CommanderMiscGroup: TXPGroupBox
         Left = 8
-        Top = 122
+        Top = 146
         Width = 362
         Height = 53
         Anchors = [akLeft, akTop, akRight]
@@ -476,6 +496,19 @@ object PreferencesDialog: TPreferencesDialog
         Width = 529
         Height = 345
         TabOrder = 0
+        inherited RemotePropertiesGroup: TXPGroupBox
+          Left = 8
+          Top = 156
+          Width = 182
+          Height = 126
+          Caption = 'Upload options'
+          inherited RightsFrame: TRightsFrame
+            Height = 88
+          end
+          inherited RemotePreserveTimeCheck: TCheckBox
+            Top = 161
+          end
+        end
         inherited CommonPropertiesGroup: TXPGroupBox
           Left = 198
           Top = 156
@@ -508,19 +541,6 @@ object PreferencesDialog: TPreferencesDialog
             Width = 164
           end
         end
-        inherited RemotePropertiesGroup: TXPGroupBox
-          Left = 8
-          Top = 156
-          Width = 182
-          Height = 126
-          Caption = 'Upload options'
-          inherited RightsFrame: TRightsFrame
-            Height = 88
-          end
-          inherited RemotePreserveTimeCheck: TCheckBox
-            Top = 161
-          end
-        end
         inherited ChangeCaseGroup: TXPGroupBox
           Left = 247
           Top = 8
@@ -770,11 +790,182 @@ object PreferencesDialog: TPreferencesDialog
           Top = 120
           Width = 305
           Height = 25
-          Caption = 'Add upload shortcut to Explorer'#39's '#39'Send to'#39' context menu'
+          Caption = 'Add upload shortcut to Explorer'#39's '#39'&Send to'#39' context menu'
           TabOrder = 3
           OnClick = IconButtonClick
         end
       end
+      object XPGroupBox1: TXPGroupBox
+        Left = 8
+        Top = 224
+        Width = 362
+        Height = 78
+        Anchors = [akLeft, akTop, akRight]
+        Caption = 'External applications'
+        TabOrder = 1
+        DesignSize = (
+          362
+          78)
+        object Label2: TLabel
+          Left = 16
+          Top = 24
+          Width = 61
+          Height = 13
+          Caption = '&PuTTY path:'
+        end
+        object PuttyPathEdit: TFilenameEdit
+          Left = 16
+          Top = 41
+          Width = 330
+          Height = 21
+          OnAfterDialog = ExternalEditorEditAfterDialog
+          Filter = 
+            'PuTTY executable (putty.exe)|putty.exe|Executable files (*.exe)|' +
+            '*.exe|All files (*.*)|*.*'
+          ClickKey = 16397
+          Anchors = [akLeft, akTop, akRight]
+          TabOrder = 0
+          Text = 'PuttyPathEdit'
+          OnChange = ExternalEditorEditChange
+          OnExit = ExternalEditorEditExit
+        end
+      end
+    end
+    object CustomCommandsSheet: TTabSheet
+      Caption = 'Commands'
+      ImageIndex = 9
+      DesignSize = (
+        378
+        339)
+      object CustomCommandsGroup: TXPGroupBox
+        Left = 8
+        Top = 8
+        Width = 362
+        Height = 321
+        Anchors = [akLeft, akTop, akRight, akBottom]
+        Caption = 'Custom commands (SCP only)'
+        TabOrder = 0
+        DesignSize = (
+          362
+          321)
+        object LocalDirectoryLabel: TLabel
+          Left = 16
+          Top = 24
+          Width = 141
+          Height = 13
+          Caption = 'Custom command descri&ption:'
+          FocusControl = CustomCommandDescEdit
+        end
+        object RemoteDirectoryLabel: TLabel
+          Left = 16
+          Top = 72
+          Width = 232
+          Height = 13
+          Caption = '&Custom command (! is replaced with name of file):'
+          FocusControl = CustomCommandEdit
+        end
+        object CustomCommandDescEdit: TEdit
+          Left = 16
+          Top = 41
+          Width = 330
+          Height = 21
+          Anchors = [akLeft, akTop, akRight]
+          TabOrder = 0
+          Text = 'CustomCommandDescEdit'
+          OnChange = ControlChange
+        end
+        object CustomCommandEdit: TEdit
+          Left = 16
+          Top = 89
+          Width = 330
+          Height = 21
+          Anchors = [akLeft, akTop, akRight]
+          MaxLength = 1000
+          TabOrder = 1
+          Text = 'CustomCommandEdit'
+          OnChange = ControlChange
+        end
+        object CustomCommandsView: TListView
+          Left = 16
+          Top = 120
+          Width = 238
+          Height = 185
+          Anchors = [akLeft, akTop, akRight, akBottom]
+          Columns = <
+            item
+              Caption = 'Description'
+              Width = 80
+            end
+            item
+              Caption = 'Command'
+              Width = 140
+            end>
+          ColumnClick = False
+          DragMode = dmAutomatic
+          HideSelection = False
+          OwnerData = True
+          ReadOnly = True
+          RowSelect = True
+          TabOrder = 2
+          ViewStyle = vsReport
+          OnData = CustomCommandsViewData
+          OnDragDrop = CustomCommandsViewDragDrop
+          OnDragOver = CustomCommandsViewDragOver
+          OnKeyDown = CustomCommandsViewKeyDown
+          OnSelectItem = CustomCommandsViewSelectItem
+          OnStartDrag = CustomCommandsViewStartDrag
+        end
+        object AddCommandButton: TButton
+          Left = 263
+          Top = 120
+          Width = 83
+          Height = 25
+          Anchors = [akTop, akRight]
+          Caption = '&Add'
+          TabOrder = 3
+          OnClick = AddCommandButtonClick
+        end
+        object RemoveCommandButton: TButton
+          Left = 263
+          Top = 184
+          Width = 83
+          Height = 25
+          Anchors = [akTop, akRight]
+          Caption = '&Remove'
+          TabOrder = 5
+          OnClick = RemoveCommandButtonClick
+        end
+        object UpCommandButton: TButton
+          Left = 263
+          Top = 248
+          Width = 83
+          Height = 25
+          Anchors = [akTop, akRight]
+          Caption = '&Up'
+          TabOrder = 6
+          OnClick = UpDownCommandButtonClick
+        end
+        object DownCommandButton: TButton
+          Left = 263
+          Top = 280
+          Width = 83
+          Height = 25
+          Anchors = [akTop, akRight]
+          Caption = '&Down'
+          TabOrder = 7
+          OnClick = UpDownCommandButtonClick
+        end
+        object SaveCommandButton: TButton
+          Left = 263
+          Top = 152
+          Width = 83
+          Height = 25
+          Anchors = [akTop, akRight]
+          Caption = '&Save'
+          TabOrder = 4
+          OnClick = SaveCommandButtonClick
+        end
+      end
     end
   end
   object OKButton: TButton

+ 42 - 1
forms/Preferences.h

@@ -21,7 +21,8 @@
 #include "CopyParams.h"
 #include "GeneralSettings.h"
 #include "LogSettings.h"
-#include "UpDownEdit.hpp"           
+#include "UpDownEdit.hpp"
+#include "IEComboBox.hpp"
 //----------------------------------------------------------------------------
 class TPreferencesDialog : public TForm
 {
@@ -95,6 +96,23 @@ __published:
   TLabel *ShellIconsLabel;
   TXPGroupBox *CommanderMiscGroup;
   TCheckBox *UseLocationProfilesCheck;
+  TTabSheet *CustomCommandsSheet;
+  TXPGroupBox *CustomCommandsGroup;
+  TLabel *LocalDirectoryLabel;
+  TEdit *CustomCommandDescEdit;
+  TLabel *RemoteDirectoryLabel;
+  TEdit *CustomCommandEdit;
+  TListView *CustomCommandsView;
+  TButton *AddCommandButton;
+  TButton *RemoveCommandButton;
+  TButton *UpCommandButton;
+  TButton *DownCommandButton;
+  TButton *SaveCommandButton;
+  TCheckBox *ContinueOnErrorCheck;
+  TCheckBox *PreserveLocalDirectoryCheck;
+  TXPGroupBox *XPGroupBox1;
+  TFilenameEdit *PuttyPathEdit;
+  TLabel *Label2;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);
@@ -104,10 +122,29 @@ __published:
   void __fastcall ExternalEditorEditChange(TObject *Sender);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
   void __fastcall IconButtonClick(TObject *Sender);
+  void __fastcall CustomCommandsViewData(TObject *Sender, TListItem *Item);
+  void __fastcall CustomCommandsViewSelectItem(TObject *Sender,
+          TListItem *Item, bool Selected);
+  void __fastcall CustomCommandsViewKeyDown(TObject *Sender, WORD &Key,
+          TShiftState Shift);
+  void __fastcall AddCommandButtonClick(TObject *Sender);
+  void __fastcall SaveCommandButtonClick(TObject *Sender);
+  void __fastcall RemoveCommandButtonClick(TObject *Sender);
+  void __fastcall UpDownCommandButtonClick(TObject *Sender);
+  void __fastcall CustomCommandsViewStartDrag(TObject *Sender,
+          TDragObject *&DragObject);
+  void __fastcall CustomCommandsViewDragDrop(TObject *Sender,
+          TObject *Source, int X, int Y);
+  void __fastcall CustomCommandsViewDragOver(TObject *Sender,
+          TObject *Source, int X, int Y, TDragState State, bool &Accept);
 private:
   TPreferencesMode FPreferencesMode;
   TFont * FEditorFont;
+  TStrings * FCustomCommands;
+  bool FCustomCommandChanging;
   bool FAfterExternalEditorDialog;
+  int FCustomCommandDragSource;
+  int FCustomCommandDragDest;
   void __fastcall SetPreferencesMode(TPreferencesMode value);
 public:
   virtual __fastcall ~TPreferencesDialog();
@@ -119,6 +156,10 @@ protected:
   void __fastcall LoggingGetDefaultLogFileName(System::TObject * Sender, AnsiString & DefaultLogFileName);
   void __fastcall SaveConfiguration();
   void __fastcall UpdateControls();
+  void __fastcall UpdateCustomCommandsView();
+  AnsiString __fastcall CustomCommandString(int Index = -1);
+  void __fastcall CustomCommandMove(int Source, int Dest);
+  bool __fastcall AllowCustomCommandsDrag(int X, int Y);
 };
 //----------------------------------------------------------------------------
 #endif

+ 48 - 24
forms/Progress.cpp

@@ -45,34 +45,20 @@ __fastcall TProgressForm::~TProgressForm()
 //---------------------------------------------------------------------
 void __fastcall TProgressForm::UpdateControls()
 {
-  assert((FData.Operation >= foCopy) && (FData.Operation <= foSetProperties));
+  assert(FData.Operation == foCustomCommand ||
+    ((FData.Operation >= foCopy) && (FData.Operation <= foSetProperties)));
+
   bool TransferOperation =
     ((FData.Operation == foCopy) || (FData.Operation == foMove));
-    
+
   if (FData.Operation != FLastOperation)
   {
-    bool AVisible = (FData.Operation != foSetProperties);
-    int Delta = 0;
-    if (AVisible && !Animate->Visible) Delta = Animate->Height;
-      else
-    if (!AVisible && Animate->Visible) Delta = -Animate->Height;
-
-    MainPanel->Top = MainPanel->Top + Delta;
-    TransferPanel->Top = TransferPanel->Top + Delta;
-    SpeedPanel->Top = SpeedPanel->Top + Delta;
-    Animate->Visible = AVisible;
-
-    if (TransferOperation && !TransferPanel->Visible) Delta += TransferPanel->Height;
-      else
-    if (!TransferOperation && TransferPanel->Visible) Delta += -TransferPanel->Height;
-    TransferPanel->Visible = TransferOperation;
-    SpeedPanel->Visible = TransferOperation && WinConfiguration->ExpertMode;
-
-    ClientHeight = ClientHeight + Delta;
-    DisconnectWhenCompleteCheck->Top = DisconnectWhenCompleteCheck->Top + Delta;
+    bool AVisible;
+    THandle ShellModule;
 
     try
     {
+      AVisible = true;
       switch (FData.Operation) {
         case foCopy:
         case foMove:
@@ -87,17 +73,55 @@ void __fastcall TProgressForm::UpdateControls()
             Animate->CommonAVI = aviDeleteFile;
           break;
 
+        case foSetProperties:
+          ShellModule = SafeLoadLibrary("shell32.dll");
+          if (!ShellModule)
+          {
+            Abort();
+          }
+          // workaround, VCL is not able to set both ResId and ResHandle otherwise
+          Animate->Active = false;
+          Animate->ResHandle = 0;
+          Animate->ComponentState << csLoading;
+          Animate->ResId = 165;
+          Animate->ResHandle = ShellModule;
+          Animate->ComponentState >> csLoading;
+          Animate->Active = true;
+          break;
+
         default:
-          assert(FData.Operation == foSetProperties);
+          assert(FData.Operation == foSetProperties || FData.Operation == foCustomCommand);
           Animate->CommonAVI = aviNone;
+          AVisible = false;
       }
-      Animate->Active = (Animate->CommonAVI != aviNone);
     }
     catch (...)
     {
+      AVisible = false;
     };
 
-    const int Captions[] = {PROGRESS_COPY, PROGRESS_MOVE, PROGRESS_DELETE, PROGRESS_SETPROPERTIES};
+    int Delta = 0;
+    if (AVisible && !Animate->Visible) Delta = Animate->Height;
+      else
+    if (!AVisible && Animate->Visible) Delta = -Animate->Height;
+
+    MainPanel->Top = MainPanel->Top + Delta;
+    TransferPanel->Top = TransferPanel->Top + Delta;
+    SpeedPanel->Top = SpeedPanel->Top + Delta;
+    Animate->Visible = AVisible;
+    Animate->Active = AVisible;
+
+    if (TransferOperation && !TransferPanel->Visible) Delta += TransferPanel->Height;
+      else
+    if (!TransferOperation && TransferPanel->Visible) Delta += -TransferPanel->Height;
+    TransferPanel->Visible = TransferOperation;
+    SpeedPanel->Visible = TransferOperation && WinConfiguration->ExpertMode;
+
+    ClientHeight = ClientHeight + Delta;
+    DisconnectWhenCompleteCheck->Top = DisconnectWhenCompleteCheck->Top + Delta;
+
+    const int Captions[] = { PROGRESS_COPY, PROGRESS_MOVE, PROGRESS_DELETE,
+      PROGRESS_SETPROPERTIES, 0, PROGRESS_CUSTOM_COMAND };
     Caption = LoadStr(Captions[(int)FData.Operation - 1]);
 
     TargetLabel->Visible = TransferOperation;

+ 2 - 2
forms/Properties.dfm

@@ -1,6 +1,6 @@
 object PropertiesDialog: TPropertiesDialog
-  Left = 362
-  Top = 120
+  Left = 523
+  Top = 133
   BorderStyle = bsDialog
   Caption = 'Properties'
   ClientHeight = 416

+ 29 - 9
forms/Rights.cpp

@@ -91,7 +91,7 @@ bool __fastcall TRightsFrame::GetAllowUndef()
     }
     else if (Result != Check->AllowGrayed)
     {
-      throw Exception("AllowGrayed property of all checkboxes of TRightsFrame don't have same value");
+      assert(false);
     }
   }
   return Result;
@@ -219,20 +219,25 @@ void __fastcall TRightsFrame::SetEnabled(bool Value)
   UpdateControls();
 }
 //---------------------------------------------------------------------------
+void __fastcall TRightsFrame::UpdateByOctal()
+{
+  if (!OctalEdit->Text.IsEmpty())
+  {
+    TRights R = Rights;
+    R.Octal = OctalEdit->Text;
+    Rights = R;
+  }
+  UpdateControls();
+  OctalEdit->Modified = false;
+}
+//---------------------------------------------------------------------------
 void __fastcall TRightsFrame::OctalEditExit(TObject * /*Sender*/)
 {
   if (OctalEdit->Modified)
   {
     try
     {
-      if (!OctalEdit->Text.IsEmpty())
-      {
-        TRights R = Rights;
-        R.Octal = OctalEdit->Text;
-        Rights = R;
-      }
-      UpdateControls();
-      OctalEdit->Modified = false;
+      UpdateByOctal();
     }
     catch(...)
     {
@@ -243,4 +248,19 @@ void __fastcall TRightsFrame::OctalEditExit(TObject * /*Sender*/)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TRightsFrame::OctalEditChange(TObject * /*Sender*/)
+{
+  if (OctalEdit->Modified && OctalEdit->Text.Length() == 3)
+  {
+    try
+    {
+      UpdateByOctal();
+    }
+    catch(...)
+    {
+      OctalEdit->Modified = true;
+    }
+  }
+}
+//---------------------------------------------------------------------------
 

+ 1 - 0
forms/Rights.dfm

@@ -164,6 +164,7 @@ object RightsFrame: TRightsFrame
     MaxLength = 3
     TabOrder = 9
     Text = 'OctalEdit'
+    OnChange = OctalEditChange
     OnExit = OctalEditExit
   end
   object DirectoriesXCheck: TCheckBox

+ 2 - 0
forms/Rights.h

@@ -37,6 +37,7 @@ __published:
   void __fastcall RightsButtonsClick(TObject *Sender);
   void __fastcall FrameEnter(TObject *Sender);
   void __fastcall OctalEditExit(TObject *Sender);
+  void __fastcall OctalEditChange(TObject *Sender);
 private:
   bool FAllowAddXToDirectories;
   TNotifyEvent FOnChange;
@@ -70,6 +71,7 @@ protected:
   void __fastcall DoChange();
   void __fastcall UpdateControls();
   virtual void __fastcall SetEnabled(bool Value);
+  void __fastcall UpdateByOctal();
 };
 //---------------------------------------------------------------------------
 #endif

+ 44 - 22
forms/ScpCommander.cpp

@@ -13,6 +13,7 @@
 #include "NonVisual.h"
 #include "Tools.h"
 #include "WinConfiguration.h"
+#include "TerminalManager.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma link "AssociatedStatusBar"
@@ -39,6 +40,7 @@ __fastcall TScpCommanderForm::TScpCommanderForm(TComponent* Owner)
   FSynchronization = ssStopped;
   FSynchronizeDialog = NULL;
   FSynchronisingBrowse = false;
+  FFirstTerminal = true;
 
   LocalBackButton->DropdownMenu = LocalDirView->BackMenu;
   LocalForwardButton->DropdownMenu = LocalDirView->ForwardMenu;
@@ -146,6 +148,16 @@ void __fastcall TScpCommanderForm::UpdateSessionData(TSessionData * Data)
   {
     assert(LocalDirView);
     Data->LocalDirectory = LocalDirView->PathName;
+    Terminal->UserObject = NULL;
+  }
+  else
+  {
+    if (!Terminal->UserObject)
+    {
+      Terminal->UserObject = new TTerminalUserObject();
+    }
+    dynamic_cast<TTerminalUserObject *>(Terminal->UserObject)->LocalDirectory =
+      LocalDirView->PathName;
   }
 }
 //---------------------------------------------------------------------------
@@ -236,33 +248,46 @@ void __fastcall TScpCommanderForm::TerminalChanged()
   TCustomScpExplorerForm::TerminalChanged();
   if (Terminal)
   {
-    AnsiString LocalDirectory = Terminal->SessionData->LocalDirectory;
-    bool DocumentsDir = LocalDirectory.IsEmpty();
-
-    if (!DocumentsDir)
+    if (FFirstTerminal || !WinConfiguration->ScpCommander.PreserveLocalDirectory)
     {
-      try
+      AnsiString LocalDirectory;
+
+      if (Terminal->UserObject)
       {
-        LocalDirView->Path = LocalDirectory;
+        LocalDirectory = dynamic_cast<TTerminalUserObject *>(Terminal->UserObject)->LocalDirectory;
       }
-      catch(Exception & E)
+      else
       {
-        DocumentsDir = true;
-        ShowExtendedException(&E, this);
+        LocalDirectory = Terminal->SessionData->LocalDirectory;
       }
-    }
+      bool DocumentsDir = LocalDirectory.IsEmpty();
 
-    if (DocumentsDir)
-    {
-      LocalDirView->HomeDirectory = "";
-      LocalDirView->ExecuteHomeDirectory();
-    }
+      if (!DocumentsDir)
+      {
+        try
+        {
+          LocalDirView->Path = LocalDirectory;
+        }
+        catch(Exception & E)
+        {
+          DocumentsDir = true;
+          ShowExtendedException(&E, this);
+        }
+      }
 
-    if (Configuration->DefaultDirIsHome &&
-        !Terminal->SessionData->UpdateDirectories)
-    {
-      LocalDirView->HomeDirectory = LocalDirectory;
+      if (DocumentsDir)
+      {
+        LocalDirView->HomeDirectory = "";
+        LocalDirView->ExecuteHomeDirectory();
+      }
+
+      if (Configuration->DefaultDirIsHome &&
+          !Terminal->SessionData->UpdateDirectories)
+      {
+        LocalDirView->HomeDirectory = LocalDirectory;
+      }
     }
+    FFirstTerminal = false;
   }
 }
 //---------------------------------------------------------------------------
@@ -729,6 +754,3 @@ void __fastcall TScpCommanderForm::DoOpenDirectoryDialog(TOpenDirectoryMode Mode
   }
 }
 
-
-
-

+ 15 - 5
forms/ScpCommander.dfm

@@ -69,7 +69,7 @@ inherited ScpCommanderForm: TScpCommanderForm
     object MenuToolBar: TToolBar
       Left = 9
       Top = 0
-      Width = 311
+      Width = 343
       Height = 22
       Hint = '|E'
       Align = alLeft
@@ -105,12 +105,21 @@ inherited ScpCommanderForm: TScpCommanderForm
         Top = 0
         Hint = 'File operation commands'
         AutoSize = True
+        Caption = '&Files'
+        Grouped = True
+        MenuItem = NonVisualDataModule.CommanderFilesMenu
+      end
+      object ToolButton49: TToolButton
+        Left = 104
+        Top = 0
+        Hint = 'Other commands'
+        AutoSize = True
         Caption = '&Commands'
         Grouped = True
         MenuItem = NonVisualDataModule.CommanderCommandsMenu
       end
       object ToolButton19: TToolButton
-        Left = 135
+        Left = 167
         Top = 0
         Hint = 'Session commands'
         AutoSize = True
@@ -119,7 +128,7 @@ inherited ScpCommanderForm: TScpCommanderForm
         MenuItem = NonVisualDataModule.CommonSessionMenu
       end
       object ToolButton7: TToolButton
-        Left = 183
+        Left = 215
         Top = 0
         Hint = 'Change program layout/preferences'
         AutoSize = True
@@ -128,7 +137,7 @@ inherited ScpCommanderForm: TScpCommanderForm
         MenuItem = NonVisualDataModule.CommanderOptionsMenu
       end
       object ToolButton3: TToolButton
-        Left = 230
+        Left = 262
         Top = 0
         Hint = 'Change remote panel layout or change displayed directory'
         AutoSize = True
@@ -137,7 +146,7 @@ inherited ScpCommanderForm: TScpCommanderForm
         MenuItem = NonVisualDataModule.CommanderRemoteMenu
       end
       object ToolButton20: TToolButton
-        Left = 278
+        Left = 310
         Top = 0
         Hint = 'Help'
         AutoSize = True
@@ -571,6 +580,7 @@ inherited ScpCommanderForm: TScpCommanderForm
       PathLabel = RemotePathLabel
       AddParentDir = True
       OnLoaded = DirViewLoaded
+      OnWarnLackOfTempSpace = nil
     end
     object RemoteCoolBar: TCoolBar
       Left = 0

+ 2 - 0
forms/ScpCommander.h

@@ -123,6 +123,7 @@ __published:
   TToolButton *ToolButton47;
   TToolButton *ToolButton48;
   TComboBox *SessionCombo;
+  TToolButton *ToolButton49;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall SplitterMoved(TObject *Sender);
   void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
@@ -150,6 +151,7 @@ private:
   TSynchronizeParamType FSynchronizeParams;
   TSynchronizeDialog * FSynchronizeDialog;
   AnsiString FPrevPath[2];
+  bool FFirstTerminal;
   void __fastcall SetLocalPanelWidth(float value);
   float __fastcall GetLocalPanelWidth();
 

+ 14 - 14
forms/SelectMask.cpp

@@ -21,7 +21,8 @@ bool __fastcall DoSelectMaskDialog(TCustomDirView * Parent, bool Select,
 {
 	bool Result;
   TSelectMaskDialog * Dialog = new TSelectMaskDialog(Application);
-  try {
+  try
+  {
     CenterFormOn(Dialog, Parent);
     Dialog->Select = Select;
     DefaultFileFilter(*Filter);
@@ -29,12 +30,15 @@ bool __fastcall DoSelectMaskDialog(TCustomDirView * Parent, bool Select,
     Filter->Directories = WinConfiguration->SelectDirectories;
     Dialog->FileFilter = *Filter;
     Result = Dialog->Execute();
+    if (Result)
     {
       *Filter = Dialog->FileFilter;
       WinConfiguration->SelectMask = Filter->Masks;
       WinConfiguration->SelectDirectories = Filter->Directories;
     }
-  } __finally {
+  }
+  __finally
+  {
     delete Dialog;
   }
   return Result;
@@ -54,10 +58,10 @@ void __fastcall TSelectMaskDialog::FormCloseQuery(TObject * /*Sender*/,
   if (ModalResult != mrCancel)
   {
     TFileMasks Masks = MaskEdit->Text;
-    Integer Start, Length;
+    int Start, Length;
     if (!Masks.IsValid(Start, Length))
     {
-      CanClose = False;
+      CanClose = false;
       SimpleErrorDialog(FMTLOAD(MASK_ERROR, (Masks.Masks.SubString(Start+1, Length))));
       // After closing dialog whole text is selected, we want to select only invalid mask
       MaskEdit->SetFocus();
@@ -67,11 +71,11 @@ void __fastcall TSelectMaskDialog::FormCloseQuery(TObject * /*Sender*/,
   }
 }
 //---------------------------------------------------------------------------
-Boolean __fastcall TSelectMaskDialog::Execute()
+bool __fastcall TSelectMaskDialog::Execute()
 {
   MaskEdit->Items->Text = WinConfiguration->MaskHistory;
   ActiveControl = MaskEdit;
-  Boolean Result = (ShowModal() == mrOk);
+  bool Result = (ShowModal() == mrOk);
   if (Result)
   {
     MaskEdit->SaveToHistory();
@@ -85,19 +89,15 @@ void __fastcall TSelectMaskDialog::SetSelect(Boolean value)
   if (FSelect != value)
   {
     FSelect = value;
-    if (Select) Caption = LoadStr(SELECT_MASK_SELECT_CAPTION);
-      else Caption = LoadStr(SELECT_MASK_DESELECT_CAPTION);
+    Caption = LoadStr(Select ? SELECT_MASK_SELECT_CAPTION : SELECT_MASK_DESELECT_CAPTION);
   }
 } /* TSelectMaskDialog::SetSelect */
 //---------------------------------------------------------------------------
 void __fastcall TSelectMaskDialog::SetFileFilter(TFileFilter value)
 {
-  //if (FFileFilter != value)
-  {
-    FFileFilter = value;
-    IncludingDirectoriesCheck->Checked = FFileFilter.Directories;
-    MaskEdit->Text = FFileFilter.Masks;
-  }
+  FFileFilter = value;
+  IncludingDirectoriesCheck->Checked = FFileFilter.Directories;
+  MaskEdit->Text = FFileFilter.Masks;
 } /* TSelectMaskDialog::SetFileFilter */
 //---------------------------------------------------------------------------
 TFileFilter __fastcall TSelectMaskDialog::GetFileFilter()

+ 4 - 4
forms/SelectMask.h

@@ -23,15 +23,15 @@ __published:
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
 private:
   TFileFilter FFileFilter;
-  Boolean FSelect;
+  bool FSelect;
   void __fastcall SetFileFilter(TFileFilter value);
   TFileFilter __fastcall GetFileFilter();
-  void __fastcall SetSelect(Boolean value);
+  void __fastcall SetSelect(bool value);
 public:
-  Boolean __fastcall Execute();
+  bool __fastcall Execute();
   __fastcall TSelectMaskDialog(TComponent* Owner);
   __property TFileFilter FileFilter = { read = GetFileFilter, write = SetFileFilter };
-  __property Boolean Select = { read = FSelect, write = SetSelect };
+  __property bool Select = { read = FSelect, write = SetSelect };
 };
 //---------------------------------------------------------------------------
 #endif

+ 1 - 1
general/moje komponenty/ToolbarPanel.pas

@@ -443,7 +443,7 @@ end; { CreateButtons }
 procedure TCustomToolbarPanel.BeginUpdate;
 begin
   Inc(FUpdating);
-  Assert(FUpdating < 5);
+  Assert(FUpdating < 20);
 end; { BeginUpdate }
 
 procedure TCustomToolbarPanel.ConstraintsChange(Sender: TObject);

+ 16 - 10
putty/MAKEFILE.BOR

@@ -121,7 +121,7 @@ pscp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj misc.obj \
 		sshdh.obj sshdss.obj sshmd5.obj sshpubk.obj sshrand.obj \
 		sshrsa.obj sshsh512.obj sshsha.obj sshzlib.obj tree234.obj \
 		version.obj wildcard.obj windefs.obj winmisc.obj winnet.obj \
-		winstore.obj x11fwd.obj pscp.rsp
+		winsftp.obj winstore.obj x11fwd.obj pscp.rsp
 	ilink32 -ap -Gn -L$(BCB)\lib @pscp.rsp
 
 psftp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj \
@@ -131,7 +131,7 @@ psftp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj \
 		sshdes.obj sshdh.obj sshdss.obj sshmd5.obj sshpubk.obj \
 		sshrand.obj sshrsa.obj sshsh512.obj sshsha.obj sshzlib.obj \
 		tree234.obj version.obj wildcard.obj windefs.obj winmisc.obj \
-		winnet.obj winstore.obj x11fwd.obj psftp.rsp
+		winnet.obj winsftp.obj winstore.obj x11fwd.obj psftp.rsp
 	ilink32 -ap -Gn -L$(BCB)\lib @psftp.rsp
 
 putty.exe: be_all.obj cmdline.obj config.obj dialog.obj ldisc.obj \
@@ -201,7 +201,7 @@ pscp.rsp: $(MAKEFILE)
 	echo sshpubk.obj sshrand.obj sshrsa.obj sshsh512.obj + >> pscp.rsp
 	echo sshsha.obj sshzlib.obj tree234.obj version.obj + >> pscp.rsp
 	echo wildcard.obj windefs.obj winmisc.obj winnet.obj + >> pscp.rsp
-	echo winstore.obj x11fwd.obj >> pscp.rsp
+	echo winsftp.obj winstore.obj x11fwd.obj >> pscp.rsp
 	echo pscp.exe >> pscp.rsp
 	echo nul,cw32 import32, >> pscp.rsp
 	echo scp.res >> pscp.rsp
@@ -217,7 +217,7 @@ psftp.rsp: $(MAKEFILE)
 	echo sshpubk.obj sshrand.obj sshrsa.obj sshsh512.obj + >> psftp.rsp
 	echo sshsha.obj sshzlib.obj tree234.obj version.obj + >> psftp.rsp
 	echo wildcard.obj windefs.obj winmisc.obj winnet.obj + >> psftp.rsp
-	echo winstore.obj x11fwd.obj >> psftp.rsp
+	echo winsftp.obj winstore.obj x11fwd.obj >> psftp.rsp
 	echo psftp.exe >> psftp.rsp
 	echo nul,cw32 import32, >> psftp.rsp
 	echo scp.res >> psftp.rsp
@@ -383,13 +383,13 @@ portfwd.obj: portfwd.c putty.h ssh.h puttyps.h network.h misc.h puttymem.h \
 pproxy.obj: pproxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
 		charset\charset.h
-printing.obj: printing.c putty.h puttyps.h network.h misc.h winstuff.h \
-		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
+printing.obj: printing.c putty.h winstuff.h puttyps.h network.h misc.h \
+		tree234.h winhelp.h mac\macstuff.h unix\unix.h puttymem.h \
 		charset\charset.h
 proxy.obj: proxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
 		charset\charset.h
-psftp.obj: psftp.c putty.h storage.h ssh.h sftp.h int64.h puttyps.h \
+psftp.obj: psftp.c putty.h psftp.h storage.h ssh.h sftp.h int64.h puttyps.h \
 		network.h misc.h puttymem.h winstuff.h mac\macstuff.h \
 		unix\unix.h tree234.h winhelp.h charset\charset.h
 pterm.obj: unix\pterm.c putty.h terminal.h puttyps.h network.h misc.h \
@@ -412,9 +412,9 @@ rlogin.obj: rlogin.c putty.h puttyps.h network.h misc.h winstuff.h \
 		charset\charset.h
 sbcs.obj: charset\sbcs.c charset\charset.h charset\internal.h
 sbcsdat.obj: charset\sbcsdat.c charset\charset.h charset\internal.h
-scp.obj: scp.c putty.h ssh.h sftp.h winstuff.h storage.h puttyps.h network.h \
-		misc.h puttymem.h int64.h tree234.h winhelp.h mac\macstuff.h \
-		unix\unix.h charset\charset.h
+scp.obj: scp.c putty.h psftp.h ssh.h sftp.h storage.h puttyps.h network.h \
+		misc.h puttymem.h int64.h winstuff.h mac\macstuff.h \
+		unix\unix.h tree234.h winhelp.h charset\charset.h
 scp.res: scp.rc scp.ico
 settings.obj: settings.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
@@ -505,6 +505,9 @@ uxputty.obj: unix\uxputty.c putty.h storage.h puttyps.h network.h misc.h \
 uxsel.obj: unix\uxsel.c putty.h tree234.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h winhelp.h \
 		charset\charset.h
+uxsftp.obj: unix\uxsftp.c putty.h psftp.h puttyps.h network.h misc.h \
+		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
+		winhelp.h charset\charset.h
 uxstore.obj: unix\uxstore.c putty.h storage.h tree234.h puttyps.h network.h \
 		misc.h winstuff.h mac\macstuff.h unix\unix.h puttymem.h \
 		winhelp.h charset\charset.h
@@ -543,6 +546,9 @@ winmisc.obj: winmisc.c putty.h winstuff.h puttyps.h network.h misc.h \
 winnet.obj: winnet.c putty.h network.h tree234.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h winhelp.h \
 		charset\charset.h
+winsftp.obj: winsftp.c putty.h psftp.h puttyps.h network.h misc.h winstuff.h \
+		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
+		charset\charset.h
 winstore.obj: winstore.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
 		winhelp.h charset\charset.h

+ 24 - 18
putty/MAKEFILE.CYG

@@ -135,34 +135,34 @@ pscp.exe: be_none.o cmdline.o console.o int64.o logging.o misc.o noise.o \
 		settings.o sftp.o ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o \
 		sshcrcda.o sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o \
 		sshrand.o sshrsa.o sshsh512.o sshsha.o sshzlib.o tree234.o \
-		version.o wildcard.o windefs.o winmisc.o winnet.o winstore.o \
-		x11fwd.o
+		version.o wildcard.o windefs.o winmisc.o winnet.o winsftp.o \
+		winstore.o x11fwd.o
 	$(CC) $(LDFLAGS) -o $@ be_none.o cmdline.o console.o int64.o \
 		logging.o misc.o noise.o pageantc.o portfwd.o pproxy.o \
 		proxy.o scp.o scp.res.o settings.o sftp.o ssh.o sshaes.o \
 		sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \
 		sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \
 		sshsha.o sshzlib.o tree234.o version.o wildcard.o windefs.o \
-		winmisc.o winnet.o winstore.o x11fwd.o -ladvapi32 -lcomctl32 \
-		-lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm \
-		-lwinspool -lwsock32
+		winmisc.o winnet.o winsftp.o winstore.o x11fwd.o -ladvapi32 \
+		-lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 \
+		-lwinmm -lwinspool -lwsock32
 
 psftp.exe: be_none.o cmdline.o console.o int64.o logging.o misc.o noise.o \
 		pageantc.o portfwd.o pproxy.o proxy.o psftp.o scp.res.o \
 		settings.o sftp.o ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o \
 		sshcrcda.o sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o \
 		sshrand.o sshrsa.o sshsh512.o sshsha.o sshzlib.o tree234.o \
-		version.o wildcard.o windefs.o winmisc.o winnet.o winstore.o \
-		x11fwd.o
+		version.o wildcard.o windefs.o winmisc.o winnet.o winsftp.o \
+		winstore.o x11fwd.o
 	$(CC) $(LDFLAGS) -o $@ be_none.o cmdline.o console.o int64.o \
 		logging.o misc.o noise.o pageantc.o portfwd.o pproxy.o \
 		proxy.o psftp.o scp.res.o settings.o sftp.o ssh.o sshaes.o \
 		sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \
 		sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \
 		sshsha.o sshzlib.o tree234.o version.o wildcard.o windefs.o \
-		winmisc.o winnet.o winstore.o x11fwd.o -ladvapi32 -lcomctl32 \
-		-lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm \
-		-lwinspool -lwsock32
+		winmisc.o winnet.o winsftp.o winstore.o x11fwd.o -ladvapi32 \
+		-lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 \
+		-lwinmm -lwinspool -lwsock32
 
 putty.exe: be_all.o cmdline.o config.o dialog.o ldisc.o ldiscucs.o logging.o \
 		misc.o noise.o pageantc.o portfwd.o pproxy.o printing.o \
@@ -326,15 +326,15 @@ portfwd.o: portfwd.c putty.h ssh.h puttyps.h network.h misc.h puttymem.h \
 pproxy.o: pproxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac/macstuff.h unix/unix.h puttymem.h tree234.h winhelp.h \
 		charset/charset.h
-printing.o: printing.c putty.h puttyps.h network.h misc.h winstuff.h \
-		mac/macstuff.h unix/unix.h puttymem.h tree234.h winhelp.h \
+printing.o: printing.c putty.h winstuff.h puttyps.h network.h misc.h \
+		tree234.h winhelp.h mac/macstuff.h unix/unix.h puttymem.h \
 		charset/charset.h
 proxy.o: proxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac/macstuff.h unix/unix.h puttymem.h tree234.h winhelp.h \
 		charset/charset.h
-psftp.o: psftp.c putty.h storage.h ssh.h sftp.h int64.h puttyps.h network.h \
-		misc.h puttymem.h winstuff.h mac/macstuff.h unix/unix.h \
-		tree234.h winhelp.h charset/charset.h
+psftp.o: psftp.c putty.h psftp.h storage.h ssh.h sftp.h int64.h puttyps.h \
+		network.h misc.h puttymem.h winstuff.h mac/macstuff.h \
+		unix/unix.h tree234.h winhelp.h charset/charset.h
 pterm.o: unix/pterm.c putty.h terminal.h puttyps.h network.h misc.h \
 		tree234.h winstuff.h mac/macstuff.h unix/unix.h puttymem.h \
 		winhelp.h charset/charset.h
@@ -355,9 +355,9 @@ rlogin.o: rlogin.c putty.h puttyps.h network.h misc.h winstuff.h \
 		charset/charset.h
 sbcs.o: charset/sbcs.c charset/charset.h charset/internal.h
 sbcsdat.o: charset/sbcsdat.c charset/charset.h charset/internal.h
-scp.o: scp.c putty.h ssh.h sftp.h winstuff.h storage.h puttyps.h network.h \
-		misc.h puttymem.h int64.h tree234.h winhelp.h mac/macstuff.h \
-		unix/unix.h charset/charset.h
+scp.o: scp.c putty.h psftp.h ssh.h sftp.h storage.h puttyps.h network.h \
+		misc.h puttymem.h int64.h winstuff.h mac/macstuff.h \
+		unix/unix.h tree234.h winhelp.h charset/charset.h
 scp.res.o: scp.rc scp.ico
 settings.o: settings.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac/macstuff.h unix/unix.h puttymem.h tree234.h \
@@ -448,6 +448,9 @@ uxputty.o: unix/uxputty.c putty.h storage.h puttyps.h network.h misc.h \
 uxsel.o: unix/uxsel.c putty.h tree234.h puttyps.h network.h misc.h \
 		winstuff.h mac/macstuff.h unix/unix.h puttymem.h winhelp.h \
 		charset/charset.h
+uxsftp.o: unix/uxsftp.c putty.h psftp.h puttyps.h network.h misc.h \
+		winstuff.h mac/macstuff.h unix/unix.h puttymem.h tree234.h \
+		winhelp.h charset/charset.h
 uxstore.o: unix/uxstore.c putty.h storage.h tree234.h puttyps.h network.h \
 		misc.h winstuff.h mac/macstuff.h unix/unix.h puttymem.h \
 		winhelp.h charset/charset.h
@@ -486,6 +489,9 @@ winmisc.o: winmisc.c putty.h winstuff.h puttyps.h network.h misc.h tree234.h \
 winnet.o: winnet.c putty.h network.h tree234.h puttyps.h misc.h winstuff.h \
 		mac/macstuff.h unix/unix.h puttymem.h winhelp.h \
 		charset/charset.h
+winsftp.o: winsftp.c putty.h psftp.h puttyps.h network.h misc.h winstuff.h \
+		mac/macstuff.h unix/unix.h puttymem.h tree234.h winhelp.h \
+		charset/charset.h
 winstore.o: winstore.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac/macstuff.h unix/unix.h puttymem.h tree234.h \
 		winhelp.h charset/charset.h

+ 18 - 11
putty/MAKEFILE.VC

@@ -115,7 +115,7 @@ pscp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj misc.obj \
 		sshdh.obj sshdss.obj sshmd5.obj sshpubk.obj sshrand.obj \
 		sshrsa.obj sshsh512.obj sshsha.obj sshzlib.obj tree234.obj \
 		version.obj wildcard.obj windefs.obj winmisc.obj winnet.obj \
-		winstore.obj x11fwd.obj pscp.rsp
+		winsftp.obj winstore.obj x11fwd.obj pscp.rsp
 	link $(LFLAGS) -out:pscp.exe -map:pscp.map @pscp.rsp
 
 psftp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj \
@@ -125,7 +125,7 @@ psftp.exe: be_none.obj cmdline.obj console.obj int64.obj logging.obj \
 		sshdes.obj sshdh.obj sshdss.obj sshmd5.obj sshpubk.obj \
 		sshrand.obj sshrsa.obj sshsh512.obj sshsha.obj sshzlib.obj \
 		tree234.obj version.obj wildcard.obj windefs.obj winmisc.obj \
-		winnet.obj winstore.obj x11fwd.obj psftp.rsp
+		winnet.obj winsftp.obj winstore.obj x11fwd.obj psftp.rsp
 	link $(LFLAGS) -out:psftp.exe -map:psftp.map @psftp.rsp
 
 putty.exe: be_all.obj cmdline.obj config.obj dialog.obj ldisc.obj \
@@ -197,7 +197,8 @@ pscp.rsp: $(MAKEFILE)
 	echo sshrsa.obj sshsh512.obj sshsha.obj sshzlib.obj >> pscp.rsp
 	echo tree234.obj user32.lib version.obj wildcard.obj >> pscp.rsp
 	echo windefs.obj winmisc.obj winmm.lib winnet.obj >> pscp.rsp
-	echo winspool.lib winstore.obj wsock32.lib x11fwd.obj >> pscp.rsp
+	echo winsftp.obj winspool.lib winstore.obj wsock32.lib >> pscp.rsp
+	echo x11fwd.obj >> pscp.rsp
 
 psftp.rsp: $(MAKEFILE)
 	echo /nologo /subsystem:console > psftp.rsp
@@ -212,8 +213,8 @@ psftp.rsp: $(MAKEFILE)
 	echo sshrand.obj sshrsa.obj sshsh512.obj sshsha.obj >> psftp.rsp
 	echo sshzlib.obj tree234.obj user32.lib version.obj >> psftp.rsp
 	echo wildcard.obj windefs.obj winmisc.obj winmm.lib >> psftp.rsp
-	echo winnet.obj winspool.lib winstore.obj wsock32.lib >> psftp.rsp
-	echo x11fwd.obj >> psftp.rsp
+	echo winnet.obj winsftp.obj winspool.lib winstore.obj >> psftp.rsp
+	echo wsock32.lib x11fwd.obj >> psftp.rsp
 
 putty.rsp: $(MAKEFILE)
 	echo /nologo /subsystem:windows > putty.rsp
@@ -376,13 +377,13 @@ portfwd.obj: portfwd.c putty.h ssh.h puttyps.h network.h misc.h puttymem.h \
 pproxy.obj: pproxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
 		charset\charset.h
-printing.obj: printing.c putty.h puttyps.h network.h misc.h winstuff.h \
-		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
+printing.obj: printing.c putty.h winstuff.h puttyps.h network.h misc.h \
+		tree234.h winhelp.h mac\macstuff.h unix\unix.h puttymem.h \
 		charset\charset.h
 proxy.obj: proxy.c putty.h network.h proxy.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
 		charset\charset.h
-psftp.obj: psftp.c putty.h storage.h ssh.h sftp.h int64.h puttyps.h \
+psftp.obj: psftp.c putty.h psftp.h storage.h ssh.h sftp.h int64.h puttyps.h \
 		network.h misc.h puttymem.h winstuff.h mac\macstuff.h \
 		unix\unix.h tree234.h winhelp.h charset\charset.h
 pterm.obj: unix\pterm.c putty.h terminal.h puttyps.h network.h misc.h \
@@ -405,9 +406,9 @@ rlogin.obj: rlogin.c putty.h puttyps.h network.h misc.h winstuff.h \
 		charset\charset.h
 sbcs.obj: charset\sbcs.c charset\charset.h charset\internal.h
 sbcsdat.obj: charset\sbcsdat.c charset\charset.h charset\internal.h
-scp.obj: scp.c putty.h ssh.h sftp.h winstuff.h storage.h puttyps.h network.h \
-		misc.h puttymem.h int64.h tree234.h winhelp.h mac\macstuff.h \
-		unix\unix.h charset\charset.h
+scp.obj: scp.c putty.h psftp.h ssh.h sftp.h storage.h puttyps.h network.h \
+		misc.h puttymem.h int64.h winstuff.h mac\macstuff.h \
+		unix\unix.h tree234.h winhelp.h charset\charset.h
 scp.res: scp.rc scp.ico
 settings.obj: settings.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
@@ -498,6 +499,9 @@ uxputty.obj: unix\uxputty.c putty.h storage.h puttyps.h network.h misc.h \
 uxsel.obj: unix\uxsel.c putty.h tree234.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h winhelp.h \
 		charset\charset.h
+uxsftp.obj: unix\uxsftp.c putty.h psftp.h puttyps.h network.h misc.h \
+		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
+		winhelp.h charset\charset.h
 uxstore.obj: unix\uxstore.c putty.h storage.h tree234.h puttyps.h network.h \
 		misc.h winstuff.h mac\macstuff.h unix\unix.h puttymem.h \
 		winhelp.h charset\charset.h
@@ -536,6 +540,9 @@ winmisc.obj: winmisc.c putty.h winstuff.h puttyps.h network.h misc.h \
 winnet.obj: winnet.c putty.h network.h tree234.h puttyps.h misc.h winstuff.h \
 		mac\macstuff.h unix\unix.h puttymem.h winhelp.h \
 		charset\charset.h
+winsftp.obj: winsftp.c putty.h psftp.h puttyps.h network.h misc.h winstuff.h \
+		mac\macstuff.h unix\unix.h puttymem.h tree234.h winhelp.h \
+		charset\charset.h
 winstore.obj: winstore.c putty.h storage.h puttyps.h network.h misc.h \
 		winstuff.h mac\macstuff.h unix\unix.h puttymem.h tree234.h \
 		winhelp.h charset\charset.h

+ 6 - 3
putty/MISC.C

@@ -11,9 +11,12 @@
 
 char *dupstr(const char *s)
 {
-    int len = strlen(s);
-    char *p = snewn(len + 1, char);
-    strcpy(p, s);
+    char *p = NULL;
+    if (s) {
+        int len = strlen(s);
+        p = snewn(len + 1, char);
+        strcpy(p, s);
+    }
     return p;
 }
 

+ 2 - 2
putty/MKFILES.PL

@@ -586,8 +586,8 @@ print
 "\trm -f *.o". (join "", map { " $_" } &progrealnames("XU")) . "\n".
 "\n",
 "install:\n",
-map("\t\$(INSTALL_PROGRAM) -m 755 $_ \$(bindir)/$_\n", &progrealnames("XU")),
-map("\t\$(INSTALL_DATA) -m 644 $_ \$(man1dir)/$_\n", &manpages("XU", "1")),
+map("\t\$(INSTALL_PROGRAM) -m 755 $_ \$(DESTDIR)\$(bindir)/$_\n", &progrealnames("XU")),
+map("\t\$(INSTALL_DATA) -m 644 $_ \$(DESTDIR)\$(man1dir)/$_\n", &manpages("XU", "1")),
 "\n",
 "install-strip:\n",
 "\t\$(MAKE) install INSTALL_PROGRAM=\"\$(INSTALL_PROGRAM) -s\"\n",

+ 5 - 0
putty/NETWORK.H

@@ -75,6 +75,8 @@ struct plug_function_table {
 };
 
 /* proxy indirection layer */
+/* NB, control of 'addr' is passed via new_connection, which takes
+ * responsibility for freeing it */
 Socket new_connection(SockAddr addr, char *hostname,
 		      int port, int privport,
 		      int oobinline, int nodelay, Plug plug,
@@ -85,6 +87,7 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
 		     const Config *cfg);
 
 /* platform-dependent callback from new_connection() */
+/* (same caveat about addr as new_connection()) */
 Socket platform_new_connection(SockAddr addr, char *hostname,
 			       int port, int privport,
 			       int oobinline, int nodelay, Plug plug,
@@ -105,6 +108,8 @@ int sk_addrtype(SockAddr addr);
 void sk_addrcopy(SockAddr addr, char *buf);
 void sk_addr_free(SockAddr addr);
 
+/* NB, control of 'addr' is passed via sk_new, which takes responsibility
+ * for freeing it, as for new_connection() */
 Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 	      int nodelay, Plug p);
 

+ 9 - 8
putty/PAGEANT.C

@@ -416,7 +416,7 @@ static void add_keyfile(Filename filename)
 	int i, nkeys, bloblen;
 
 	if (type == SSH_KEYTYPE_SSH1) {
-	    if (!rsakey_pubblob(&filename, &blob, &bloblen)) {
+	    if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
 		MessageBox(NULL, "Couldn't load private key.", APPNAME,
 			   MB_OK | MB_ICONERROR);
 		return;
@@ -424,7 +424,7 @@ static void add_keyfile(Filename filename)
 	    keylist = get_keylist1();
 	} else {
 	    unsigned char *blob2;
-	    blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen);
+	    blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
 	    if (!blob) {
 		MessageBox(NULL, "Couldn't load private key.", APPNAME,
 			   MB_OK | MB_ICONERROR);
@@ -498,9 +498,9 @@ static void add_keyfile(Filename filename)
 	} else
 	    *passphrase = '\0';
 	if (type == SSH_KEYTYPE_SSH1)
-	    ret = loadrsakey(&filename, rkey, passphrase);
+	    ret = loadrsakey(&filename, rkey, passphrase, NULL);
 	else {
-	    skey = ssh2_load_userkey(&filename, passphrase);
+	    skey = ssh2_load_userkey(&filename, passphrase, NULL);
 	    if (skey == SSH2_WRONG_PASSPHRASE)
 		ret = -1;
 	    else if (!skey)
@@ -1810,7 +1810,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     WNDCLASS wndclass;
     MSG msg;
-    OSVERSIONINFO osi;
     HMODULE advapi;
     char *command = NULL;
     int added_keys = 0;
@@ -1821,9 +1820,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * Determine whether we're an NT system (should have security
      * APIs) or a non-NT system (don't do security).
      */
-    memset(&osi, 0, sizeof(OSVERSIONINFO));
-    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-    if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    if (!init_winver())
+    {
+	modalfatalbox("Windows refuses to report a version");
+    }
+    if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
 	has_security = TRUE;
     } else
 	has_security = FALSE;

+ 11 - 0
putty/PLINK.C

@@ -240,6 +240,7 @@ static void usage(void)
     printf("  -1 -2     force use of particular protocol version\n");
     printf("  -C        enable compression\n");
     printf("  -i key    private key file for authentication\n");
+    printf("  -s        remote command is an SSH subsystem (SSH-2 only)\n");
     exit(1);
 }
 
@@ -279,6 +280,7 @@ int main(int argc, char **argv)
     int connopen;
     int exitcode;
     int errors;
+    int use_subsystem = 0;
 
     ssh_get_line = console_get_line;
 
@@ -331,6 +333,9 @@ int main(int argc, char **argv)
 		continue;
 	    } else if (!strcmp(p, "-batch")) {
 		console_batch_mode = 1;
+	    } else if (!strcmp(p, "-s")) {
+		/* Save status to write to cfg later. */
+		use_subsystem = 1;
 	    } else {
 		fprintf(stderr, "plink: unknown option \"%s\"\n", p);
 		errors = 1;
@@ -488,6 +493,12 @@ int main(int argc, char **argv)
      */
     cmdline_run_saved(&cfg);
 
+    /*
+     * Apply subsystem status.
+     */
+    if (use_subsystem)
+	cfg.ssh_subsys = TRUE;
+
     /*
      * Trim a colon suffix off the hostname if it's there.
      */

+ 3 - 2
putty/PORTFWD.C

@@ -350,8 +350,10 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
      * Try to find host.
      */
     addr = name_lookup(hostname, port, &dummy_realhost, cfg);
-    if ((err = sk_addr_error(addr)) != NULL)
+    if ((err = sk_addr_error(addr)) != NULL) {
+	sk_addr_free(addr);
 	return err;
+    }
 
     /*
      * Open socket.
@@ -373,7 +375,6 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
     }
 
     sk_set_private_ptr(*s, pr);
-    sk_addr_free(addr);
     return NULL;
 }
 

+ 50 - 26
putty/PRINTING.C

@@ -4,35 +4,22 @@
 
 #include <windows.h>
 #include "putty.h"
-
-/*
- * Boggle. Flipping between the two branches of this #if appears to
- * make all the difference as to whether network printers show up
- * under PRINTER_ENUM_CONNECTIONS on NT 4. I don't pretend to
- * understand this...
- */
-#if 0
-#define ENUM_LEVEL 5
-#define ENUM_PTR LPPRINTER_INFO_5
-#define ENUM_TYPE PRINTER_INFO_5
-#define ENUM_MEMBER pPrinterName
-#else
-#define ENUM_LEVEL 1
-#define ENUM_PTR LPPRINTER_INFO_1
-#define ENUM_TYPE PRINTER_INFO_1
-#define ENUM_MEMBER pName
-#endif
+#include "winstuff.h"
 
 struct printer_enum_tag {
     int nprinters;
-    ENUM_PTR info;
+    DWORD enum_level;
+    union {
+	LPPRINTER_INFO_4 i4;
+	LPPRINTER_INFO_5 i5;
+    } info;
 };
 
 struct printer_job_tag {
     HANDLE hprinter;
 };
 
-static char *printer_add_enum(int param, char *buffer,
+static char *printer_add_enum(int param, DWORD level, char *buffer,
                               int offset, int *nprinters_ptr)
 {
     DWORD needed, nprinters;
@@ -44,7 +31,7 @@ static char *printer_add_enum(int param, char *buffer,
      * we'll need for the output. Discard the return value since it
      * will almost certainly be a failure due to lack of space.
      */
-    EnumPrinters(param, NULL, ENUM_LEVEL, buffer+offset, 512,
+    EnumPrinters(param, NULL, level, buffer+offset, 512,
 		 &needed, &nprinters);
 
     if (needed < 512)
@@ -52,7 +39,7 @@ static char *printer_add_enum(int param, char *buffer,
 
     buffer = sresize(buffer, offset+needed, char);
 
-    if (EnumPrinters(param, NULL, ENUM_LEVEL, buffer+offset,
+    if (EnumPrinters(param, NULL, level, buffer+offset,
                      needed, &needed, &nprinters) == 0)
         return NULL;
 
@@ -69,14 +56,37 @@ printer_enum *printer_start_enum(int *nprinters_ptr)
     *nprinters_ptr = 0;		       /* default return value */
     buffer = snewn(512, char);
 
+    /*
+     * Determine what enumeration level to use.
+     * When enumerating printers, we need to use PRINTER_INFO_4 on
+     * NT-class systems to avoid Windows looking too hard for them and
+     * slowing things down; and we need to avoid PRINTER_INFO_5 as
+     * we've seen network printers not show up.
+     * On 9x-class systems, PRINTER_INFO_4 isn't available and
+     * PRINTER_INFO_5 is recommended.
+     * Bletch.
+     */
+    if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+	ret->enum_level = 5;
+    } else {
+	ret->enum_level = 4;
+    }
+
     retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
-			      buffer, 0, nprinters_ptr);
+			      ret->enum_level, buffer, 0, nprinters_ptr);
     if (!retval)
         goto error;
     else
         buffer = retval;
 
-    ret->info = (ENUM_PTR)buffer;
+    switch (ret->enum_level) {
+      case 4:
+	ret->info.i4 = (LPPRINTER_INFO_4)buffer;
+	break;
+      case 5:
+	ret->info.i5 = (LPPRINTER_INFO_5)buffer;
+	break;
+    }
     ret->nprinters = *nprinters_ptr;
     
     return ret;
@@ -94,14 +104,28 @@ char *printer_get_name(printer_enum *pe, int i)
 	return NULL;
     if (i < 0 || i >= pe->nprinters)
 	return NULL;
-    return pe->info[i].ENUM_MEMBER;
+    switch (pe->enum_level) {
+      case 4:
+	return pe->info.i4[i].pPrinterName;
+      case 5:
+	return pe->info.i5[i].pPrinterName;
+      default:
+	return NULL;
+    }
 }
 
 void printer_finish_enum(printer_enum *pe)
 {
     if (!pe)
 	return;
-    sfree(pe->info);
+    switch (pe->enum_level) {
+      case 4:
+	sfree(pe->info.i4);
+	break;
+      case 5:
+	sfree(pe->info.i5);
+	break;
+    }
     sfree(pe);
 }
 

+ 2 - 3
putty/PROXY.C

@@ -90,6 +90,7 @@ static void sk_proxy_close (Socket s)
     Proxy_Socket ps = (Proxy_Socket) s;
 
     sk_close(ps->sub_socket);
+    sk_addr_free(ps->remote_addr);
     sfree(ps);
 }
 
@@ -391,7 +392,7 @@ Socket new_connection(SockAddr addr, char *hostname,
 	ret->fn = &socket_fn_table;
 	ret->cfg = *cfg;	       /* STRUCTURE COPY */
 	ret->plug = plug;
-	ret->remote_addr = addr;
+	ret->remote_addr = addr;       /* will need to be freed on close */
 	ret->remote_port = port;
 
 	ret->error = NULL;
@@ -443,8 +444,6 @@ Socket new_connection(SockAddr addr, char *hostname,
 	if (sk_socket_error(ret->sub_socket) != NULL)
 	    return (Socket) ret;
 
-	sk_addr_free(proxy_addr);
-
 	/* start the proxy negotiation process... */
 	sk_set_frozen(ret->sub_socket, 0);
 	ret->negotiate(ret, PROXY_CHANGE_NEW);

+ 31 - 96
putty/PSFTP.C

@@ -2,8 +2,6 @@
  * psftp.c: front end for PSFTP.
  */
 
-#include <windows.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -12,6 +10,7 @@
 
 #define PUTTY_DO_GLOBALS
 #include "putty.h"
+#include "psftp.h"
 #include "storage.h"
 #include "ssh.h"
 #include "sftp.h"
@@ -895,20 +894,20 @@ int sftp_cmd_chmod(struct sftp_command *cmd)
 		  default:
 		    printf("chmod: file mode '%.*s' contains unrecognised"
 			   " user/group/other specifier '%c'\n",
-			   strcspn(modebegin, ","), modebegin, *mode);
+			   (int)strcspn(modebegin, ","), modebegin, *mode);
 		    return 0;
 		}
 		mode++;
 	    }
 	    if (!*mode || *mode == ',') {
 		printf("chmod: file mode '%.*s' is incomplete\n",
-		       strcspn(modebegin, ","), modebegin);
+		       (int)strcspn(modebegin, ","), modebegin);
 		return 0;
 	    }
 	    action = *mode++;
 	    if (!*mode || *mode == ',') {
 		printf("chmod: file mode '%.*s' is incomplete\n",
-		       strcspn(modebegin, ","), modebegin);
+		       (int)strcspn(modebegin, ","), modebegin);
 		return 0;
 	    }
 	    perms = 0;
@@ -923,7 +922,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd)
 			(subset & 06777) != 02070) {
 			printf("chmod: file mode '%.*s': set[ug]id bit should"
 			       " be used with exactly one of u or g only\n",
-			       strcspn(modebegin, ","), modebegin);
+			       (int)strcspn(modebegin, ","), modebegin);
 			return 0;
 		    }
 		    perms |= 06000;
@@ -931,7 +930,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd)
 		  default:
 		    printf("chmod: file mode '%.*s' contains unrecognised"
 			   " permission specifier '%c'\n",
-			   strcspn(modebegin, ","), modebegin, *mode);
+			   (int)strcspn(modebegin, ","), modebegin, *mode);
 		    return 0;
 		}
 		mode++;
@@ -939,7 +938,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd)
 	    if (!(subset & 06777) && (perms &~ subset)) {
 		printf("chmod: file mode '%.*s' contains no user/group/other"
 		       " specifier and permissions other than 't' \n",
-		       strcspn(modebegin, ","), modebegin);
+		       (int)strcspn(modebegin, ","), modebegin);
 		return 0;
 	    }
 	    perms &= subset;
@@ -1024,34 +1023,21 @@ static int sftp_cmd_open(struct sftp_command *cmd)
 
 static int sftp_cmd_lcd(struct sftp_command *cmd)
 {
-    char *currdir;
-    int len;
+    char *currdir, *errmsg;
 
     if (cmd->nwords < 2) {
 	printf("lcd: expects a local directory name\n");
 	return 0;
     }
 
-    if (!SetCurrentDirectory(cmd->words[1])) {
-	LPVOID message;
-	int i;
-	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-		      FORMAT_MESSAGE_FROM_SYSTEM |
-		      FORMAT_MESSAGE_IGNORE_INSERTS,
-		      NULL, GetLastError(),
-		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		      (LPTSTR)&message, 0, NULL);
-	i = strcspn((char *)message, "\n");
-	printf("lcd: unable to change directory: %.*s\n", i, (LPCTSTR)message);
-	LocalFree(message);
+    errmsg = psftp_lcd(cmd->words[1]);
+    if (errmsg) {
+	printf("lcd: unable to change directory: %s\n", errmsg);
+	sfree(errmsg);
 	return 0;
     }
 
-    currdir = snewn(256, char);
-    len = GetCurrentDirectory(256, currdir);
-    if (len > 256)
-	currdir = sresize(currdir, len, char);
-    GetCurrentDirectory(len, currdir);
+    currdir = psftp_getcwd();
     printf("New local directory is %s\n", currdir);
     sfree(currdir);
 
@@ -1061,13 +1047,8 @@ static int sftp_cmd_lcd(struct sftp_command *cmd)
 static int sftp_cmd_lpwd(struct sftp_command *cmd)
 {
     char *currdir;
-    int len;
 
-    currdir = snewn(256, char);
-    len = GetCurrentDirectory(256, currdir);
-    if (len > 256)
-	currdir = sresize(currdir, len, char);
-    GetCurrentDirectory(len, currdir);
+    currdir = psftp_getcwd();
     printf("Current local directory is %s\n", currdir);
     sfree(currdir);
 
@@ -1108,9 +1089,10 @@ static struct sftp_cmd_lookup {
      * in ASCII order.
      */
     {
-	"!", TRUE, "run a local Windows command",
+	"!", TRUE, "run a local command",
 	    "<command>\n"
-	    "  Runs a local Windows command. For example, \"!del myfile\".\n",
+	    /* FIXME: this example is crap for non-Windows. */
+	    "  Runs a local command. For example, \"!del myfile\".\n",
 	    sftp_cmd_pling
     },
     {
@@ -1620,20 +1602,6 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
     assert(len == 0);
 }
 
-/*
- * Be told what socket we're supposed to be using.
- */
-static SOCKET sftp_ssh_socket;
-char *do_select(SOCKET skt, int startup)
-{
-    if (startup)
-	sftp_ssh_socket = skt;
-    else
-	sftp_ssh_socket = INVALID_SOCKET;
-    return NULL;
-}
-extern int select_result(WPARAM, LPARAM);
-
 /*
  * In psftp, all agent requests should be synchronous, so this is a
  * never-called stub.
@@ -1729,13 +1697,8 @@ int sftp_recvdata(char *buf, int len)
     }
 
     while (outlen > 0) {
-	fd_set readfds;
-
-	FD_ZERO(&readfds);
-	FD_SET(sftp_ssh_socket, &readfds);
-	if (select(1, &readfds, NULL, NULL, NULL) < 0)
+	if (ssh_sftp_loop_iteration() < 0)
 	    return 0;		       /* doom */
-	select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
     }
 
     return 1;
@@ -1746,42 +1709,6 @@ int sftp_senddata(char *buf, int len)
     return 1;
 }
 
-/*
- * Loop through the ssh connection and authentication process.
- */
-static void ssh_sftp_init(void)
-{
-    if (sftp_ssh_socket == INVALID_SOCKET)
-	return;
-    while (!back->sendok(backhandle)) {
-	fd_set readfds;
-	FD_ZERO(&readfds);
-	FD_SET(sftp_ssh_socket, &readfds);
-	if (select(1, &readfds, NULL, NULL, NULL) < 0)
-	    return;		       /* doom */
-	select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
-    }
-}
-
-/*
- *  Initialize the Win$ock driver.
- */
-static void init_winsock(void)
-{
-    WORD winsock_ver;
-    WSADATA wsadata;
-
-    winsock_ver = MAKEWORD(1, 1);
-    if (WSAStartup(winsock_ver, &wsadata)) {
-	fprintf(stderr, "Unable to initialise WinSock");
-	cleanup_exit(1);
-    }
-    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
-	fprintf(stderr, "WinSock version is incompatible with 1.1");
-	cleanup_exit(1);
-    }
-}
-
 /*
  *  Short description of parameters.
  */
@@ -1813,6 +1740,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
 {
     char *host, *realhost;
     const char *err;
+    void *logctx;
 
     /* Separate host and username */
     host = userhost;
@@ -1963,7 +1891,12 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
     logctx = log_init(NULL, &cfg);
     back->provide_logctx(backhandle, logctx);
     console_provide_logctx(logctx);
-    ssh_sftp_init();
+    while (!back->sendok(backhandle)) {
+	if (ssh_sftp_loop_iteration() < 0) {
+	    fprintf(stderr, "ssh_init: error during SSH connection setup\n");
+	    return 1;
+	}
+    }
     if (verbose && realhost != NULL)
 	printf("Connected to %s\n", realhost);
     return 0;
@@ -1983,7 +1916,7 @@ void cmdline_error(char *p, ...)
 /*
  * Main program. Parse arguments etc.
  */
-int main(int argc, char *argv[])
+int psftp_main(int argc, char *argv[])
 {
     int i;
     int portnumber = 0;
@@ -1993,10 +1926,13 @@ int main(int argc, char *argv[])
     char *batchfile = NULL;
     int errors = 0;
 
-    flags = FLAG_STDERR | FLAG_INTERACTIVE | FLAG_SYNCAGENT;
+    flags = FLAG_STDERR | FLAG_INTERACTIVE
+#ifdef FLAG_SYNCAGENT
+	| FLAG_SYNCAGENT
+#endif
+	;
     cmdline_tooltype = TOOLTYPE_FILETRANSFER;
     ssh_get_line = &console_get_line;
-    init_winsock();
     sk_init();
 
     userhost = user = NULL;
@@ -2064,7 +2000,6 @@ int main(int argc, char *argv[])
 	back->special(backhandle, TS_EOF);
 	sftp_recvdata(&ch, 1);
     }
-    WSACleanup();
     random_save_seed();
 
     return 0;

+ 150 - 0
putty/PSFTP.H

@@ -0,0 +1,150 @@
+/*
+ * psftp.h: interface between psftp.c / scp.c and each
+ * platform-specific SFTP module.
+ */
+
+#ifndef PUTTY_PSFTP_H
+#define PUTTY_PSFTP_H
+
+/*
+ * psftp_getcwd returns the local current directory. The returned
+ * string must be freed by the caller.
+ */
+char *psftp_getcwd(void);
+
+/*
+ * psftp_lcd changes the local current directory. The return value
+ * is NULL on success, or else an error message which must be freed
+ * by the caller.
+ */
+char *psftp_lcd(char *newdir);
+
+/*
+ * Retrieve file times on a local file. Must return two unsigned
+ * longs in POSIX time_t format.
+ */
+void get_file_times(char *filename, unsigned long *mtime,
+		    unsigned long *atime);
+
+/*
+ * One iteration of the PSFTP event loop: wait for network data and
+ * process it, once.
+ */
+int ssh_sftp_loop_iteration(void);
+
+/*
+ * The main program in psftp.c. Called from main() in the platform-
+ * specific code, after doing any platform-specific initialisation.
+ */
+int psftp_main(int argc, char *argv[]);
+
+/*
+ * These functions are used by PSCP to transmit progress updates
+ * and error information to a GUI window managing it. This will
+ * probably only ever be supported on Windows, so these functions
+ * can safely be stubs on all other platforms.
+ */
+void gui_update_stats(char *name, unsigned long size,
+		      int percentage, unsigned long elapsed,
+		      unsigned long done, unsigned long eta,
+		      unsigned long ratebs);
+void gui_send_errcount(int list, int errs);
+void gui_send_char(int is_stderr, int c);
+void gui_enable(char *arg);
+
+/*
+ * It's likely that a given platform's implementation of file
+ * transfer utilities is going to want to do things with them that
+ * aren't present in stdio. Hence we supply an alternative
+ * abstraction for file access functions.
+ * 
+ * This abstraction tells you the size and access times when you
+ * open an existing file (platforms may choose the meaning of the
+ * file times if it's not clear; whatever they choose will be what
+ * PSCP sends to the server as mtime and atime), and lets you set
+ * the times when saving a new file.
+ * 
+ * On the other hand, the abstraction is pretty simple: it supports
+ * only opening a file and reading it, or creating a file and
+ * writing it. (FIXME: to use this in PSFTP it will also need to
+ * support seeking to a starting point for restarted transfers.)
+ * None of this read-and-write, seeking-back-and-forth stuff.
+ */
+typedef struct RFile RFile;
+typedef struct WFile WFile;
+/* Output params size, mtime and atime can all be NULL if desired */
+RFile *open_existing_file(char *name, unsigned long *size,
+			  unsigned long *mtime, unsigned long *atime);
+/* Returns <0 on error, 0 on eof, or number of bytes read, as usual */
+int read_from_file(RFile *f, void *buffer, int length);
+/* Closes and frees the RFile */
+void close_rfile(RFile *f);
+WFile *open_new_file(char *name);
+/* Returns <0 on error, 0 on eof, or number of bytes written, as usual */
+int write_to_file(WFile *f, void *buffer, int length);
+void set_file_times(WFile *f, unsigned long mtime, unsigned long atime);
+/* Closes and frees the WFile */
+void close_wfile(WFile *f);
+
+/*
+ * Determine the type of a file: nonexistent, file, directory or
+ * weird. `weird' covers anything else - named pipes, Unix sockets,
+ * device files, fish, badgers, you name it. Things marked `weird'
+ * will be skipped over in recursive file transfers, so the only
+ * real reason for not lumping them in with `nonexistent' is that
+ * it allows a slightly more sane error message.
+ */
+enum {
+    FILE_TYPE_NONEXISTENT, FILE_TYPE_FILE, FILE_TYPE_DIRECTORY, FILE_TYPE_WEIRD
+};
+int file_type(char *name);
+
+/*
+ * Read all the file names out of a directory.
+ */
+typedef struct DirHandle DirHandle;
+DirHandle *open_directory(char *name);
+/* The string returned from this will need freeing if not NULL */
+char *read_filename(DirHandle *dir);
+void close_directory(DirHandle *dir);
+
+/*
+ * Test a filespec to see whether it's a local wildcard or not.
+ * Return values:
+ * 
+ *  - WCTYPE_WILDCARD (this is a wildcard).
+ *  - WCTYPE_FILENAME (this is a single file name).
+ *  - WCTYPE_NONEXISTENT (whichever it was, nothing of that name exists).
+ * 
+ * Some platforms may choose not to support local wildcards when
+ * they come from the command line; in this case they simply never
+ * return WCTYPE_WILDCARD, but still test the file's existence.
+ * (However, all platforms will probably want to support wildcards
+ * inside the PSFTP CLI.)
+ */
+enum {
+    WCTYPE_NONEXISTENT, WCTYPE_FILENAME, WCTYPE_WILDCARD
+};
+int test_wildcard(char *name, int cmdline);
+
+/*
+ * Actually return matching file names for a local wildcard.
+ */
+typedef struct WildcardMatcher WildcardMatcher;
+WildcardMatcher *begin_wildcard_matching(char *name);
+/* The string returned from this will need freeing if not NULL */
+char *wildcard_get_filename(WildcardMatcher *dir);
+void finish_wildcard_matching(WildcardMatcher *dir);
+
+/*
+ * Create a directory. Returns 0 on error, !=0 on success.
+ */
+int create_directory(char *name);
+
+/*
+ * Concatenate a directory name and a file name. The way this is
+ * done will depend on the OS.
+ */
+char *dir_file_cat(char *dir, char *file);
+
+#endif /* PUTTY_PSFTP_H */

+ 2 - 2
putty/PUTTYGEN.C

@@ -681,14 +681,14 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
 	if (type == SSH_KEYTYPE_SSH1) {
 	    if (realtype == type)
 		ret = loadrsakey(&filename, &newkey1,
-				 passphrase);
+				 passphrase, NULL);
 	    else
 		ret = import_ssh1(&filename, realtype,
 				  &newkey1, passphrase);
 	} else {
 	    if (realtype == type)
 		newkey2 = ssh2_load_userkey(&filename,
-					    passphrase);
+					    passphrase, NULL);
 	    else
 		newkey2 = import_ssh2(&filename, realtype,
 				      passphrase);

+ 3 - 3
putty/RAW.C

@@ -97,8 +97,10 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
 	sfree(buf);
     }
     addr = name_lookup(host, port, realhost, cfg);
-    if ((err = sk_addr_error(addr)) != NULL)
+    if ((err = sk_addr_error(addr)) != NULL) {
+	sk_addr_free(addr);
 	return err;
+    }
 
     if (port < 0)
 	port = 23;		       /* default telnet port */
@@ -118,8 +120,6 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
     if ((err = sk_socket_error(raw->s)) != NULL)
 	return err;
 
-    sk_addr_free(addr);
-
     return NULL;
 }
 

+ 6 - 2
putty/RECIPE

@@ -139,8 +139,9 @@ putty    : [G] GUITERM NONSSH WINSSH be_all WINMISC win_res.res LIBS1
 puttytel : [G] GUITERM NONSSH be_nossh WINMISC win_res.res LIBS1
 plink    : [C] plink console NONSSH WINSSH be_all logging WINMISC
          + plink.res LIBS2
-pscp     : [C] scp console WINSSH be_none SFTP wildcard WINMISC scp.res LIBS1
-psftp    : [C] psftp console WINSSH be_none SFTP WINMISC scp.res LIBS1
+pscp     : [C] scp winsftp console WINSSH be_none SFTP wildcard WINMISC
+         + scp.res LIBS1
+psftp    : [C] psftp winsftp console WINSSH be_none SFTP WINMISC scp.res LIBS1
 
 pageant  : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234
          + misc sshaes sshsha pageantc sshdss sshsh512 winutils winmisc
@@ -159,6 +160,9 @@ puttytel : [X] UXTERM uxmisc misc ldisc settings pty uxsel be_nossh uxstore
 
 plink    : [U] uxplink uxcons NONSSH UXSSH be_all logging UXMISC signal ux_x11
 
+pscp     : [U] scp uxsftp uxcons UXSSH be_none SFTP wildcard UXMISC
+psftp    : [U] psftp uxsftp uxcons UXSSH be_none SFTP UXMISC
+
 PuTTY    : [M] terminal wcwidth ldiscucs logging be_all mac macdlg macevlog
          + macterm macucs mac_res.rsrc testback NONSSH MACSSH MACMISC CHARSET
          + stricmp vsnprint dialog config macctrls

+ 3 - 3
putty/RLOGIN.C

@@ -130,8 +130,10 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
 	sfree(buf);
     }
     addr = name_lookup(host, port, realhost, cfg);
-    if ((err = sk_addr_error(addr)) != NULL)
+    if ((err = sk_addr_error(addr)) != NULL) {
+	sk_addr_free(addr);
 	return err;
+    }
 
     if (port < 0)
 	port = 513;		       /* default rlogin port */
@@ -151,8 +153,6 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
     if ((err = sk_socket_error(rlogin->s)) != NULL)
 	return err;
 
-    sk_addr_free(addr);
-
     /*
      * Send local username, remote username, terminal/speed
      */

+ 102 - 341
putty/SCP.C

@@ -12,14 +12,6 @@
  * to licensing issues.)
  */
 
-#include <windows.h>
-#ifndef AUTO_WINSOCK
-#ifdef WINSOCK_TWO
-#include <winsock2.h>
-#else
-#include <winsock.h>
-#endif
-#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -29,54 +21,11 @@
 
 #define PUTTY_DO_GLOBALS
 #include "putty.h"
+#include "psftp.h"
 #include "ssh.h"
 #include "sftp.h"
-#include "winstuff.h"
 #include "storage.h"
 
-#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
-	((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
-#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
-	((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
-
-/* GUI Adaptation - Sept 2000 */
-
-/* This is just a base value from which the main message numbers are
- * derived. */
-#define   WM_APP_BASE		0x8000
-
-/* These two pass a single character value in wParam. They represent
- * the visible output from PSCP. */
-#define   WM_STD_OUT_CHAR	( WM_APP_BASE+400 )
-#define   WM_STD_ERR_CHAR	( WM_APP_BASE+401 )
-
-/* These pass a transfer status update. WM_STATS_CHAR passes a single
- * character in wParam, and is called repeatedly to pass the name of
- * the file, terminated with "\n". WM_STATS_SIZE passes the size of
- * the file being transferred in wParam. WM_STATS_ELAPSED is called
- * to pass the elapsed time (in seconds) in wParam, and
- * WM_STATS_PERCENT passes the percentage of the transfer which is
- * complete, also in wParam. */
-#define   WM_STATS_CHAR		( WM_APP_BASE+402 )
-#define   WM_STATS_SIZE 	( WM_APP_BASE+403 )
-#define   WM_STATS_PERCENT	( WM_APP_BASE+404 )
-#define   WM_STATS_ELAPSED	( WM_APP_BASE+405 )
-
-/* These are used at the end of a run to pass an error code in
- * wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT
- * is used after a copy, and WM_LS_RET_ERR_CNT is used after a file
- * list operation. */
-#define   WM_RET_ERR_CNT	( WM_APP_BASE+406 )
-#define   WM_LS_RET_ERR_CNT	( WM_APP_BASE+407 )
-
-/* More transfer status update messages. WM_STATS_DONE passes the
- * number of bytes sent so far in wParam. WM_STATS_ETA passes the
- * estimated time to completion (in seconds). WM_STATS_RATEBS passes
- * the average transfer rate (in bytes per second). */
-#define   WM_STATS_DONE		( WM_APP_BASE+408 )
-#define   WM_STATS_ETA		( WM_APP_BASE+409 )
-#define   WM_STATS_RATEBS	( WM_APP_BASE+410 )
-
 static int list = 0;
 static int verbose = 0;
 static int recursive = 0;
@@ -86,17 +35,7 @@ static int statistics = 1;
 static int prev_stats_len = 0;
 static int scp_unsafe_mode = 0;
 static int errs = 0;
-/* GUI Adaptation - Sept 2000 */
-#define NAME_STR_MAX 2048
-static char statname[NAME_STR_MAX + 1];
-static unsigned long statsize = 0;
-static unsigned long statdone = 0;
-static unsigned long stateta = 0;
-static unsigned long statratebs = 0;
-static int statperct = 0;
-static unsigned long statelapsed = 0;
 static int gui_mode = 0;
-static char *gui_hwnd = NULL;
 static int using_sftp = 0;
 
 static Backend *back;
@@ -106,14 +45,6 @@ static Config cfg;
 static void source(char *src);
 static void rsource(char *src);
 static void sink(char *targ, char *src);
-/* GUI Adaptation - Sept 2000 */
-static void tell_char(FILE * stream, char c);
-static void tell_str(FILE * stream, char *str);
-static void tell_user(FILE * stream, char *fmt, ...);
-static void gui_update_stats(char *name, unsigned long size,
-			     int percentage, unsigned long elapsed, 
-			     unsigned long done, unsigned long eta,
-			     unsigned long ratebs);
 
 /*
  * The maximum amount of queued data we accept before we stop and
@@ -132,23 +63,12 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
     assert(len == 0);
 }
 
-/* GUI Adaptation - Sept 2000 */
-static void send_msg(HWND h, UINT message, WPARAM wParam)
-{
-    while (!PostMessage(h, message, wParam, 0))
-	SleepEx(1000, TRUE);
-}
-
 static void tell_char(FILE * stream, char c)
 {
     if (!gui_mode)
 	fputc(c, stream);
-    else {
-	unsigned int msg_id = WM_STD_OUT_CHAR;
-	if (stream == stderr)
-	    msg_id = WM_STD_ERR_CHAR;
-	send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
-    }
+    else
+	gui_send_char(stream == stderr, c);
 }
 
 static void tell_str(FILE * stream, char *str)
@@ -172,48 +92,6 @@ static void tell_user(FILE * stream, char *fmt, ...)
     sfree(str2);
 }
 
-static void gui_update_stats(char *name, unsigned long size,
-			     int percentage, unsigned long elapsed,
-			     unsigned long done, unsigned long eta,
-			     unsigned long ratebs)
-{
-    unsigned int i;
-
-    if (strcmp(name, statname) != 0) {
-	for (i = 0; i < strlen(name); ++i)
-	    send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
-		     (WPARAM) name[i]);
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
-	strcpy(statname, name);
-    }
-    if (statsize != size) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
-	statsize = size;
-    }
-    if (statdone != done) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_DONE, (WPARAM) done);
-	statdone = done;
-    }
-    if (stateta != eta) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_ETA, (WPARAM) eta);
-	stateta = eta;
-    }
-    if (statratebs != ratebs) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_RATEBS, (WPARAM) ratebs);
-	statratebs = ratebs;
-    }
-    if (statelapsed != elapsed) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
-		 (WPARAM) elapsed);
-	statelapsed = elapsed;
-    }
-    if (statperct != percentage) {
-	send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
-		 (WPARAM) percentage);
-	statperct = percentage;
-    }
-}
-
 /*
  *  Print an error message and perform a fatal exit.
  */
@@ -230,14 +108,8 @@ void fatalbox(char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode) {
-	unsigned int msg_id = WM_RET_ERR_CNT;
-	if (list)
-	    msg_id = WM_LS_RET_ERR_CNT;
-	while (!PostMessage
-	       ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
-		0 /*lParam */ ))SleepEx(1000, TRUE);
-    }
+    if (gui_mode)
+	gui_send_errcount(list, errs);
 
     cleanup_exit(1);
 }
@@ -254,14 +126,8 @@ void modalfatalbox(char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode) {
-	unsigned int msg_id = WM_RET_ERR_CNT;
-	if (list)
-	    msg_id = WM_LS_RET_ERR_CNT;
-	while (!PostMessage
-	       ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
-		0 /*lParam */ ))SleepEx(1000, TRUE);
-    }
+    if (gui_mode)
+	gui_send_errcount(list, errs);
 
     cleanup_exit(1);
 }
@@ -278,32 +144,12 @@ void connection_fatal(void *frontend, char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode) {
-	unsigned int msg_id = WM_RET_ERR_CNT;
-	if (list)
-	    msg_id = WM_LS_RET_ERR_CNT;
-	while (!PostMessage
-	       ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
-		0 /*lParam */ ))SleepEx(1000, TRUE);
-    }
+    if (gui_mode)
+	gui_send_errcount(list, errs);
 
     cleanup_exit(1);
 }
 
-/*
- * Be told what socket we're supposed to be using.
- */
-static SOCKET scp_ssh_socket;
-char *do_select(SOCKET skt, int startup)
-{
-    if (startup)
-	scp_ssh_socket = skt;
-    else
-	scp_ssh_socket = INVALID_SOCKET;
-    return NULL;
-}
-extern int select_result(WPARAM, LPARAM);
-
 /*
  * In pscp, all agent requests should be synchronous, so this is a
  * never-called stub.
@@ -373,17 +219,6 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
 
     return 0;
 }
-static int scp_process_network_event(void)
-{
-    fd_set readfds;
-
-    FD_ZERO(&readfds);
-    FD_SET(scp_ssh_socket, &readfds);
-    if (select(1, &readfds, NULL, NULL, NULL) < 0)
-	return 0;		       /* doom */
-    select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
-    return 1;
-}
 static int ssh_scp_recv(unsigned char *buf, int len)
 {
     outptr = buf;
@@ -412,7 +247,7 @@ static int ssh_scp_recv(unsigned char *buf, int len)
     }
 
     while (outlen > 0) {
-	if (!scp_process_network_event())
+	if (ssh_sftp_loop_iteration() < 0)
 	    return 0;		       /* doom */
     }
 
@@ -424,15 +259,9 @@ static int ssh_scp_recv(unsigned char *buf, int len)
  */
 static void ssh_scp_init(void)
 {
-    if (scp_ssh_socket == INVALID_SOCKET)
-	return;
     while (!back->sendok(backhandle)) {
-	fd_set readfds;
-	FD_ZERO(&readfds);
-	FD_SET(scp_ssh_socket, &readfds);
-	if (select(1, &readfds, NULL, NULL, NULL) < 0)
+	if (ssh_sftp_loop_iteration() < 0)
 	    return;		       /* doom */
-	select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
     }
     using_sftp = !ssh_fallback_cmd(backhandle);
     if (verbose) {
@@ -465,14 +294,8 @@ static void bump(char *fmt, ...)
 	ssh_scp_recv(&ch, 1);
     }
 
-    if (gui_mode) {
-	unsigned int msg_id = WM_RET_ERR_CNT;
-	if (list)
-	    msg_id = WM_LS_RET_ERR_CNT;
-	while (!PostMessage
-	       ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
-		0 /*lParam */ ))SleepEx(1000, TRUE);
-    }
+    if (gui_mode)
+	gui_send_errcount(list, errs);
 
     cleanup_exit(1);
 }
@@ -484,7 +307,7 @@ static void do_cmd(char *host, char *user, char *cmd)
 {
     const char *err;
     char *realhost;
-    DWORD namelen;
+    void *logctx;
 
     if (host == NULL || host[0] == '\0')
 	bump("Empty host name");
@@ -558,16 +381,16 @@ static void do_cmd(char *host, char *user, char *cmd)
 	strncpy(cfg.username, user, sizeof(cfg.username) - 1);
 	cfg.username[sizeof(cfg.username) - 1] = '\0';
     } else if (cfg.username[0] == '\0') {
-	namelen = 0;
-	if (GetUserName(user, &namelen) == FALSE)
+	user = get_username();
+	if (!user)
 	    bump("Empty user name");
-	user = snewn(namelen, char);
-	GetUserName(user, &namelen);
-	if (verbose)
-	    tell_user(stderr, "Guessing user name: %s", user);
-	strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-	cfg.username[sizeof(cfg.username) - 1] = '\0';
-	free(user);
+	else {
+	    if (verbose)
+		tell_user(stderr, "Guessing user name: %s", user);
+	    strncpy(cfg.username, user, sizeof(cfg.username) - 1);
+	    cfg.username[sizeof(cfg.username) - 1] = '\0';
+	    sfree(user);
+	}
     }
 
     /*
@@ -632,11 +455,10 @@ static void print_stats(char *name, unsigned long size, unsigned long done,
 
     pct = (int) (100 * (done * 1.0 / size));
 
-    if (gui_mode)
-	/* GUI Adaptation - Sept 2000 */
+    if (gui_mode) {
 	gui_update_stats(name, size, pct, elap, done, eta, 
 			 (unsigned long) ratebs);
-    else {
+    } else {
 	len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
 		     name, done / 1024, ratebs / 1024.0, etastr, pct);
 	if (len < prev_stats_len)
@@ -991,7 +813,7 @@ int scp_send_filedata(char *data, int len)
 	 * we have space in the buffer again.
 	 */
 	while (bufsize > MAX_SCP_BUFSIZE) {
-	    if (!scp_process_network_event())
+	    if (ssh_sftp_loop_iteration() < 0)
 		return 1;
 	    bufsize = back->sendbuffer(backhandle);
 	}
@@ -1645,20 +1467,23 @@ static void run_err(const char *fmt, ...)
 static void source(char *src)
 {
     unsigned long size;
+    unsigned long mtime, atime;
     char *last;
-    HANDLE f;
-    DWORD attr;
+    RFile *f;
+    int attr;
     unsigned long i;
     unsigned long stat_bytes;
     time_t stat_starttime, stat_lasttime;
 
-    attr = GetFileAttributes(src);
-    if (attr == (DWORD) - 1) {
-	run_err("%s: No such file or directory", src);
+    attr = file_type(src);
+    if (attr == FILE_TYPE_NONEXISTENT ||
+	attr == FILE_TYPE_WEIRD) {
+	run_err("%s: %s file or directory", src,
+		(attr == FILE_TYPE_WEIRD ? "Not a" : "No such"));
 	return;
     }
 
-    if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+    if (attr == FILE_TYPE_DIRECTORY) {
 	if (recursive) {
 	    /*
 	     * Avoid . and .. directories.
@@ -1690,24 +1515,16 @@ static void source(char *src)
     if (last == src && strchr(src, ':') != NULL)
 	last = strchr(src, ':') + 1;
 
-    f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
-		   OPEN_EXISTING, 0, 0);
-    if (f == INVALID_HANDLE_VALUE) {
+    f = open_existing_file(src, &size, &mtime, &atime);
+    if (f == NULL) {
 	run_err("%s: Cannot open file", src);
 	return;
     }
-
     if (preserve) {
-	FILETIME actime, wrtime;
-	unsigned long mtime, atime;
-	GetFileTime(f, NULL, &actime, &wrtime);
-	TIME_WIN_TO_POSIX(actime, atime);
-	TIME_WIN_TO_POSIX(wrtime, mtime);
 	if (scp_send_filetimes(mtime, atime))
 	    return;
     }
 
-    size = GetFileSize(f, NULL);
     if (verbose)
 	tell_user(stderr, "Sending file %s, size=%lu", last, size);
     if (scp_send_filename(last, size, 0644))
@@ -1719,11 +1536,11 @@ static void source(char *src)
 
     for (i = 0; i < size; i += 4096) {
 	char transbuf[4096];
-	DWORD j, k = 4096;
+	int j, k = 4096;
 
 	if (i + k > size)
 	    k = size - i;
-	if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
+	if ((j = read_from_file(f, transbuf, k)) != k) {
 	    if (statistics)
 		printf("\n");
 	    bump("%s: Read error", src);
@@ -1741,7 +1558,7 @@ static void source(char *src)
 	}
 
     }
-    CloseHandle(f);
+    close_rfile(f);
 
     (void) scp_send_finish();
 }
@@ -1751,11 +1568,9 @@ static void source(char *src)
  */
 static void rsource(char *src)
 {
-    char *last, *findfile;
+    char *last;
     char *save_target;
-    HANDLE dir;
-    WIN32_FIND_DATA fdat;
-    int ok;
+    DirHandle *dir;
 
     if ((last = strrchr(src, '/')) == NULL)
 	last = src;
@@ -1775,22 +1590,17 @@ static void rsource(char *src)
     if (scp_send_dirname(last, 0755))
 	return;
 
-    findfile = dupcat(src, "/*", NULL);
-    dir = FindFirstFile(findfile, &fdat);
-    ok = (dir != INVALID_HANDLE_VALUE);
-    while (ok) {
-	if (strcmp(fdat.cFileName, ".") == 0 ||
-	    strcmp(fdat.cFileName, "..") == 0) {
-	    /* ignore . and .. */
-	} else {
-	    char *foundfile = dupcat(src, "/", fdat.cFileName, NULL);
+    dir = open_directory(src);
+    if (dir != NULL) {
+	char *filename;
+	while ((filename = read_filename(dir)) != NULL) {
+	    char *foundfile = dupcat(src, "/", filename, NULL);
 	    source(foundfile);
 	    sfree(foundfile);
+	    sfree(filename);
 	}
-	ok = FindNextFile(dir, &fdat);
     }
-    FindClose(dir);
-    sfree(findfile);
+    close_directory(dir);
 
     (void) scp_send_enddir();
 
@@ -1805,16 +1615,16 @@ static void sink(char *targ, char *src)
     char *destfname;
     int targisdir = 0;
     int exists;
-    DWORD attr;
-    HANDLE f;
+    int attr;
+    WFile *f;
     unsigned long received;
     int wrerror = 0;
     unsigned long stat_bytes;
     time_t stat_starttime, stat_lasttime;
     char *stat_name;
 
-    attr = GetFileAttributes(targ);
-    if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+    attr = file_type(targ);
+    if (attr == FILE_TYPE_DIRECTORY)
 	targisdir = 1;
 
     if (targetshouldbedirectory && !targisdir)
@@ -1901,7 +1711,7 @@ static void sink(char *targ, char *src)
 	    }
 
 	    if (targ[0] != '\0')
-		destfname = dupcat(targ, "\\", striptarget, NULL);
+		destfname = dir_file_cat(targ, striptarget);
 	    else
 		destfname = dupstr(striptarget);
 	} else {
@@ -1912,16 +1722,16 @@ static void sink(char *targ, char *src)
 	     */
 	    destfname = dupstr(targ);
 	}
-	attr = GetFileAttributes(destfname);
-	exists = (attr != (DWORD) - 1);
+	attr = file_type(destfname);
+	exists = (attr != FILE_TYPE_NONEXISTENT);
 
 	if (act.action == SCP_SINK_DIR) {
-	    if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+	    if (exists && attr != FILE_TYPE_DIRECTORY) {
 		run_err("%s: Not a directory", destfname);
 		continue;
 	    }
 	    if (!exists) {
-		if (!CreateDirectory(destfname, NULL)) {
+		if (!create_directory(destfname)) {
 		    run_err("%s: Cannot create directory", destfname);
 		    continue;
 		}
@@ -1931,9 +1741,8 @@ static void sink(char *targ, char *src)
 	    continue;
 	}
 
-	f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
-		       CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
-	if (f == INVALID_HANDLE_VALUE) {
+	f = open_new_file(destfname);
+	if (f == NULL) {
 	    run_err("%s: Cannot create file", destfname);
 	    continue;
 	}
@@ -1949,17 +1758,16 @@ static void sink(char *targ, char *src)
 	received = 0;
 	while (received < act.size) {
 	    char transbuf[4096];
-	    DWORD blksize, read, written;
+	    int blksize, read;
 	    blksize = 4096;
-	    if (blksize > act.size - received)
+	    if (blksize > (int)(act.size - received))
 		blksize = act.size - received;
 	    read = scp_recv_filedata(transbuf, blksize);
 	    if (read <= 0)
 		bump("Lost connection");
 	    if (wrerror)
 		continue;
-	    if (!WriteFile(f, transbuf, read, &written, NULL) ||
-		written != read) {
+	    if (write_to_file(f, transbuf, read) != (int)read) {
 		wrerror = 1;
 		/* FIXME: in sftp we can actually abort the transfer */
 		if (statistics)
@@ -1980,13 +1788,10 @@ static void sink(char *targ, char *src)
 	    received += read;
 	}
 	if (act.settime) {
-	    FILETIME actime, wrtime;
-	    TIME_POSIX_TO_WIN(act.atime, actime);
-	    TIME_POSIX_TO_WIN(act.mtime, wrtime);
-	    SetFileTime(f, NULL, &actime, &wrtime);
+	    set_file_times(f, act.mtime, act.atime);
 	}
 
-	CloseHandle(f);
+	close_wfile(f);
 	if (wrerror) {
 	    run_err("%s: Write error", destfname);
 	    continue;
@@ -2004,7 +1809,7 @@ static void toremote(int argc, char *argv[])
 {
     char *src, *targ, *host, *user;
     char *cmd;
-    int i;
+    int i, wc_type;
 
     targ = argv[argc - 1];
 
@@ -2031,18 +1836,14 @@ static void toremote(int argc, char *argv[])
     }
 
     if (argc == 2) {
-	/* Find out if the source filespec covers multiple files
-	   if so, we should set the targetshouldbedirectory flag */
-	HANDLE fh;
-	WIN32_FIND_DATA fdat;
 	if (colon(argv[0]) != NULL)
 	    bump("%s: Remote to remote not supported", argv[0]);
-	fh = FindFirstFile(argv[0], &fdat);
-	if (fh == INVALID_HANDLE_VALUE)
+
+	wc_type = test_wildcard(argv[0], 1);
+	if (wc_type == WCTYPE_NONEXISTENT)
 	    bump("%s: No such file or directory\n", argv[0]);
-	if (FindNextFile(fh, &fdat))
+	else if (wc_type == WCTYPE_WILDCARD)
 	    targetshouldbedirectory = 1;
-	FindClose(fh);
     }
 
     cmd = dupprintf("scp%s%s%s%s -t %s",
@@ -2056,9 +1857,6 @@ static void toremote(int argc, char *argv[])
     scp_source_setup(targ, targetshouldbedirectory);
 
     for (i = 0; i < argc - 1; i++) {
-	char *srcpath, *last;
-	HANDLE dir;
-	WIN32_FIND_DATA fdat;
 	src = argv[i];
 	if (colon(src) != NULL) {
 	    tell_user(stderr, "%s: Remote to remote not supported\n", src);
@@ -2066,49 +1864,30 @@ static void toremote(int argc, char *argv[])
 	    continue;
 	}
 
-	/*
-	 * Trim off the last pathname component of `src', to
-	 * provide the base pathname which will be prepended to
-	 * filenames returned from Find{First,Next}File.
-	 */
-	srcpath = dupstr(src);
-	last = stripslashes(srcpath, 1);
-	*last = '\0';
-
-	dir = FindFirstFile(src, &fdat);
-	if (dir == INVALID_HANDLE_VALUE) {
+	wc_type = test_wildcard(src, 1);
+	if (wc_type == WCTYPE_NONEXISTENT) {
 	    run_err("%s: No such file or directory", src);
 	    continue;
-	}
-	do {
+	} else if (wc_type == WCTYPE_FILENAME) {
+	    source(src);
+	    continue;
+	} else {
+	    WildcardMatcher *wc;
 	    char *filename;
-	    /*
-	     * Ensure that . and .. are never matched by wildcards,
-	     * but only by deliberate action.
-	     */
-	    if (!strcmp(fdat.cFileName, ".") ||
-		!strcmp(fdat.cFileName, "..")) {
-		/*
-		 * Find*File has returned a special dir. We require
-		 * that _either_ `src' ends in a backslash followed
-		 * by that string, _or_ `src' is precisely that
-		 * string.
-		 */
-		int len = strlen(src), dlen = strlen(fdat.cFileName);
-		if (len == dlen && !strcmp(src, fdat.cFileName)) {
-		    /* ok */ ;
-		} else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
-			   !strcmp(src + len - dlen, fdat.cFileName)) {
-		    /* ok */ ;
-		} else
-		    continue;	       /* ignore this one */
+
+	    wc = begin_wildcard_matching(src);
+	    if (wc == NULL) {
+		run_err("%s: No such file or directory", src);
+		continue;
 	    }
-	    filename = dupcat(srcpath, fdat.cFileName, NULL);
-	    source(filename);
-	    sfree(filename);
-	} while (FindNextFile(dir, &fdat));
-	FindClose(dir);
-	sfree(srcpath);
+
+	    while ((filename = wildcard_get_filename(wc)) != NULL) {
+		source(filename);
+		sfree(filename);
+	    }
+
+	    finish_wildcard_matching(wc);
+	}
     }
 }
 
@@ -2222,21 +2001,6 @@ static void get_dir_list(int argc, char *argv[])
     }
 }
 
-/*
- *  Initialize the Win$ock driver.
- */
-static void init_winsock(void)
-{
-    WORD winsock_ver;
-    WSADATA wsadata;
-
-    winsock_ver = MAKEWORD(1, 1);
-    if (WSAStartup(winsock_ver, &wsadata))
-	bump("Unable to initialise WinSock");
-    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
-	bump("WinSock version is incompatible with 1.1");
-}
-
 /*
  *  Short description of parameters.
  */
@@ -2288,18 +2052,22 @@ void cmdline_error(char *p, ...)
 }
 
 /*
- *  Main program (no, really?)
+ * Main program. (Called `psftp_main' because it gets called from
+ * *sftp.c; bit silly, I know, but it had to be called _something_.)
  */
-int main(int argc, char *argv[])
+int psftp_main(int argc, char *argv[])
 {
     int i;
 
     default_protocol = PROT_TELNET;
 
-    flags = FLAG_STDERR | FLAG_SYNCAGENT;
+    flags = FLAG_STDERR
+#ifdef FLAG_SYNCAGENT
+	| FLAG_SYNCAGENT
+#endif
+	;
     cmdline_tooltype = TOOLTYPE_FILETRANSFER;
     ssh_get_line = &console_get_line;
-    init_winsock();
     sk_init();
 
     for (i = 1; i < argc; i++) {
@@ -2324,7 +2092,7 @@ int main(int argc, char *argv[])
 	} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) {
 	    usage();
 	} else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
-	    gui_hwnd = argv[++i];
+	    gui_enable(argv[++i]);
 	    gui_mode = 1;
 	    console_batch_mode = TRUE;
         } else if (strcmp(argv[i], "-ls") == 0) {
@@ -2367,18 +2135,11 @@ int main(int argc, char *argv[])
 	back->special(backhandle, TS_EOF);
 	ssh_scp_recv(&ch, 1);
     }
-    WSACleanup();
     random_save_seed();
 
-    /* GUI Adaptation - August 2000 */
-    if (gui_mode) {
-	unsigned int msg_id = WM_RET_ERR_CNT;
-	if (list)
-	    msg_id = WM_LS_RET_ERR_CNT;
-	while (!PostMessage
-	       ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
-		0 /*lParam */ ))SleepEx(1000, TRUE);
-    }
+    if (gui_mode)
+	gui_send_errcount(list, errs);
+
     return (errs == 0 ? 0 : 1);
 }
 

+ 5 - 0
putty/SETTINGS.C

@@ -29,6 +29,7 @@ static void gpps(void *handle, const char *name, const char *def,
 	pdef = platform_default_s(name);
 	if (pdef) {
 	    strncpy(val, pdef, len);
+	    sfree(pdef);
 	} else {
 	    strncpy(val, def, len);
 	}
@@ -279,6 +280,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "TermHeight", cfg->height);
     write_setting_fontspec(sesskey, "Font", cfg->font);
     write_setting_i(sesskey, "FontVTMode", cfg->vtmode);
+    write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);
     write_setting_i(sesskey, "TryPalette", cfg->try_palette);
     write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);
     for (i = 0; i < 22; i++) {
@@ -351,6 +353,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "LoginShell", cfg->login_shell);
     write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left);
     write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont);
+    write_setting_i(sesskey, "ShadowBold", cfg->shadowbold);
     write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset);
 }
 
@@ -532,6 +535,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "TermHeight", 24, &cfg->height);
     gppfont(sesskey, "Font", &cfg->font);
     gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode);
+    gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);
     gppi(sesskey, "TryPalette", 0, &cfg->try_palette);
     gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);
     for (i = 0; i < 22; i++) {
@@ -644,6 +648,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp);
     gppi(sesskey, "LoginShell", 1, &cfg->login_shell);
     gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);
+    gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold);
     gppfont(sesskey, "BoldFont", &cfg->boldfont);
     gppfont(sesskey, "WideFont", &cfg->widefont);
     gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont);

+ 3 - 2
putty/SFTP.C

@@ -67,10 +67,12 @@ static struct sftp_packet *sftp_pkt_init(int pkt_type)
     sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
     return pkt;
 }
+/*
 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
 {
     sftp_pkt_adddata(pkt, &value, 1);
 }
+*/
 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
 			       unsigned long value)
 {
@@ -282,7 +284,6 @@ static tree234 *sftp_requests;
 
 static struct sftp_request *sftp_alloc_request(void)
 {
-    const unsigned CHANNEL_NUMBER_OFFSET = 256;
     unsigned low, high, mid;
     int tsize;
     struct sftp_request *r;
@@ -296,7 +297,7 @@ static struct sftp_request *sftp_alloc_request(void)
      * B-tree to find the largest ID which is in a contiguous
      * sequence from the beginning. (Precisely everything in that
      * sequence must have ID equal to its tree index plus
-     * SEQUENCE_NUMBER_OFFSET.)
+     * REQUEST_ID_OFFSET.)
      */
     tsize = count234(sftp_requests);
 

+ 16 - 8
putty/SSH.C

@@ -2169,7 +2169,6 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
     ssh->fn = &fn_table;
     ssh->s = new_connection(addr, *realhost, port,
 			    0, 1, nodelay, (Plug) ssh, &ssh->cfg);
-    sk_addr_free(addr);
     if ((err = sk_socket_error(ssh->s)) != NULL) {
 	ssh->s = NULL;
 	return err;
@@ -2603,7 +2602,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
     /* Load the public half of ssh->cfg.keyfile so we notice if it's in Pageant */
     if (!filename_is_null(ssh->cfg.keyfile)) {
 	if (!rsakey_pubblob(&ssh->cfg.keyfile,
-			    &s->publickey_blob, &s->publickey_bloblen))
+			    &s->publickey_blob, &s->publickey_bloblen, NULL))
 	    s->publickey_blob = NULL;
     } else
 	s->publickey_blob = NULL;
@@ -2889,11 +2888,15 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 	    s->tried_publickey = 1;
 	    
 	    {
-		int ret = loadrsakey(&ssh->cfg.keyfile, &s->key, s->password);
+		const char *error = NULL;
+		int ret = loadrsakey(&ssh->cfg.keyfile, &s->key, s->password,
+				     &error);
 		if (ret == 0) {
 		    c_write_str(ssh, "Couldn't load private key from ");
 		    c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));
-		    c_write_str(ssh, ".\r\n");
+		    c_write_str(ssh, " (");
+		    c_write_str(ssh, error);
+		    c_write_str(ssh, ").\r\n");
 		    continue;	       /* go and try password */
 		}
 		if (ret == -1) {
@@ -4587,7 +4590,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 	    if (keytype == SSH_KEYTYPE_SSH2) {
 		s->publickey_blob =
 		    ssh2_userkey_loadpub(&ssh->cfg.keyfile, NULL,
-					 &s->publickey_bloblen);
+					 &s->publickey_bloblen, NULL);
 	    } else {
 		char *msgbuf;
 		logeventf(ssh, "Unable to use this key file (%s)",
@@ -4917,7 +4920,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 		pub_blob =
 		    (unsigned char *)ssh2_userkey_loadpub(&ssh->cfg.keyfile,
 							  &algorithm,
-							  &pub_blob_len);
+							  &pub_blob_len,
+							  NULL);
 		if (pub_blob) {
 		    ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
 		    ssh2_pkt_addstring(ssh, s->username);
@@ -5094,14 +5098,18 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 		 * We have our passphrase. Now try the actual authentication.
 		 */
 		struct ssh2_userkey *key;
+		const char *error = NULL;
 
-		key = ssh2_load_userkey(&ssh->cfg.keyfile, s->password);
+		key = ssh2_load_userkey(&ssh->cfg.keyfile, s->password,
+					&error);
 		if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
 		    if (key == SSH2_WRONG_PASSPHRASE) {
 			c_write_str(ssh, "Wrong passphrase\r\n");
 			s->tried_pubkey_config = FALSE;
 		    } else {
-			c_write_str(ssh, "Unable to load private key\r\n");
+			c_write_str(ssh, "Unable to load private key (");
+			c_write_str(ssh, error);
+			c_write_str(ssh, ")\r\n");
 			s->tried_pubkey_config = TRUE;
 		    }
 		    /* Send a spurious AUTH_NONE to return to the top. */

+ 5 - 4
putty/SSH.H

@@ -327,9 +327,10 @@ Bignum dh_create_e(void *, int nbits);
 Bignum dh_find_K(void *, Bignum f);
 
 int loadrsakey(const Filename *filename, struct RSAKey *key,
-	       char *passphrase);
+	       char *passphrase, const char **errorstr);
 int rsakey_encrypted(const Filename *filename, char **comment);
-int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen);
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
+		   const char **errorstr);
 
 int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase);
 
@@ -344,9 +345,9 @@ extern struct ssh2_userkey ssh2_wrong_passphrase;
 
 int ssh2_userkey_encrypted(const Filename *filename, char **comment);
 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
-				       char *passphrase);
+				       char *passphrase, const char **errorstr);
 char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
-			   int *pub_blob_len);
+			   int *pub_blob_len, const char **errorstr);
 int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
 		      char *passphrase);
 

+ 2 - 0
putty/SSHBN.C

@@ -729,6 +729,7 @@ Bignum bigmuladd(Bignum a, Bignum b, Bignum addend)
     }
     ret[0] = maxspot;
 
+    sfree(workspace);
     return ret;
 }
 
@@ -1009,5 +1010,6 @@ char *bignum_decimal(Bignum x)
     /*
      * Done.
      */
+    sfree(workspace);
     return ret;
 }

+ 80 - 24
putty/SSHPUBK.C

@@ -34,7 +34,8 @@
                           (x)=='/' ? 63 : 0 )
 
 static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
-			   char **commentptr, char *passphrase)
+			   char **commentptr, char *passphrase,
+			   const char **error)
 {
     unsigned char buf[16384];
     unsigned char keybuf[16];
@@ -44,13 +45,18 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
     struct MD5Context md5c;
     char *comment;
 
+    *error = NULL;
+
     /* Slurp the whole file (minus the header) into a buffer. */
     len = fread(buf, 1, sizeof(buf), fp);
     fclose(fp);
-    if (len < 0 || len == sizeof(buf))
+    if (len < 0 || len == sizeof(buf)) {
+	*error = "error reading file";
 	goto end;		       /* file too big or not read */
+    }
 
     i = 0;
+    *error = "file format error";
 
     /*
      * A zero byte. (The signature includes a terminating NUL.)
@@ -98,7 +104,9 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
     if (key)
 	key->comment = comment;
     if (!key) {
-	return ciphertype != 0;
+	ret = ciphertype != 0;
+	*error = NULL;
+	goto end;
     }
 
     /*
@@ -119,6 +127,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
     if (len - i < 4)
 	goto end;
     if (buf[i] != buf[i + 2] || buf[i + 1] != buf[i + 3]) {
+	*error = "wrong passphrase";
 	ret = -1;
 	goto end;
     }
@@ -143,6 +152,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
 	goto end;
 
     if (!rsa_verify(key)) {
+	*error = "rsa_verify failed";
 	freersakey(key);
 	ret = 0;
     } else
@@ -153,28 +163,39 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
     return ret;
 }
 
-int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase)
+int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase,
+	       const char **errorstr)
 {
     FILE *fp;
     char buf[64];
+    int ret = 0;
+    const char *error = NULL;
 
     fp = f_open(*filename, "rb");
-    if (!fp)
-	return 0;		       /* doesn't even exist */
+    if (!fp) {
+	error = "can't open file";
+	goto end;
+    }
 
     /*
      * Read the first line of the file and see if it's a v1 private
      * key file.
      */
     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
-	return loadrsakey_main(fp, key, FALSE, NULL, passphrase);
+	ret = loadrsakey_main(fp, key, FALSE, NULL, passphrase, &error);
+	goto end;
     }
 
     /*
      * Otherwise, we have nothing. Return empty-handed.
      */
     fclose(fp);
-    return 0;
+    error = "not an SSH-1 RSA file";
+
+  end:
+    if ((ret != 1) && errorstr)
+	*errorstr = error;
+    return ret;
 }
 
 /*
@@ -195,7 +216,8 @@ int rsakey_encrypted(const Filename *filename, char **comment)
      * key file.
      */
     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
-	return loadrsakey_main(fp, NULL, FALSE, comment, NULL);
+	const char *dummy;
+	return loadrsakey_main(fp, NULL, FALSE, comment, NULL, &dummy);
     }
     fclose(fp);
     return 0;			       /* wasn't the right kind of file */
@@ -206,12 +228,14 @@ int rsakey_encrypted(const Filename *filename, char **comment)
  * an RSA key, as given in the agent protocol (modulus bits,
  * exponent, modulus).
  */
-int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen)
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
+		   const char **errorstr)
 {
     FILE *fp;
     char buf[64];
     struct RSAKey key;
     int ret;
+    const char *error = NULL;
 
     /* Default return if we fail. */
     *blob = NULL;
@@ -219,8 +243,10 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen)
     ret = 0;
 
     fp = f_open(*filename, "rb");
-    if (!fp)
-	return 0;		       /* doesn't even exist */
+    if (!fp) {
+	error = "can't open file";
+	goto end;
+    }
 
     /*
      * Read the first line of the file and see if it's a v1 private
@@ -228,13 +254,19 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen)
      */
     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
 	memset(&key, 0, sizeof(key));
-	if (loadrsakey_main(fp, &key, TRUE, NULL, NULL)) {
+	if (loadrsakey_main(fp, &key, TRUE, NULL, NULL, &error)) {
 	    *blob = rsa_public_blob(&key, bloblen);
 	    freersakey(&key);
 	    ret = 1;
 	}
+    } else {
+	error = "not an SSH-1 RSA file";
+        fclose(fp);
     }
-    fclose(fp);
+
+  end:
+    if ((ret != 1) && errorstr)
+	*errorstr = error;
     return ret;
 }
 
@@ -401,8 +433,7 @@ int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase)
  *    data    "putty-private-key-file-mac-key"
  *    data    passphrase
  *
- * Encrypted keys should have a MAC, whereas unencrypted ones must
- * have a hash.
+ * (An empty passphrase is used for unencrypted keys.)
  *
  * If the key is encrypted, the encryption key is derived from the
  * passphrase by means of a succession of SHA-1 hashes. Each hash
@@ -575,7 +606,7 @@ struct ssh2_userkey ssh2_wrong_passphrase = {
 };
 
 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
-				       char *passphrase)
+				       char *passphrase, const char **errorstr)
 {
     FILE *fp;
     char header[40], *b, *encryption, *comment, *mac;
@@ -586,14 +617,17 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
     int public_blob_len, private_blob_len;
     int i, is_mac, old_fmt;
     int passlen = passphrase ? strlen(passphrase) : 0;
+    const char *error = NULL;
 
     ret = NULL;			       /* return NULL for most errors */
     encryption = comment = mac = NULL;
     public_blob = private_blob = NULL;
 
     fp = f_open(*filename, "rb");
-    if (!fp)
+    if (!fp) {
+	error = "can't open file";
 	goto error;
+    }
 
     /* Read the first header line which contains the key type. */
     if (!read_header(fp, header))
@@ -604,8 +638,11 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 	/* this is an old key file; warn and then continue */
 	old_keyfile_warning();
 	old_fmt = 1;
-    } else
+    } else {
+	error = "not a PuTTY SSH-2 private key";
 	goto error;
+    }
+    error = "file format error";
     if ((b = read_body(fp)) == NULL)
 	goto error;
     /* Select key algorithm structure. */
@@ -746,7 +783,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 
 	    SHA_Init(&s);
 	    SHA_Bytes(&s, header, sizeof(header)-1);
-	    if (passphrase)
+	    if (cipher && passphrase)
 		SHA_Bytes(&s, passphrase, passlen);
 	    SHA_Final(&s, mackey);
 
@@ -769,7 +806,12 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 	if (strcmp(mac, realmac)) {
 	    /* An incorrect MAC is an unconditional Error if the key is
 	     * unencrypted. Otherwise, it means Wrong Passphrase. */
-	    ret = cipher ? SSH2_WRONG_PASSPHRASE : NULL;
+	    if (cipher) {
+		ret = SSH2_WRONG_PASSPHRASE;
+	    } else {
+		error = "MAC failed";
+		ret = NULL;
+	    }
 	    goto error;
 	}
     }
@@ -787,10 +829,14 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 	sfree(ret->comment);
 	sfree(ret);
 	ret = NULL;
+	error = "createkey failed";
+	goto error;
     }
     sfree(public_blob);
     sfree(private_blob);
     sfree(encryption);
+    if (errorstr)
+	*errorstr = NULL;
     return ret;
 
     /*
@@ -809,11 +855,13 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 	sfree(public_blob);
     if (private_blob)
 	sfree(private_blob);
+    if (errorstr)
+	*errorstr = error;
     return ret;
 }
 
 char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
-			   int *pub_blob_len)
+			   int *pub_blob_len, const char **errorstr)
 {
     FILE *fp;
     char header[40], *b;
@@ -821,18 +869,24 @@ char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
     unsigned char *public_blob;
     int public_blob_len;
     int i;
+    const char *error = NULL;
 
     public_blob = NULL;
 
     fp = f_open(*filename, "rb");
-    if (!fp)
+    if (!fp) {
+	error = "can't open file";
 	goto error;
+    }
 
     /* Read the first header line which contains the key type. */
     if (!read_header(fp, header)
 	|| (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
-	    0 != strcmp(header, "PuTTY-User-Key-File-1")))
+	    0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
+	error = "not a PuTTY SSH-2 private key";
 	goto error;
+    }
+    error = "file format error";
     if ((b = read_body(fp)) == NULL)
 	goto error;
     /* Select key algorithm structure. Currently only ssh-rsa. */
@@ -885,6 +939,8 @@ char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
 	fclose(fp);
     if (public_blob)
 	sfree(public_blob);
+    if (errorstr)
+	*errorstr = error;
     return NULL;
 }
 

+ 3 - 3
putty/TELNET.C

@@ -710,8 +710,10 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
 	sfree(buf);
     }
     addr = name_lookup(host, port, realhost, &telnet->cfg);
-    if ((err = sk_addr_error(addr)) != NULL)
+    if ((err = sk_addr_error(addr)) != NULL) {
+	sk_addr_free(addr);
 	return err;
+    }
 
     if (port < 0)
 	port = 23;		       /* default telnet port */
@@ -731,8 +733,6 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
     if ((err = sk_socket_error(telnet->s)) != NULL)
 	return err;
 
-    sk_addr_free(addr);
-
     /*
      * Initialise option states.
      */

+ 4 - 0
putty/WINCFG.C

@@ -271,6 +271,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
     ctrl_checkbox(s, "Attempt to use logical palettes", 'l',
 		  HELPCTX(colours_logpal),
 		  dlg_stdcheckbox_handler, I(offsetof(Config,try_palette)));
+    ctrl_checkbox(s, "Use system colours", 's',
+                  HELPCTX(colours_system),
+                  dlg_stdcheckbox_handler, I(offsetof(Config,system_colour)));
+
 
     /*
      * Resize-by-changing-font is a Windows insanity.

+ 39 - 11
putty/WINDOW.C

@@ -75,6 +75,7 @@ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 			unsigned char *output);
 static void cfgtopalette(void);
+static void systopalette(void);
 static void init_palette(void);
 static void init_fonts(int, int);
 static void another_font(int);
@@ -179,8 +180,6 @@ static int compose_state = 0;
 
 static int wsa_started = 0;
 
-static OSVERSIONINFO osVersion;
-
 static UINT wm_mousewheel = WM_MOUSEWHEEL;
 
 /* Dummy routine, only required in plink. */
@@ -221,16 +220,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * config box. */
     defuse_showwindow();
 
+    if (!init_winver())
     {
-	ZeroMemory(&osVersion, sizeof(osVersion));
-	osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
-	if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
-	    char *str = dupprintf("%s Fatal Error", appname);
-            MessageBox(NULL, "Windows refuses to report a version",
-                       str, MB_OK | MB_ICONEXCLAMATION);
-	    sfree(str);
-	    return 1;
-        }
+	char *str = dupprintf("%s Fatal Error", appname);
+	MessageBox(NULL, "Windows refuses to report a version",
+		   str, MB_OK | MB_ICONEXCLAMATION);
+	sfree(str);
+	return 1;
     }
 
     /*
@@ -1020,6 +1016,38 @@ static void cfgtopalette(void)
 	defpal[i].rgbtGreen = cfg.colours[w][1];
 	defpal[i].rgbtBlue = cfg.colours[w][2];
     }
+
+    /* Override with system colours if appropriate */
+    if (cfg.system_colour)
+        systopalette();
+}
+
+/*
+ * Override bit of defpal with colours from the system.
+ * (NB that this takes a copy the system colours at the time this is called,
+ * so subsequent colour scheme changes don't take effect. To fix that we'd
+ * probably want to be using GetSysColorBrush() and the like.)
+ */
+static void systopalette(void)
+{
+    int i;
+    static const struct { int nIndex; int norm; int bold; } or[] =
+    {
+	{ COLOR_WINDOWTEXT,	16, 17 }, /* Default Foreground */
+	{ COLOR_WINDOW,		18, 19 }, /* Default Background */
+	{ COLOR_HIGHLIGHTTEXT,	20, 21 }, /* Cursor Text */
+	{ COLOR_HIGHLIGHT,	22, 23 }, /* Cursor Colour */
+    };
+
+    for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) {
+	COLORREF colour = GetSysColor(or[i].nIndex);
+	defpal[or[i].norm].rgbtRed =
+	   defpal[or[i].bold].rgbtRed = GetRValue(colour);
+	defpal[or[i].norm].rgbtGreen =
+	   defpal[or[i].bold].rgbtGreen = GetGValue(colour);
+	defpal[or[i].norm].rgbtBlue =
+	   defpal[or[i].bold].rgbtBlue = GetBValue(colour);
+    }
 }
 
 /*

+ 1 - 0
putty/WINHELP.H

@@ -92,6 +92,7 @@
 #define WINHELP_CTX_selection_linedraw "selection.linedraw"
 #define WINHELP_CTX_selection_rtf "selection.rtf"
 #define WINHELP_CTX_colours_bold "colours.bold"
+#define WINHELP_CTX_colours_system "colours.system"
 #define WINHELP_CTX_colours_logpal "colours.logpal"
 #define WINHELP_CTX_colours_config "colours.config"
 #define WINHELP_CTX_translation_codepage "translation.codepage"

+ 24 - 0
putty/WINMISC.C

@@ -8,6 +8,8 @@
 #include "putty.h"
 #include "winstuff.h"
 
+OSVERSIONINFO osVersion;
+
 void platform_get_x11_auth(char *display, int *proto,
                            unsigned char *data, int *datalen)
 {
@@ -37,6 +39,21 @@ int filename_is_null(Filename fn)
     return !*fn.path;
 }
 
+char *get_username(void)
+{
+    DWORD namelen;
+    char *user;
+
+    namelen = 0;
+    if (GetUserName(NULL, &namelen) == FALSE)
+	return NULL;
+
+    user = snewn(namelen, char);
+    GetUserName(user, &namelen);
+
+    return user;
+}
+
 int SaneDialogBox(HINSTANCE hinst,
 		  LPCTSTR tmpl,
 		  HWND hwndparent,
@@ -88,6 +105,13 @@ void SaneEndDialog(HWND hwnd, int ret)
     SetWindowLong(hwnd, BOXFLAGS, DF_END);
 }
 
+BOOL init_winver(void)
+{
+    ZeroMemory(&osVersion, sizeof(osVersion));
+    osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+    return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
+}
+
 #ifdef DEBUG
 static FILE *debug_fp = NULL;
 static HANDLE debug_hdl = INVALID_HANDLE_VALUE;

+ 3 - 0
putty/WINNET.C

@@ -701,6 +701,9 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 
     add234(sktree, ret);
 
+    /* We're done with 'addr' now. */
+    sk_addr_free(addr);
+
     return (Socket) ret;
 }
 

+ 533 - 0
putty/WINSFTP.C

@@ -0,0 +1,533 @@
+/*
+ * winsftp.c: the Windows-specific parts of PSFTP and PSCP.
+ */
+
+#include <windows.h>
+#ifndef AUTO_WINSOCK
+#ifdef WINSOCK_TWO
+#include <winsock2.h>
+#else
+#include <winsock.h>
+#endif
+#endif
+
+#include "putty.h"
+#include "psftp.h"
+
+/* ----------------------------------------------------------------------
+ * Interface to GUI driver program.
+ */
+
+/* This is just a base value from which the main message numbers are
+ * derived. */
+#define   WM_APP_BASE		0x8000
+
+/* These two pass a single character value in wParam. They represent
+ * the visible output from PSCP. */
+#define   WM_STD_OUT_CHAR	( WM_APP_BASE+400 )
+#define   WM_STD_ERR_CHAR	( WM_APP_BASE+401 )
+
+/* These pass a transfer status update. WM_STATS_CHAR passes a single
+ * character in wParam, and is called repeatedly to pass the name of
+ * the file, terminated with "\n". WM_STATS_SIZE passes the size of
+ * the file being transferred in wParam. WM_STATS_ELAPSED is called
+ * to pass the elapsed time (in seconds) in wParam, and
+ * WM_STATS_PERCENT passes the percentage of the transfer which is
+ * complete, also in wParam. */
+#define   WM_STATS_CHAR		( WM_APP_BASE+402 )
+#define   WM_STATS_SIZE 	( WM_APP_BASE+403 )
+#define   WM_STATS_PERCENT	( WM_APP_BASE+404 )
+#define   WM_STATS_ELAPSED	( WM_APP_BASE+405 )
+
+/* These are used at the end of a run to pass an error code in
+ * wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT
+ * is used after a copy, and WM_LS_RET_ERR_CNT is used after a file
+ * list operation. */
+#define   WM_RET_ERR_CNT	( WM_APP_BASE+406 )
+#define   WM_LS_RET_ERR_CNT	( WM_APP_BASE+407 )
+
+/* More transfer status update messages. WM_STATS_DONE passes the
+ * number of bytes sent so far in wParam. WM_STATS_ETA passes the
+ * estimated time to completion (in seconds). WM_STATS_RATEBS passes
+ * the average transfer rate (in bytes per second). */
+#define   WM_STATS_DONE		( WM_APP_BASE+408 )
+#define   WM_STATS_ETA		( WM_APP_BASE+409 )
+#define   WM_STATS_RATEBS	( WM_APP_BASE+410 )
+
+#define NAME_STR_MAX 2048
+static char statname[NAME_STR_MAX + 1];
+static unsigned long statsize = 0;
+static unsigned long statdone = 0;
+static unsigned long stateta = 0;
+static unsigned long statratebs = 0;
+static int statperct = 0;
+static unsigned long statelapsed = 0;
+
+static HWND gui_hwnd = NULL;
+
+static void send_msg(HWND h, UINT message, WPARAM wParam)
+{
+    while (!PostMessage(h, message, wParam, 0))
+	SleepEx(1000, TRUE);
+}
+
+void gui_send_char(int is_stderr, int c)
+{
+    unsigned int msg_id = WM_STD_OUT_CHAR;
+    if (is_stderr)
+	msg_id = WM_STD_ERR_CHAR;
+    send_msg(gui_hwnd, msg_id, (WPARAM) c);
+}
+
+void gui_send_errcount(int list, int errs)
+{
+    unsigned int msg_id = WM_RET_ERR_CNT;
+    if (list)
+	msg_id = WM_LS_RET_ERR_CNT;
+    while (!PostMessage(gui_hwnd, msg_id, (WPARAM) errs, 0))
+	SleepEx(1000, TRUE);
+}
+
+void gui_update_stats(char *name, unsigned long size,
+		      int percentage, unsigned long elapsed,
+		      unsigned long done, unsigned long eta,
+		      unsigned long ratebs)
+{
+    unsigned int i;
+
+    if (strcmp(name, statname) != 0) {
+	for (i = 0; i < strlen(name); ++i)
+	    send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) name[i]);
+	send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) '\n');
+	strcpy(statname, name);
+    }
+    if (statsize != size) {
+	send_msg(gui_hwnd, WM_STATS_SIZE, (WPARAM) size);
+	statsize = size;
+    }
+    if (statdone != done) {
+	send_msg(gui_hwnd, WM_STATS_DONE, (WPARAM) done);
+	statdone = done;
+    }
+    if (stateta != eta) {
+	send_msg(gui_hwnd, WM_STATS_ETA, (WPARAM) eta);
+	stateta = eta;
+    }
+    if (statratebs != ratebs) {
+	send_msg(gui_hwnd, WM_STATS_RATEBS, (WPARAM) ratebs);
+	statratebs = ratebs;
+    }
+    if (statelapsed != elapsed) {
+	send_msg(gui_hwnd, WM_STATS_ELAPSED, (WPARAM) elapsed);
+	statelapsed = elapsed;
+    }
+    if (statperct != percentage) {
+	send_msg(gui_hwnd, WM_STATS_PERCENT, (WPARAM) percentage);
+	statperct = percentage;
+    }
+}
+
+void gui_enable(char *arg)
+{
+    gui_hwnd = (HWND) atoi(arg);
+}
+
+/* ----------------------------------------------------------------------
+ * File access abstraction.
+ */
+
+/*
+ * Set local current directory. Returns NULL on success, or else an
+ * error message which must be freed after printing.
+ */
+char *psftp_lcd(char *dir)
+{
+    char *ret = NULL;
+
+    if (!SetCurrentDirectory(dir)) {
+	LPVOID message;
+	int i;
+	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		      FORMAT_MESSAGE_FROM_SYSTEM |
+		      FORMAT_MESSAGE_IGNORE_INSERTS,
+		      NULL, GetLastError(),
+		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		      (LPTSTR)&message, 0, NULL);
+	i = strcspn((char *)message, "\n");
+	ret = dupprintf("%.*s", i, (LPCTSTR)message);
+	LocalFree(message);
+    }
+
+    return ret;
+}
+
+/*
+ * Get local current directory. Returns a string which must be
+ * freed.
+ */
+char *psftp_getcwd(void)
+{
+    char *ret = snewn(256, char);
+    int len = GetCurrentDirectory(256, ret);
+    if (len > 256)
+	ret = sresize(ret, len, char);
+    GetCurrentDirectory(len, ret);
+    return ret;
+}
+
+#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
+	((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
+#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
+	((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
+
+struct RFile {
+    HANDLE h;
+};
+
+RFile *open_existing_file(char *name, unsigned long *size,
+			  unsigned long *mtime, unsigned long *atime)
+{
+    HANDLE h;
+    RFile *ret;
+
+    h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
+		   OPEN_EXISTING, 0, 0);
+    if (h == INVALID_HANDLE_VALUE)
+	return NULL;
+
+    ret = snew(RFile);
+    ret->h = h;
+
+    if (size)
+	*size = GetFileSize(h, NULL);
+
+    if (mtime || atime) {
+	FILETIME actime, wrtime;
+	GetFileTime(h, NULL, &actime, &wrtime);
+	if (atime)
+	    TIME_WIN_TO_POSIX(actime, *atime);
+	if (mtime)
+	    TIME_WIN_TO_POSIX(wrtime, *mtime);
+    }
+
+    return ret;
+}
+
+int read_from_file(RFile *f, void *buffer, int length)
+{
+    int ret, read;
+    ret = ReadFile(f->h, buffer, length, &read, NULL);
+    if (!ret)
+	return -1;		       /* error */
+    else
+	return read;
+}
+
+void close_rfile(RFile *f)
+{
+    CloseHandle(f->h);
+    sfree(f);
+}
+
+struct WFile {
+    HANDLE h;
+};
+
+WFile *open_new_file(char *name)
+{
+    HANDLE h;
+    WFile *ret;
+
+    h = CreateFile(name, GENERIC_WRITE, 0, NULL,
+		   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+    if (h == INVALID_HANDLE_VALUE)
+	return NULL;
+
+    ret = snew(WFile);
+    ret->h = h;
+
+    return ret;
+}
+
+int write_to_file(WFile *f, void *buffer, int length)
+{
+    int ret, written;
+    ret = WriteFile(f->h, buffer, length, &written, NULL);
+    if (!ret)
+	return -1;		       /* error */
+    else
+	return written;
+}
+
+void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
+{
+    FILETIME actime, wrtime;
+    TIME_POSIX_TO_WIN(atime, actime);
+    TIME_POSIX_TO_WIN(mtime, wrtime);
+    SetFileTime(f->h, NULL, &actime, &wrtime);
+}
+
+void close_wfile(WFile *f)
+{
+    CloseHandle(f->h);
+    sfree(f);
+}
+
+int file_type(char *name)
+{
+    DWORD attr;
+    attr = GetFileAttributes(name);
+    /* We know of no `weird' files under Windows. */
+    if (attr == (DWORD)-1)
+	return FILE_TYPE_NONEXISTENT;
+    else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+	return FILE_TYPE_DIRECTORY;
+    else
+	return FILE_TYPE_FILE;
+}
+
+struct DirHandle {
+    HANDLE h;
+    char *name;
+};
+
+DirHandle *open_directory(char *name)
+{
+    HANDLE h;
+    WIN32_FIND_DATA fdat;
+    char *findfile;
+    DirHandle *ret;
+
+    /* To enumerate files in dir `foo', we search for `foo/*'. */
+    findfile = dupcat(name, "/*", NULL);
+    h = FindFirstFile(findfile, &fdat);
+    if (h == INVALID_HANDLE_VALUE)
+	return NULL;
+    sfree(findfile);
+
+    ret = snew(DirHandle);
+    ret->h = h;
+    ret->name = dupstr(fdat.cFileName);
+    return ret;
+}
+
+char *read_filename(DirHandle *dir)
+{
+    while (!dir->name) {
+	WIN32_FIND_DATA fdat;
+	int ok = FindNextFile(dir->h, &fdat);
+
+	if (!ok)
+	    return NULL;
+
+	if (fdat.cFileName[0] == '.' &&
+	    (fdat.cFileName[1] == '\0' ||
+	     (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
+	    dir->name = NULL;
+	else
+	    dir->name = dupstr(fdat.cFileName);
+    }
+
+    if (dir->name) {
+	char *ret = dir->name;
+	dir->name = NULL;
+	return ret;
+    } else
+	return NULL;
+}
+
+void close_directory(DirHandle *dir)
+{
+    FindClose(dir->h);
+    if (dir->name)
+	sfree(dir->name);
+    sfree(dir);
+}
+
+int test_wildcard(char *name, int cmdline)
+{
+    HANDLE fh;
+    WIN32_FIND_DATA fdat;
+
+    /* First see if the exact name exists. */
+    if (GetFileAttributes(name) != (DWORD)-1)
+	return WCTYPE_FILENAME;
+
+    /* Otherwise see if a wildcard match finds anything. */
+    fh = FindFirstFile(name, &fdat);
+    if (fh == INVALID_HANDLE_VALUE)
+	return WCTYPE_NONEXISTENT;
+
+    FindClose(fh);
+    return WCTYPE_WILDCARD;
+}
+
+struct WildcardMatcher {
+    HANDLE h;
+    char *name;
+    char *srcpath;
+};
+
+/*
+ * Return a pointer to the portion of str that comes after the last
+ * slash (or backslash or colon, if `local' is TRUE).
+ */
+static char *stripslashes(char *str, int local)
+{
+    char *p;
+
+    if (local) {
+        p = strchr(str, ':');
+        if (p) str = p+1;
+    }
+
+    p = strrchr(str, '/');
+    if (p) str = p+1;
+
+    if (local) {
+	p = strrchr(str, '\\');
+	if (p) str = p+1;
+    }
+
+    return str;
+}
+
+WildcardMatcher *begin_wildcard_matching(char *name)
+{
+    HANDLE h;
+    WIN32_FIND_DATA fdat;
+    WildcardMatcher *ret;
+    char *last;
+
+    h = FindFirstFile(name, &fdat);
+    if (h == INVALID_HANDLE_VALUE)
+	return NULL;
+
+    ret = snew(WildcardMatcher);
+    ret->h = h;
+    ret->srcpath = dupstr(name);
+    last = stripslashes(ret->srcpath, 1);
+    *last = '\0';
+    if (fdat.cFileName[0] == '.' &&
+	(fdat.cFileName[1] == '\0' ||
+	 (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
+	ret->name = NULL;
+    else
+	ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);
+
+    return ret;
+}
+
+char *wildcard_get_filename(WildcardMatcher *dir)
+{
+    while (!dir->name) {
+	WIN32_FIND_DATA fdat;
+	int ok = FindNextFile(dir->h, &fdat);
+
+	if (!ok)
+	    return NULL;
+
+	if (fdat.cFileName[0] == '.' &&
+	    (fdat.cFileName[1] == '\0' ||
+	     (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
+	    dir->name = NULL;
+	else
+	    dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);
+    }
+
+    if (dir->name) {
+	char *ret = dir->name;
+	dir->name = NULL;
+	return ret;
+    } else
+	return NULL;
+}
+
+void finish_wildcard_matching(WildcardMatcher *dir)
+{
+    FindClose(dir->h);
+    if (dir->name)
+	sfree(dir->name);
+    sfree(dir->srcpath);
+    sfree(dir);
+}
+
+int create_directory(char *name)
+{
+    return CreateDirectory(name, NULL) != 0;
+}
+
+char *dir_file_cat(char *dir, char *file)
+{
+    return dupcat(dir, "\\", file, NULL);
+}
+
+/* ----------------------------------------------------------------------
+ * Platform-specific network handling.
+ */
+
+/*
+ * Be told what socket we're supposed to be using.
+ */
+static SOCKET sftp_ssh_socket;
+char *do_select(SOCKET skt, int startup)
+{
+    if (startup)
+	sftp_ssh_socket = skt;
+    else
+	sftp_ssh_socket = INVALID_SOCKET;
+    return NULL;
+}
+extern int select_result(WPARAM, LPARAM);
+
+/*
+ * Initialize the WinSock driver.
+ */
+static void init_winsock(void)
+{
+    WORD winsock_ver;
+    WSADATA wsadata;
+
+    winsock_ver = MAKEWORD(1, 1);
+    if (WSAStartup(winsock_ver, &wsadata)) {
+	fprintf(stderr, "Unable to initialise WinSock");
+	cleanup_exit(1);
+    }
+    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
+	fprintf(stderr, "WinSock version is incompatible with 1.1");
+	cleanup_exit(1);
+    }
+}
+
+/*
+ * Wait for some network data and process it.
+ */
+int ssh_sftp_loop_iteration(void)
+{
+    fd_set readfds;
+
+    if (sftp_ssh_socket == INVALID_SOCKET)
+	return -1;		       /* doom */
+
+    FD_ZERO(&readfds);
+    FD_SET(sftp_ssh_socket, &readfds);
+    if (select(1, &readfds, NULL, NULL, NULL) < 0)
+	return -1;		       /* doom */
+
+    select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
+    return 0;
+}
+
+/* ----------------------------------------------------------------------
+ * Main program. Parse arguments etc.
+ */
+int main(int argc, char *argv[])
+{
+    int ret;
+
+    init_winsock();
+    ret = psftp_main(argc, argv);
+    WSACleanup();
+
+    return ret;
+}

+ 3 - 0
putty/WINSTUFF.H

@@ -322,6 +322,9 @@ int SaneDialogBox(HINSTANCE hinst,
 
 void SaneEndDialog(HWND hwnd, int ret);
 
+extern OSVERSIONINFO osVersion;
+BOOL init_winver(void);
+
 /*
  * Exports from sizetip.c.
  */

+ 3 - 2
putty/X11FWD.C

@@ -277,8 +277,10 @@ const char *x11_init(Socket * s, char *display, void *c, void *auth,
      * Try to find host.
      */
     addr = name_lookup(host, port, &dummy_realhost, cfg);
-    if ((err = sk_addr_error(addr)) != NULL)
+    if ((err = sk_addr_error(addr)) != NULL) {
+	sk_addr_free(addr);
 	return err;
+    }
 
     /*
      * Open socket.
@@ -315,7 +317,6 @@ const char *x11_init(Socket * s, char *display, void *c, void *auth,
     }
 
     sk_set_private_ptr(*s, pr);
-    sk_addr_free(addr);
     return NULL;
 }
 

+ 0 - 19
putty/putty.h

@@ -1,30 +1,11 @@
 #ifndef PUTTY_PUTTY_H
 
-// workaround for some error in Borland's preprocessor when Local Options are set
-/*#ifndef GLOBAL
-#ifdef PUTTY_DO_GLOBALS
-#define GLOBAL
-#else
-#define GLOBAL extern
-#endif
-#endif
-
-#include <windows.h>
-#include "winstuff.h"
-#define PUTTY_PUTTYPS_H*/
-
 #include "putty.org.h"
 
 #ifndef PUTTY_ORIGINAL
 
 #if defined(PUTTY_LIB)
 
-// safemalloc() and saferealloc() in misc.c call MessageBox() directly
-// we want to raise exception instead.
-//#pragma option push -w-dup
-//#define MessageBox(A, Message, B, C) fatalbox(Message)
-//#pragma option pop
-
 long reg_open_winscp_key(HKEY Key, const char * SubKey, HKEY * Result);
 long reg_create_winscp_key(HKEY Key, const char * SubKey, HKEY * Result);
 

+ 3 - 0
putty/putty.org.h

@@ -429,6 +429,7 @@ struct config_tag {
     char answerback[256];
     char printer[128];
     /* Colour options */
+    int system_colour;
     int try_palette;
     int bold_colour;
     unsigned char colours[22][3];
@@ -470,6 +471,7 @@ struct config_tag {
     int stamp_utmp;
     int login_shell;
     int scrollbar_on_left;
+    int shadowbold;
     FontSpec boldfont;
     FontSpec widefont;
     FontSpec wideboldfont;
@@ -844,5 +846,6 @@ Filename filename_from_str(const char *string);
 const char *filename_to_str(const Filename *fn);
 int filename_equal(Filename f1, Filename f2);
 int filename_is_null(Filename fn);
+char *get_username(void);	       /* return value needs freeing */
 
 #endif

+ 0 - 7
putty/puttyps.bak.h

@@ -1,7 +0,0 @@
-#ifndef PUTTY_PUTTYPS_H
-#define PUTTY_PUTTYPS_H
-
-#include <windows.h>
-#include "winstuff.h"
-
-#endif

+ 1 - 0
resource/TextsCore.h

@@ -100,6 +100,7 @@
 #define SFTP_STATUS_WRITE_PROTECT 194
 #define SFTP_STATUS_NO_MEDIA    195
 #define DECODE_UTF_ERROR        196
+#define CUSTOM_COMMAND_ERROR    197
 #define CORE_CONFIRMATION_STRINGS 300
 #define CONFIRM_PROLONG_TIMEOUT 301
 #define PROMPT_SESSION_PASSWORD 302

BIN
resource/TextsCore.res


+ 2 - 0
resource/TextsCore1.rc

@@ -93,6 +93,7 @@
 #define SFTP_STATUS_WRITE_PROTECT 194
 #define SFTP_STATUS_NO_MEDIA    195
 #define DECODE_UTF_ERROR        196
+#define CUSTOM_COMMAND_ERROR    197
 
 #define CORE_CONFIRMATION_STRINGS 300
 #define CONFIRM_PROLONG_TIMEOUT 301
@@ -212,6 +213,7 @@ BEGIN
         SFTP_STATUS_WRITE_PROTECT, "Write protect."
         SFTP_STATUS_NO_MEDIA, "No media."
         DECODE_UTF_ERROR, "Error decoding UTF-8 string."
+        CUSTOM_COMMAND_ERROR, "Error executing custom command '%s' on file '%s'."
 
         CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
         CONFIRM_PROLONG_TIMEOUT, "Host hasn't answered for %d seconds.\n\nWait for another %0:d seconds? Pressing 'Abort' button will close session."

+ 184 - 169
resource/TextsWin.h

@@ -1,175 +1,190 @@
 #ifndef TextsWinH 
 #define TextsWinH 
  
-#define LICENCE         1001
-#define LICENCE_2       1002
-#define LICENCE_3       1003
-#define LICENCE_4       1004
-#define LICENCE_5       1005
-#define LICENCE_6       1006
-#define LICENCE_7       1007
-#define LICENCE_8       1008
-#define LICENCE_9       1009
-#define LICENCE_10      1010
-#define LICENCE_11      1011
-#define LICENCE_12      1012
-#define LICENCE_13      1013
-#define LICENCE_14      1014
-#define LICENCE_15      1015
-#define LICENCE_16      1016
-#define LICENCE_17      1017
-#define LICENCE_18      1018
-#define LICENCE_PUTTY   1021
-#define LICENCE_PUTTY_2 1022
+#define LICENCE         1001
+#define LICENCE_2       1002
+#define LICENCE_3       1003
+#define LICENCE_4       1004
+#define LICENCE_5       1005
+#define LICENCE_6       1006
+#define LICENCE_7       1007
+#define LICENCE_8       1008
+#define LICENCE_9       1009
+#define LICENCE_10      1010
+#define LICENCE_11      1011
+#define LICENCE_12      1012
+#define LICENCE_13      1013
+#define LICENCE_14      1014
+#define LICENCE_15      1015
+#define LICENCE_16      1016
+#define LICENCE_17      1017
+#define LICENCE_18      1018
+#define LICENCE_PUTTY   1021
+#define LICENCE_PUTTY_2 1022
  
-#define WIN_ERROR_STRINGS       1100
-#define MASK_ERROR              1101
-#define WARN_FATAL_ERROR        1102
-#define NEWSESSION_ERROR        1103
-#define LOADSESSION_ERROR       1104
-#define SESSION_NOT_EXISTS_ERROR 1105
-#define RECONNECT_ERROR         1106
-#define CREATE_SHORTCUT_ERROR   1107
-#define CANNOT_OVERWRITE_SPECIAL_SESSION 1108
-#define EXPLORE_LOCAL_DIR_ERROR 1109
-#define NO_UPLOAD_LIST_ERROR    1110
-#define CREATE_LOCAL_DIR_ERROR  1111
-#define DELETE_LOCAL_FILE_ERROR 1112
-#define DELETE_TEMP_EXECUTE_FILE_ERROR 1113
-#define EXECUTE_FILE_ERROR      1114
-#define EDITOR_ERROR            1115
-#define DOCUMENT_WAIT_ERROR     1116
-#define SYNC_DIR_BROWSE_ERROR   1117
-#define RESOLVE_SHORTCUT_ERROR  1118
-#define DUPLICATE_BOOKMARK      1119
-#define BOOKMARK_INVALID_NAME   1120
-#define BOOKMARK_FOLDER_INVALID_NAME 1121
-#define DUPLICATE_BOOKMARK_FOLDER 1122
-#define WIN_CONFIRMATION_STRINGS 1300
-#define CONFIRM_OVERWRITE_SESSION 1301
-#define CREATE_LOCAL_DIRECTORY  1302
-#define CANCEL_OPERATION        1303
-#define CANCEL_OPERATION_FATAL  1304
-#define CONFIRM_DELETE_FILE     1305
-#define CONFIRM_DELETE_FILES    1306
-#define CLOSE_SESSION           1307
-#define NEVER_ASK_AGAIN         1308
-#define DD_WARN_LACK_OF_TEMP_SPACE 1309
-#define DD_WARN_UNKNOWN_TEMP_SPACE 1310
-#define ADD_BOOKMARK_CONFIRM    1311
-#define RECONNECT_BUTTON        1312
-#define CONFIRM_CREATE_SHORTCUT 1313
-#define SET_DEFAULT_SESSION_SETTINGS 1314
-#define SKIP_BUTTON             1315
-#define RESUME_BUTTON           1316
-#define SAVE_CHANGES            1317
-#define CONFIRM_CREATE_SENDTO   1318
-#define CONFIRM_CREATE_ICON     1319
-#define CLOSE_SESSIONS          1320
-#define DELETE_BOOKMARK_FOLDER  1321
-#define WIN_INFORMATION_STRINGS 1400
-#define APP_CAPTION             1401
-#define COMPARE_NO_DIFFERENCES  1402
-#define SHORTCUT_INFO_TIP       1403
-#define DOCUMENT_WAIT           1404
-#define SESSION_SENDTO_HOOK_NAME 1405
-#define SENDTO_HOOK_NAME        1406
-#define BOOKMARK_INFO_TIP       1407
-#define WIN_STATUS_STRINGS      1450
-#define STATUS_CLOSED           1451
-#define STATUS_INITWINSOCK      1452
-#define STATUS_LOOKUPHOST       1453
-#define STATUS_CONNECT          1454
-#define STATUS_AUTHENTICATE     1455
-#define STATUS_AUTHENTICATED    1456
-#define STATUS_STARTUP          1457
-#define STATUS_OPEN_DIRECTORY   1458
-#define STATUS_READY            1459
-#define WIN_FORMS_STRINGS       1500
-#define LOG_NOLOG               1501
-#define LOG_NOLOGFILE           1502
-#define LOG_NOLOGCAPTION        1503
-#define LOG_CAPTION             1504
-#define COPY_FILE               1505
-#define COPY_FILES              1506
-#define COPY_COPY               1507
-#define COPY_MOVE               1508
-#define COPY_TOLOCAL            1509
-#define COPY_TOREMOTE           1510
-#define COPY_COPY_CAPTION       1511
-#define COPY_MOVE_CAPTION       1512
-#define COPY_TODROP             1513
-#define COPY_COPY_BUTTON        1514
-#define COPY_MOVE_BUTTON        1515
-#define PROGRESS_COPY           1516
-#define PROGRESS_MOVE           1517
-#define PROGRESS_DELETE         1518
-#define PROGRESS_SETPROPERTIES  1519
-#define PROGRESS_DRAGDROP_TARGET 1520
-#define NEW_FOLDER              1521
-#define CREATE_FOLDER_PROMPT    1522
-#define CREATE_FOLDER_CAPTION   1523
-#define SELECT_MASK_DESELECT_CAPTION 1524
-#define SELECT_MASK_SELECT_CAPTION 1525
-#define PROPERTIES_FILE         1526
-#define PROPERTIES_FILES        1527
-#define PROPERTIES_DIRECTORY    1528
-#define PROPERTIES_DIRECTORIES  1529
-#define PROPERTIES_SYMLINK      1530
-#define PROPERTIES_SYMLINKS     1531
-#define PROPERTIES_FILE_CAPTION 1532
-#define PROPERTIES_FILES_CAPTION 1533
-#define PROPERTIES_INVALID_GROUP 1534
-#define PROPERTIES_INVALID_OWNER 1535
-#define EXPLORER_BACK_HINT      1536
-#define EXPLORER_FORWARD_HINT   1537
-#define STATUS_SENT_HINT        1538
-#define STATUS_RECEIVED_HINT    1539
-#define STATUS_DURATION_HINT    1540
-#define STATUS_COMPRESSION_HINT 1541
-#define STATUS_ENCRYPTION_HINT  1542
-#define STATUS_VERSION_HINT     1543
-#define STATUS_FILEINFO_HINT    1544
-#define STATUS_COMPRESSION2_HINT 1545
-#define STATUS_ENCRYPTION2_HINT 1546
-#define SAVEDSESSION_HINT       1547
-#define STATUS_FS_PROTOCOL      1548
-#define LICENCE_CAPTION         1549
-#define SYCHRONIZE_DESCRIPTION  1550
-#define SYCHRONIZE_WAITING      1551
-#define CIPHER_NAME_WARN        1552
-#define CIPHER_NAME_3DES        1553
-#define CIPHER_NAME_BLOWFISH    1554
-#define CIPHER_NAME_AES         1555
-#define CIPHER_NAME_DES         1556
-#define OPEN_DIRECTORY_BROWSE_CAPTION 1557
-#define OPEN_DIRECTORY_ADD_BOOMARK_ACTION 1558
-#define RESUME_ENABLED          1559
-#define RESUME_DISABLED         1560
-#define RESUME_NOT_AVAILABLE    1561
-#define TRANSFER_ASCII          1562
-#define TRANSFER_BINARY         1563
-#define EDITOR_LINE_STATUS      1564
-#define EDITOR_COLUMN_STATUS    1565
-#define EDITOR_CHARACTER_STATUS 1566
-#define EDITOR_MODIFIED         1567
-#define EDITOR_FIND_END         1568
-#define EDITOR_REPLACE_END      1569
-#define EDITOR_GO_TO_LINE       1570
-#define EDITOR_LINE_NUMBER      1571
-#define EDITOR_INVALID_LINE     1572
-#define LINK_EDIT_CAPTION       1573
-#define LINK_ADD_CAPTION        1574
-#define STATUS_DISCONNECTED     1575
-#define STATUS_CONNECTING       1576
-#define OPENEDSESSION_HINT      1577
-#define ADD_BOOKMARK_CAPTION    1578
-#define ADD_BOOKMARK_PROMPT     1579
-#define MOVE_BOOKMARK_CAPTION   1580
-#define MOVE_BOOKMARK_PROMPT    1581
-#define SAVE_SESSION_CAPTION    1582
-#define SAVE_SESSION_PROMPT     1583
-#define RENAME_BOOKMARK_CAPTION 1584
-#define RENAME_BOOKMARK_PROMPT  1585
+#define WIN_ERROR_STRINGS       1100
+#define MASK_ERROR              1101
+#define WARN_FATAL_ERROR        1102
+#define NEWSESSION_ERROR        1103
+#define LOADSESSION_ERROR       1104
+#define SESSION_NOT_EXISTS_ERROR 1105
+#define RECONNECT_ERROR         1106
+#define CREATE_SHORTCUT_ERROR   1107
+#define CANNOT_OVERWRITE_SPECIAL_SESSION 1108
+#define EXPLORE_LOCAL_DIR_ERROR 1109
+#define NO_UPLOAD_LIST_ERROR    1110
+#define CREATE_LOCAL_DIR_ERROR  1111
+#define DELETE_LOCAL_FILE_ERROR 1112
+#define DELETE_TEMP_EXECUTE_FILE_ERROR 1113
+#define EXECUTE_FILE_ERROR      1114
+#define EDITOR_ERROR            1115
+#define DOCUMENT_WAIT_ERROR     1116
+#define SYNC_DIR_BROWSE_ERROR   1117
+#define RESOLVE_SHORTCUT_ERROR  1118
+#define DUPLICATE_BOOKMARK      1119
+#define BOOKMARK_INVALID_NAME   1120
+#define BOOKMARK_FOLDER_INVALID_NAME 1121
+#define DUPLICATE_BOOKMARK_FOLDER 1122
+#define CUSTOM_COMMAND_INVALID  1123
+#define CUSTOM_COMMAND_DUPLICATE 1124
+#define CHECK_FOR_UPDATES_HTTP  1125
+#define CHECK_FOR_UPDATES_ERROR 1126
+#define EXECUTE_APP_ERROR       1127
+#define FILE_NOT_FOUND          1128
+#define WIN_CONFIRMATION_STRINGS 1300
+#define CONFIRM_OVERWRITE_SESSION 1301
+#define CREATE_LOCAL_DIRECTORY  1302
+#define CANCEL_OPERATION        1303
+#define CANCEL_OPERATION_FATAL  1304
+#define CONFIRM_DELETE_FILE     1305
+#define CONFIRM_DELETE_FILES    1306
+#define CLOSE_SESSION           1307
+#define NEVER_ASK_AGAIN         1308
+#define DD_WARN_LACK_OF_TEMP_SPACE 1309
+#define DD_WARN_UNKNOWN_TEMP_SPACE 1310
+#define ADD_BOOKMARK_CONFIRM    1311
+#define RECONNECT_BUTTON        1312
+#define CONFIRM_CREATE_SHORTCUT 1313
+#define SET_DEFAULT_SESSION_SETTINGS 1314
+#define SKIP_BUTTON             1315
+#define SAVE_CHANGES            1316
+#define CONFIRM_CREATE_SENDTO   1317
+#define CONFIRM_CREATE_ICON     1318
+#define CLOSE_SESSIONS          1319
+#define DELETE_BOOKMARK_FOLDER  1320
+#define PREV_BUTTON             1321
+#define NEXT_BUTTON             1322
+#define WIN_INFORMATION_STRINGS 1400
+#define APP_CAPTION             1401
+#define COMPARE_NO_DIFFERENCES  1402
+#define SHORTCUT_INFO_TIP       1403
+#define DOCUMENT_WAIT           1404
+#define SESSION_SENDTO_HOOK_NAME 1405
+#define SENDTO_HOOK_NAME        1406
+#define BOOKMARK_INFO_TIP       1407
+#define CUSTOM_COMMAND_TOUCH    1408
+#define CUSTOM_COMMAND_EXECUTE  1409
+#define ERROR_LIST_COUNT        1410
+#define ERROR_LIST_NUMBER       1411
+#define NO_NEW_VERSION          1412
+#define NEW_VERSION             1413
+#define WIN_STATUS_STRINGS      1450
+#define STATUS_CLOSED           1451
+#define STATUS_INITWINSOCK      1452
+#define STATUS_LOOKUPHOST       1453
+#define STATUS_CONNECT          1454
+#define STATUS_AUTHENTICATE     1455
+#define STATUS_AUTHENTICATED    1456
+#define STATUS_STARTUP          1457
+#define STATUS_OPEN_DIRECTORY   1458
+#define STATUS_READY            1459
+#define WIN_FORMS_STRINGS       1500
+#define LOG_NOLOG               1501
+#define LOG_NOLOGFILE           1502
+#define LOG_NOLOGCAPTION        1503
+#define LOG_CAPTION             1504
+#define COPY_FILE               1505
+#define COPY_FILES              1506
+#define COPY_COPY               1507
+#define COPY_MOVE               1508
+#define COPY_TOLOCAL            1509
+#define COPY_TOREMOTE           1510
+#define COPY_COPY_CAPTION       1511
+#define COPY_MOVE_CAPTION       1512
+#define COPY_TODROP             1513
+#define COPY_COPY_BUTTON        1514
+#define COPY_MOVE_BUTTON        1515
+#define PROGRESS_COPY           1516
+#define PROGRESS_MOVE           1517
+#define PROGRESS_DELETE         1518
+#define PROGRESS_SETPROPERTIES  1519
+#define PROGRESS_DRAGDROP_TARGET 1520
+#define NEW_FOLDER              1521
+#define CREATE_FOLDER_PROMPT    1522
+#define CREATE_FOLDER_CAPTION   1523
+#define SELECT_MASK_DESELECT_CAPTION 1524
+#define SELECT_MASK_SELECT_CAPTION 1525
+#define PROPERTIES_FILE         1526
+#define PROPERTIES_FILES        1527
+#define PROPERTIES_DIRECTORY    1528
+#define PROPERTIES_DIRECTORIES  1529
+#define PROPERTIES_SYMLINK      1530
+#define PROPERTIES_SYMLINKS     1531
+#define PROPERTIES_FILE_CAPTION 1532
+#define PROPERTIES_FILES_CAPTION 1533
+#define PROPERTIES_INVALID_GROUP 1534
+#define PROPERTIES_INVALID_OWNER 1535
+#define EXPLORER_BACK_HINT      1536
+#define EXPLORER_FORWARD_HINT   1537
+#define STATUS_SENT_HINT        1538
+#define STATUS_RECEIVED_HINT    1539
+#define STATUS_DURATION_HINT    1540
+#define STATUS_COMPRESSION_HINT 1541
+#define STATUS_ENCRYPTION_HINT  1542
+#define STATUS_VERSION_HINT     1543
+#define STATUS_FILEINFO_HINT    1544
+#define STATUS_COMPRESSION2_HINT 1545
+#define STATUS_ENCRYPTION2_HINT 1546
+#define SAVEDSESSION_HINT       1547
+#define STATUS_FS_PROTOCOL      1548
+#define LICENCE_CAPTION         1549
+#define SYCHRONIZE_DESCRIPTION  1550
+#define SYCHRONIZE_WAITING      1551
+#define CIPHER_NAME_WARN        1552
+#define CIPHER_NAME_3DES        1553
+#define CIPHER_NAME_BLOWFISH    1554
+#define CIPHER_NAME_AES         1555
+#define CIPHER_NAME_DES         1556
+#define OPEN_DIRECTORY_BROWSE_CAPTION 1557
+#define OPEN_DIRECTORY_ADD_BOOMARK_ACTION 1558
+#define RESUME_ENABLED          1559
+#define RESUME_DISABLED         1560
+#define RESUME_NOT_AVAILABLE    1561
+#define TRANSFER_ASCII          1562
+#define TRANSFER_BINARY         1563
+#define EDITOR_LINE_STATUS      1564
+#define EDITOR_COLUMN_STATUS    1565
+#define EDITOR_CHARACTER_STATUS 1566
+#define EDITOR_MODIFIED         1567
+#define EDITOR_FIND_END         1568
+#define EDITOR_REPLACE_END      1569
+#define EDITOR_GO_TO_LINE       1570
+#define EDITOR_LINE_NUMBER      1571
+#define EDITOR_INVALID_LINE     1572
+#define LINK_EDIT_CAPTION       1573
+#define LINK_ADD_CAPTION        1574
+#define STATUS_DISCONNECTED     1575
+#define STATUS_CONNECTING       1576
+#define OPENEDSESSION_HINT      1577
+#define ADD_BOOKMARK_CAPTION    1578
+#define ADD_BOOKMARK_PROMPT     1579
+#define MOVE_BOOKMARK_CAPTION   1580
+#define MOVE_BOOKMARK_PROMPT    1581
+#define SAVE_SESSION_CAPTION    1582
+#define SAVE_SESSION_PROMPT     1583
+#define RENAME_BOOKMARK_CAPTION 1584
+#define RENAME_BOOKMARK_PROMPT  1585
+#define PROGRESS_CUSTOM_COMAND  1586
+#define CUSTOM_COMMAND_HINT     1587
  
 #endif // TextsWin 

BIN
resource/TextsWin.res


+ 37 - 7
resource/TextsWin1.rc

@@ -21,6 +21,12 @@
 #define BOOKMARK_INVALID_NAME   1120
 #define BOOKMARK_FOLDER_INVALID_NAME 1121
 #define DUPLICATE_BOOKMARK_FOLDER 1122
+#define CUSTOM_COMMAND_INVALID  1123
+#define CUSTOM_COMMAND_DUPLICATE 1124
+#define CHECK_FOR_UPDATES_HTTP  1125
+#define CHECK_FOR_UPDATES_ERROR 1126
+#define EXECUTE_APP_ERROR       1127
+#define FILE_NOT_FOUND          1128
 
 #define WIN_CONFIRMATION_STRINGS 1300
 #define CONFIRM_OVERWRITE_SESSION 1301
@@ -38,12 +44,13 @@
 #define CONFIRM_CREATE_SHORTCUT 1313
 #define SET_DEFAULT_SESSION_SETTINGS 1314
 #define SKIP_BUTTON             1315
-#define RESUME_BUTTON           1316
-#define SAVE_CHANGES            1317
-#define CONFIRM_CREATE_SENDTO   1318
-#define CONFIRM_CREATE_ICON     1319
-#define CLOSE_SESSIONS          1320
-#define DELETE_BOOKMARK_FOLDER  1321
+#define SAVE_CHANGES            1316
+#define CONFIRM_CREATE_SENDTO   1317
+#define CONFIRM_CREATE_ICON     1318
+#define CLOSE_SESSIONS          1319
+#define DELETE_BOOKMARK_FOLDER  1320
+#define PREV_BUTTON             1321
+#define NEXT_BUTTON             1322
 
 #define WIN_INFORMATION_STRINGS 1400
 #define APP_CAPTION             1401
@@ -53,6 +60,12 @@
 #define SESSION_SENDTO_HOOK_NAME 1405
 #define SENDTO_HOOK_NAME        1406
 #define BOOKMARK_INFO_TIP       1407
+#define CUSTOM_COMMAND_TOUCH    1408
+#define CUSTOM_COMMAND_EXECUTE  1409
+#define ERROR_LIST_COUNT        1410
+#define ERROR_LIST_NUMBER       1411
+#define NO_NEW_VERSION          1412
+#define NEW_VERSION             1413
 
 #define WIN_STATUS_STRINGS      1450
 #define STATUS_CLOSED           1451
@@ -151,6 +164,8 @@
 #define SAVE_SESSION_PROMPT     1583
 #define RENAME_BOOKMARK_CAPTION 1584
 #define RENAME_BOOKMARK_PROMPT  1585
+#define PROGRESS_CUSTOM_COMAND  1586
+#define CUSTOM_COMMAND_HINT     1587
 
 STRINGTABLE
 BEGIN
@@ -177,6 +192,12 @@ BEGIN
         BOOKMARK_INVALID_NAME, "'%s' is not valid Location Profile name."
         BOOKMARK_FOLDER_INVALID_NAME, "'%s' is not valid Location Profile folder name."
         DUPLICATE_BOOKMARK_FOLDER, "Location Profile folder with name '%s' already exists."
+        CUSTOM_COMMAND_INVALID, "Custom command description cannot contain '%s'."
+        CUSTOM_COMMAND_DUPLICATE, "Custom command with description '%s' already exists."
+        CHECK_FOR_UPDATES_ERROR, "Error checking for application updates."
+        CHECK_FOR_UPDATES_HTTP, "Cannot query application homepage for updates information."
+        EXECUTE_APP_ERROR, "Cannot execute '%s'."
+        FILE_NOT_FOUND, "File '%s' not found."
 
         WIN_CONFIRMATION_STRINGS, "WIN_CONFIRMATION"
         CONFIRM_OVERWRITE_SESSION, "Session with name '%s' already exists. Overwrite?"
@@ -196,18 +217,25 @@ BEGIN
         CONFIRM_CREATE_ICON, "Create selected icon/shortcut?"
         CLOSE_SESSIONS, "Terminate all sessions and close application?"
         DELETE_BOOKMARK_FOLDER, "Delete selected Location Profile folder?"
+        PREV_BUTTON, "&Previous"
+        NEXT_BUTTON, "&Next"
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         APP_CAPTION, "%s - %s"
         COMPARE_NO_DIFFERENCES, "No differences found."
         SHORTCUT_INFO_TIP, "Opens session '%s'\n%s"
         SKIP_BUTTON, "&Skip"
-        RESUME_BUTTON, "Resu&me"
         SAVE_CHANGES, "File was modified. Save changes?"
         DOCUMENT_WAIT, "Waiting for the document to close..."
         SESSION_SENDTO_HOOK_NAME, "%s (upload using SFTP or SCP)"
         SENDTO_HOOK_NAME, "%s (upload using SFTP or SCP)"
         BOOKMARK_INFO_TIP, "Local: %s\nRemote: %s"
+        CUSTOM_COMMAND_TOUCH, "&Touch"
+        CUSTOM_COMMAND_EXECUTE, "&Execute"
+        ERROR_LIST_COUNT, "%d error(s) occured during last operation. Do you want to see it/them?"
+        ERROR_LIST_NUMBER, "Error %d of %d:\n%s"
+        NO_NEW_VERSION, "You have the latest version."
+        NEW_VERSION, "New version %s was released. Do you want to open application download page?\n\nHint: Use Help menu to open application history page to see list of new features. "
 
         WIN_STATUS_STRINGS, "WIN_STATUS_STRINGS"
         STATUS_CLOSED, "Connection terminated."
@@ -307,4 +335,6 @@ BEGIN
         SAVE_SESSION_PROMPT, "&Save session as:"
         RENAME_BOOKMARK_CAPTION, "Rename Location Profile"
         RENAME_BOOKMARK_PROMPT, "New profile name"
+        PROGRESS_CUSTOM_COMAND, "Executing custom command"
+        CUSTOM_COMMAND_HINT, "Execute custom command '%s'"
 END

+ 22 - 19
windows/Bookmarks.cpp

@@ -90,26 +90,29 @@ void __fastcall TBookmarks::LoadLevel(THierarchicalStorage * Storage, const Ansi
       {
         Name = Directory;
       }
-      Bookmark = BookmarkList->FindByName(Key, Name);
-      bool New;
-      New = (Bookmark == NULL);
-      if (New)
+      if (!Name.IsEmpty())
       {
-        Bookmark = new TBookmark();
-        Bookmark->Node = Key;
-        Bookmark->Name = Name;
-      }
-      if (Local)
-      {
-        Bookmark->Local = Directory;
-      }
-      else
-      {
-        Bookmark->Remote = Directory;
-      }
-      if (New)
-      {
-        BookmarkList->Add(Bookmark);
+        Bookmark = BookmarkList->FindByName(Key, Name);
+        bool New;
+        New = (Bookmark == NULL);
+        if (New)
+        {
+          Bookmark = new TBookmark();
+          Bookmark->Node = Key;
+          Bookmark->Name = Name;
+        }
+        if (Local)
+        {
+          Bookmark->Local = Directory;
+        }
+        else
+        {
+          Bookmark->Remote = Directory;
+        }
+        if (New)
+        {
+          BookmarkList->Add(Bookmark);
+        }
       }
     }
 

+ 1 - 1
windows/Bookmarks.h

@@ -14,6 +14,7 @@ public:
   void __fastcall Load(THierarchicalStorage * Storage);
   void __fastcall Save(THierarchicalStorage * Storage);
   void __fastcall ModifyAll(bool Modify);
+  void __fastcall Clear();
 
   __property TBookmarkList * Bookmarks[AnsiString Index] = { read = GetBookmarks, write = SetBookmarks };
 
@@ -24,7 +25,6 @@ private:
   void __fastcall SetBookmarks(AnsiString Index, TBookmarkList * value);
   void __fastcall LoadLevel(THierarchicalStorage * Storage, const AnsiString Key,
     bool Local, TBookmarkList * BookmarkList);
-  void __fastcall Clear();
 };
 //---------------------------------------------------------------------------
 class TBookmarkList : public TPersistent

+ 35 - 33
windows/TerminalManager.cpp

@@ -96,7 +96,6 @@ void __fastcall TTerminalManager::FreeActiveTerminal()
 {
   if (FTerminalPendingAction == tpNull)
   {
-    //SaveActiveTerminal();
     assert(ActiveTerminal);
     FreeTerminal(ActiveTerminal);
   }
@@ -191,26 +190,6 @@ bool __fastcall TTerminalManager::ConnectActiveTerminal()
   return Result;
 }
 //---------------------------------------------------------------------------
-/*void __fastcall TTerminalManager::SaveActiveTerminal()
-{
-  assert(ActiveTerminal);
-
-  if (!ActiveTerminal->SessionData->Name.IsEmpty())
-  {
-    assert(ScpExplorer);
-    ScpExplorer->UpdateSessionData();
-
-    TSessionData * Data;
-    Data = (TSessionData *)StoredSessions->FindByName(
-      ActiveTerminal->SessionData->Name);
-    if (Data)
-    {
-      Data->Assign(ActiveTerminal->SessionData);
-      StoredSessions->Save();
-    }
-  }
-} */
-//---------------------------------------------------------------------------
 void __fastcall TTerminalManager::ReconnectActiveTerminal()
 {
   assert(ActiveTerminal);
@@ -309,17 +288,9 @@ void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal)
         ActiveTerminal = NULL;
       }
     }
-
-    if (!Terminal->SessionData->Name.IsEmpty())
+    else
     {
-      TSessionData * Data;
-      Data = (TSessionData *)StoredSessions->FindByName(
-        Terminal->SessionData->Name);
-      if (Data)
-      {
-        Data->Assign(Terminal->SessionData);
-        StoredSessions->Save();
-      }
+      SaveTerminal(Terminal);
     }
 
     delete Terminal;
@@ -394,6 +365,12 @@ void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value)
         ScpExplorer->Terminal = NULL;
       }
     }
+
+    if (PActiveTerminal && !PActiveTerminal->Active)
+    {
+      SaveTerminal(PActiveTerminal);
+    }
+
     if (ActiveTerminal)
     {
       if (!PActiveTerminal)
@@ -418,6 +395,20 @@ void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminalManager::SaveTerminal(TTerminal * Terminal)
+{
+  if (!Terminal->SessionData->Name.IsEmpty())
+  {
+    TSessionData * Data;
+    Data = (TSessionData *)StoredSessions->FindByName(Terminal->SessionData->Name);
+    if (Data)
+    {
+      Data->Assign(Terminal->SessionData);
+      StoredSessions->Save();
+    }
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminalManager::CreateLogMemo()
 {
   assert(!FLogMemo);
@@ -462,8 +453,19 @@ void __fastcall TTerminalManager::TerminalQueryUser(TObject * /*Sender*/,
   {
     MessageParams |= mpNeverAskAgainCheck;
   }
-  
-  Answer = MoreMessageDialog(AQuery, MoreMessages, Type, Answers, 0, MessageParams);
+  if (Params & qpAllowContinueOnError)
+  {
+    MessageParams |= mpAllowContinueOnError;
+  }
+
+  if (ScpExplorer)
+  {
+    Answer = ScpExplorer->MoreMessageDialog(AQuery, MoreMessages, Type, Answers, 0, MessageParams);
+  }
+  else
+  {
+    Answer = MoreMessageDialog(AQuery, MoreMessages, Type, Answers, 0, MessageParams);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalManager::ConfigurationChange(TObject * /*Sender*/)

+ 12 - 0
windows/TerminalManager.h

@@ -9,6 +9,17 @@ class TLogMemo;
 //---------------------------------------------------------------------------
 enum TTerminalPendingAction { tpNull, tpNone, tpReconnect, tpFree };
 //---------------------------------------------------------------------------
+class TTerminalUserObject : public TObject
+{
+public:
+  __fastcall TTerminalUserObject() : TObject() {};
+
+  __property AnsiString LocalDirectory = { read = FLocalDirectory, write = FLocalDirectory }; 
+
+private:
+  AnsiString FLocalDirectory;
+};
+//---------------------------------------------------------------------------
 class TTerminalManager : public TTerminalList
 {
 public:
@@ -64,6 +75,7 @@ private:
   TStrings * __fastcall GetTerminalList();
   int __fastcall GetActiveTerminalIndex();
   AnsiString __fastcall GetActiveTerminalTitle();
+  void __fastcall SaveTerminal(TTerminal * Terminal);
 };
 //---------------------------------------------------------------------------
 #endif

+ 6 - 2
windows/VCLCommon.cpp

@@ -10,17 +10,21 @@
 //---------------------------------------------------------------------------
 void __fastcall AdjustListColumnsWidth(TListView* ListView)
 {
-	Integer OriginalWidth, NewWidth, i, CWidth;
+  int OriginalWidth, NewWidth, i, CWidth;
 
   OriginalWidth = 0;
   for (i = 0; i < ListView->Columns->Count; i++)
+  {
   	OriginalWidth += ListView->Columns->Items[i]->Width;
+  }
 
   NewWidth = 0;
   CWidth = ListView->ClientWidth;
   if ((ListView->VisibleRowCount < ListView->Items->Count) &&
       (ListView->Width - ListView->ClientWidth < GetSystemMetrics(SM_CXVSCROLL)))
-     		CWidth -= GetSystemMetrics(SM_CXVSCROLL);
+  {
+  	CWidth -= GetSystemMetrics(SM_CXVSCROLL);
+  }
   for (i = 0; i < ListView->Columns->Count-1;i++)
   {
     if (ListView->Columns->Items[i]->Tag == 0)

+ 88 - 31
windows/WinConfiguration.cpp

@@ -4,9 +4,11 @@
 #include "WinConfiguration.h"
 #include "Common.h"
 #include "Bookmarks.h"
+#include "TextsWin.h"
 #include <stdio.h>
 //---------------------------------------------------------------------------
 #define CSIDL_PERSONAL                  0x0005        // My Documents
+#define CSIDL_PROGRAM_FILES             0x0026      // C:\Program Files
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -16,6 +18,7 @@ __fastcall TWinConfiguration::TWinConfiguration(): TGUIConfiguration()
 {
   FBookmarks = new TBookmarks();
   FCommandsHistory = new TStringList();
+  FCustomCommands = new TStringList();
   Default();
 }
 //---------------------------------------------------------------------------
@@ -26,6 +29,7 @@ __fastcall TWinConfiguration::~TWinConfiguration()
 
   delete FBookmarks;
   delete FCommandsHistory;
+  delete FCustomCommands;
 }
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::Default()
@@ -54,6 +58,11 @@ void __fastcall TWinConfiguration::Default()
   FAutoStartSession = "";
   FExpertMode = true;
   FUseLocationProfiles = true;
+  FContinueOnError = false;
+  FPuttySession = "WinSCP temporary session";
+  AnsiString ProgramsFolder;
+  SpecialFolderLocation(CSIDL_PROGRAM_FILES, ProgramsFolder);
+  FPuttyPath = IncludeTrailingBackslash(ProgramsFolder) + "PuTTY\\putty.exe";
 
   FEditor.Editor = edInternal;
   FEditor.ExternalEditor = "notepad.exe";
@@ -86,6 +95,7 @@ void __fastcall TWinConfiguration::Default()
   FScpCommander.StatusBar = true;
   FScpCommander.ToolBar = true;
   FScpCommander.ExplorerStyleSelection = false;
+  FScpCommander.PreserveLocalDirectory = false;
   FScpCommander.CoolBarLayout = "5,0,0,219,6;1,1,0,319,5;4,0,0,227,4;3,1,0,136,3;6,1,0,121,2;2,1,1,67,1;0,1,1,649,0";
   FScpCommander.CurrentPanel = osLocal;
   FScpCommander.RemotePanel.DirViewParams = "0;1;0|150,1;70,1;101,1;79,1;62,1;55,0|0;1;2;3;4;5";
@@ -95,33 +105,15 @@ void __fastcall TWinConfiguration::Default()
   FScpCommander.LocalPanel.StatusBar = true;
   FScpCommander.LocalPanel.CoolBarLayout = "2,1,0,137,2;1,1,0,86,1;0,1,1,91,0";
 
-}
-//---------------------------------------------------------------------------
-void __fastcall TWinConfiguration::Load()
-{
-  TGUIConfiguration::Load();
-
-  TRegistry * Registry = new TRegistry();
-  try
-  {
-    Registry->Access = KEY_READ;
-    Registry->RootKey = HKEY_LOCAL_MACHINE;
-    FDisableOpenEdit = false;
-    if (Registry->OpenKey(RegistryStorageKey, false))
-    {
-      try
-      {
-        FDisableOpenEdit = Registry->ReadBool("DisableOpenEdit");
-      }
-      catch(...)
-      {
-      }
-    }
-  }
-  __finally
-  {
-    delete Registry;
-  }
+  FBookmarks->Clear();
+  FCommandsHistory->Clear();
+  FCommandsHistoryModified = false;
+  FCustomCommands->Clear();
+  FCustomCommands->Values[LoadStr(CUSTOM_COMMAND_EXECUTE)] =
+    FORMAT("\"%s\"", (CustomCommandFileNamePattern));
+  FCustomCommands->Values[LoadStr(CUSTOM_COMMAND_TOUCH)] =
+    FORMAT("touch \"%s\"", (CustomCommandFileNamePattern));
+  FCustomCommandsModified = false;
 }
 //---------------------------------------------------------------------------
 TStorage __fastcall TWinConfiguration::GetStorage()
@@ -148,6 +140,8 @@ void __fastcall TWinConfiguration::ModifyAll()
 {
   TGUIConfiguration::ModifyAll();
   FBookmarks->ModifyAll(true);
+  FCommandsHistoryModified = true;
+  FCustomCommandsModified = true;
 }
 //---------------------------------------------------------------------------
 THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
@@ -189,6 +183,9 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(Bool,     ConfirmClosingSession); \
     KEY(String,   AutoStartSession); \
     KEY(Bool,     UseLocationProfiles); \
+    KEY(Bool,     ContinueOnError); \
+    KEY(String,   PuttyPath); \
+    KEY(String,   PuttySession); \
   ); \
   BLOCK("Interface\\Editor", CANCREATE, \
     KEY(Integer,  Editor.Editor); \
@@ -220,6 +217,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(Bool,    ScpCommander.ToolBar); \
     KEY(String,  ScpCommander.WindowParams); \
     KEY(Bool,    ScpCommander.ExplorerStyleSelection); \
+    KEY(Bool,    ScpCommander.PreserveLocalDirectory); \
   ); \
   BLOCK("Interface\\Commander\\LocalPanel", CANCREATE, \
     KEY(String, ScpCommander.LocalPanel.CoolBarLayout); \
@@ -252,10 +250,17 @@ void __fastcall TWinConfiguration::SaveSpecial(THierarchicalStorage * Storage)
 
     Storage->CloseSubKey();
   }
-  if (Storage->OpenSubKey("Commands", true))
+  if (FCommandsHistoryModified && Storage->OpenSubKey("Commands", true))
   {
     Storage->WriteValues(FCommandsHistory);
     Storage->CloseSubKey();
+    FCommandsHistoryModified = false;
+  }
+  if (FCustomCommandsModified && Storage->OpenSubKey("CustomCommands", true))
+  {
+    Storage->WriteValues(FCustomCommands, true);
+    Storage->CloseSubKey();
+    FCustomCommandsModified = false;
   }
 }
 //---------------------------------------------------------------------------
@@ -275,12 +280,36 @@ void __fastcall TWinConfiguration::LoadSpecial(THierarchicalStorage * Storage)
     FBookmarks->Load(Storage);
     Storage->CloseSubKey();
   }
-  FCommandsHistory->Clear();
+
   if (Storage->OpenSubKey("Commands", false))
   {
+    FCommandsHistory->Clear();
     Storage->ReadValues(FCommandsHistory);
     Storage->CloseSubKey();
   }
+  else if (FCommandsHistoryModified)
+  {
+    FCommandsHistory->Clear();
+  }
+  FCommandsHistoryModified = false;
+
+  if (Storage->OpenSubKey("CustomCommands", false))
+  {
+    FCustomCommands->Clear();
+    Storage->ReadValues(FCustomCommands, true);
+    Storage->CloseSubKey();
+  }
+  else if (FCustomCommandsModified)
+  {
+    FCustomCommands->Clear();
+  }
+  FCustomCommandsModified = false;
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::LoadAdmin(THierarchicalStorage * Storage)
+{
+  TConfiguration::LoadAdmin(Storage);
+  FDisableOpenEdit = Storage->ReadBool("DisableOpenEdit", FDisableOpenEdit);
 }
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::ClearTemporaryLoginData()
@@ -476,6 +505,11 @@ void __fastcall TWinConfiguration::SetUseLocationProfiles(bool value)
   SET_CONFIG_PROPERTY(UseLocationProfiles);
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetContinueOnError(bool value)
+{
+  SET_CONFIG_PROPERTY(ContinueOnError);
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetConfirmClosingSession(bool value)
 {
   SET_CONFIG_PROPERTY(ConfirmClosingSession);
@@ -506,10 +540,34 @@ void __fastcall TWinConfiguration::SetExpertMode(bool value)
   SET_CONFIG_PROPERTY(ExpertMode);
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetPuttyPath(const AnsiString value)
+{
+  SET_CONFIG_PROPERTY(PuttyPath);
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetPuttySession(const AnsiString value)
+{
+  SET_CONFIG_PROPERTY(PuttySession);
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetCommandsHistory(TStrings * value)
 {
   assert(FCommandsHistory);
-  FCommandsHistory->Assign(value);
+  if (!FCommandsHistory->Equals(value))
+  {
+    FCommandsHistory->Assign(value);
+    FCommandsHistoryModified = true;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetCustomCommands(TStrings * value)
+{
+  assert(FCustomCommands);
+  if (!FCustomCommands->Equals(value))
+  {
+    FCustomCommands->Assign(value);
+    FCustomCommandsModified = true;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetBookmarks(AnsiString Key,
@@ -540,4 +598,3 @@ AnsiString __fastcall TWinConfiguration::GetDefaultKeyFile()
   return FTemporaryKeyFile;
 }
 
-

+ 18 - 2
windows/WinConfiguration.h

@@ -39,12 +39,13 @@ struct TScpCommanderConfiguration {
   bool ToolBar;
   TOperationSide CurrentPanel;
   bool ExplorerStyleSelection;
+  bool PreserveLocalDirectory;
   TScpCommanderPanelConfiguration LocalPanel;
   TScpCommanderPanelConfiguration RemotePanel;
   bool __fastcall operator !=(TScpCommanderConfiguration & rhc)
     { return C(WindowParams) C(LocalPanelWidth) C(CoolBarLayout) C(StatusBar)
       C(LocalPanel) C(RemotePanel) C(CurrentPanel) C(ToolBar)
-      C(ExplorerStyleSelection) 0; };
+      C(ExplorerStyleSelection) C(PreserveLocalDirectory) 0; };
 };
 //---------------------------------------------------------------------------
 struct TEditorConfiguration {
@@ -80,6 +81,7 @@ private:
   bool FDDTransferConfirmation;
   bool FDeleteToRecycleBin;
   bool FDimmHiddenFiles;
+  bool FContinueOnError;
   TLogView FLogView;
   bool FLogWindowOnStartup;
   AnsiString FLogWindowParams;
@@ -101,10 +103,15 @@ private:
   AnsiString FTemporaryKeyFile;
   TBookmarks * FBookmarks;
   TStrings * FCommandsHistory;
+  bool FCommandsHistoryModified;
+  TStrings * FCustomCommands;
+  bool FCustomCommandsModified;
   TEditorConfiguration FEditor;
   bool FEmbeddedSessions;
   bool FExpertMode;
   bool FDisableOpenEdit;
+  AnsiString FPuttyPath;
+  AnsiString FPuttySession;
 
   void __fastcall SetCopyOnDoubleClick(bool value);
   void __fastcall SetCopyOnDoubleClickConfirmation(bool value);
@@ -125,6 +132,7 @@ private:
   void __fastcall SetShowAdvancedLoginOptions(bool value);
   void __fastcall SetConfirmDeleting(bool value);
   void __fastcall SetUseLocationProfiles(bool value);
+  void __fastcall SetContinueOnError(bool value);
   void __fastcall SetDDTemporaryDirectory(AnsiString value);
   void __fastcall SetDDWarnLackOfTempSpace(bool value);
   void __fastcall SetConfirmClosingSession(bool value);
@@ -135,6 +143,9 @@ private:
   void __fastcall SetCommandsHistory(TStrings * value);
   void __fastcall SetExpertMode(bool value);
   void __fastcall SetEditor(TEditorConfiguration value);
+  void __fastcall SetCustomCommands(TStrings * value);
+  void __fastcall SetPuttyPath(const AnsiString value);
+  void __fastcall SetPuttySession(const AnsiString value);
 
 protected:
   virtual TStorage __fastcall GetStorage();
@@ -142,14 +153,15 @@ protected:
     const AnsiString ResName, const AnsiString FileName);
   virtual void __fastcall SaveSpecial(THierarchicalStorage * Storage);
   virtual void __fastcall LoadSpecial(THierarchicalStorage * Storage);
+  virtual void __fastcall LoadAdmin(THierarchicalStorage * Storage);
   virtual AnsiString __fastcall GetDefaultKeyFile();
   virtual void __fastcall ModifyAll();
+  bool __fastcall SameStringLists(TStrings * Strings1, TStrings * Strings2); 
 
 public:
   __fastcall TWinConfiguration();
   __fastcall ~TWinConfiguration();
   virtual void __fastcall Default();
-  virtual void __fastcall Load();
   void __fastcall RestoreForm(AnsiString Data, TCustomForm * Form);
   AnsiString __fastcall StoreForm(TCustomForm * Form);
   void __fastcall ClearTemporaryLoginData();
@@ -178,6 +190,7 @@ public:
   __property AnsiString MaskHistory = { read = FMaskHistory, write = SetMaskHistory };
   __property bool ConfirmDeleting = { read = FConfirmDeleting, write = SetConfirmDeleting};
   __property bool UseLocationProfiles = { read = FUseLocationProfiles, write = SetUseLocationProfiles};
+  __property bool ContinueOnError = { read = FContinueOnError, write = SetContinueOnError};
   __property AnsiString DDTemporaryDirectory  = { read=FDDTemporaryDirectory, write=SetDDTemporaryDirectory };
   __property bool DDWarnLackOfTempSpace  = { read=FDDWarnLackOfTempSpace, write=SetDDWarnLackOfTempSpace };
   __property bool ConfirmClosingSession  = { read=FConfirmClosingSession, write=SetConfirmClosingSession };
@@ -187,6 +200,9 @@ public:
   __property bool EmbeddedSessions = { read = FEmbeddedSessions };
   __property bool ExpertMode = { read = FExpertMode, write = SetExpertMode };
   __property bool DisableOpenEdit = { read = FDisableOpenEdit };
+  __property TStrings * CustomCommands = { read = FCustomCommands, write = SetCustomCommands };
+  __property AnsiString PuttyPath = { read = FPuttyPath, write = SetPuttyPath };
+  __property AnsiString PuttySession = { read = FPuttySession, write = SetPuttySession };
 };
 //---------------------------------------------------------------------------
 #define WinConfiguration ((TWinConfiguration *) Configuration)

+ 49 - 36
windows/WinInterface.cpp

@@ -15,8 +15,7 @@
 #include "WinConfiguration.h"
 #include "TerminalManager.h"
 
-#define mrResume (mrYesToAll    + 1)
-#define mrCustom (mrResume  + 1)
+#define mrCustom (mrYesToAll + 1)
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -118,10 +117,21 @@ TForm * CreateMessageDialogEx(const AnsiString Msg, TQueryType Type,
     Buttons << mbIgnore;
   }
 
-  if ((Answers & qaResume) || (Answers & qaCustom))
+  if (Answers & qaPrev)
+  {
+    assert((Answers & qaYes) == 0);
+    Buttons << mbYes;
+  }
+
+  if (Answers & qaNext)
+  {
+    assert((Answers & qaNo) == 0);
+    Buttons << mbNo;
+  }
+
+  if (Answers & qaCustom)
   {
     assert((Answers & qaHelp) == 0);
-    assert(((Answers & qaResume) == 0) || ((Answers & qaCustom) == 0));
     Buttons << mbHelp;
   }
 
@@ -133,33 +143,39 @@ TForm * CreateMessageDialogEx(const AnsiString Msg, TQueryType Type,
   {
     if (Answers & qaSkip)
     {
-      TButton * IgnoreButton = (TButton*)(Dialog->FindComponent("Ignore"));
+      TButton * IgnoreButton = dynamic_cast<TButton *>(Dialog->FindComponent("Ignore"));
       assert(IgnoreButton);
       IgnoreButton->Caption = LoadStr(SKIP_BUTTON);
     }
 
-    if ((Answers & qaResume) || (Answers & qaCustom))
+    if (Answers & qaPrev)
     {
-      TButton * HelpButton = (TButton*)(Dialog->FindComponent("Help"));
+      TButton * YesButton = dynamic_cast<TButton *>(Dialog->FindComponent("Yes"));
+      assert(YesButton);
+      YesButton->Caption = LoadStr(PREV_BUTTON);
+    }
+
+    if (Answers & qaNext)
+    {
+      TButton * NoButton = dynamic_cast<TButton *>(Dialog->FindComponent("No"));
+      assert(NoButton);
+      NoButton->Caption = LoadStr(NEXT_BUTTON);
+    }
+
+    if (Answers & qaCustom)
+    {
+      TButton * HelpButton = dynamic_cast<TButton *>(Dialog->FindComponent("Help"));
       assert(HelpButton);
-      if (Answers & qaResume)
-      {
-        HelpButton->Caption = LoadStr(RESUME_BUTTON);
-        HelpButton->ModalResult = mrResume;
-      }
-      else
-      {
-        HelpButton->Name = "Custom";
-        HelpButton->ModalResult = mrCustom;
-      }
+      HelpButton->Name = "Custom";
+      HelpButton->ModalResult = mrCustom;
     }
 
     // temporary fix of accelerators (&Abort vs. &All/Yes to &All)
     // must be removed
     for (int Index = 0; Index < Dialog->ComponentCount; Index++)
     {
-      TButton * B = (TButton *)Dialog->Components[Index];
-      if (B->InheritsFrom(__classid(TButton)))
+      TButton * B = dynamic_cast<TButton *>(Dialog->Components[Index]);
+      if (B)
       {
         if (B->Caption == "&All") B->Caption = "A&ll";
           else
@@ -199,8 +215,6 @@ int ExecuteMessageDialog(TForm * Dialog, int Answers, int Params)
 
   switch (Result) {
     #define MAP_RESULT(RESULT) case mr ## RESULT: Answer = qa ## RESULT; break;
-    MAP_RESULT(Yes);
-    MAP_RESULT(No);
     MAP_RESULT(Cancel);
     MAP_RESULT(Abort);
     MAP_RESULT(Retry);
@@ -208,7 +222,6 @@ int ExecuteMessageDialog(TForm * Dialog, int Answers, int Params)
     MAP_RESULT(NoToAll);
     MAP_RESULT(YesToAll);
 
-    MAP_RESULT(Resume);
     MAP_RESULT(Custom);
     #undef MAP_RESULT
 
@@ -217,21 +230,22 @@ int ExecuteMessageDialog(TForm * Dialog, int Answers, int Params)
       break;
 
     case mrIgnore:
-      if (Answers & qaSkip)
-      {
-        Answer = qaSkip;
-      }
-      else
-      {
-        Answer = qaIgnore;
-      }
+      Answer = (Answers & qaSkip) ? qaSkip : qaIgnore;
+      break;
+
+    case mrYes:
+      Answer = (Answers & qaPrev) ? qaPrev : qaYes;
+      break;
+
+    case mrNo:
+      Answer = (Answers & qaNext) ? qaNext : qaNo;
       break;
   }
 
   if (Params & mpNeverAskAgainCheck)
   {
     TCheckBox * NeverAskAgainCheck =
-      (TCheckBox *)(Dialog->FindComponent("NeverAskAgainCheck"));
+      dynamic_cast<TCheckBox *>(Dialog->FindComponent("NeverAskAgainCheck"));
     assert(NeverAskAgainCheck);
 
     if (NeverAskAgainCheck->Checked &&
@@ -241,7 +255,7 @@ int ExecuteMessageDialog(TForm * Dialog, int Answers, int Params)
     }
   }
 
-  TMoreButton * MoreButton = (TMoreButton *)(Dialog->FindComponent("Custom"));
+  TMoreButton * MoreButton = dynamic_cast<TMoreButton *>(Dialog->FindComponent("MoreButton"));
   if (MoreButton)
   {
     // store state even when user selects 'Cancel'?
@@ -283,8 +297,8 @@ TForm * __fastcall CreateMoreMessageDialog(const AnsiString Message,
     Dialog = CreateMessageDialogEx(Message, Type, Answers, HelpCtx, Params);
     try
     {
-      TButton * CustomButton = (TButton*)(Dialog->FindComponent("Custom"));
-      TLabel * MsgLabel = (TLabel*)(Dialog->FindComponent("Message"));
+      TButton * CustomButton = dynamic_cast<TButton *>(Dialog->FindComponent("Custom"));
+      TLabel * MsgLabel = dynamic_cast<TLabel *>(Dialog->FindComponent("Message"));
       assert(MsgLabel && CustomButton);
 
       int OrigButtonTop = CustomButton->Top;
@@ -408,7 +422,7 @@ int __fastcall FatalExceptionMessageDialog(Exception * E,
     Answers, HelpCtx, 0);
   try
   {
-    TButton * RetryButton = (TButton *)(Dialog->FindComponent("Retry"));
+    TButton * RetryButton = dynamic_cast<TButton *>(Dialog->FindComponent("Retry"));
     assert(RetryButton);
     RetryButton->Caption = LoadStr(RECONNECT_BUTTON);
 
@@ -455,4 +469,3 @@ AnsiString __fastcall SshVersionString()
 {
   return FORMAT("WinSCP-release-%s", (Configuration->Version));
 }
-

+ 6 - 2
windows/WinInterface.h

@@ -9,7 +9,8 @@ class TStoredSessionList;
 class TConfiguration;
 class TTerminal;
 
-const int mpNeverAskAgainCheck = 1;
+const int mpNeverAskAgainCheck =   0x01;
+const int mpAllowContinueOnError = 0x02;
 
 // windows\WinInterface.cpp
 int __fastcall MessageDialog(const AnsiString Msg, TQueryType Type,
@@ -80,7 +81,7 @@ bool __fastcall LocationProfilesDialog(TOpenDirectoryMode Mode,
   TStrings * RemoteDirectories, TTerminal * Terminal);
 
 // forms\Preferences.cpp
-enum TPreferencesMode { pmDefault, pmLogin, pmEditor };
+enum TPreferencesMode { pmDefault, pmLogin, pmEditor, pmCustomCommands };
 typedef void __fastcall (__closure *TGetDefaultLogFileName)
   (System::TObject* Sender, AnsiString &DefaultLogFileName);
 bool __fastcall DoPreferencesDialog(TPreferencesMode APreferencesMode);
@@ -134,5 +135,8 @@ void __fastcall DoEditorForm(const AnsiString FileName, TCustomForm * ParentForm
 bool __fastcall DoSymlinkDialog(AnsiString & FileName, AnsiString & PointTo,
   TOperationSide Side, bool & SymbolicLink, bool Edit, bool AllowSymbolic);
 
+// windows\WinMain.cpp
+void __fastcall CheckForUpdates();
+
 //---------------------------------------------------------------------------
 #endif // WinInterfaceH

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