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);
}
}
}