diff --git a/Renci.SshClient/Renci.SshClient/Sftp/SftpCommand.cs b/Renci.SshClient/Renci.SshClient/Sftp/SftpCommand.cs index f02c65a5..1cc1812d 100644 --- a/Renci.SshClient/Renci.SshClient/Sftp/SftpCommand.cs +++ b/Renci.SshClient/Renci.SshClient/Sftp/SftpCommand.cs @@ -7,6 +7,9 @@ using Renci.SshClient.Messages.Sftp; namespace Renci.SshClient.Sftp { + /// + /// Base class for all SFTP Commands + /// internal abstract class SftpCommand { private SftpSession _sftpSession; @@ -17,6 +20,8 @@ namespace Renci.SshClient.Sftp private Exception _error; + private bool _handleCloseMessageSent; + protected SftpAsyncResult AsyncResult { get; private set; } public int CommandTimeout { get; set; } @@ -70,6 +75,12 @@ namespace Renci.SshClient.Sftp protected virtual void OnStatus(StatusCodes statusCode, string errorMessage, string language) { + if (this._handleCloseMessageSent) + { + this.OnHandleClosed(); + + this._handleCloseMessageSent = false; + } } protected virtual void OnName(IEnumerable files) @@ -107,6 +118,8 @@ namespace Renci.SshClient.Sftp { Handle = handle, }); + + this._handleCloseMessageSent = true; } protected void SendReadMessage(string handle, ulong offset, uint bufferSize) @@ -262,19 +275,19 @@ namespace Renci.SshClient.Sftp if (this._requestId == e.Message.RequestId) { this.OnStatus(e.Message.StatusCode, e.Message.ErrorMessage, e.Message.Language); - } - if (e.Message.StatusCode == StatusCodes.NoSuchFile || - e.Message.StatusCode == StatusCodes.PermissionDenied || - e.Message.StatusCode == StatusCodes.Failure || - e.Message.StatusCode == StatusCodes.BadMessage || - e.Message.StatusCode == StatusCodes.NoConnection || - e.Message.StatusCode == StatusCodes.ConnectionLost || - e.Message.StatusCode == StatusCodes.OperationUnsupported - ) - { - // Throw an exception if it was not handled by the command - throw new SshException(e.Message.ErrorMessage); + if (e.Message.StatusCode == StatusCodes.NoSuchFile || + e.Message.StatusCode == StatusCodes.PermissionDenied || + e.Message.StatusCode == StatusCodes.Failure || + e.Message.StatusCode == StatusCodes.BadMessage || + e.Message.StatusCode == StatusCodes.NoConnection || + e.Message.StatusCode == StatusCodes.ConnectionLost || + e.Message.StatusCode == StatusCodes.OperationUnsupported + ) + { + // Throw an exception if it was not handled by the command + throw new SshException(e.Message.ErrorMessage); + } } } diff --git a/Renci.SshClient/Renci.SshClient/Sftp/SftpSession.cs b/Renci.SshClient/Renci.SshClient/Sftp/SftpSession.cs index 7a517861..b59181b4 100644 --- a/Renci.SshClient/Renci.SshClient/Sftp/SftpSession.cs +++ b/Renci.SshClient/Renci.SshClient/Sftp/SftpSession.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -89,15 +90,39 @@ namespace Renci.SshClient.Sftp private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e) { + var packets = new Queue(); + if (this._packetData == null) { - var packetLength = (uint)(e.Data[0] << 24 | e.Data[1] << 16 | e.Data[2] << 8 | e.Data[3]); + var dataOffset = 0; + while (true) + { + // Read SFTP packet length + var packetLength = (e.Data[dataOffset + 0] << 24 | e.Data[dataOffset + 1] << 16 | e.Data[dataOffset + 2] << 8 | e.Data[dataOffset + 3]); - this._packetData = new StringBuilder((int)packetLength, (int)packetLength); - this._packetData.Append(e.Data.GetSshBytes().Skip(4).GetSshString()); + // Create data holder for SFTP packet + this._packetData = new StringBuilder(packetLength, packetLength); + + // Add data to the packet holder + this._packetData.Append(e.Data.GetSshBytes().Skip(dataOffset + 4).Take((int)packetLength).GetSshString()); + + dataOffset += (packetLength + 4); + + if (dataOffset < e.Data.Length) + { + // If there is another SFTP packet in current message then queue this data and read next one + packets.Enqueue(this._packetData.ToString()); + continue; + } + else + { + break; + } + } } else { + // Add message data to packet data this._packetData.Append(e.Data); } @@ -107,18 +132,25 @@ namespace Renci.SshClient.Sftp return; } - dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes()); + // Add last packet to the queue of packet data that need to be proccessed + packets.Enqueue(this._packetData.ToString()); this._packetData = null; - try + foreach (var packetData in packets) { - // TODO: Check to run on different thread - this.HandleMessage(sftpMessage); - } - catch (Exception exp) - { - this.RaiseError(exp); + dynamic sftpMessage = SftpMessage.Load(packetData.GetSshBytes()); + + try + { + // TODO: Check to run on different thread + this.HandleMessage(sftpMessage); + } + catch (Exception exp) + { + this.RaiseError(exp); + break; + } } }