Browse Source

Remote file duplication in scripting, .NET assembly and XML log + Scripting command mv and .NET assembly function Session.MoveFile report when the server does not support file renaming/moving.

Source commit: 974f6dff9af8f3bb1e39a7b2ef453af0c7b2d0ec
Martin Prikryl 8 years ago
parent
commit
32d26ba76e

+ 29 - 0
dotnet/Session.cs

@@ -1054,6 +1054,35 @@ namespace WinSCP
             }
         }
 
+        public void DuplicateFile(string sourcePath, string targetPath)
+        {
+            using (Logger.CreateCallstackAndLock())
+            {
+                CheckOpened();
+
+                string sourceArgument = Tools.ArgumentEscape(EscapeFileMask(sourcePath));
+                string targetArgument = Tools.ArgumentEscape(targetPath);
+                string command = string.Format(CultureInfo.InvariantCulture, "cp \"{0}\" \"{1}\"", sourceArgument, targetArgument);
+                WriteCommand(command);
+
+                using (ElementLogReader groupReader = _reader.WaitForGroupAndCreateLogReader())
+                {
+                    if (!groupReader.TryWaitForNonEmptyElement("cp", LogReadFlags.ThrowFailures))
+                    {
+                        throw Logger.WriteException(new SessionRemoteException(this, string.Format(CultureInfo.CurrentCulture, "{0} not found.", sourcePath)));
+                    }
+                    else
+                    {
+                        using (ElementLogReader cpReader = groupReader.CreateLogReader())
+                        {
+                            ReadElement(cpReader, 0);
+                            groupReader.ReadToEnd(LogReadFlags.ThrowFailures);
+                        }
+                    }
+                }
+            }
+        }
+
         // This is not static method only to make it visible to COM
         public string EscapeFileMask(string fileMask)
         {

+ 25 - 2
source/core/Script.cpp

@@ -365,6 +365,7 @@ void __fastcall TScript::Init()
   FCommands->Register(L"rmdir", SCRIPT_RMDIR_DESC, SCRIPT_RMDIR_HELP, &RmDirProc, 1, -1, false);
   FCommands->Register(L"mv", SCRIPT_MV_DESC, SCRIPT_MV_HELP2, &MvProc, 2, -1, false);
   FCommands->Register(L"rename", 0, SCRIPT_MV_HELP2, &MvProc, 2, -1, false);
+  FCommands->Register(L"cp", SCRIPT_CP_DESC, SCRIPT_CP_HELP, &CpProc, 2, -1, false);
   FCommands->Register(L"chmod", SCRIPT_CHMOD_DESC, SCRIPT_CHMOD_HELP2, &ChModProc, 2, -1, false);
   FCommands->Register(L"ln", SCRIPT_LN_DESC, SCRIPT_LN_HELP, &LnProc, 2, 2, false);
   FCommands->Register(L"symlink", 0, SCRIPT_LN_HELP, &LnProc, 2, 2, false);
@@ -1331,10 +1332,15 @@ void __fastcall TScript::RmDirProc(TScriptProcParams * Parameters)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TScript::MvProc(TScriptProcParams * Parameters)
+void __fastcall TScript::DoMvOrCp(TScriptProcParams * Parameters, TFSCapability Capability, bool Cp)
 {
   CheckSession();
 
+  if (!FTerminal->IsCapable[Capability])
+  {
+    NotSupported();
+  }
+
   TStrings * FileList = CreateFileList(Parameters, 1, Parameters->ParamCount - 1,
     fltMask);
   try
@@ -1346,7 +1352,14 @@ void __fastcall TScript::MvProc(TScriptProcParams * Parameters)
 
     Target = UnixIncludeTrailingBackslash(TargetDirectory) + FileMask;
     CheckMultiFilesToOne(FileList, Target, true);
-    FTerminal->MoveFiles(FileList, TargetDirectory, FileMask);
+    if (Cp)
+    {
+      FTerminal->CopyFiles(FileList, TargetDirectory, FileMask);
+    }
+    else
+    {
+      FTerminal->MoveFiles(FileList, TargetDirectory, FileMask);
+    }
   }
   __finally
   {
@@ -1354,6 +1367,16 @@ void __fastcall TScript::MvProc(TScriptProcParams * Parameters)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::MvProc(TScriptProcParams * Parameters)
+{
+  DoMvOrCp(Parameters, fcRemoteMove, false);
+}
+//---------------------------------------------------------------------------
+void __fastcall TScript::CpProc(TScriptProcParams * Parameters)
+{
+  DoMvOrCp(Parameters, fcRemoteCopy, true);
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::ChModProc(TScriptProcParams * Parameters)
 {
   CheckSession();

+ 2 - 0
source/core/Script.h

@@ -136,6 +136,7 @@ protected:
   void __fastcall RmProc(TScriptProcParams * Parameters);
   void __fastcall RmDirProc(TScriptProcParams * Parameters);
   void __fastcall MvProc(TScriptProcParams * Parameters);
+  void __fastcall CpProc(TScriptProcParams * Parameters);
   void __fastcall ChModProc(TScriptProcParams * Parameters);
   void __fastcall LnProc(TScriptProcParams * Parameters);
   void __fastcall MkDirProc(TScriptProcParams * Parameters);
@@ -179,6 +180,7 @@ private:
   void __fastcall NotSupported();
   void __fastcall CheckMultiFilesToOne(TStrings * FileList, const UnicodeString & Target, bool Unix);
   void __fastcall LogOption(const UnicodeString & LogStr);
+  void __fastcall DoMvOrCp(TScriptProcParams * Parameters, TFSCapability Capability, bool Cp);
 };
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TScriptInputEvent)(TScript * Script, const UnicodeString Prompt, UnicodeString & Str);

+ 8 - 0
source/core/SessionInfo.cpp

@@ -296,6 +296,7 @@ protected:
       case laMkdir: return L"mkdir";
       case laRm: return L"rm";
       case laMv: return L"mv";
+      case laCp: return L"cp";
       case laCall: return L"call";
       case laLs: return L"ls";
       case laStat: return L"stat";
@@ -532,6 +533,13 @@ __fastcall TMvSessionAction::TMvSessionAction(TActionLog * Log,
   Destination(ADestination);
 }
 //---------------------------------------------------------------------------
+__fastcall TCpSessionAction::TCpSessionAction(TActionLog * Log,
+    const UnicodeString & FileName, const UnicodeString & ADestination) :
+  TFileLocationSessionAction(Log, laCp, FileName)
+{
+  Destination(ADestination);
+}
+//---------------------------------------------------------------------------
 __fastcall TCallSessionAction::TCallSessionAction(TActionLog * Log,
     const UnicodeString & Command, const UnicodeString & Destination) :
   TSessionAction(Log, laCall)

+ 8 - 1
source/core/SessionInfo.h

@@ -80,7 +80,7 @@ public:
 enum TLogLineType { llOutput, llInput, llStdError, llMessage, llException };
 enum TLogAction
 {
-  laUpload, laDownload, laTouch, laChmod, laMkdir, laRm, laMv, laCall, laLs,
+  laUpload, laDownload, laTouch, laChmod, laMkdir, laRm, laMv, laCp, laCall, laLs,
   laStat, laChecksum, laCwd
 };
 //---------------------------------------------------------------------------
@@ -180,6 +180,13 @@ public:
     const UnicodeString & Destination);
 };
 //---------------------------------------------------------------------------
+class TCpSessionAction : public TFileLocationSessionAction
+{
+public:
+  __fastcall TCpSessionAction(TActionLog * Log, const UnicodeString & FileName,
+    const UnicodeString & Destination);
+};
+//---------------------------------------------------------------------------
 class TCallSessionAction : public TSessionAction
 {
 public:

+ 2 - 1
source/core/Terminal.cpp

@@ -4485,6 +4485,7 @@ void __fastcall TTerminal::DoCopyFile(const UnicodeString FileName,
   TRetryOperationLoop RetryLoop(this);
   do
   {
+    TCpSessionAction Action(ActionLog, AbsolutePath(FileName, true), AbsolutePath(NewName, true));
     try
     {
       DebugAssert(FFileSystem);
@@ -4503,7 +4504,7 @@ void __fastcall TTerminal::DoCopyFile(const UnicodeString FileName,
     }
     catch(Exception & E)
     {
-      RetryLoop.Error(E, FMTLOAD(COPY_FILE_ERROR, (FileName, NewName)));
+      RetryLoop.Error(E, Action, FMTLOAD(COPY_FILE_ERROR, (FileName, NewName)));
     }
   }
   while (RetryLoop.Retry());

+ 3 - 0
source/resource/TextsCore.h

@@ -31,6 +31,7 @@
 #define SCRIPT_ECHO_HELP        27
 #define SCRIPT_STAT_HELP        28
 #define SCRIPT_CHECKSUM_HELP    29
+#define SCRIPT_CP_HELP          30
 
 #define CORE_ERROR_STRINGS      100
 #define KEY_NOT_VERIFIED        101
@@ -320,6 +321,7 @@
 #define S3_ACCESS_KEY_ID_PROMPT 357
 #define S3_SECRET_ACCESS_KEY_TITLE 358
 #define S3_SECRET_ACCESS_KEY_PROMPT 359
+#define DUPLICATE_FOLDER_NOT_SUPPORTED 360
 
 #define CORE_INFORMATION_STRINGS 400
 #define YES_STR                 401
@@ -468,6 +470,7 @@
 #define AND_STR                 556
 #define AUTH_CHANGING_PASSWORD  557
 #define PASTE_KEY_BUTTON        558
+#define SCRIPT_CP_DESC          559
 
 #define CORE_VARIABLE_STRINGS   600
 #define PUTTY_BASED_ON          601

+ 2 - 0
source/resource/TextsCore1.rc

@@ -237,6 +237,7 @@ BEGIN
   S3_ERROR_FURTHER_DETAILS, "Further details: %s"
   S3_ERROR_EXTRA_DETAILS, "Extra Details: "
   S3_STATUS_ACCESS_DENIED, "Access denied."
+  DUPLICATE_FOLDER_NOT_SUPPORTED, "Direct duplication of folders is not supported. Use a duplication via a local temporary copy."
 
   CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
   CONFIRM_PROLONG_TIMEOUT3, "Host is not communicating for %d seconds.\n\nWait for another %0:d seconds?"
@@ -438,6 +439,7 @@ BEGIN
   AND_STR, "%s and %s"
   AUTH_CHANGING_PASSWORD, "Changing password."
   PASTE_KEY_BUTTON, "&Paste key"
+  SCRIPT_CP_DESC, "Duplicates remote file"
 
   CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
   PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"

+ 14 - 0
source/resource/TextsCore2.rc

@@ -376,4 +376,18 @@ BEGIN
     "  Calculates checksum of remote file.\n"
     "example:\n"
     "  checksum sha-1 index.html\n"
+  SCRIPT_CP_HELP,
+    "cp <file> [ <file2> ... ] [ <directory>/ ][ <newname> ]\n"
+    "  Duplicates one or more remote files. Destination directory or new\n"
+    "  name or both must be specified. Destination directory must end with\n"
+    "  slash. Operation mask can be used instead of new name.\n"
+    "  Filename can be replaced with wildcard to select multiple files.\n"
+    "effective option:\n"
+    "  failonnomatch\n"
+    "examples:\n"
+    "  cp index.html public_html/\n"
+    "  cp index.html about.*\n"
+    "  cp index.html public_html/about.*\n"
+    "  cp public_html/index.html public_html/about.html /home/martin/*.bak\n"
+    "  cp *.html /home/backup/*.bak\n"
 END