<PackageReference Include="SSH.NET" Version="2016.0.0" />

SftpSession

using Renci.SshNet.Common; using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading; namespace Renci.SshNet.Sftp { internal class SftpSession : SubsystemSession, ISftpSession, ISubsystemSession, IDisposable { private const int MaximumSupportedVersion = 3; private const int MinimumSupportedVersion = 0; private readonly Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>(); private readonly List<byte> _data = new List<byte>(32768); private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); private IDictionary<string, string> _supportedExtensions; private long _requestId; public string WorkingDirectory { get; set; } public uint ProtocolVersion { get; set; } public uint NextRequestId => (uint)Interlocked.Increment(ref _requestId); public SftpSession(ISession session, TimeSpan operationTimeout, Encoding encoding) : base(session, "sftp", operationTimeout, encoding) { } public void ChangeDirectory(string path) { string canonicalPath = GetCanonicalPath(path); byte[] handle = RequestOpenDir(canonicalPath, false); RequestClose(handle); WorkingDirectory = canonicalPath; } internal void SendMessage(SftpMessage sftpMessage) { byte[] bytes = sftpMessage.GetBytes(); SendData(bytes); } public string GetCanonicalPath(string path) { string fullRemotePath = GetFullRemotePath(path); string text = string.Empty; KeyValuePair<string, SftpFileAttributes>[] array = RequestRealPath(fullRemotePath, true); KeyValuePair<string, SftpFileAttributes> keyValuePair; if (array != null) { keyValuePair = array.First(); text = keyValuePair.Key; } if (!string.IsNullOrEmpty(text)) return text; if (fullRemotePath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || fullRemotePath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || fullRemotePath.Equals("/", StringComparison.OrdinalIgnoreCase) || fullRemotePath.IndexOf('/') < 0) return fullRemotePath; string[] array2 = fullRemotePath.Split(new char[1] { '/' }); string text2 = string.Join("/", array2, 0, array2.Length - 1); if (string.IsNullOrEmpty(text2)) text2 = "/"; array = RequestRealPath(text2, true); if (array != null) { keyValuePair = array.First(); text = keyValuePair.Key; } if (string.IsNullOrEmpty(text)) return fullRemotePath; string text3 = string.Empty; if (text[text.Length - 1] != '/') text3 = "/"; return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", new object[3] { text, text3, array2[array2.Length - 1] }); } internal string GetFullRemotePath(string path) { string result = path; if (!string.IsNullOrEmpty(path) && path[0] != '/' && WorkingDirectory != null) result = ((WorkingDirectory[WorkingDirectory.Length - 1] != '/') ? string.Format(CultureInfo.InvariantCulture, "{0}/{1}", new object[2] { WorkingDirectory, path }) : string.Format(CultureInfo.InvariantCulture, "{0}{1}", new object[2] { WorkingDirectory, path })); return result; } protected override void OnChannelOpen() { SendMessage(new SftpInitRequest(3)); WaitOnHandle(_sftpVersionConfirmed, base.OperationTimeout); if (ProtocolVersion > 3 || ProtocolVersion < 0) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", new object[1] { ProtocolVersion })); WorkingDirectory = RequestRealPath(".", false).First().Key; } protected override void OnDataReceived(byte[] data) { _data.AddRange(data); while (_data.Count > 5) { int num = (_data[0] << 24) | (_data[1] << 16) | (_data[2] << 8) | _data[3]; if (_data.Count < num + 4) break; int num2 = num + 4; byte[] array = new byte[num2]; _data.CopyTo(0, array, 0, num2); _data.RemoveRange(0, num2); SftpMessage sftpMessage = SftpMessage.Load(ProtocolVersion, array, base.Encoding); try { SftpVersionResponse sftpVersionResponse = sftpMessage as SftpVersionResponse; if (sftpVersionResponse != null) { ProtocolVersion = sftpVersionResponse.Version; _supportedExtensions = sftpVersionResponse.Extentions; _sftpVersionConfirmed.Set(); } else HandleResponse(sftpMessage as SftpResponse); } catch (Exception error) { RaiseError(error); return; } } } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing && _sftpVersionConfirmed != null) { _sftpVersionConfirmed.Dispose(); _sftpVersionConfirmed = null; } } private void SendRequest(SftpRequest request) { lock (_requests) { _requests.Add(request.RequestId, request); } SendMessage(request); } public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) { byte[] handle = null; SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpOpenRequest request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, base.Encoding, flags, delegate(SftpHandleResponse response) { handle = response.Handle; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return handle; } public void RequestClose(byte[] handle) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpCloseRequest request = new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public byte[] RequestRead(byte[] handle, ulong offset, uint length) { SshException exception = null; byte[] data = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadRequest request = new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, delegate(SftpDataResponse response) { data = response.Data; wait.Set(); }, delegate(SftpStatusResponse response) { if (response.StatusCode != StatusCodes.Eof) exception = GetSftpException(response); data = Array<byte>.Empty; wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return data; } public void RequestWrite(byte[] handle, ulong offset, byte[] data, int length, AutoResetEvent wait, Action<SftpStatusResponse> writeCompleted = null) { SshException exception = null; SftpWriteRequest request = new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, offset, data, length, delegate(SftpStatusResponse response) { if (writeCompleted != null) writeCompleted(response); exception = GetSftpException(response); if (wait != null) wait.Set(); }); SendRequest(request); if (wait != null) WaitOnHandle(wait, base.OperationTimeout); if (exception != null) throw exception; } public SftpFileAttributes RequestLStat(string path) { SshException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpLStatRequest request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return attributes; } public SftpFileAttributes RequestFStat(byte[] handle) { SshException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpFStatRequest request = new SftpFStatRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return attributes; } public void RequestSetStat(string path, SftpFileAttributes attributes) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpSetStatRequest request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, path, base.Encoding, attributes, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpFSetStatRequest request = new SftpFSetStatRequest(ProtocolVersion, NextRequestId, handle, attributes, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public byte[] RequestOpenDir(string path, bool nullOnError = false) { SshException exception = null; byte[] handle = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpOpenDirRequest request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpHandleResponse response) { handle = response.Handle; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return handle; } public KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle) { SshException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadDirRequest request = new SftpReadDirRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpNameResponse response) { result = response.Files; wait.Set(); }, delegate(SftpStatusResponse response) { if (response.StatusCode != StatusCodes.Eof) exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; return result; } public void RequestRemove(string path) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRemoveRequest request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestMkDir(string path) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpMkDirRequest request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestRmDir(string path) { SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRmDirRequest request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false) { SshException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRealPathRequest request = new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpNameResponse response) { result = response.Files; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return result; } internal SftpFileAttributes RequestStat(string path, bool nullOnError = false) { SshException exception = null; SftpFileAttributes attributes = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpStatRequest request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpAttrsResponse response) { attributes = response.Attributes; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return attributes; } public void RequestRename(string oldPath, string newPath) { if (ProtocolVersion < 2) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpRenameRequest request = new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } internal KeyValuePair<string, SftpFileAttributes>[] RequestReadLink(string path, bool nullOnError = false) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; KeyValuePair<string, SftpFileAttributes>[] result = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpReadLinkRequest request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpNameResponse response) { result = response.Files; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return result; } public void RequestSymLink(string linkpath, string targetpath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { SftpSymLinkRequest request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, linkpath, targetpath, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); SendRequest(request); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public void RequestPosixRename(string oldPath, string newPath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { PosixRenameRequest posixRenameRequest = new PosixRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, base.Encoding, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); if (!_supportedExtensions.ContainsKey(posixRenameRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", new object[1] { posixRenameRequest.Name })); SendRequest(posixRenameRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; SftpFileSytemInformation information = null; AutoResetEvent wait = new AutoResetEvent(false); try { StatVfsRequest statVfsRequest = new StatVfsRequest(ProtocolVersion, NextRequestId, path, base.Encoding, delegate(SftpExtendedReplyResponse response) { information = response.GetReply<StatVfsReplyInfo>().Information; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); if (!_supportedExtensions.ContainsKey(statVfsRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", new object[1] { statVfsRequest.Name })); SendRequest(statVfsRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return information; } internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; SftpFileSytemInformation information = null; AutoResetEvent wait = new AutoResetEvent(false); try { FStatVfsRequest fStatVfsRequest = new FStatVfsRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpExtendedReplyResponse response) { information = response.GetReply<StatVfsReplyInfo>().Information; wait.Set(); }, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); if (!_supportedExtensions.ContainsKey(fStatVfsRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", new object[1] { fStatVfsRequest.Name })); SendRequest(fStatVfsRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (!nullOnError && exception != null) throw exception; return information; } internal void HardLink(string oldPath, string newPath) { if (ProtocolVersion < 3) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", new object[1] { ProtocolVersion })); SshException exception = null; AutoResetEvent wait = new AutoResetEvent(false); try { HardLinkRequest hardLinkRequest = new HardLinkRequest(ProtocolVersion, NextRequestId, oldPath, newPath, delegate(SftpStatusResponse response) { exception = GetSftpException(response); wait.Set(); }); if (!_supportedExtensions.ContainsKey(hardLinkRequest.Name)) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", new object[1] { hardLinkRequest.Name })); SendRequest(hardLinkRequest); WaitOnHandle(wait, base.OperationTimeout); } finally { if (wait != null) ((IDisposable)wait).Dispose(); } if (exception != null) throw exception; } public uint CalculateOptimalReadLength(uint bufferSize) { uint localPacketSize = base.Channel.LocalPacketSize; return Math.Min(bufferSize, localPacketSize) - 13; } public uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle) { uint num = (uint)(25 + handle.Length); uint remotePacketSize = base.Channel.RemotePacketSize; return Math.Min(bufferSize, remotePacketSize) - num; } private static SshException GetSftpException(SftpStatusResponse response) { if (response.StatusCode == StatusCodes.Ok) return null; if (response.StatusCode == StatusCodes.PermissionDenied) return new SftpPermissionDeniedException(response.ErrorMessage); if (response.StatusCode == StatusCodes.NoSuchFile) return new SftpPathNotFoundException(response.ErrorMessage); return new SshException(response.ErrorMessage); } private void HandleResponse(SftpResponse response) { SftpRequest value; lock (_requests) { _requests.TryGetValue(response.ResponseId, out value); if (value != null) _requests.Remove(response.ResponseId); } if (value == null) throw new InvalidOperationException("Invalid response."); value.Complete(response); } } }