浏览代码

File checksum calculation support for SCP protocol and SFTP protocol via secondary shell session using shell commands like sha256sum

Part of Bug 52
https://winscp.net/tracker/52

Source commit: 6804a5d18405a91e8d0a2e0beccf2c31d43ed3f1
Martin Prikryl 3 年之前
父节点
当前提交
b4c490b497

+ 1 - 0
source/core/Configuration.cpp

@@ -120,6 +120,7 @@ void __fastcall TConfiguration::Default()
   FParallelDurationThreshold = 10;
   FMimeTypes = UnicodeString();
   FCertificateStorage = EmptyStr;
+  FChecksumCommands = EmptyStr;
   FDontReloadMoreThanSessions = 1000;
   FScriptProgressFileNameLimit = 25;
   FKeyVersion = 0;

+ 2 - 0
source/core/Configuration.h

@@ -81,6 +81,7 @@ private:
   int FScriptProgressFileNameLimit;
   int FKeyVersion;
   UnicodeString FCertificateStorage;
+  UnicodeString FChecksumCommands;
 
   bool FDisablePasswordStoring;
   bool FForceBanners;
@@ -331,6 +332,7 @@ public:
   __property UnicodeString ExternalIpAddress = { read = FExternalIpAddress, write = SetExternalIpAddress };
   __property UnicodeString CertificateStorage = { read = FCertificateStorage, write = SetCertificateStorage };
   __property UnicodeString CertificateStorageExpanded = { read = GetCertificateStorageExpanded };
+  __property UnicodeString ChecksumCommands = { read = FChecksumCommands };
   __property int LocalPortNumberMin = { read = FLocalPortNumberMin, write = SetLocalPortNumberMin };
   __property int LocalPortNumberMax = { read = FLocalPortNumberMax, write = SetLocalPortNumberMax };
   __property bool TryFtpWhenSshFails = { read = FTryFtpWhenSshFails, write = SetTryFtpWhenSshFails };

+ 2 - 1
source/core/Queue.cpp

@@ -1213,8 +1213,9 @@ private:
 __fastcall TBackgroundTerminal::TBackgroundTerminal(TTerminal * MainTerminal,
     TSessionData * SessionData, TConfiguration * Configuration, TTerminalItem * Item,
     const UnicodeString & Name) :
-  TSecondaryTerminal(MainTerminal, SessionData, Configuration, Name), FItem(Item)
+  TSecondaryTerminal(MainTerminal, SessionData, Configuration, Name, NULL), FItem(Item)
 {
+  ActionLog->Enabled = false;
 }
 //---------------------------------------------------------------------------
 bool __fastcall TBackgroundTerminal::DoQueryReopen(Exception * /*E*/)

+ 185 - 8
source/core/ScpFileSystem.cpp

@@ -442,6 +442,7 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
     case fcRemoteCopy:
     case fcRemoveCtrlZUpload:
     case fcRemoveBOMUpload:
+    case fcCalculatingChecksum:
       return true;
 
     case fcTextMode:
@@ -454,7 +455,6 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
     case fcLoadingAdditionalProperties:
     case fcCheckingSpaceAvailable:
     case fcIgnorePermErrors:
-    case fcCalculatingChecksum:
     case fcSecondaryShell: // has fcShellAnyCommand
     case fcGroupOwnerChangingByID: // by name
     case fcMoveToQueue:
@@ -681,6 +681,11 @@ void __fastcall TSCPFileSystem::ExecCommand(const UnicodeString & Cmd, int Param
   ReadCommandOutput(COParams, &CmdString);
 }
 //---------------------------------------------------------------------------
+void TSCPFileSystem::InvalidOutputError(const UnicodeString & Command)
+{
+  FTerminal->TerminalError(FMTLOAD(INVALID_OUTPUT_ERROR, (Command, Output->Text)));
+}
+//---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::ExecCommand(TFSCommand Cmd, const TVarRec * args,
   int size, int Params)
 {
@@ -695,8 +700,7 @@ void __fastcall TSCPFileSystem::ExecCommand(TFSCommand Cmd, const TVarRec * args
     if (((MinL >= 0) && (MinL > FOutput->Count)) ||
         ((MaxL >= 0) && (MaxL > FOutput->Count)))
     {
-      FTerminal->TerminalError(FmtLoadStr(INVALID_OUTPUT_ERROR,
-        ARRAYOFCONST((FullCommand, Output->Text))));
+      InvalidOutputError(FullCommand);
     }
   }
 }
@@ -1299,11 +1303,184 @@ bool __fastcall TSCPFileSystem::LoadFilesProperties(TStrings * /*FileList*/ )
   return false;
 }
 //---------------------------------------------------------------------------
+UnicodeString TSCPFileSystem::CalculateFilesChecksumInitialize(const UnicodeString & Alg)
+{
+  std::unique_ptr<TStrings> Algs(new TStringList());
+  GetSupportedChecksumAlgs(Algs.get());
+  return FindIdent(Alg, Algs.get());
+}
+//---------------------------------------------------------------------------
+UnicodeString TSCPFileSystem::ParseFileChecksum(
+  const UnicodeString & Line, const UnicodeString & FileName, const UnicodeString & Command)
+{
+  int P = Line.Pos(L" ");
+  if ((P < 0) ||
+      (P == Line.Length()) ||
+      ((Line[P + 1] != L' ') && (Line[P + 1] != L'*')) ||
+      (Line.SubString(P + 2, Line.Length() - P - 1) != FileName))
+  {
+    InvalidOutputError(Command);
+  }
+  return Line.SubString(1, P - 1);
+}
+//---------------------------------------------------------------------------
+void TSCPFileSystem::ProcessFileChecksum(
+  TCalculatedChecksumEvent OnCalculatedChecksum, TChecksumSessionAction & Action, TFileOperationProgressType * OperationProgress,
+  bool FirstLevel, const UnicodeString & FileName, const UnicodeString & Alg, const UnicodeString & Checksum)
+{
+  bool Success = !Checksum.IsEmpty();
+  if (Success)
+  {
+    if (OnCalculatedChecksum != NULL)
+    {
+      OnCalculatedChecksum(UnixExtractFileName(FileName), Alg, Checksum);
+    }
+    Action.Checksum(Alg, Checksum);
+  }
+  if (FirstLevel)
+  {
+    TOnceDoneOperation OnceDoneOperation; // not used
+    OperationProgress->Finish(FileName, Success, OnceDoneOperation);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::CalculateFilesChecksum(
-  const UnicodeString & DebugUsedArg(Alg), TStrings * DebugUsedArg(FileList), TCalculatedChecksumEvent,
-  TFileOperationProgressType *, bool DebugUsedArg(FirstLevel))
+  const UnicodeString & Alg, TStrings * FileList, TCalculatedChecksumEvent OnCalculatedChecksum,
+  TFileOperationProgressType * OperationProgress, bool FirstLevel)
 {
-  DebugFail();
+  FTerminal->CalculateSubFoldersChecksum(Alg, FileList, OnCalculatedChecksum, OperationProgress, FirstLevel);
+
+  TStrings * AlgDefs = FTerminal->GetShellChecksumAlgDefs();
+  int AlgIndex = AlgDefs->IndexOfName(Alg);
+  UnicodeString AlgCommand = (AlgIndex >= 0) ? AlgDefs->ValueFromIndex[AlgIndex] : Alg;
+
+  int Index = 0;
+  while ((Index < FileList->Count) && !OperationProgress->Cancel)
+  {
+    std::unique_ptr<TStrings> BatchFileList(new TStringList());
+    UnicodeString FileListCommandLine;
+    __int64 BatchSize = 0;
+    while (Index < FileList->Count)
+    {
+      TRemoteFile * File = DebugNotNull(dynamic_cast<TRemoteFile *>(FileList->Objects[Index]));
+      if (!File->IsDirectory)
+      {
+        UnicodeString FileName = FileList->Strings[Index];
+        UnicodeString FileListCommandLineBak = FileListCommandLine;
+        AddToShellFileListCommandLine(FileListCommandLine, FileName);
+        BatchSize += File->Size;
+        if (!FileListCommandLineBak.IsEmpty() &&
+            ((FileListCommandLine.Length() > 2048) ||
+             (BatchSize > 1024*1024*1024)))
+        {
+          FileListCommandLine = FileListCommandLineBak;
+          break;
+        }
+        else
+        {
+          BatchFileList->AddObject(FileName, File);
+        }
+      }
+      Index++;
+    }
+
+    if (!FileListCommandLine.IsEmpty())
+    {
+      bool IndividualCommands = false;
+      std::unique_ptr<TStrings> BatchChecksums(new TStringList());
+      try
+      {
+        UnicodeString BatchCommand = FORMAT(L"%s %s", (AlgCommand, FileListCommandLine));
+        AnyCommand(BatchCommand, NULL);
+        if (Output->Count != BatchFileList->Count)
+        {
+          InvalidOutputError(BatchCommand);
+        }
+
+        // First do everything that can throw.
+        // Only once everything is checked, distribute the results.
+        for (int BatchIndex = 0; BatchIndex < BatchFileList->Count; BatchIndex++)
+        {
+          UnicodeString FileName = BatchFileList->Strings[BatchIndex];
+          UnicodeString Line = Output->Strings[BatchIndex];
+          UnicodeString Checksum = ParseFileChecksum(Line, FileName, BatchCommand);
+          BatchChecksums->Add(Checksum);
+        }
+      }
+      catch (Exception & E)
+      {
+        if (!FTerminal->Active)
+        {
+          throw;
+        }
+        else
+        {
+          IndividualCommands = true;
+        }
+      }
+
+      if (!IndividualCommands)
+      {
+        // None of this should throw
+        for (int BatchIndex = 0; BatchIndex < BatchFileList->Count; BatchIndex++)
+        {
+          UnicodeString FileName = BatchFileList->Strings[BatchIndex];
+          TRemoteFile * File = DebugNotNull(dynamic_cast<TRemoteFile *>(BatchFileList->Objects[BatchIndex]));
+          TChecksumSessionAction Action(FTerminal->ActionLog);
+          Action.FileName(File->FullFileName);
+          OperationProgress->SetFile(FileName);
+          UnicodeString Checksum = BatchChecksums->Strings[BatchIndex];
+          ProcessFileChecksum(OnCalculatedChecksum, Action, OperationProgress, FirstLevel, FileName, Alg, Checksum);
+        }
+      }
+      else
+      {
+        FTerminal->LogEvent(
+          L"Batch checksum calculation failed, falling back to calculating checksum individually for each file...");
+
+        for (int BatchIndex = 0; BatchIndex < BatchFileList->Count; BatchIndex++)
+        {
+          TRemoteFile * File = DebugNotNull(dynamic_cast<TRemoteFile *>(BatchFileList->Objects[BatchIndex]));
+          TChecksumSessionAction Action(FTerminal->ActionLog);
+          try
+          {
+            UnicodeString FileName = BatchFileList->Strings[BatchIndex];
+            OperationProgress->SetFile(FileName);
+            Action.FileName(File->FullFileName);
+            bool Success = false;
+
+            UnicodeString Checksum;
+            try
+            {
+              UnicodeString Command = FORMAT(L"%s %s", (AlgCommand, ShellQuoteStr(FileName)));
+              AnyCommand(Command, NULL);
+              if (Output->Count != 1)
+              {
+                InvalidOutputError(Command);
+              }
+              UnicodeString Line = Output->Strings[0];
+              Checksum = ParseFileChecksum(Line, FileName, Command);
+            }
+            __finally
+            {
+              ProcessFileChecksum(OnCalculatedChecksum, Action, OperationProgress, FirstLevel, FileName, Alg, Checksum);
+            }
+          }
+          catch (Exception & E)
+          {
+            FTerminal->RollbackAction(Action, OperationProgress, &E);
+
+            UnicodeString Error = FMTLOAD(CHECKSUM_ERROR, (File->FullFileName));
+            FTerminal->CommandError(&E, Error);
+            // Abort loop.
+            // TODO: retries? resume?
+            BatchIndex = BatchFileList->Count;
+            Index = FileList->Count;
+          }
+        }
+      }
+    }
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::CustomCommandOnFile(const UnicodeString FileName,
@@ -2668,9 +2845,9 @@ void __fastcall TSCPFileSystem::SCPSink(const UnicodeString TargetDir,
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSCPFileSystem::GetSupportedChecksumAlgs(TStrings * /*Algs*/)
+void __fastcall TSCPFileSystem::GetSupportedChecksumAlgs(TStrings * Algs)
 {
-  // NOOP
+  FTerminal->GetShellChecksumAlgs(Algs);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSCPFileSystem::LockFile(const UnicodeString & /*FileName*/, const TRemoteFile * /*File*/)

+ 7 - 0
source/core/ScpFileSystem.h

@@ -30,6 +30,7 @@ public:
     const TRemoteFile * File, const TRemoteProperties * Properties,
     TChmodSessionAction & Action);
   virtual bool __fastcall LoadFilesProperties(TStrings * FileList);
+  virtual UnicodeString CalculateFilesChecksumInitialize(const UnicodeString & Alg);
   virtual void __fastcall CalculateFilesChecksum(
     const UnicodeString & Alg, TStrings * FileList, TCalculatedChecksumEvent OnCalculatedChecksum,
     TFileOperationProgressType * OperationProgress, bool FirstLevel);
@@ -117,6 +118,7 @@ private:
     const UnicodeString & CmdString);
   void __fastcall ExecCommand(TFSCommand Cmd, const TVarRec * args = NULL,
     int size = 0, int Params = -1);
+  void InvalidOutputError(const UnicodeString & Command);
   void __fastcall ReadCommandOutput(int Params, const UnicodeString * Cmd = NULL);
   void __fastcall SCPResponse(bool * GotLastLine = NULL);
   void __fastcall SCPDirectorySource(const UnicodeString DirectoryName,
@@ -145,6 +147,11 @@ private:
     TOperationSide Side,
     const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,
     int Params, TFileOperationProgressType * OperationProgress);
+  UnicodeString ParseFileChecksum(
+    const UnicodeString & Line, const UnicodeString & FileName, const UnicodeString & Command);
+  void ProcessFileChecksum(
+    TCalculatedChecksumEvent OnCalculatedChecksum, TChecksumSessionAction & Action, TFileOperationProgressType * OperationProgress,
+    bool FirstLevel, const UnicodeString & FileName, const UnicodeString & Alg, const UnicodeString & Checksum);
 
   static bool __fastcall RemoveLastLine(UnicodeString & Line,
     int & ReturnCode, UnicodeString LastLine = L"");

+ 24 - 15
source/core/Script.cpp

@@ -1214,28 +1214,37 @@ void __fastcall TScript::DoCalculatedChecksum(
 void __fastcall TScript::ChecksumProc(TScriptProcParams * Parameters)
 {
   CheckSession();
-  if (!FTerminal->IsCapable[fcCalculatingChecksum])
+  if (!FTerminal->IsCapable[fcCalculatingChecksum] &&
+      (!FTerminal->IsCapable[fcSecondaryShell] || FTerminal->IsEncryptingFiles()))
   {
     NotSupported();
   }
 
-  UnicodeString Alg = Parameters->Param[1];
-  TStrings * FileList = CreateFileList(Parameters, 2, 2, fltQueryServer);
-  FTerminal->ExceptionOnFail = true;
-  try
+  // this is used only to log failures to open separate shell session,
+  // the actual call logging is done in TTerminal::CalculateFilesChecksum
+  TChecksumSessionAction Action(FTerminal->ActionLog);
+  if (EnsureCommandSessionFallback(fcCalculatingChecksum, Action))
   {
-    if ((FileList->Count != 1) ||
-        DebugNotNull(dynamic_cast<TRemoteFile *>(FileList->Objects[0]))->IsDirectory)
+    Action.Cancel();
+
+    UnicodeString Alg = Parameters->Param[1];
+    TStrings * FileList = CreateFileList(Parameters, 2, 2, fltQueryServer);
+    FTerminal->ExceptionOnFail = true;
+    try
     {
-      throw Exception(FMTLOAD(NOT_FILE_ERROR, (FileList->Strings[0])));
-    }
+      if ((FileList->Count != 1) ||
+          DebugNotNull(dynamic_cast<TRemoteFile *>(FileList->Objects[0]))->IsDirectory)
+      {
+        throw Exception(FMTLOAD(NOT_FILE_ERROR, (FileList->Strings[0])));
+      }
 
-    FTerminal->CalculateFilesChecksum(Alg, FileList, DoCalculatedChecksum);
-  }
-  __finally
-  {
-    FTerminal->ExceptionOnFail = false;
-    FreeFileList(FileList);
+      FTerminal->CalculateFilesChecksum(Alg, FileList, DoCalculatedChecksum);
+    }
+    __finally
+    {
+      FTerminal->ExceptionOnFail = false;
+      FreeFileList(FileList);
+    }
   }
 }
 //---------------------------------------------------------------------------

+ 83 - 15
source/core/Terminal.cpp

@@ -999,15 +999,23 @@ bool TParallelOperation::UpdateFileList(TQueueFileList * UpdateFileList)
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-__fastcall TTerminal::TTerminal(TSessionData * SessionData,
-  TConfiguration * Configuration)
+__fastcall TTerminal::TTerminal(TSessionData * SessionData, TConfiguration * Configuration, TActionLog * ActionLog)
 {
   FConfiguration = Configuration;
   FSessionData = new TSessionData(L"");
   FSessionData->Assign(SessionData);
   TDateTime Started = Now(); // use the same time for session and XML log
   FLog = new TSessionLog(this, Started, FSessionData, Configuration);
-  FActionLog = new TActionLog(this, Started, FSessionData, Configuration);
+  if (ActionLog != NULL)
+  {
+    FActionLog = ActionLog;
+    FActionLogOwned = false;
+  }
+  else
+  {
+    FActionLog = new TActionLog(this, Started, FSessionData, Configuration);
+    FActionLogOwned = true;
+  }
   FFiles = new TRemoteDirectory(this);
   FExceptionOnFail = 0;
   FInTransaction = 0;
@@ -1078,7 +1086,14 @@ __fastcall TTerminal::~TTerminal()
 
   SAFE_DESTROY_EX(TCustomFileSystem, FFileSystem);
   SAFE_DESTROY_EX(TSessionLog, FLog);
-  SAFE_DESTROY_EX(TActionLog, FActionLog);
+  if (FActionLogOwned)
+  {
+    SAFE_DESTROY_EX(TActionLog, FActionLog);
+  }
+  else
+  {
+    FActionLog = NULL;
+  }
   delete FFiles;
   delete FDirectoryCache;
   delete FDirectoryChangesCache;
@@ -4512,6 +4527,7 @@ void __fastcall TTerminal::CalculateSubFoldersChecksum(
     TOnceDoneOperation OnceDoneOperation; // unused
     while ((Index < FileList->Count) && !OperationProgress->Cancel)
     {
+      UnicodeString FileName = FileList->Strings[Index];
       TRemoteFile * File = DebugNotNull(dynamic_cast<TRemoteFile *>(FileList->Objects[Index]));
 
       if (File->IsDirectory &&
@@ -4532,7 +4548,8 @@ void __fastcall TTerminal::CalculateSubFoldersChecksum(
             for (int Index = 0; Index < SubFiles->Count; Index++)
             {
               TRemoteFile * SubFile = SubFiles->Files[Index];
-              SubFileList->AddObject(SubFile->FullFileName, SubFile);
+              UnicodeString SubFileName = UnixCombinePaths(FileName, SubFile->FileName);
+              SubFileList->AddObject(SubFileName, SubFile);
             }
 
             FFileSystem->CalculateFilesChecksum(Alg, SubFileList.get(), OnCalculatedChecksum, OperationProgress, false);
@@ -4561,9 +4578,11 @@ void __fastcall TTerminal::CalculateFilesChecksum(
 
   try
   {
-    UnicodeString NormalizedAlg = FFileSystem->CalculateFilesChecksumInitialize(Alg);
+    TCustomFileSystem * FileSystem = GetFileSystemForCapability(fcCalculatingChecksum);
+
+    UnicodeString NormalizedAlg = FileSystem->CalculateFilesChecksumInitialize(Alg);
 
-    FFileSystem->CalculateFilesChecksum(NormalizedAlg, FileList, OnCalculatedChecksum, &Progress, true);
+    FileSystem->CalculateFilesChecksum(NormalizedAlg, FileList, OnCalculatedChecksum, &Progress, true);
   }
   __finally
   {
@@ -4937,7 +4956,7 @@ bool __fastcall TTerminal::GetCommandSessionOpened()
 //---------------------------------------------------------------------------
 TTerminal * __fastcall TTerminal::CreateSecondarySession(const UnicodeString & Name, TSessionData * SessionData)
 {
-  std::unique_ptr<TTerminal> Result(new TSecondaryTerminal(this, SessionData, Configuration, Name));
+  std::unique_ptr<TTerminal> Result(new TSecondaryTerminal(this, SessionData, Configuration, Name, ActionLog));
 
   Result->AutoReadDirectory = false;
 
@@ -6662,9 +6681,55 @@ const TFileSystemInfo & __fastcall TTerminal::GetFileSystemInfo(bool Retrieve)
   return FFileSystem->GetFileSystemInfo(Retrieve);
 }
 //---------------------------------------------------------------------
+TStrings * TTerminal::GetShellChecksumAlgDefs()
+{
+  if (FShellChecksumAlgDefs.get() == NULL)
+  {
+    UnicodeString ChecksumCommandsDef = Configuration->ChecksumCommands;
+    if (ChecksumCommandsDef.IsEmpty())
+    {
+      UnicodeString Delimiter(L",");
+      AddToList(ChecksumCommandsDef, Sha512ChecksumAlg + L"=sha512sum", Delimiter);
+      AddToList(ChecksumCommandsDef, Sha384ChecksumAlg + L"=sha384sum", Delimiter);
+      AddToList(ChecksumCommandsDef, Sha256ChecksumAlg + L"=sha256sum", Delimiter);
+      AddToList(ChecksumCommandsDef, Sha224ChecksumAlg + L"=sha224sum", Delimiter);
+      AddToList(ChecksumCommandsDef, Sha1ChecksumAlg + L"=sha1sum", Delimiter);
+      AddToList(ChecksumCommandsDef, Md5ChecksumAlg + L"=md5sums", Delimiter);
+    }
+
+    FShellChecksumAlgDefs.reset(new TStringList());
+    FShellChecksumAlgDefs->CommaText = ChecksumCommandsDef;
+  }
+  return FShellChecksumAlgDefs.get();
+}
+//---------------------------------------------------------------------
+void TTerminal::GetShellChecksumAlgs(TStrings * Algs)
+{
+  TStrings * AlgDefs = GetShellChecksumAlgDefs();
+  for (int Index = 0; Index < AlgDefs->Count; Index++)
+  {
+    Algs->Add(AlgDefs->Names[Index]);
+  }
+}
+//---------------------------------------------------------------------
 void __fastcall TTerminal::GetSupportedChecksumAlgs(TStrings * Algs)
 {
-  FFileSystem->GetSupportedChecksumAlgs(Algs);
+  if (!IsCapable[fcCalculatingChecksum] && IsCapable[fcSecondaryShell])
+  {
+    // Both return the same anyway
+    if (CommandSessionOpened)
+    {
+      FCommandSession->GetSupportedChecksumAlgs(Algs);
+    }
+    else
+    {
+      GetShellChecksumAlgs(Algs);
+    }
+  }
+  else
+  {
+    FFileSystem->GetSupportedChecksumAlgs(Algs);
+  }
 }
 //---------------------------------------------------------------------
 UnicodeString __fastcall TTerminal::GetPassword()
@@ -7789,8 +7854,11 @@ void __fastcall TTerminal::ReflectSettings()
 {
   DebugAssert(FLog != NULL);
   FLog->ReflectSettings();
-  DebugAssert(FActionLog != NULL);
-  FActionLog->ReflectSettings();
+  if (FActionLogOwned)
+  {
+    DebugAssert(FActionLog != NULL);
+    FActionLog->ReflectSettings();
+  }
   // also FTunnelLog ?
 }
 //---------------------------------------------------------------------------
@@ -8371,13 +8439,13 @@ bool TTerminal::IsValidFile(TRemoteFile * File)
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-__fastcall TSecondaryTerminal::TSecondaryTerminal(TTerminal * MainTerminal,
-  TSessionData * ASessionData, TConfiguration * Configuration, const UnicodeString & Name) :
-  TTerminal(ASessionData, Configuration),
+__fastcall TSecondaryTerminal::TSecondaryTerminal(
+  TTerminal * MainTerminal, TSessionData * ASessionData, TConfiguration * Configuration,
+  const UnicodeString & Name, TActionLog * ActionLog) :
+  TTerminal(ASessionData, Configuration, ActionLog),
   FMainTerminal(MainTerminal)
 {
   Log->SetParent(FMainTerminal->Log, Name);
-  ActionLog->Enabled = false;
   SessionData->NonPersistant();
   DebugAssert(FMainTerminal != NULL);
   FMainTerminal->FSecondaryTerminals++;

+ 8 - 4
source/core/Terminal.h

@@ -160,6 +160,7 @@ private:
   TSessionData * FSessionData;
   TSessionLog * FLog;
   TActionLog * FActionLog;
+  bool FActionLogOwned;
   TConfiguration * FConfiguration;
   UnicodeString FCurrentDirectory;
   Integer FExceptionOnFail;
@@ -211,6 +212,7 @@ private:
   TNotifyEvent FOnClose;
   TCallbackGuard * FCallbackGuard;
   TFindingFileEvent FOnFindingFile;
+  std::unique_ptr<TStrings> FShellChecksumAlgDefs;
   bool FEnableSecureShellUsage;
   bool FCollectFileSystemUsage;
   bool FRememberedPasswordTried;
@@ -491,6 +493,8 @@ protected:
   void __fastcall CalculateSubFoldersChecksum(
     const UnicodeString & Alg, TStrings * FileList, TCalculatedChecksumEvent OnCalculatedChecksum,
     TFileOperationProgressType * OperationProgress, bool FirstLevel);
+  void GetShellChecksumAlgs(TStrings * Algs);
+  TStrings * GetShellChecksumAlgDefs();
 
   UnicodeString __fastcall EncryptFileName(const UnicodeString & Path, bool EncryptNewFiles);
   UnicodeString __fastcall DecryptFileName(const UnicodeString & Path, bool DecryptFullPath, bool DontCache);
@@ -500,7 +504,7 @@ protected:
   __property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
 
 public:
-  __fastcall TTerminal(TSessionData * SessionData, TConfiguration * Configuration);
+  __fastcall TTerminal(TSessionData * SessionData, TConfiguration * Configuration, TActionLog * ActionLog = NULL);
   __fastcall ~TTerminal();
   void __fastcall Open();
   void __fastcall Close();
@@ -673,9 +677,9 @@ public:
 class TSecondaryTerminal : public TTerminal
 {
 public:
-  __fastcall TSecondaryTerminal(TTerminal * MainTerminal,
-    TSessionData * SessionData, TConfiguration * Configuration,
-    const UnicodeString & Name);
+  __fastcall TSecondaryTerminal(
+    TTerminal * MainTerminal, TSessionData * SessionData, TConfiguration * Configuration,
+    const UnicodeString & Name, TActionLog * ActionLog);
 
   void __fastcall UpdateFromMain();
 

+ 4 - 3
source/forms/CustomScpExplorer.cpp

@@ -4494,8 +4494,8 @@ void __fastcall TCustomScpExplorerForm::CalculateChecksum(const UnicodeString &
   TStrings * FileList, TCalculatedChecksumEvent OnCalculatedChecksum,
   bool & Close)
 {
-  // terminal can be already closed (e.g. dropped connection)
-  if (Terminal != NULL)
+  if ((Terminal != NULL) && // terminal can be already closed (e.g. dropped connection)
+      EnsureCommandSessionFallback(fcCalculatingChecksum))
   {
     Configuration->Usage->Inc(L"ChecksumCalculated");
 
@@ -4606,7 +4606,8 @@ bool __fastcall TCustomScpExplorerForm::SetProperties(TOperationSide Side, TStri
       if (CapableGroupChanging) Flags |= cpGroup;
 
       TCalculateChecksumEvent CalculateChecksumEvent = NULL;
-      if (Terminal->IsCapable[fcCalculatingChecksum])
+      if (Terminal->IsCapable[fcCalculatingChecksum] ||
+          (Terminal->IsCapable[fcSecondaryShell] && !Terminal->IsEncryptingFiles()))
       {
         CalculateChecksumEvent = CalculateChecksum;
       }