| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107 | using System;using System.Linq;using System.Text;using System.Threading;using Renci.SshNet.Common;using System.Collections.Generic;using System.Globalization;using Renci.SshNet.Sftp.Responses;using Renci.SshNet.Sftp.Requests;namespace Renci.SshNet.Sftp{    internal class SftpSession : SubsystemSession    {        private const int MAXIMUM_SUPPORTED_VERSION = 3;        private const int MINIMUM_SUPPORTED_VERSION = 0;        private readonly Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>();        private readonly List<byte> _data = new List<byte>(16 * 1024);        private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false);        private IDictionary<string, string> _supportedExtensions;        /// <summary>        /// Gets remote working directory.        /// </summary>        public string WorkingDirectory { get; private set; }        /// <summary>        /// Gets SFTP protocol version.        /// </summary>        public uint ProtocolVersion { get; private set; }        private long _requestId;        /// <summary>        /// Gets the next request id for sftp session.        /// </summary>        public uint NextRequestId        {            get            {#if WINDOWS_PHONE                lock (this)                {                    this._requestId++;                }                return (uint)this._requestId;#else                return ((uint)Interlocked.Increment(ref this._requestId));#endif            }        }        public SftpSession(Session session, TimeSpan operationTimeout, Encoding encoding)            : base(session, "sftp", operationTimeout, encoding)        {        }        public void ChangeDirectory(string path)        {            var fullPath = this.GetCanonicalPath(path);            var handle = this.RequestOpenDir(fullPath);            this.RequestClose(handle);            this.WorkingDirectory = fullPath;        }        internal void SendMessage(SftpMessage sftpMessage)        {            var messageData = sftpMessage.GetBytes();            var data = new byte[4 + messageData.Length];            ((uint)messageData.Length).GetBytes().CopyTo(data, 0);            messageData.CopyTo(data, 4);            this.SendData(data);        }        /// <summary>        /// Resolves path into absolute path on the server.        /// </summary>        /// <param name="path">Path to resolve.</param>        /// <returns>Absolute path</returns>        internal string GetCanonicalPath(string path)        {            var fullPath = GetFullRemotePath(path);            var canonizedPath = string.Empty;            var realPathFiles = this.RequestRealPath(fullPath, true);            if (realPathFiles != null)            {                canonizedPath = realPathFiles.First().Key;            }            if (!string.IsNullOrEmpty(canonizedPath))                return canonizedPath;            //  Check for special cases            if (fullPath.EndsWith("/.", StringComparison.InvariantCultureIgnoreCase) ||                fullPath.EndsWith("/..", StringComparison.InvariantCultureIgnoreCase) ||                fullPath.Equals("/", StringComparison.InvariantCultureIgnoreCase) ||                fullPath.IndexOf('/') < 0)                return fullPath;            var pathParts = fullPath.Split(new char[] { '/' });            var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1);            if (string.IsNullOrEmpty(partialFullPath))                partialFullPath = "/";            realPathFiles = this.RequestRealPath(partialFullPath, true);            if (realPathFiles != null)            {                canonizedPath = realPathFiles.First().Key;            }            if (string.IsNullOrEmpty(canonizedPath))            {                return fullPath;            }            var slash = string.Empty;            if (canonizedPath[canonizedPath.Length - 1] != '/')                slash = "/";            return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]);        }        internal string GetFullRemotePath(string path)        {            var fullPath = path;            if (!string.IsNullOrEmpty(path) && path[0] != '/' && this.WorkingDirectory != null)            {                if (this.WorkingDirectory[this.WorkingDirectory.Length - 1] == '/')                {                    fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.WorkingDirectory, path);                }                else                {                    fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", this.WorkingDirectory, path);                }            }            return fullPath;        }        protected override void OnChannelOpen()        {            this.SendMessage(new SftpInitRequest(MAXIMUM_SUPPORTED_VERSION));            this.WaitOnHandle(this._sftpVersionConfirmed, this._operationTimeout);            if (this.ProtocolVersion > MAXIMUM_SUPPORTED_VERSION || this.ProtocolVersion < MINIMUM_SUPPORTED_VERSION)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", this.ProtocolVersion));            }            //  Resolve current directory            this.WorkingDirectory = this.RequestRealPath(".").First().Key;        }        protected override void OnDataReceived(uint dataTypeCode, byte[] data)        {            //  Add channel data to internal data holder            this._data.AddRange(data);            while (this._data.Count > 4 + 1)            {                //  Extract packet length                var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]);                //  Check if complete packet data is available                if (this._data.Count < packetLength + 4)                {                    //  Wait for complete message to arrive first                    break;                }                this._data.RemoveRange(0, 4);                //  Create buffer to hold packet data                var packetData = new byte[packetLength];                //  Cope packet data to array                this._data.CopyTo(0, packetData, 0, packetLength);                //  Remove loaded data from _data holder                this._data.RemoveRange(0, packetLength);                //  Load SFTP Message and handle it                var response = SftpMessage.Load(this.ProtocolVersion, packetData, this.Encoding);                try                {                    var versionResponse = response as SftpVersionResponse;                    if (versionResponse != null)                    {                        this.ProtocolVersion = versionResponse.Version;                        this._supportedExtensions = versionResponse.Extentions;                        this._sftpVersionConfirmed.Set();                    }                    else                    {                        this.HandleResponse(response as SftpResponse);                    }                }                catch (Exception exp)                {                    this.RaiseError(exp);                    break;                }            }        }        protected override void Dispose(bool disposing)        {            base.Dispose(disposing);            if (disposing)            {                if (this._sftpVersionConfirmed != null)                {                    this._sftpVersionConfirmed.Dispose();                    this._sftpVersionConfirmed = null;                }            }        }        private void SendRequest(SftpRequest request)        {            lock (this._requests)            {                this._requests.Add(request.RequestId, request);            }            this.SendMessage(request);        }        #region SFTP API functions        /// <summary>        /// Performs SSH_FXP_OPEN request        /// </summary>        /// <param name="path">The path.</param>        /// <param name="flags">The flags.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns>File handle.</returns>        internal byte[] RequestOpen(string path, Flags flags, bool nullOnError = false)        {            byte[] handle = null;            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpOpenRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, flags,                    response =>                    {                        handle = response.Handle;                        wait.Set();                    },                    response =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }            return handle;        }        /// <summary>        /// Performs SSH_FXP_CLOSE request.        /// </summary>        /// <param name="handle">The handle.</param>        internal void RequestClose(byte[] handle)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpCloseRequest(this.ProtocolVersion, this.NextRequestId, handle,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_READ request.        /// </summary>        /// <param name="handle">The handle.</param>        /// <param name="offset">The offset.</param>        /// <param name="length">The length.</param>        /// <returns>data array; null if EOF</returns>        internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length)        {            SshException exception = null;            var data = new byte[0];            using (var wait = new AutoResetEvent(false))            {                var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length,                    (response) =>                    {                        data = response.Data;                        wait.Set();                    },                    (response) =>                    {                        if (response.StatusCode != StatusCodes.Eof)                        {                            exception = this.GetSftpException(response);                        }                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }            return data;        }        /// <summary>        /// Performs SSH_FXP_WRITE request.        /// </summary>        /// <param name="handle">The handle.</param>        /// <param name="offset">The offset.</param>        /// <param name="data">The data to send.</param>        /// <param name="wait">The wait event handle if needed.</param>        /// <param name="writeCompleted">The callback to invoke when the write has completed.</param>        internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait, Action<SftpStatusResponse> writeCompleted = null)        {            SshException exception = null;            var request = new SftpWriteRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, data,                (response) =>                {                    if (writeCompleted != null)                    {                        writeCompleted(response);                    }                    exception = this.GetSftpException(response);                    if (wait != null)                        wait.Set();                });            this.SendRequest(request);            if (wait != null)                this.WaitOnHandle(wait, this._operationTimeout);            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_LSTAT request.        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns>        /// File attributes        /// </returns>        internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false)        {            SshException exception = null;            SftpFileAttributes attributes = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpLStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        attributes = response.Attributes;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }            return attributes;        }        /// <summary>        /// Performs SSH_FXP_FSTAT request.        /// </summary>        /// <param name="handle">The handle.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns>        /// File attributes        /// </returns>        internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false)        {            SshException exception = null;            SftpFileAttributes attributes = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpFStatRequest(this.ProtocolVersion, this.NextRequestId, handle,                    (response) =>                    {                        attributes = response.Attributes;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }            return attributes;        }        /// <summary>        /// Performs SSH_FXP_SETSTAT request.        /// </summary>        /// <param name="path">The path.</param>        /// <param name="attributes">The attributes.</param>        internal void RequestSetStat(string path, SftpFileAttributes attributes)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpSetStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, attributes,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_FSETSTAT request.        /// </summary>        /// <param name="handle">The handle.</param>        /// <param name="attributes">The attributes.</param>        internal void RequestFSetStat(byte[] handle, SftpFileAttributes attributes)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpFSetStatRequest(this.ProtocolVersion, this.NextRequestId, handle, attributes,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_OPENDIR request        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns>File handle.</returns>        internal byte[] RequestOpenDir(string path, bool nullOnError = false)        {            SshException exception = null;            byte[] handle = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpOpenDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        handle = response.Handle;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }            return handle;        }        /// <summary>        /// Performs SSH_FXP_READDIR request        /// </summary>        /// <param name="handle">The handle.</param>        /// <returns></returns>        internal KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle)        {            SshException exception = null;            KeyValuePair<string, SftpFileAttributes>[] result = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpReadDirRequest(this.ProtocolVersion, this.NextRequestId, handle,                    (response) =>                    {                        result = response.Files;                        wait.Set();                    },                    (response) =>                    {                        if (response.StatusCode != StatusCodes.Eof)                        {                            exception = this.GetSftpException(response);                        }                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }            return result;        }        /// <summary>        /// Performs SSH_FXP_REMOVE request.        /// </summary>        /// <param name="path">The path.</param>        internal void RequestRemove(string path)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpRemoveRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_MKDIR request.        /// </summary>        /// <param name="path">The path.</param>        internal void RequestMkDir(string path)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpMkDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_RMDIR request.        /// </summary>        /// <param name="path">The path.</param>        internal void RequestRmDir(string path)        {            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpRmDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_REALPATH request        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns></returns>        internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false)        {            SshException exception = null;            KeyValuePair<string, SftpFileAttributes>[] result = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpRealPathRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        result = response.Files;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }                        return result;        }        /// <summary>        /// Performs SSH_FXP_STAT request.        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns>        /// File attributes        /// </returns>        internal SftpFileAttributes RequestStat(string path, bool nullOnError = false)        {            SshException exception = null;            SftpFileAttributes attributes = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        attributes = response.Attributes;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }            return attributes;        }        /// <summary>        /// Performs SSH_FXP_RENAME request.        /// </summary>        /// <param name="oldPath">The old path.</param>        /// <param name="newPath">The new path.</param>        internal void RequestRename(string oldPath, string newPath)        {            if (this.ProtocolVersion < 2)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs SSH_FXP_READLINK request.        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> returns null instead of throwing an exception.</param>        /// <returns></returns>        internal KeyValuePair<string, SftpFileAttributes>[] RequestReadLink(string path, bool nullOnError = false)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            KeyValuePair<string, SftpFileAttributes>[] result = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpReadLinkRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        result = response.Files;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }            return result;        }        /// <summary>        /// Performs SSH_FXP_SYMLINK request.        /// </summary>        /// <param name="linkpath">The linkpath.</param>        /// <param name="targetpath">The targetpath.</param>        internal void RequestSymLink(string linkpath, string targetpath)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new SftpSymLinkRequest(this.ProtocolVersion, this.NextRequestId, linkpath, targetpath, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        #endregion        #region SFTP Extended API functions        /// <summary>        /// Performs posix-rename@openssh.com extended request.        /// </summary>        /// <param name="oldPath">The old path.</param>        /// <param name="newPath">The new path.</param>        internal void RequestPosixRename(string oldPath, string newPath)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new PosixRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                if (!this._supportedExtensions.ContainsKey(request.Name))                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name));                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        /// <summary>        /// Performs statvfs@openssh.com extended request.        /// </summary>        /// <param name="path">The path.</param>        /// <param name="nullOnError">if set to <c>true</c> [null on error].</param>        /// <returns></returns>        internal SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            SftpFileSytemInformation information = null;            using (var wait = new AutoResetEvent(false))            {                var request = new StatVfsRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,                    (response) =>                    {                        information = response.GetReply<StatVfsReplyInfo>().Information;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                if (!this._supportedExtensions.ContainsKey(request.Name))                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name));                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (!nullOnError && exception != null)            {                throw exception;            }            return information;        }        /// <summary>        /// Performs fstatvfs@openssh.com extended request.        /// </summary>        /// <param name="handle">The file handle.</param>        /// <param name="nullOnError">if set to <c>true</c> [null on error].</param>        /// <returns></returns>        /// <exception cref="System.NotSupportedException"></exception>        internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            SftpFileSytemInformation information = null;            using (var wait = new AutoResetEvent(false))            {                var request = new FStatVfsRequest(this.ProtocolVersion, this.NextRequestId, handle,                    (response) =>                    {                        information = response.GetReply<StatVfsReplyInfo>().Information;                        wait.Set();                    },                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                if (!this._supportedExtensions.ContainsKey(request.Name))                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name));                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }                        if (!nullOnError && exception != null)            {                throw exception;            }            return information;        }        /// <summary>        /// Performs hardlink@openssh.com extended request.        /// </summary>        /// <param name="oldPath">The old path.</param>        /// <param name="newPath">The new path.</param>        internal void HardLink(string oldPath, string newPath)        {            if (this.ProtocolVersion < 3)            {                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));            }            SshException exception = null;            using (var wait = new AutoResetEvent(false))            {                var request = new HardLinkRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath,                    (response) =>                    {                        exception = this.GetSftpException(response);                        wait.Set();                    });                if (!this._supportedExtensions.ContainsKey(request.Name))                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name));                this.SendRequest(request);                this.WaitOnHandle(wait, this._operationTimeout);            }            if (exception != null)            {                throw exception;            }        }        #endregion        private SshException GetSftpException(SftpStatusResponse response)        {            if (response.StatusCode == StatusCodes.Ok)            {                return null;            }            if (response.StatusCode == StatusCodes.PermissionDenied)            {                return new SftpPermissionDeniedException(response.ErrorMessage);            }            else if (response.StatusCode == StatusCodes.NoSuchFile)            {                return new SftpPathNotFoundException(response.ErrorMessage);            }            else            {                return new SshException(response.ErrorMessage);            }        }        private void HandleResponse(SftpResponse response)        {            SftpRequest request;            lock (this._requests)            {                this._requests.TryGetValue(response.ResponseId, out request);                if (request != null)                {                    this._requests.Remove(response.ResponseId);                }            }            if (request == null)                throw new InvalidOperationException("Invalid response.");            request.Complete(response);        }    }}
 |