From c245040773440a7a0337981f4525226c4ab3e727 Mon Sep 17 00:00:00 2001 From: lirunki Date: Tue, 1 Oct 2019 11:19:14 -0700 Subject: [PATCH 01/12] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0cc04fb9b..df8f61ad6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ SSH.NET is a Secure Shell (SSH-2) library for .NET, optimized for parallelism. ## Introduction This project was inspired by **Sharp.SSH** library which was ported from java and it seems like was not supported for quite some time. This library is a complete rewrite, without any third party dependencies, using parallelism to achieve the best performance possible. +This specific branch has an additional feature which is for sftp client to always use absolute paths on the server (i.e. changedirectory will keep track of the absolute path and all file actions will provide absolute paths to the server). This gives support for buggy server implementations where changedirectory will use the grown permissions. + ## Features * Execution of SSH command using both synchronous and asynchronous methods * Return command execution exit status and other information From d0d262fa8069e88f0c4c7d2619863736260ce122 Mon Sep 17 00:00:00 2001 From: lirunki Date: Tue, 1 Oct 2019 11:20:20 -0700 Subject: [PATCH 02/12] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df8f61ad6..2f76c9570 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ This specific branch has an additional feature which is for sftp client to alwa * Supports DES-EDE3-CBC, DES-EDE3-CFB, DES-CBC, AES-128-CBC, AES-192-CBC and AES-256-CBC algorithms for private key encryption * Supports two-factor or higher authentication * Supports SOCKS4, SOCKS5 and HTTP Proxy +* Sample of sftp client exe ## Key Exchange Method From f723010e2eb7e2a5c2e5bdc4185e1a285105c6c5 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Tue, 1 Oct 2019 11:21:41 -0700 Subject: [PATCH 03/12] Add sftp support for localdirectorytracking add sftp.exe sample --- src/Renci.SshNet.VS2017.sln | 25 +++ src/Renci.SshNet/Sftp/SftpSession.cs | 140 +++++++++++++---- src/Renci.SshNet/SftpClient.cs | 6 + src/Renci.sftp/Program.cs | 182 ++++++++++++++++++++++ src/Renci.sftp/Properties/AssemblyInfo.cs | 36 +++++ src/Renci.sftp/Renci.sftp.csproj | 53 +++++++ 6 files changed, 412 insertions(+), 30 deletions(-) create mode 100644 src/Renci.sftp/Program.cs create mode 100644 src/Renci.sftp/Properties/AssemblyInfo.cs create mode 100644 src/Renci.sftp/Renci.sftp.csproj diff --git a/src/Renci.SshNet.VS2017.sln b/src/Renci.SshNet.VS2017.sln index 00469f612..00db49bf3 100644 --- a/src/Renci.SshNet.VS2017.sln +++ b/src/Renci.SshNet.VS2017.sln @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.NET35", "Renci EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.NETCore", "Renci.SshNet.NETCore\Renci.SshNet.NETCore.csproj", "{8E8229EB-6780-4A8A-B470-E2023FA55AB5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.sftp", "renci.sftp\Renci.sftp.csproj", "{C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,6 +105,26 @@ Global {8E8229EB-6780-4A8A-B470-E2023FA55AB5}.Release|x64.Build.0 = Release|Any CPU {8E8229EB-6780-4A8A-B470-E2023FA55AB5}.Release|x86.ActiveCfg = Release|Any CPU {8E8229EB-6780-4A8A-B470-E2023FA55AB5}.Release|x86.Build.0 = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|ARM.Build.0 = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|x64.ActiveCfg = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|x64.Build.0 = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|x86.ActiveCfg = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Debug|x86.Build.0 = Debug|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|Any CPU.Build.0 = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|ARM.ActiveCfg = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|ARM.Build.0 = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|x64.ActiveCfg = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|x64.Build.0 = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|x86.ActiveCfg = Release|Any CPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -111,6 +133,9 @@ Global {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BE1E1CE2-57C9-4766-858E-1DE90D2F51FD} + EndGlobalSection GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = Renci.SshNet1.vsmdi EndGlobalSection diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index 283ff6179..d2b608b03 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -21,6 +21,8 @@ internal class SftpSession : SubsystemSession, ISftpSession private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); private IDictionary _supportedExtensions; + private string localWorkingDirectory; + /// /// Gets the character encoding to use. /// @@ -68,11 +70,78 @@ public SftpSession(ISession session, int operationTimeout, Encoding encoding, IS /// The new working directory. public void ChangeDirectory(string path) { - var fullPath = GetCanonicalPath(path); - var handle = RequestOpenDir(fullPath); + if (!SftpClient.ChangeDirIsLocal) + { + var fullPath = GetCanonicalPath(path); + var handle = RequestOpenDir(fullPath); + RequestClose(handle); + WorkingDirectory = fullPath; + } + else + { + string fullPath = GetRelativePathIfNeeded(path); + + var handle = RequestOpenDir(path); + RequestClose(handle); + this.localWorkingDirectory = fullPath; + WorkingDirectory = GetCanonicalPath(fullPath); + } + } + + /// + /// returns the path relative to the original local path based on the given path + /// + /// + /// + private string GetRelativePathIfNeeded(string path) + { + if (!path.StartsWith("/") && SftpClient.ChangeDirIsLocal) + { + path = Compact(localWorkingDirectory + "/" + path); + } + + return path; + } + + /// + /// Compacts an absolute path, removing the .. on the path + /// + /// the original uncompacted path + /// the compacted path + private string Compact(string path) + { + string[] pieces = path.Split('/'); + for (int i = 0; i < pieces.Length; i++) + { + if (pieces[i] == ".") + { + pieces[i] = null; + continue; + } - RequestClose(handle); - WorkingDirectory = fullPath; + if (pieces[i] == "..") + { + if (i > 0) + { + pieces[i - 1] = null; + } + pieces[i] = null; + continue; + } + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < pieces.Length; i++) + { + if (String.IsNullOrEmpty(pieces[i])) + { + continue; + } + sb.Append("/"); + sb.Append(pieces[i]); + } + + return sb.ToString(); } internal void SendMessage(SftpMessage sftpMessage) @@ -90,11 +159,21 @@ internal void SendMessage(SftpMessage sftpMessage) /// public string GetCanonicalPath(string path) { - var fullPath = GetFullRemotePath(path); + return GetCanonicalPathForRemotePath(GetRelativePathIfNeeded(path)); + } + /// + /// Resolves a given path into an absolute path on the server. + /// + /// The path to resolve relative to the server. + /// + /// The absolute path. + /// + private string GetCanonicalPathForRemotePath(string remotePath) + { var canonizedPath = string.Empty; - var realPathFiles = RequestRealPath(fullPath, true); + var realPathFiles = RequestRealPath(remotePath, true); if (realPathFiles != null) { @@ -105,13 +184,13 @@ public string GetCanonicalPath(string path) return canonizedPath; // Check for special cases - if (fullPath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || - fullPath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || - fullPath.Equals("/", StringComparison.OrdinalIgnoreCase) || - fullPath.IndexOf('/') < 0) - return fullPath; + if (remotePath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || + remotePath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || + remotePath.Equals("/", StringComparison.OrdinalIgnoreCase) || + remotePath.IndexOf('/') < 0) + return remotePath; - var pathParts = fullPath.Split('/'); + var pathParts = remotePath.Split('/'); var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); @@ -127,7 +206,7 @@ public string GetCanonicalPath(string path) if (string.IsNullOrEmpty(canonizedPath)) { - return fullPath; + return remotePath; } var slash = string.Empty; @@ -172,6 +251,7 @@ protected override void OnChannelOpen() // Resolve current directory WorkingDirectory = RequestRealPath(".")[0].Key; + localWorkingDirectory = "."; } protected override void OnDataReceived(byte[] data) @@ -360,7 +440,7 @@ public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) using (var wait = new AutoResetEvent(false)) { - var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, Encoding, flags, + var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, flags, response => { handle = response.Handle; @@ -399,7 +479,7 @@ public SftpOpenAsyncResult BeginOpen(string path, Flags flags, AsyncCallback cal { var asyncResult = new SftpOpenAsyncResult(callback, state); - var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, Encoding, flags, + var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, flags, response => { asyncResult.SetAsCompleted(response.Handle, false); @@ -693,7 +773,7 @@ public SftpFileAttributes RequestLStat(string path) SftpFileAttributes attributes = null; using (var wait = new AutoResetEvent(false)) { - var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { attributes = response.Attributes; @@ -731,7 +811,7 @@ public SFtpStatAsyncResult BeginLStat(string path, AsyncCallback callback, objec { var asyncResult = new SFtpStatAsyncResult(callback, state); - var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { asyncResult.SetAsCompleted(response.Attributes, false); @@ -822,7 +902,7 @@ public void RequestSetStat(string path, SftpFileAttributes attributes) using (var wait = new AutoResetEvent(false)) { - var request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, path, Encoding, attributes, + var request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, attributes, response => { exception = GetSftpException(response); @@ -883,7 +963,7 @@ public byte[] RequestOpenDir(string path, bool nullOnError = false) using (var wait = new AutoResetEvent(false)) { - var request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { handle = response.Handle; @@ -959,7 +1039,7 @@ public void RequestRemove(string path) using (var wait = new AutoResetEvent(false)) { - var request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { exception = GetSftpException(response); @@ -987,7 +1067,7 @@ public void RequestMkDir(string path) using (var wait = new AutoResetEvent(false)) { - var request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { exception = GetSftpException(response); @@ -1015,7 +1095,7 @@ public void RequestRmDir(string path) using (var wait = new AutoResetEvent(false)) { - var request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { exception = GetSftpException(response); @@ -1143,7 +1223,7 @@ public SftpFileAttributes RequestStat(string path, bool nullOnError = false) using (var wait = new AutoResetEvent(false)) { - var request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpStatRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { attributes = response.Attributes; @@ -1181,7 +1261,7 @@ public SFtpStatAsyncResult BeginStat(string path, AsyncCallback callback, object { var asyncResult = new SFtpStatAsyncResult(callback, state); - var request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpStatRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { asyncResult.SetAsCompleted(response.Attributes, false); @@ -1237,7 +1317,7 @@ public void RequestRename(string oldPath, string newPath) using (var wait = new AutoResetEvent(false)) { - var request = new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, Encoding, + var request = new SftpRenameRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(oldPath), GetRelativePathIfNeeded(newPath), Encoding, response => { exception = GetSftpException(response); @@ -1274,7 +1354,7 @@ internal KeyValuePair[] RequestReadLink(string path, using (var wait = new AutoResetEvent(false)) { - var request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { result = response.Files; @@ -1315,7 +1395,7 @@ public void RequestSymLink(string linkpath, string targetpath) using (var wait = new AutoResetEvent(false)) { - var request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, linkpath, targetpath, Encoding, + var request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(linkpath), GetRelativePathIfNeeded(targetpath), Encoding, response => { exception = GetSftpException(response); @@ -1353,7 +1433,7 @@ public void RequestPosixRename(string oldPath, string newPath) using (var wait = new AutoResetEvent(false)) { - var request = new PosixRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, Encoding, + var request = new PosixRenameRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(oldPath), GetRelativePathIfNeeded(newPath), Encoding, response => { exception = GetSftpException(response); @@ -1393,7 +1473,7 @@ public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = f using (var wait = new AutoResetEvent(false)) { - var request = new StatVfsRequest(ProtocolVersion, NextRequestId, path, Encoding, + var request = new StatVfsRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(path), Encoding, response => { information = response.GetReply().Information; @@ -1485,7 +1565,7 @@ internal void HardLink(string oldPath, string newPath) using (var wait = new AutoResetEvent(false)) { - var request = new HardLinkRequest(ProtocolVersion, NextRequestId, oldPath, newPath, + var request = new HardLinkRequest(ProtocolVersion, NextRequestId, GetRelativePathIfNeeded(oldPath), GetRelativePathIfNeeded(newPath), response => { exception = GetSftpException(response); diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 5a0993009..59293d8b8 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -35,6 +35,12 @@ public class SftpClient : BaseClient /// Holds the size of the buffer. /// private uint _bufferSize; + + /// + /// Gets or sets a value indicating if ChangeDir must be kept local to the client. + /// By default, this is false. + /// + public static bool ChangeDirIsLocal { get; set; } /// /// Gets or sets the operation timeout. diff --git a/src/Renci.sftp/Program.cs b/src/Renci.sftp/Program.cs new file mode 100644 index 000000000..f2db5fac4 --- /dev/null +++ b/src/Renci.sftp/Program.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace renci.sftp +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 0) + { + Usage(); + return; + } + + int port = 22; + string destination = "127.0.0.1"; + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "P") + { + if (i + 1 < args.Length) + { + port = int.Parse(args[i + 1]); + i++; + } + } + + destination = args[i]; + } + + Console.Write("user: "); + string username = Console.ReadLine(); + Console.Write("pwd: "); + string password = ReadPassword(); + Console.WriteLine("connecting..."); + Renci.SshNet.SftpClient.ChangeDirIsLocal = true; + using (var client = new Renci.SshNet.SftpClient(destination, port, username, password)) + { + client.Connect(); + while (true) + { + Console.WriteLine(); + string current = client.WorkingDirectory; + Console.Write(current); + Console.Write(">>");Console.Out.Flush(); + string line = Console.ReadLine(); + try + { + if (!Process(client, line.Split(' '))) + { + break; + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } + } + + private static bool Process(Renci.SshNet.SftpClient client, string[] line) + { + switch (line[0]) + { + case "cd": + if (line.Length == 1) + { + Console.WriteLine(client.WorkingDirectory); + } + else + { + client.ChangeDirectory(line[1]); + } + break; + case "pwd": + Console.WriteLine(Environment.CurrentDirectory); + break; + case "rm": + client.DeleteFile(line[1]); + break; + case "put": + using (var l = File.OpenRead(line[1])) + { + string dest = (line.Length > 2 ? line[2] : line[1]); + using (var f = client.OpenWrite(dest)) + { + byte[] buffer = new byte[1024 * 16]; + int r; + while ((r = l.Read(buffer, 0, buffer.Length)) > 0) + { + f.Write(buffer, 0, r); + } + f.Flush(); + } + } + break; + case "cat": + Console.WriteLine(client.ReadAllText(line[1])); + break; + case "get": + using (var l = client.OpenRead(line[1])) + { + string dest = (line.Length > 2 ? line[2] : line[1]); + using (var f = File.OpenWrite(dest)) + { + byte[] buffer = new byte[1024 * 16]; + int r; + while ((r = l.Read(buffer, 0, buffer.Length)) > 0) + { + f.Write(buffer, 0, r); + } + f.Flush(); + } + } + break; + case "dir": + { + string path = (line.Length > 1 ? line[1] : "."); + foreach (var el in Directory.GetFileSystemEntries(path)) + { + Console.WriteLine(el); + } + break; + } + case "ls": + { + string path = (line.Length > 1 ? line[1] : "."); + + foreach (var el in client.ListDirectory(path)) + { + Console.WriteLine(el.FullName); + } + break; + } + case "exit": + case "quit": + return false; + default: + throw new ArgumentException("unsupported command " + line[0]); + } + + return true; + } + + private static string ReadPassword() + { + string pass = String.Empty; + do + { + var key = Console.ReadKey(true); + + if (key.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + break; + } + + if (key.Key == ConsoleKey.Backspace && pass.Length > 0) + { + pass = pass.Substring(0, (pass.Length - 1)); + Console.Write("\b \b"); + } + else + { + pass = pass + key.KeyChar; + } + } while (true); + + return pass; + } + + private static void Usage() + { + Console.WriteLine(@"usage: Renci.sftp [-P port] destination"); + } + } +} diff --git a/src/Renci.sftp/Properties/AssemblyInfo.cs b/src/Renci.sftp/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..8377b8ddb --- /dev/null +++ b/src/Renci.sftp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Renci.sftp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Renci.sftp")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c0b7112c-064e-4a9a-a22c-7dcbd77fc48b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Renci.sftp/Renci.sftp.csproj b/src/Renci.sftp/Renci.sftp.csproj new file mode 100644 index 000000000..8c4a671b8 --- /dev/null +++ b/src/Renci.sftp/Renci.sftp.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {C0B7112C-064E-4A9A-A22C-7DCBD77FC48B} + Exe + Renci.sftp + Renci.sftp + v3.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + {dd1c552f-7f48-4269-abb3-2e4c89b7e43a} + Renci.SshNet.NET35 + + + + \ No newline at end of file From f2b79f4d6d2db4f642d46c9f70a59caaa55a49f0 Mon Sep 17 00:00:00 2001 From: lirunki Date: Tue, 1 Oct 2019 11:22:36 -0700 Subject: [PATCH 04/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f76c9570..584a9ed8c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ SSH.NET is a Secure Shell (SSH-2) library for .NET, optimized for parallelism. ## Introduction This project was inspired by **Sharp.SSH** library which was ported from java and it seems like was not supported for quite some time. This library is a complete rewrite, without any third party dependencies, using parallelism to achieve the best performance possible. -This specific branch has an additional feature which is for sftp client to always use absolute paths on the server (i.e. changedirectory will keep track of the absolute path and all file actions will provide absolute paths to the server). This gives support for buggy server implementations where changedirectory will use the grown permissions. +This specific fork has an additional feature which is for sftp client to always use absolute paths on the server (i.e. changedirectory will keep track of the absolute path and all file actions will provide absolute paths to the server). This gives support for buggy server implementations where changedirectory will use the grown permissions. ## Features * Execution of SSH command using both synchronous and asynchronous methods From 8557ae154013b564517cb1e1b61daff6b1822157 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Tue, 1 Oct 2019 11:26:47 -0700 Subject: [PATCH 05/12] nugget spec changes --- build/nuget/SSH.NET.nuspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/nuget/SSH.NET.nuspec b/build/nuget/SSH.NET.nuspec index a3520842a..802bb0dff 100644 --- a/build/nuget/SSH.NET.nuspec +++ b/build/nuget/SSH.NET.nuspec @@ -1,15 +1,15 @@  - SSH.NET - 2017.0.0-beta1 - SSH.NET + SSH.NET.SftpAlwaysAbsolute + 2017.0.0-aa1 + SSH.NET.SftpAlwaysAbsolute Renci olegkap,drieseng https://github.com/sshnet/SSH.NET/blob/master/LICENSE https://github.com/sshnet/SSH.NET/ false - SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism and with broad framework support. + SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism and with broad framework support. This fork allows for sftp client to always use absolute paths on the server. https://github.com/sshnet/SSH.NET/releases/tag/2017.0.0-beta1 A Secure Shell (SSH) library for .NET, optimized for parallelism. 2012-2017, RENCI From 8bef6ab47f0da61efc8c1ddf6d1890566a02f89a Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:14:57 -0700 Subject: [PATCH 06/12] readme comments --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 584a9ed8c..4d17e4186 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ SSH.NET is a Secure Shell (SSH-2) library for .NET, optimized for parallelism. ## Introduction This project was inspired by **Sharp.SSH** library which was ported from java and it seems like was not supported for quite some time. This library is a complete rewrite, without any third party dependencies, using parallelism to achieve the best performance possible. -This specific fork has an additional feature which is for sftp client to always use absolute paths on the server (i.e. changedirectory will keep track of the absolute path and all file actions will provide absolute paths to the server). This gives support for buggy server implementations where changedirectory will use the grown permissions. - ## Features * Execution of SSH command using both synchronous and asynchronous methods * Return command execution exit status and other information @@ -28,6 +26,7 @@ This specific fork has an additional feature which is for sftp client to always * Supports two-factor or higher authentication * Supports SOCKS4, SOCKS5 and HTTP Proxy * Sample of sftp client exe +* sftp client can now be configured to always use absolute paths on the server (i.e. changedirectory will keep track of the absolute path and all file actions will provide absolute paths to the server). This gives support for buggy server implementations where changedirectory will use the grown permissions ## Key Exchange Method From 415e5b80807b3b11950296f75ce4c646675624d4 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:19:56 -0700 Subject: [PATCH 07/12] make the flag instance property --- src/Renci.SshNet/IServiceFactory.cs | 3 ++- src/Renci.SshNet/ServiceFactory.cs | 5 +++-- src/Renci.SshNet/Sftp/SftpSession.cs | 20 +++++++++++++++++--- src/Renci.SshNet/SftpClient.cs | 5 +++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Renci.SshNet/IServiceFactory.cs b/src/Renci.SshNet/IServiceFactory.cs index 8941af093..406e1abc3 100644 --- a/src/Renci.SshNet/IServiceFactory.cs +++ b/src/Renci.SshNet/IServiceFactory.cs @@ -32,10 +32,11 @@ internal partial interface IServiceFactory /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// The encoding. /// The factory to use for creating SFTP messages. + /// If true, the sftp client will always pass absolute paths to serve, and will locally mimmick 'changedir' operations /// /// An . /// - ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory); + ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory, bool changeDirIsLocal); /// /// Create a new . diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index 9d7e8310b..09721eefe 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -53,12 +53,13 @@ public ISession CreateSession(ConnectionInfo connectionInfo) /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// The encoding. /// The factory to use for creating SFTP messages. + /// If true, the sftp client will always pass absolute paths to serve, and will locally mimmick 'changedir' operations /// /// An . /// - public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory) + public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory, bool changeDirIsLocal) { - return new SftpSession(session, operationTimeout, encoding, sftpMessageFactory); + return new SftpSession(session, operationTimeout, encoding, sftpMessageFactory, changeDirIsLocal); } /// diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index d2b608b03..6d0255f2f 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -23,6 +23,11 @@ internal class SftpSession : SubsystemSession, ISftpSession private string localWorkingDirectory; + /// + /// If true, the sftp client will always pass absolute paths to serve, and will locally mimmick 'changedir' operations + /// + private bool changeDirIsLocal; + /// /// Gets the character encoding to use. /// @@ -57,11 +62,20 @@ public uint NextRequestId } } - public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory) + /// + /// + /// + /// + /// + /// + /// + /// If true, the sftp client will always pass absolute paths to serve, and will locally mimmick 'changedir' operations + public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory, bool changeDirIsLocal) : base(session, "sftp", operationTimeout) { Encoding = encoding; _sftpResponseFactory = sftpResponseFactory; + this.changeDirIsLocal = changeDirIsLocal; } /// @@ -70,7 +84,7 @@ public SftpSession(ISession session, int operationTimeout, Encoding encoding, IS /// The new working directory. public void ChangeDirectory(string path) { - if (!SftpClient.ChangeDirIsLocal) + if (!this.changeDirIsLocal) { var fullPath = GetCanonicalPath(path); var handle = RequestOpenDir(fullPath); @@ -95,7 +109,7 @@ public void ChangeDirectory(string path) /// private string GetRelativePathIfNeeded(string path) { - if (!path.StartsWith("/") && SftpClient.ChangeDirIsLocal) + if (!path.StartsWith("/") && this.changeDirIsLocal) { path = Compact(localWorkingDirectory + "/" + path); } diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 59293d8b8..7d916056c 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -40,7 +40,7 @@ public class SftpClient : BaseClient /// Gets or sets a value indicating if ChangeDir must be kept local to the client. /// By default, this is false. /// - public static bool ChangeDirIsLocal { get; set; } + public bool ChangeDirIsLocal { get; set; } /// /// Gets or sets the operation timeout. @@ -2213,7 +2213,8 @@ private ISftpSession CreateAndConnectToSftpSession() var sftpSession = ServiceFactory.CreateSftpSession(Session, _operationTimeout, ConnectionInfo.Encoding, - ServiceFactory.CreateSftpResponseFactory()); + ServiceFactory.CreateSftpResponseFactory(), + this.ChangeDirIsLocal); try { sftpSession.Connect(); From 656befdf08973fd99a063ea9be3007c9a44552d6 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:21:58 -0700 Subject: [PATCH 08/12] nuspec --- build/nuget/SSH.NET.nuspec | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/nuget/SSH.NET.nuspec b/build/nuget/SSH.NET.nuspec index 802bb0dff..c4f60076b 100644 --- a/build/nuget/SSH.NET.nuspec +++ b/build/nuget/SSH.NET.nuspec @@ -1,15 +1,15 @@  - SSH.NET.SftpAlwaysAbsolute - 2017.0.0-aa1 - SSH.NET.SftpAlwaysAbsolute + SSH.NET + 2017.0.0-beta1 + SSH.NET Renci olegkap,drieseng https://github.com/sshnet/SSH.NET/blob/master/LICENSE https://github.com/sshnet/SSH.NET/ false - SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism and with broad framework support. This fork allows for sftp client to always use absolute paths on the server. + SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism and with broad framework support. https://github.com/sshnet/SSH.NET/releases/tag/2017.0.0-beta1 A Secure Shell (SSH) library for .NET, optimized for parallelism. 2012-2017, RENCI @@ -32,7 +32,7 @@ - + \ No newline at end of file From 61350ada6568552974c31956ef0ae77f216a6887 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:22:48 -0700 Subject: [PATCH 09/12] more nuspec --- build/nuget/SSH.NET.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/nuget/SSH.NET.nuspec b/build/nuget/SSH.NET.nuspec index c4f60076b..a3520842a 100644 --- a/build/nuget/SSH.NET.nuspec +++ b/build/nuget/SSH.NET.nuspec @@ -32,7 +32,7 @@ - + \ No newline at end of file From 1930949bc194985a57cf3f417eefb0f1a4387702 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:26:32 -0700 Subject: [PATCH 10/12] sftp fixed --- src/Renci.sftp/Program.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Renci.sftp/Program.cs b/src/Renci.sftp/Program.cs index f2db5fac4..89bdfe082 100644 --- a/src/Renci.sftp/Program.cs +++ b/src/Renci.sftp/Program.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; -namespace renci.sftp +namespace Renci.sftp { class Program { @@ -18,15 +18,25 @@ static void Main(string[] args) int port = 22; string destination = "127.0.0.1"; + bool localChangeDir = false; + for (int i = 0; i < args.Length; i++) { - if (args[i] == "P") + if (args[i] == "-P") { if (i + 1 < args.Length) { port = int.Parse(args[i + 1]); i++; } + + continue; + } + + if (args[i] == "-LCHD") + { + localChangeDir = true; + continue; } destination = args[i]; @@ -37,10 +47,11 @@ static void Main(string[] args) Console.Write("pwd: "); string password = ReadPassword(); Console.WriteLine("connecting..."); - Renci.SshNet.SftpClient.ChangeDirIsLocal = true; - using (var client = new Renci.SshNet.SftpClient(destination, port, username, password)) + using (var client = new SshNet.SftpClient(destination, port, username, password)) { + client.ChangeDirIsLocal = localChangeDir; client.Connect(); + while (true) { Console.WriteLine(); From 79e8c01c3a76849aee1e5a98abb17646a5abdcc0 Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 14:55:43 -0700 Subject: [PATCH 11/12] ifixed UTs --- src/Renci.SshNet/Sftp/SftpSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index 6d0255f2f..4be01b34b 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -70,7 +70,7 @@ public uint NextRequestId /// /// /// If true, the sftp client will always pass absolute paths to serve, and will locally mimmick 'changedir' operations - public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory, bool changeDirIsLocal) + public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory, bool changeDirIsLocal=false) : base(session, "sftp", operationTimeout) { Encoding = encoding; From 62b347a07409afe7e27073b012263cff9f2bc56b Mon Sep 17 00:00:00 2001 From: Luis Irun-Briz Date: Wed, 2 Oct 2019 15:02:43 -0700 Subject: [PATCH 12/12] added arg --- .../SftpClientTest_Connect_SftpSessionConnectFailure.cs | 2 +- .../Classes/SftpClientTest_Dispose_Connected.cs | 4 ++-- .../Classes/SftpClientTest_Dispose_Disconnected.cs | 4 ++-- .../Classes/SftpClientTest_Dispose_Disposed.cs | 4 ++-- .../Classes/SftpClientTest_Finalize_Connected.cs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs index afbc247b3..e65d72256 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs @@ -64,7 +64,7 @@ private void SetupMocks() .Setup(p => p.CreateSftpResponseFactory()) .Returns(_sftpResponseFactoryMock.Object); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, -1, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, -1, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence) .Setup(p => p.Connect()) diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs index ba7d080fb..8f93575a9 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs @@ -49,7 +49,7 @@ protected void Arrange() .Setup(p => p.CreateSftpResponseFactory()) .Returns(_sftpResponseFactoryMock.Object); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); @@ -74,7 +74,7 @@ public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs index 516b8aebf..a4a65d2ee 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs @@ -45,7 +45,7 @@ protected void Arrange() .Setup(p => p.CreateSftpResponseFactory()) .Returns(_sftpResponseFactoryMock.Object); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); @@ -71,7 +71,7 @@ public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs index 73b07ad46..8fb6a210b 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs @@ -49,7 +49,7 @@ protected void Arrange() .Setup(p => p.CreateSftpResponseFactory()) .Returns(_sftpResponseFactoryMock.Object); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); @@ -75,7 +75,7 @@ public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs index 1c1328d75..507c08a37 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs @@ -40,7 +40,7 @@ protected void Arrange() _sessionMock.Setup(p => p.Connect()); _serviceFactoryMock.Setup(p => p.CreateSftpResponseFactory()) .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) + _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object, false)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.Setup(p => p.Connect());