From b84ad09fd5138ecdec9ad57cde96bbce64020d3d Mon Sep 17 00:00:00 2001 From: olegkap_cp Date: Mon, 29 Nov 2010 18:29:27 +0000 Subject: [PATCH] Temporary remove SFTP functionality while refactoring to allow asynch execution --- .../Channels/ChannelSessionSftp.cs | 580 ------------------ .../Channels/ChannelSessionShell.cs | 99 --- .../Renci.SshClient/Common/SshData.cs | 2 +- .../Renci.SshClient/Messages/Message.cs | 2 +- .../Messages/Sftp/AttrsMessage.cs | 2 +- .../Messages/Sftp/CloseMessage.cs | 2 +- .../Messages/Sftp/DataMessage.cs | 2 +- .../Messages/Sftp/ExtendedMessage.cs | 2 +- .../Messages/Sftp/ExtendedReplyMessage.cs | 2 +- .../Renci.SshClient/Messages/Sftp/FSetStat.cs | 2 +- .../Messages/Sftp/FSetStatMessage.cs | 2 +- .../Messages/Sftp/FStatMessage.cs | 2 +- .../Messages/Sftp/HandleMessage.cs | 2 +- .../Messages/Sftp/InitMessage.cs | 3 +- .../Messages/Sftp/LStatMessage.cs | 2 +- .../Messages/Sftp/MkDirMessage.cs | 3 +- .../Messages/Sftp/NameMessage.cs | 2 +- .../Messages/Sftp/OpenDirMessage.cs | 12 +- .../Messages/Sftp/OpenMessage.cs | 2 +- .../Messages/Sftp/ReadDirMessage.cs | 12 +- .../Messages/Sftp/ReadLinkMessage.cs | 2 +- .../Messages/Sftp/ReadMessage.cs | 2 +- .../Messages/Sftp/RealPathMessage.cs | 2 +- .../Messages/Sftp/RemoveMessage.cs | 2 +- .../Messages/Sftp/RenameMessage.cs | 2 +- .../Messages/Sftp/RmDirMessage.cs | 2 +- .../Messages/Sftp/SetStatMessage.cs | 2 +- .../Messages/Sftp/SftpMessage.cs | 11 +- .../Messages/Sftp/SftpRequestMessage.cs | 20 + .../Messages/Sftp/StatMessage.cs | 2 +- .../Messages/Sftp/StatusMessage.cs | 2 +- .../Messages/Sftp/SymLinkMessage.cs | 2 +- .../Messages/Sftp/VersionMessage.cs | 1 - .../Messages/Sftp/WriteMessage.cs | 2 +- .../Renci.SshClient/Renci.SshClient.csproj | 4 +- Renci.SshClient/Renci.SshClient/Sftp.cs | 281 +++++++-- .../Renci.SshClient/SftpAsyncResult.cs | 58 ++ Renci.SshClient/Renci.SshClient/SshClient.cs | 41 +- 38 files changed, 391 insertions(+), 784 deletions(-) delete mode 100644 Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs delete mode 100644 Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs create mode 100644 Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpRequestMessage.cs create mode 100644 Renci.SshClient/Renci.SshClient/SftpAsyncResult.cs diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs deleted file mode 100644 index 8284fd17..00000000 --- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionSftp.cs +++ /dev/null @@ -1,580 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshClient.Common; -using Renci.SshClient.Messages.Sftp; - -namespace Renci.SshClient.Channels -{ - // TODO: Add Begin* and End* methods for async calls - - internal class ChannelSessionSftp : ChannelSession - { - private EventWaitHandle _channelRequestSuccessWaitHandle = new AutoResetEvent(false); - - private EventWaitHandle _responseMessageReceivedWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - - private uint _requestId; - - private SftpMessage _responseMessage; - - private string _remoteCurrentDir; - - private string _localCurentDir; - - private StringBuilder _packetData; - - // TODO: Repalce with SFTP specific async class if needed - private CommandAsyncResult _asyncResult; - - public ChannelSessionSftp() - : base() - { - - } - - public override void Open() - { - base.Open(); - - // Send channel command request - this.SendSubsystemRequest("sftp"); - - this.WaitHandle(this._channelRequestSuccessWaitHandle); - - this.SendMessage(new InitMessage - { - Version = 3, - }); - - var versionMessage = this.ReceiveMessage(); - - if (versionMessage == null) - { - throw new InvalidOperationException("Version message expected."); - } - - if (versionMessage.Version != 3) - { - throw new NotSupportedException(string.Format("Server SFTP version {0} is not supported.", versionMessage.Version)); - } - - // Get default current directories - var files = this.GetRealPath("."); - - this._remoteCurrentDir = files.First().Name; - this._localCurentDir = Directory.GetCurrentDirectory(); - } - - public void UploadFile(Stream source, string destination) - { - this.Open(); - - string handle = string.Empty; - - try - { - handle = this.OpenRemoteFile(destination, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate); - - var buffer = new byte[1024]; - ulong offset = 0; - var bytesRead = 0; - while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) - { - this.RemoteWrite(handle, offset, buffer.Take(bytesRead).GetSshString()); - offset += (ulong)buffer.Length; - } - } - finally - { - if (!string.IsNullOrEmpty(handle)) - this.CloseRemoteHandle(handle); - } - - this.Close(); - } - - internal void DownloadFile(string fileName, Stream destination) - { - this.Open(); - - string handle = string.Empty; - - try - { - handle = this.OpenRemoteFile(fileName, Flags.Read); - - ulong offset = 0; - uint bufferSize = 1024; - string data; - - while ((data = this.RemoteRead(handle, offset, bufferSize)) != null) - { - var fileData = data.GetSshBytes().ToArray(); - destination.Write(fileData, 0, fileData.Length); - destination.Flush(); - offset += (ulong)fileData.Length; - } - - } - finally - { - if (!string.IsNullOrEmpty(handle)) - this.CloseRemoteHandle(handle); - } - - this.Close(); - } - - public void CreateDirectory(string directoryName) - { - this.Open(); - - this.CreateRemoteDirectory(directoryName); - - this.Close(); - } - - public void RemoveDirectory(string directoryName) - { - this.Open(); - - this.RemoveRemoteDirectory(directoryName); - - this.Close(); - } - - public void RemoveFile(string fileName) - { - this.Open(); - - this.RemoveRemoteFile(fileName); - - this.Close(); - } - - public void RenameFile(string oldFileName, string newFileName) - { - this.Open(); - - this.RenameRemoteFile(oldFileName, newFileName); - - this.Close(); - } - - public IEnumerable ListDirectory(string path) - { - // Open channel - this.Open(); - - string handle = string.Empty; - IEnumerable files = null; - - try - { - // Open directory - handle = this.OpenRemoteDirectory(path); - - // Read directory data - files = this.ReadRemoteDirectory(handle); - } - finally - { - // Close directory - if (!string.IsNullOrEmpty(handle)) - this.CloseRemoteHandle(handle); - } - - // Read directory - this.Close(); - - return files; - - } - - protected override void OnSuccess() - { - base.OnSuccess(); - - this._channelRequestSuccessWaitHandle.Set(); - } - - - protected override void OnData(string data) - { - base.OnData(data); - - if (this._packetData == null) - { - var packetLength = (uint)(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]); - - this._packetData = new StringBuilder((int)packetLength, (int)packetLength); - this._packetData.Append(data.GetSshBytes().Skip(4).GetSshString()); - } - else - { - this._packetData.Append(data); - } - - if (this._packetData.Length < this._packetData.MaxCapacity) - { - // Wait for more packet data - return; - } - - - dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes()); - - this._packetData = null; - - if (sftpMessage.RequestId != null) - { - if (sftpMessage.RequestId != this._requestId) - { - throw new InvalidOperationException("Invalid request id."); - } - - this._requestId++; - } - - this._responseMessage = sftpMessage; - - this._responseMessageReceivedWaitHandle.Set(); - } - - private T ReceiveMessage() where T : SftpMessage - { - var message = this.ReceiveMessage() as T; - - if (message == null) - { - throw new InvalidOperationException(string.Format("Message of type '{0}' expected in this context.", typeof(T).Name)); - } - return message; - - } - - private SftpMessage ReceiveMessage() - { - this.WaitHandle(this._responseMessageReceivedWaitHandle); - - var statusMessage = this._responseMessage as StatusMessage; - - if (statusMessage != null) - { - // Handle error status messages - switch (statusMessage.StatusCode) - { - case StatusCodes.Ok: - break; - case StatusCodes.Eof: - break; - case StatusCodes.NoSuchFile: - throw new FileNotFoundException("File or directory not found on the remote server."); - case StatusCodes.PermissionDenied: - throw new NotImplementedException(); - case StatusCodes.Failure: - throw new InvalidOperationException("Operation failed."); - case StatusCodes.BadMessage: - throw new NotImplementedException(); - case StatusCodes.NoConnection: - throw new NotImplementedException(); - case StatusCodes.ConnectionLost: - throw new NotImplementedException(); - case StatusCodes.OperationUnsupported: - throw new NotSupportedException("Operation is not supported."); - default: - break; - } - } - - return this._responseMessage; - } - - private void SendMessage(SftpMessage sftpMessage) - { - sftpMessage.RequestId = this._requestId; - var message = new SftpDataMessage - { - LocalChannelNumber = this.RemoteChannelNumber, - Message = sftpMessage, - }; - - this.SendMessage(message); - - this._responseMessageReceivedWaitHandle.Reset(); - } - - private string OpenRemoteFile(string fileName, Flags flags) - { - this.SendMessage(new OpenMessage - { - Filename = fileName, - Flags = flags, - }); - - var handleMessage = this.ReceiveMessage(); - - return handleMessage.Handle; - } - - private string RemoteRead(string handle, ulong offset, uint length) - { - this.SendMessage(new ReadMessage - { - Handle = handle, - Offset = offset, - Length = length, - }); - - var message = this.ReceiveMessage(); - - var statusMessage = message as StatusMessage; - var dataMessage = message as DataMessage; - - if (statusMessage != null) - { - if (statusMessage.StatusCode == StatusCodes.Eof) - { - return null; - } - - throw new InvalidOperationException("Invalid status code."); - } - else if (dataMessage != null) - { - if (this._asyncResult != null) - { - this._asyncResult.BytesReceived += dataMessage.Data.Length; - } - return dataMessage.Data; - } - else - { - throw new InvalidOperationException(string.Format("Message type '{0}' is not valid in this context.", message.SftpMessageType)); - } - } - - private void RemoteWrite(string handle, ulong offset, string data) - { - this.SendMessage(new WriteMessage - { - Handle = handle, - Offset = offset, - Data = data, - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - - if (this._asyncResult != null) - { - this._asyncResult.BytesSent += data.Length; - } - - } - - private void RemoveRemoteFile(string fileName) - { - this.SendMessage(new RemoveMessage - { - Filename = fileName, - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private void RenameRemoteFile(string oldFileName, string newFileName) - { - this.SendMessage(new RenameMessage - { - OldPath = oldFileName, - NewPath = newFileName, - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private void CreateRemoteDirectory(string directoryName) - { - this.SendMessage(new MkDirMessage - { - Path = directoryName, - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private void RemoveRemoteDirectory(string directoryName) - { - this.SendMessage(new RmDirMessage - { - Path = directoryName, - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private string OpenRemoteDirectory(string path) - { - this.SendMessage(new OpenDirMessage - { - Path = path, - }); - - var handleMessage = this.ReceiveMessage(); - - return handleMessage.Handle; - } - - private IEnumerable ReadRemoteDirectory(string handle) - { - this.SendMessage(new ReadDirMessage - { - Handle = handle, - }); - - var message = this.ReceiveMessage(); - - return message.Files; - } - - private void CloseRemoteHandle(string handle) - { - this.SendMessage(new CloseMessage - { - Handle = handle, - }); - - var status = this.ReceiveMessage(); - var attempts = 0; - // If close fails wait a litle a try to close it again, in case server flushed data into the file during close - while (status.StatusCode != StatusCodes.Ok && attempts++ < this.ConnectionInfo.RetryAttempts) - { - Thread.Sleep(50); - status = this.ReceiveMessage(); - } - - if (status.StatusCode != StatusCodes.Ok) - { - throw new InvalidOperationException(string.Format("File handle cannot be closed after {0} attempts.", attempts)); - } - } - - private Attributes GetRemoteFileAttributes(string filename) - { - this.SendMessage(new StatMessage - { - Path = filename, - }); - - var message = this.ReceiveMessage(); - - return message.Attributes; - } - - private Attributes GetRemoteLinkFileAttributes(string filename) - { - this.SendMessage(new LStatMessage - { - Path = filename, - }); - - var message = this.ReceiveMessage(); - - return message.Attributes; - } - - private Attributes GetRemoteOpenFileAttributes(string handle) - { - this.SendMessage(new FStatMessage - { - Handle = handle, - }); - - var message = this.ReceiveMessage(); - - return message.Attributes; - } - - private void SetRemoteFileAttributes(string filename, Attributes attributes) - { - this.SendMessage(new SetStatMessage - { - Path = filename, - Attributes = attributes - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private void SetRemoteOpenFileAttributes(string handle, Attributes attributes) - { - this.SendMessage(new FSetStatMessage - { - Handle = handle, - Attributes = attributes - }); - - var message = this.ReceiveMessage(); - - this.EnsureStatusCode(message, StatusCodes.Ok); - } - - private IEnumerable GetRealPath(string path) - { - this.SendMessage(new RealPathMessage - { - Path = path, - }); - - var message = this.ReceiveMessage(); - - return message.Files; - - } - - private void EnsureStatusCode(StatusMessage message, StatusCodes code) - { - if (message.StatusCode == code) - { - return; - } - else - { - throw new InvalidOperationException("Invalid status code."); - } - } - - #region IDisposable Members - - protected override void OnDisposing() - { - // Dispose managed resources. - if (this._channelRequestSuccessWaitHandle != null) - { - this._channelRequestSuccessWaitHandle.Dispose(); - } - if (this._responseMessageReceivedWaitHandle != null) - { - this._responseMessageReceivedWaitHandle.Dispose(); - } - } - - #endregion - } -} diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs deleted file mode 100644 index 4bcb9037..00000000 --- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSessionShell.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.IO; -using System.Linq; -using System.Threading; -using Renci.SshClient.Messages.Connection; - -namespace Renci.SshClient.Channels -{ - internal class ChannelSessionShell : ChannelSession - { - - private EventWaitHandle _success = new AutoResetEvent(false); - - /// - /// Holds channel data stream - /// - private Stream _channelData; - - /// - /// Holds channel extended data stream - /// - private Stream _channelExtendedData; - - public void Start(Stream output, Stream extendedOutput) - { - this.Open(); - - this._channelData = output; - this._channelExtendedData = extendedOutput; - - this.SendPseudoTerminalRequest("xterm", 80, 24, 640, 240, ""); - - _success.WaitOne(); - - this.SendShellRequest(); - } - - public void Stop() - { - // Close channel - this.Close(); - } - - protected override void OnSuccess() - { - base.OnSuccess(); - - _success.Set(); - } - - protected override void OnFailure() - { - base.OnFailure(); - } - - protected override void OnData(string data) - { - base.OnData(data); - - this._channelData.Write(data.GetSshBytes().ToArray(), 0, data.Length); - this._channelData.Flush(); - } - - protected override void OnExtendedData(string data, uint dataTypeCode) - { - base.OnExtendedData(data, dataTypeCode); - - // TODO: dataTypeCode is not handled - this._channelExtendedData.Write(data.GetSshBytes().ToArray(), 0, data.Length); - this._channelExtendedData.Flush(); - } - - public void Send(string data) - { - this.SendMessage(new ChannelDataMessage - { - LocalChannelNumber = this.RemoteChannelNumber, - Data = data, - }); - } - - public class ShellStream : MemoryStream - { - public ShellStream() - { - - } - - public override void Write(byte[] buffer, int offset, int count) - { - base.Write(buffer, offset, count); - } - - public override void WriteByte(byte value) - { - base.WriteByte(value); - } - } - } -} diff --git a/Renci.SshClient/Renci.SshClient/Common/SshData.cs b/Renci.SshClient/Renci.SshClient/Common/SshData.cs index 2dd3dca8..560b5876 100644 --- a/Renci.SshClient/Renci.SshClient/Common/SshData.cs +++ b/Renci.SshClient/Renci.SshClient/Common/SshData.cs @@ -25,7 +25,7 @@ namespace Renci.SshClient.Common private IEnumerable _loadedData; - public virtual int ZeroReaderIndex + protected virtual int ZeroReaderIndex { get { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Message.cs b/Renci.SshClient/Renci.SshClient/Messages/Message.cs index 4109c618..77e6876f 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Message.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Message.cs @@ -23,7 +23,7 @@ namespace Renci.SshClient.Messages return message; } - public override int ZeroReaderIndex + protected override int ZeroReaderIndex { get { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/AttrsMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/AttrsMessage.cs index 45eb64e9..097bf672 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/AttrsMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/AttrsMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class AttrsMessage : SftpMessage + internal class AttrsMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/CloseMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/CloseMessage.cs index 877719af..e52a1a36 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/CloseMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/CloseMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class CloseMessage : SftpMessage + internal class CloseMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/DataMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/DataMessage.cs index 29105202..ee3ec079 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/DataMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/DataMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class DataMessage : SftpMessage + internal class DataMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedMessage.cs index afc35e01..e1e71e01 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class ExtendedMessage : SftpMessage + internal class ExtendedMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedReplyMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedReplyMessage.cs index 9c64d78e..3d854437 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedReplyMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ExtendedReplyMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class ExtendedReplyMessage : SftpMessage + internal class ExtendedReplyMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStat.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStat.cs index a82d61fa..1c7a4c84 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStat.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStat.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class FSetStat : SftpMessage + internal class FSetStat : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStatMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStatMessage.cs index 4ffed0b7..5e936b35 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStatMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FSetStatMessage.cs @@ -1,6 +1,6 @@ namespace Renci.SshClient.Messages.Sftp { - internal class FSetStatMessage : SftpMessage + internal class FSetStatMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FStatMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FStatMessage.cs index 8ab1c6ca..80ecbf07 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/FStatMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/FStatMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class FStatMessage : SftpMessage + internal class FStatMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/HandleMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/HandleMessage.cs index 87483b73..a5af06b6 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/HandleMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/HandleMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class HandleMessage : SftpMessage + internal class HandleMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/InitMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/InitMessage.cs index 1c400afa..7cc46b7c 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/InitMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/InitMessage.cs @@ -11,7 +11,8 @@ protected override void LoadData() { - + base.LoadData(); + this.Version = this.ReadUInt32(); } protected override void SaveData() diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/LStatMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/LStatMessage.cs index aeb3df62..eaec16bb 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/LStatMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/LStatMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class LStatMessage : SftpMessage + internal class LStatMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/MkDirMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/MkDirMessage.cs index 99181aa2..d40fda30 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/MkDirMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/MkDirMessage.cs @@ -1,6 +1,6 @@ namespace Renci.SshClient.Messages.Sftp { - internal class MkDirMessage : SftpMessage + internal class MkDirMessage : SftpRequestMessage { public MkDirMessage() { @@ -29,5 +29,6 @@ this.Write(this.Path); this.Write(this.Attributes); } + } } diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/NameMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/NameMessage.cs index cc0f8f01..e8f8eea3 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/NameMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/NameMessage.cs @@ -3,7 +3,7 @@ using Renci.SshClient.Common; namespace Renci.SshClient.Messages.Sftp { - internal class NameMessage : SftpMessage + internal class NameMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenDirMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenDirMessage.cs index f7c3d5ee..cc326568 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenDirMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenDirMessage.cs @@ -2,7 +2,7 @@ using System.Text; namespace Renci.SshClient.Messages.Sftp { - internal class OpenDirMessage : SftpMessage + internal class OpenDirMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { @@ -11,6 +11,16 @@ namespace Renci.SshClient.Messages.Sftp public string Path { get; set; } + public OpenDirMessage() + { + + } + + public OpenDirMessage(string path) + { + this.Path = path; + } + protected override void LoadData() { base.LoadData(); diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenMessage.cs index 1afd4bad..a7ac18e8 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/OpenMessage.cs @@ -2,7 +2,7 @@ namespace Renci.SshClient.Messages.Sftp { - internal class OpenMessage : SftpMessage + internal class OpenMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadDirMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadDirMessage.cs index 8c478fb7..a4202499 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadDirMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadDirMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class ReadDirMessage : SftpMessage + internal class ReadDirMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { @@ -10,6 +10,16 @@ namespace Renci.SshClient.Messages.Sftp public string Handle { get; set; } + public ReadDirMessage() + { + + } + + public ReadDirMessage(string handle) + { + this.Handle = handle; + } + protected override void LoadData() { base.LoadData(); diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadLinkMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadLinkMessage.cs index 1b4b4566..d84b4e80 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadLinkMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadLinkMessage.cs @@ -2,7 +2,7 @@ using System.Text; namespace Renci.SshClient.Messages.Sftp { - internal class ReadLinkMessage : SftpMessage + internal class ReadLinkMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadMessage.cs index ae8cc733..8b847559 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/ReadMessage.cs @@ -2,7 +2,7 @@ using System; namespace Renci.SshClient.Messages.Sftp { - internal class ReadMessage : SftpMessage + internal class ReadMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RealPathMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RealPathMessage.cs index cd9b2387..87da748f 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RealPathMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RealPathMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class RealPathMessage : SftpMessage + internal class RealPathMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RemoveMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RemoveMessage.cs index debfcb25..7985d884 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RemoveMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RemoveMessage.cs @@ -2,7 +2,7 @@ using System.Text; namespace Renci.SshClient.Messages.Sftp { - internal class RemoveMessage : SftpMessage + internal class RemoveMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RenameMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RenameMessage.cs index 86d3877d..f4b0a594 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RenameMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RenameMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class RenameMessage : SftpMessage + internal class RenameMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RmDirMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RmDirMessage.cs index a62a4d8f..806ffbe1 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/RmDirMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/RmDirMessage.cs @@ -2,7 +2,7 @@ namespace Renci.SshClient.Messages.Sftp { - internal class RmDirMessage : SftpMessage + internal class RmDirMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SetStatMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SetStatMessage.cs index 12c0f979..84f96e88 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SetStatMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SetStatMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class SetStatMessage : SftpMessage + internal class SetStatMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpMessage.cs index 8fc975a3..7813da17 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpMessage.cs @@ -18,7 +18,7 @@ namespace Renci.SshClient.Messages.Sftp return Load(data, messageType); } - public override int ZeroReaderIndex + protected override int ZeroReaderIndex { get { @@ -59,22 +59,13 @@ namespace Renci.SshClient.Messages.Sftp public abstract SftpMessageTypes SftpMessageType { get; } - public uint? RequestId { get; set; } - protected override void LoadData() { - // SSH_FXP_INIT and SSH_FXP_VERSION doesnt have RequestID, all other messaages do - if (!(this.SftpMessageType == SftpMessageTypes.Init || this.SftpMessageType == SftpMessageTypes.Version)) - { - this.RequestId = this.ReadUInt32(); - } } protected override void SaveData() { this.Write((byte)this.SftpMessageType); - if (this.RequestId.HasValue) - this.Write(this.RequestId.Value); } protected Attributes ReadAttributes() diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpRequestMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpRequestMessage.cs new file mode 100644 index 00000000..1996c301 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SftpRequestMessage.cs @@ -0,0 +1,20 @@ + +namespace Renci.SshClient.Messages.Sftp +{ + internal abstract class SftpRequestMessage : SftpMessage + { + public uint RequestId { get; set; } + + protected override void LoadData() + { + base.LoadData(); + this.RequestId = this.ReadUInt32(); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.RequestId); + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatMessage.cs index db30e9c5..f8f8e7da 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatMessage.cs @@ -1,7 +1,7 @@  namespace Renci.SshClient.Messages.Sftp { - internal class StatMessage : SftpMessage + internal class StatMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatusMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatusMessage.cs index 846287d3..4fdee66c 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatusMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/StatusMessage.cs @@ -1,6 +1,6 @@ namespace Renci.SshClient.Messages.Sftp { - internal class StatusMessage : SftpMessage + internal class StatusMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SymLinkMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SymLinkMessage.cs index 0d00eab3..3c2ee20a 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/SymLinkMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/SymLinkMessage.cs @@ -2,7 +2,7 @@ namespace Renci.SshClient.Messages.Sftp { - internal class SymLinkMessage : SftpMessage + internal class SymLinkMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/VersionMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/VersionMessage.cs index 77a16680..8741dbd8 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/VersionMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/VersionMessage.cs @@ -31,6 +31,5 @@ namespace Renci.SshClient.Messages.Sftp this.Write(this.Version); this.Write(this.Extentions); } - } } diff --git a/Renci.SshClient/Renci.SshClient/Messages/Sftp/WriteMessage.cs b/Renci.SshClient/Renci.SshClient/Messages/Sftp/WriteMessage.cs index aed3e1c1..0a442d0f 100644 --- a/Renci.SshClient/Renci.SshClient/Messages/Sftp/WriteMessage.cs +++ b/Renci.SshClient/Renci.SshClient/Messages/Sftp/WriteMessage.cs @@ -2,7 +2,7 @@ namespace Renci.SshClient.Messages.Sftp { - internal class WriteMessage : SftpMessage + internal class WriteMessage : SftpRequestMessage { public override SftpMessageTypes SftpMessageType { diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj index 84576884..cf5a5020 100644 --- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj +++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj @@ -64,7 +64,6 @@ - @@ -84,6 +83,8 @@ + + @@ -102,7 +103,6 @@ - diff --git a/Renci.SshClient/Renci.SshClient/Sftp.cs b/Renci.SshClient/Renci.SshClient/Sftp.cs index 475b52f9..898d7ca4 100644 --- a/Renci.SshClient/Renci.SshClient/Sftp.cs +++ b/Renci.SshClient/Renci.SshClient/Sftp.cs @@ -1,82 +1,273 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text; +using System.Threading; using Renci.SshClient.Channels; using Renci.SshClient.Common; +using Renci.SshClient.Messages.Sftp; namespace Renci.SshClient { public class Sftp { - private ChannelSessionSftp _channel; - - private ChannelSessionSftp Channel - { - get - { - if (this._channel == null) - { - this._channel = this._session.CreateChannel(); - } - return this._channel; - } - } + private readonly ChannelSession _channel; private readonly Session _session; + private StringBuilder _packetData; + + private uint _requestId; + + private Exception _exception; + + private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); + + private EventWaitHandle _sessionErrorOccuredWaitHandle = new AutoResetEvent(false); + + private IDictionary> _requestActions; + + private string _remoteCurrentDir; + + public int OperationTimeout { get; set; } + internal Sftp(Session session) { + this.OperationTimeout = -1; + this._requestActions = new Dictionary>(); this._session = session; - } + this._session.ErrorOccured += Session_ErrorOccured; + this._session.Disconnected += Session_Disconnected; + this._channel = session.CreateChannel(); + this._channel.DataReceived += Channel_DataReceived; + this._channel.Open(); + this._channel.SendSubsystemRequest("sftp"); - public IEnumerable ListDirectory(string path) - { - return this.Channel.ListDirectory(path); - } - - public void UploadFile(Stream source, string fileName) - { - this.Channel.UploadFile(source, fileName); - } - - public void UploadFile(string source, string fileName) - { - using (var sourceFile = File.OpenRead(source)) + this.SendMessage(new InitMessage { - this.Channel.UploadFile(sourceFile, fileName); + Version = 3, + }); + + this.WaitHandle(this._sftpVersionConfirmed); + + this.SendMessage(new RealPathMessage + { + Path = ".", + }, (m) => + { + var nameMessage = m as NameMessage; + if (nameMessage != null) + { + this._remoteCurrentDir = nameMessage.Files.First().Name; + } + else + { + throw new InvalidOperationException(""); + } + }); + } + + public IAsyncResult BeginListDirectory(string path, AsyncCallback callback, object state) + { + var asyncResult = new SftpAsyncResult(this._channel, callback, state); + + this.SendMessage(new OpenDirMessage(path), (m) => + { + var message = m as HandleMessage; + if (message == null) + throw new InvalidOperationException("Handle message is not expected."); + + this.SendMessage(new ReadDirMessage(message.Handle), (m1) => + { + var message1 = m1 as NameMessage; + if (message1 == null) + throw new InvalidOperationException("Handle message is not expected."); + asyncResult.Names = message1.Files; + asyncResult.IsCompleted = true; + }); + }); + + return asyncResult; + + + } + + public IEnumerable EndListDirectory(IAsyncResult result) + { + var r = result as SftpAsyncResult; + if (r == null) + { + throw new ArgumentException("Invalid IAsyncResult parameter."); + } + + r.AsyncWaitHandle.WaitOne(); + + return r.Names; + } + + + private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e) + { + if (this._packetData == null) + { + var packetLength = (uint)(e.Data[0] << 24 | e.Data[1] << 16 | e.Data[2] << 8 | e.Data[3]); + + this._packetData = new StringBuilder((int)packetLength, (int)packetLength); + this._packetData.Append(e.Data.GetSshBytes().Skip(4).GetSshString()); + } + else + { + this._packetData.Append(e.Data); + } + + if (this._packetData.Length < this._packetData.MaxCapacity) + { + // Wait for more packet data + return; + } + + dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes()); + + this._packetData = null; + + this.HandleMessage(sftpMessage); + } + + private void HandleMessage(InitMessage message) + { + throw new InvalidOperationException("Init message should not be received by client."); + } + + private void HandleMessage(VersionMessage message) + { + if (message.Version == 3) + { + this._sftpVersionConfirmed.Set(); + } + else + { + throw new NotSupportedException(string.Format("Server SFTP version {0} is not supported.", message.Version)); } } - public void DownloadFile(string fileName, Stream destination) + private void HandleMessage(SftpRequestMessage message) { - this.Channel.DownloadFile(fileName, destination); - } - - public void DownloadFile(string fileName, string destination) - { - using (var destinationFile = File.Create(destination)) + if (this._requestActions.ContainsKey(message.RequestId)) { - this.Channel.DownloadFile(fileName, destinationFile); + var action = this._requestActions[message.RequestId]; + action(message); + this._requestActions.Remove(message.RequestId); + } + else + { + throw new InvalidOperationException(string.Format("Request #{0} is invalid.", message.RequestId)); } } - public void RemoveFile(string fileName) + private void Session_Disconnected(object sender, EventArgs e) { - this.Channel.RemoveFile(fileName); } - public void RenameFile(string oldFileName, string newFileName) + private void Session_ErrorOccured(object sender, ErrorEventArgs e) { - this.Channel.RenameFile(oldFileName, newFileName); + this._exception = e.GetException(); + + this._sessionErrorOccuredWaitHandle.Set(); } - public void CreateDirectory(string directoryName) + private void SendMessage(SftpMessage sftpMessage) { - this.Channel.CreateDirectory(directoryName); + var message = new SftpDataMessage + { + LocalChannelNumber = this._channel.RemoteChannelNumber, + Message = sftpMessage, + }; + + this._session.SendMessage(message); } - public void RemoveDirectory(string directoryName) + private void SendMessage(SftpRequestMessage sftpMessage, Action action) { - this.Channel.RemoveDirectory(directoryName); + sftpMessage.RequestId = this._requestId++; + + var message = new SftpDataMessage + { + LocalChannelNumber = this._channel.RemoteChannelNumber, + Message = sftpMessage, + }; + + this._session.SendMessage(message); + + this._requestActions.Add(sftpMessage.RequestId, action); } + + private void WaitHandle(WaitHandle waitHandle) + { + var waitHandles = new WaitHandle[] + { + this._sessionErrorOccuredWaitHandle, + waitHandle, + }; + + var index = EventWaitHandle.WaitAny(waitHandles, this.OperationTimeout); + + if (index < 1) + { + throw this._exception; + } + else if (index > 1) + { + // throw time out error + throw new SshOperationTimeoutException(string.Format("Sftp operation has timed out.")); + } + } + + + //public void UploadFile(Stream source, string fileName) + //{ + // this.Channel.UploadFile(source, fileName); + //} + + //public void UploadFile(string source, string fileName) + //{ + // using (var sourceFile = File.OpenRead(source)) + // { + // this.Channel.UploadFile(sourceFile, fileName); + // } + //} + + //public void DownloadFile(string fileName, Stream destination) + //{ + // this.Channel.DownloadFile(fileName, destination); + //} + + //public void DownloadFile(string fileName, string destination) + //{ + // using (var destinationFile = File.Create(destination)) + // { + // this.Channel.DownloadFile(fileName, destinationFile); + // } + //} + + //public void RemoveFile(string fileName) + //{ + // this.Channel.RemoveFile(fileName); + //} + + //public void RenameFile(string oldFileName, string newFileName) + //{ + // this.Channel.RenameFile(oldFileName, newFileName); + //} + + //public void CreateDirectory(string directoryName) + //{ + // this.Channel.CreateDirectory(directoryName); + //} + + //public void RemoveDirectory(string directoryName) + //{ + // this.Channel.RemoveDirectory(directoryName); + //} } } diff --git a/Renci.SshClient/Renci.SshClient/SftpAsyncResult.cs b/Renci.SshClient/Renci.SshClient/SftpAsyncResult.cs new file mode 100644 index 00000000..ac657d83 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/SftpAsyncResult.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Renci.SshClient.Common; + +namespace Renci.SshClient +{ + public class SftpAsyncResult : IAsyncResult + { + private bool _isCompleted; + + private Channels.ChannelSession _channelSession; + + private AsyncCallback _callback; + + private EventWaitHandle _completedWaitHandle = new ManualResetEvent(false); + + public IList Names { get; internal set; } + + internal SftpAsyncResult(Channels.ChannelSession channelSession, AsyncCallback callback, object state) + { + this._channelSession = channelSession; + this._callback = callback; + this.AsyncState = state; + this.AsyncWaitHandle = _completedWaitHandle; + } + + #region IAsyncResult Members + + public object AsyncState { get; private set; } + + public WaitHandle AsyncWaitHandle { get; private set; } + + public bool CompletedSynchronously { get; private set; } + + public bool IsCompleted + { + get + { + return this._isCompleted; + } + internal set + { + this._isCompleted = value; + if (value) + { + if (this._callback != null) + { + this._callback(this); + } + this._completedWaitHandle.Set(); + } + } + } + + #endregion + } +} diff --git a/Renci.SshClient/Renci.SshClient/SshClient.cs b/Renci.SshClient/Renci.SshClient/SshClient.cs index 3bf389f7..40059eb8 100644 --- a/Renci.SshClient/Renci.SshClient/SshClient.cs +++ b/Renci.SshClient/Renci.SshClient/SshClient.cs @@ -23,22 +23,22 @@ namespace Renci.SshClient } } - private Sftp _sftp; - /// - /// Gets the shell. - /// - /// The shell. - public Sftp Sftp - { - get - { - if (this._sftp == null) - { - this._sftp = new Sftp(this._session); - } - return this._sftp; - } - } + //private Sftp _sftp; + ///// + ///// Gets the shell. + ///// + ///// The shell. + //public Sftp Sftp + //{ + // get + // { + // if (this._sftp == null) + // { + // this._sftp = new Sftp(this._session); + // } + // return this._sftp; + // } + //} public IEnumerable ForwardedPorts { @@ -111,7 +111,7 @@ namespace Renci.SshClient this._session.Disconnect(); // Clean up objects created using previouse session instance - this._sftp = null; + //this._sftp = null; } public T AddForwardedPort(uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new() @@ -135,7 +135,7 @@ namespace Renci.SshClient public SshCommand RunCommand(string commandText) { - var cmd = new SshCommand(this._session, commandText); + var cmd = this.CreateCommand(commandText); cmd.Execute(); return cmd; } @@ -145,6 +145,11 @@ namespace Renci.SshClient return new Shell(this._session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode); } + //public Sftp CreateSftp() + //{ + // return new Sftp(this._session); + //} + public void RemoveForwardedPort(ForwardedPort port) { this._forwardedPorts.Remove(port);