SftpSession
Represents an SFTP session.
            
                using Renci.SshNet.Common;
using Renci.SshNet.Sftp.Requests;
using Renci.SshNet.Sftp.Responses;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Renci.SshNet.Sftp
{
    internal sealed class SftpSession : SubsystemSession, ISftpSession, ISubsystemSession, IDisposable
    {
        internal const int MaximumSupportedVersion = 3;
        private const int MinimumSupportedVersion = 0;
        private readonly Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>();
        private readonly ISftpResponseFactory _sftpResponseFactory;
        private readonly Encoding _encoding;
        private ArrayBuffer _buffer = new ArrayBuffer(32768, false);
        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, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory)
            : base(session, "sftp", operationTimeout)
        {
            _encoding = encoding;
            _sftpResponseFactory = sftpResponseFactory;
        }
        public void ChangeDirectory(string path)
        {
            string canonicalPath = GetCanonicalPath(path);
            byte[] handle = RequestOpenDir(canonicalPath);
            RequestClose(handle);
            WorkingDirectory = canonicalPath;
        }
        [AsyncStateMachine(typeof(<ChangeDirectoryAsync>d__21))]
        public Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken)
        {
            <ChangeDirectoryAsync>d__21 stateMachine = default(<ChangeDirectoryAsync>d__21);
            stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
            stateMachine.<>4__this = this;
            stateMachine.path = path;
            stateMachine.cancellationToken = cancellationToken;
            stateMachine.<>1__state = -1;
            stateMachine.<>t__builder.Start(ref stateMachine);
            return stateMachine.<>t__builder.Task;
        }
        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);
            if (array != null)
                text = array[0].Key;
            if (!string.IsNullOrEmpty(text))
                return text;
            if (fullRemotePath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || fullRemotePath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || fullRemotePath.Equals("/", StringComparison.OrdinalIgnoreCase) || fullRemotePath.IndexOf('/', StringComparison.OrdinalIgnoreCase) < 0)
                return fullRemotePath;
            string[] array2 = fullRemotePath.Split('/', StringSplitOptions.None);
            string text2 = string.Join('/', array2, 0, array2.Length - 1);
            if (string.IsNullOrEmpty(text2))
                text2 = "/";
            array = RequestRealPath(text2, true);
            if (array != null)
                text = array[0].Key;
            if (string.IsNullOrEmpty(text))
                return fullRemotePath;
            string arg = string.Empty;
            if (text[text.Length - 1] != '/')
                arg = "/";
            return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", text, arg, array2[array2.Length - 1]);
        }
        [AsyncStateMachine(typeof(<GetCanonicalPathAsync>d__24))]
        public Task<string> GetCanonicalPathAsync(string path, CancellationToken cancellationToken)
        {
            <GetCanonicalPathAsync>d__24 stateMachine = default(<GetCanonicalPathAsync>d__24);
            stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
            stateMachine.<>4__this = this;
            stateMachine.path = path;
            stateMachine.cancellationToken = cancellationToken;
            stateMachine.<>1__state = -1;
            stateMachine.<>t__builder.Start(ref stateMachine);
            return stateMachine.<>t__builder.Task;
        }
        internal string GetFullRemotePath(string path)
        {
            string result = path;
            if (!string.IsNullOrEmpty(path) && path[0] != '/' && WorkingDirectory != null)
                result = ((WorkingDirectory[WorkingDirectory.Length - 1] != '/') ? (WorkingDirectory + "/" + path) : (WorkingDirectory + path));
            return result;
        }
        protected override void OnChannelOpen()
        {
            SendMessage(new SftpInitRequest(3));
            WaitOnHandle(_sftpVersionConfirmed, base.OperationTimeout);
            if (ProtocolVersion > 3)
                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", ProtocolVersion));
            WorkingDirectory = RequestRealPath(".", false)[0].Key;
        }
        protected override void OnDataReceived(ArraySegment<byte> data)
        {
            Span<byte> span;
            if (_buffer.ActiveLength == 0) {
                while (data.Count >= 4) {
                    int num = BinaryPrimitives.ReadInt32BigEndian(data);
                    if (data.Count - 4 < num)
                        break;
                    if (!TryLoadSftpMessage(data.Slice(4, num)))
                        return;
                    data = data.Slice(4 + num);
                }
                if (data.Count > 0) {
                    _buffer.EnsureAvailableSpace(data.Count);
                    span = data.AsSpan();
                    span.CopyTo(_buffer.AvailableSpan);
                    _buffer.Commit(data.Count);
                }
            } else {
                _buffer.EnsureAvailableSpace(data.Count);
                span = data.AsSpan();
                span.CopyTo(_buffer.AvailableSpan);
                _buffer.Commit(data.Count);
                while (_buffer.ActiveLength >= 4) {
                    data = new ArraySegment<byte>(_buffer.DangerousGetUnderlyingBuffer(), _buffer.ActiveStartOffset, _buffer.ActiveLength);
                    int num2 = BinaryPrimitives.ReadInt32BigEndian(data);
                    if (data.Count - 4 < num2)
                        break;
                    TryLoadSftpMessage(data.Slice(4, num2));
                    _buffer.Discard(4 + num2);
                }
            }
        }
        private bool TryLoadSftpMessage(ArraySegment<byte> packetData)
        {
            SftpMessage sftpMessage = _sftpResponseFactory.Create(ProtocolVersion, packetData.Array[packetData.Offset], _encoding);
            sftpMessage.Load(packetData.Array, packetData.Offset + 1, packetData.Count - 1);
            try {
                SftpVersionResponse sftpVersionResponse = sftpMessage as SftpVersionResponse;
                if (sftpVersionResponse != null) {
                    ProtocolVersion = sftpVersionResponse.Version;
                    _supportedExtensions = sftpVersionResponse.Extensions;
                    _sftpVersionConfirmed.Set();
                } else
                    HandleResponse(sftpMessage as SftpResponse);
                return true;
            } catch (Exception error) {
                RaiseError(error);
                return false;
            }
        }
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing) {
                EventWaitHandle sftpVersionConfirmed = _sftpVersionConfirmed;
                if (sftpVersionConfirmed != null) {
                    _sftpVersionConfirmed = null;
                    sftpVersionConfirmed.Dispose();
                }
            }
        }
        private void SendRequest(SftpRequest request)
        {
            lock (_requests) {
                _requests.Add(request.RequestId, request);
            }
            SendMessage(request);
        }
        public byte[] RequestOpen(string path, Flags flags)
        {
            byte[] handle = null;
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpOpenRequest request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, _encoding, flags, delegate(SftpHandleResponse response) {
                    handle = response.Handle;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return handle;
        }
        public Task<byte[]> RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<byte[]>(cancellationToken);
            TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpOpenRequest(ProtocolVersion, NextRequestId, path, _encoding, flags, delegate(SftpHandleResponse response) {
                tcs.TrySetResult(response.Handle);
            }, delegate(SftpStatusResponse response) {
                tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestClose(byte[] handle)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpCloseRequest request = new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Ok)
                    tcs.TrySetResult(true);
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public byte[] RequestRead(byte[] handle, ulong offset, uint length)
        {
            SftpException 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.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    if (response.StatusCode != StatusCode.Eof)
                        exception = GetSftpException(response, null);
                    else
                        data = Array.Empty<byte>();
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return data;
        }
        public Task<byte[]> RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<byte[]>(cancellationToken);
            TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, delegate(SftpDataResponse response) {
                tcs.TrySetResult(response.Data);
            }, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Eof)
                    tcs.TrySetResult(Array.Empty<byte>());
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestWrite(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, AutoResetEvent wait, Action<SftpStatusResponse> writeCompleted = null)
        {
            SftpException exception = null;
            SftpWriteRequest request = new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, serverOffset, data, offset, length, delegate(SftpStatusResponse response) {
                if (writeCompleted != null)
                    writeCompleted(response);
                else {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                }
            });
            SendRequest(request);
            if (wait != null) {
                WaitOnHandle(wait, base.OperationTimeout);
                if (exception != null)
                    throw exception;
            }
        }
        public Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, serverOffset, data, offset, length, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Ok)
                    tcs.TrySetResult(true);
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public SftpFileAttributes RequestLStat(string path)
        {
            SftpException exception = null;
            SftpFileAttributes attributes = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpLStatRequest request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) {
                    attributes = response.Attributes;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return attributes;
        }
        public Task<SftpFileAttributes> RequestLStatAsync(string path, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<SftpFileAttributes>(cancellationToken);
            TaskCompletionSource<SftpFileAttributes> tcs = new TaskCompletionSource<SftpFileAttributes>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpLStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) {
                tcs.TrySetResult(response.Attributes);
            }, delegate(SftpStatusResponse response) {
                tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public SftpFileAttributes RequestFStat(byte[] handle)
        {
            SftpException 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.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return attributes;
        }
        public Task<SftpFileAttributes> RequestFStatAsync(byte[] handle, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<SftpFileAttributes>(cancellationToken);
            TaskCompletionSource<SftpFileAttributes> tcs = new TaskCompletionSource<SftpFileAttributes>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpFStatRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpAttrsResponse response) {
                tcs.TrySetResult(response.Attributes);
            }, delegate(SftpStatusResponse response) {
                tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestSetStat(string path, SftpFileAttributes attributes)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpSetStatRequest request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, path, _encoding, attributes, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                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)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpFSetStatRequest request = new SftpFSetStatRequest(ProtocolVersion, NextRequestId, handle, attributes, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public byte[] RequestOpenDir(string path)
        {
            SftpException exception = null;
            byte[] handle = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpOpenDirRequest request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpHandleResponse response) {
                    handle = response.Handle;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return handle;
        }
        public Task<byte[]> RequestOpenDirAsync(string path, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<byte[]>(cancellationToken);
            TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpHandleResponse response) {
                tcs.TrySetResult(response.Handle);
            }, delegate(SftpStatusResponse response) {
                tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle)
        {
            SftpException 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.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    if (response.StatusCode != StatusCode.Eof)
                        exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return result;
        }
        public Task<KeyValuePair<string, SftpFileAttributes>[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<KeyValuePair<string, SftpFileAttributes>[]>(cancellationToken);
            TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]> tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpReadDirRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpNameResponse response) {
                tcs.TrySetResult(response.Files);
            }, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Eof)
                    tcs.TrySetResult(null);
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestRemove(string path)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpRemoveRequest request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public Task RequestRemoveAsync(string path, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Ok)
                    tcs.TrySetResult(true);
                else
                    tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestMkDir(string path)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpMkDirRequest request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public Task RequestMkDirAsync(string path, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Ok)
                    tcs.TrySetResult(true);
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public void RequestRmDir(string path)
        {
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpRmDirRequest request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public Task RequestRmDirAsync(string path, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpStatusResponse response) {
                SftpException sftpException = GetSftpException(response, path);
                if (sftpException != null)
                    tcs.TrySetException(sftpException);
                else
                    tcs.TrySetResult(true);
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false)
        {
            SftpException exception = null;
            KeyValuePair<string, SftpFileAttributes>[] result = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpRealPathRequest request = new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) {
                    result = response.Files;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (!nullOnError && exception != null)
                throw exception;
            return result;
        }
        internal Task<KeyValuePair<string, SftpFileAttributes>[]> RequestRealPathAsync(string path, bool nullOnError, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<KeyValuePair<string, SftpFileAttributes>[]>(cancellationToken);
            TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]> tcs = new TaskCompletionSource<KeyValuePair<string, SftpFileAttributes>[]>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) {
                tcs.TrySetResult(response.Files);
            }, delegate(SftpStatusResponse response) {
                if (nullOnError)
                    tcs.TrySetResult(null);
                else
                    tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        public SftpFileAttributes RequestStat(string path)
        {
            SftpException exception = null;
            SftpFileAttributes attributes = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpStatRequest request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpAttrsResponse response) {
                    attributes = response.Attributes;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (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.", ProtocolVersion));
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpRenameRequest request = new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                SendRequest(request);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) {
                if (response.StatusCode == StatusCode.Ok)
                    tcs.TrySetResult(true);
                else
                    tcs.TrySetException(GetSftpException(response, null));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        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.", ProtocolVersion));
            SftpException exception = null;
            KeyValuePair<string, SftpFileAttributes>[] result = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpReadLinkRequest request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpNameResponse response) {
                    result = response.Files;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                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.", ProtocolVersion));
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                SftpSymLinkRequest request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, linkpath, targetpath, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                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.", ProtocolVersion));
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                PosixRenameRequest posixRenameRequest = new PosixRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, _encoding, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                if (!_supportedExtensions.ContainsKey(posixRenameRequest.Name))
                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", posixRenameRequest.Name));
                SendRequest(posixRenameRequest);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
        }
        public SftpFileSystemInformation RequestStatVfs(string path)
        {
            if (ProtocolVersion < 3)
                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion));
            SftpException exception = null;
            SftpFileSystemInformation information = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                StatVfsRequest statVfsRequest = new StatVfsRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpExtendedReplyResponse response) {
                    information = response.GetReply<StatVfsReplyInfo>().Information;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, path);
                    wait.SetIgnoringObjectDisposed();
                });
                if (!_supportedExtensions.ContainsKey(statVfsRequest.Name))
                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", statVfsRequest.Name));
                SendRequest(statVfsRequest);
                WaitOnHandle(wait, base.OperationTimeout);
            } finally {
                if (wait != null)
                    ((IDisposable)wait).Dispose();
            }
            if (exception != null)
                throw exception;
            return information;
        }
        public Task<SftpFileSystemInformation> RequestStatVfsAsync(string path, CancellationToken cancellationToken)
        {
            if (ProtocolVersion < 3)
                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion));
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<SftpFileSystemInformation>(cancellationToken);
            TaskCompletionSource<SftpFileSystemInformation> tcs = new TaskCompletionSource<SftpFileSystemInformation>(TaskCreationOptions.RunContinuationsAsynchronously);
            SendRequest(new StatVfsRequest(ProtocolVersion, NextRequestId, path, _encoding, delegate(SftpExtendedReplyResponse response) {
                tcs.TrySetResult(response.GetReply<StatVfsReplyInfo>().Information);
            }, delegate(SftpStatusResponse response) {
                tcs.TrySetException(GetSftpException(response, path));
            }));
            return WaitOnHandleAsync(tcs, base.OperationTimeout, cancellationToken);
        }
        internal SftpFileSystemInformation 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.", ProtocolVersion));
            SftpException exception = null;
            SftpFileSystemInformation information = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                FStatVfsRequest fStatVfsRequest = new FStatVfsRequest(ProtocolVersion, NextRequestId, handle, delegate(SftpExtendedReplyResponse response) {
                    information = response.GetReply<StatVfsReplyInfo>().Information;
                    wait.SetIgnoringObjectDisposed();
                }, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                if (!_supportedExtensions.ContainsKey(fStatVfsRequest.Name))
                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", 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.", ProtocolVersion));
            SftpException exception = null;
            AutoResetEvent wait = new AutoResetEvent(false);
            try {
                HardLinkRequest hardLinkRequest = new HardLinkRequest(ProtocolVersion, NextRequestId, oldPath, newPath, delegate(SftpStatusResponse response) {
                    exception = GetSftpException(response, null);
                    wait.SetIgnoringObjectDisposed();
                });
                if (!_supportedExtensions.ContainsKey(hardLinkRequest.Name))
                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", 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;
        }
        internal static SftpException GetSftpException(SftpStatusResponse response, string path = null)
        {
            switch (response.StatusCode) {
            case StatusCode.Ok:
                return null;
            case StatusCode.PermissionDenied:
                return new SftpPermissionDeniedException(response.ErrorMessage);
            case StatusCode.NoSuchFile: {
                string text = response.ErrorMessage;
                if (!string.IsNullOrEmpty(text) && path != null) {
                    DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(9, 3);
                    defaultInterpolatedStringHandler.AppendFormatted(text);
                    string text2 = text;
                    defaultInterpolatedStringHandler.AppendFormatted((text2[text2.Length - 1] == '.') ? " " : ". ");
                    defaultInterpolatedStringHandler.AppendLiteral("Path: '");
                    defaultInterpolatedStringHandler.AppendFormatted(path);
                    defaultInterpolatedStringHandler.AppendLiteral("'.");
                    text = defaultInterpolatedStringHandler.ToStringAndClear();
                }
                return new SftpPathNotFoundException(text, path);
            }
            default:
                return new SftpException(response.StatusCode, 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);
        }
    }
}