mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 17:25:51 +00:00
Temporary remove SFTP functionality while refactoring to allow asynch execution
This commit is contained in:
@@ -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<VersionMessage>();
|
||||
|
||||
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<FtpFileInfo> ListDirectory(string path)
|
||||
{
|
||||
// Open channel
|
||||
this.Open();
|
||||
|
||||
string handle = string.Empty;
|
||||
IEnumerable<FtpFileInfo> 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<T>() 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<HandleMessage>();
|
||||
|
||||
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<StatusMessage>();
|
||||
|
||||
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<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void RenameRemoteFile(string oldFileName, string newFileName)
|
||||
{
|
||||
this.SendMessage(new RenameMessage
|
||||
{
|
||||
OldPath = oldFileName,
|
||||
NewPath = newFileName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void CreateRemoteDirectory(string directoryName)
|
||||
{
|
||||
this.SendMessage(new MkDirMessage
|
||||
{
|
||||
Path = directoryName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void RemoveRemoteDirectory(string directoryName)
|
||||
{
|
||||
this.SendMessage(new RmDirMessage
|
||||
{
|
||||
Path = directoryName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private string OpenRemoteDirectory(string path)
|
||||
{
|
||||
this.SendMessage(new OpenDirMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
|
||||
var handleMessage = this.ReceiveMessage<HandleMessage>();
|
||||
|
||||
return handleMessage.Handle;
|
||||
}
|
||||
|
||||
private IEnumerable<FtpFileInfo> ReadRemoteDirectory(string handle)
|
||||
{
|
||||
this.SendMessage(new ReadDirMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<NameMessage>();
|
||||
|
||||
return message.Files;
|
||||
}
|
||||
|
||||
private void CloseRemoteHandle(string handle)
|
||||
{
|
||||
this.SendMessage(new CloseMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var status = this.ReceiveMessage<StatusMessage>();
|
||||
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<StatusMessage>();
|
||||
}
|
||||
|
||||
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<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private Attributes GetRemoteLinkFileAttributes(string filename)
|
||||
{
|
||||
this.SendMessage(new LStatMessage
|
||||
{
|
||||
Path = filename,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private Attributes GetRemoteOpenFileAttributes(string handle)
|
||||
{
|
||||
this.SendMessage(new FStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private void SetRemoteFileAttributes(string filename, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new SetStatMessage
|
||||
{
|
||||
Path = filename,
|
||||
Attributes = attributes
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void SetRemoteOpenFileAttributes(string handle, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new FSetStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Attributes = attributes
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private IEnumerable<FtpFileInfo> GetRealPath(string path)
|
||||
{
|
||||
this.SendMessage(new RealPathMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<NameMessage>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Holds channel data stream
|
||||
/// </summary>
|
||||
private Stream _channelData;
|
||||
|
||||
/// <summary>
|
||||
/// Holds channel extended data stream
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace Renci.SshClient.Common
|
||||
|
||||
private IEnumerable<byte> _loadedData;
|
||||
|
||||
public virtual int ZeroReaderIndex
|
||||
protected virtual int ZeroReaderIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Renci.SshClient.Messages
|
||||
return message;
|
||||
}
|
||||
|
||||
public override int ZeroReaderIndex
|
||||
protected override int ZeroReaderIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class AttrsMessage : SftpMessage
|
||||
internal class AttrsMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class CloseMessage : SftpMessage
|
||||
internal class CloseMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class DataMessage : SftpMessage
|
||||
internal class DataMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ExtendedMessage : SftpMessage
|
||||
internal class ExtendedMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ExtendedReplyMessage : SftpMessage
|
||||
internal class ExtendedReplyMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FSetStat : SftpMessage
|
||||
internal class FSetStat : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FSetStatMessage : SftpMessage
|
||||
internal class FSetStatMessage : SftpRequestMessage
|
||||
{
|
||||
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FStatMessage : SftpMessage
|
||||
internal class FStatMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class HandleMessage : SftpMessage
|
||||
internal class HandleMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
|
||||
base.LoadData();
|
||||
this.Version = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class LStatMessage : SftpMessage
|
||||
internal class LStatMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class OpenMessage : SftpMessage
|
||||
internal class OpenMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Text;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ReadLinkMessage : SftpMessage
|
||||
internal class ReadLinkMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ReadMessage : SftpMessage
|
||||
internal class ReadMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RealPathMessage : SftpMessage
|
||||
internal class RealPathMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Text;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RemoveMessage : SftpMessage
|
||||
internal class RemoveMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RenameMessage : SftpMessage
|
||||
internal class RenameMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RmDirMessage : SftpMessage
|
||||
internal class RmDirMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class SetStatMessage : SftpMessage
|
||||
internal class SetStatMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class StatMessage : SftpMessage
|
||||
internal class StatMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class StatusMessage : SftpMessage
|
||||
internal class StatusMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class SymLinkMessage : SftpMessage
|
||||
internal class SymLinkMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -31,6 +31,5 @@ namespace Renci.SshClient.Messages.Sftp
|
||||
this.Write(this.Version);
|
||||
this.Write(this.Extentions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class WriteMessage : SftpMessage
|
||||
internal class WriteMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
<Compile Include="Common\ChannelEventArgs.cs" />
|
||||
<Compile Include="Common\ChannelOpenFailedEventArgs.cs" />
|
||||
<Compile Include="Common\ChannelRequestEventArgs.cs" />
|
||||
<Compile Include="Channels\ChannelSessionShell.cs" />
|
||||
<Compile Include="Common\SshConnectionException.cs" />
|
||||
<Compile Include="Common\SshOperationTimeoutException.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpen\ChannelOpenInfo.cs" />
|
||||
@@ -84,6 +83,8 @@
|
||||
<Compile Include="Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs" />
|
||||
<Compile Include="Messages\Sftp\SftpRequestMessage.cs" />
|
||||
<Compile Include="SftpAsyncResult.cs" />
|
||||
<Compile Include="SshCommand.cs" />
|
||||
<Compile Include="MessageEventArgs.cs" />
|
||||
<Compile Include="ChannelAsyncResult.cs" />
|
||||
@@ -102,7 +103,6 @@
|
||||
<Compile Include="Security\CipherAES.cs" />
|
||||
<Compile Include="Security\CipherTripleDES.cs" />
|
||||
<Compile Include="Compression\Compressor.cs" />
|
||||
<Compile Include="Channels\ChannelSessionSftp.cs" />
|
||||
<Compile Include="Common\DataReceivedEventArgs.cs" />
|
||||
<Compile Include="Common\FtpFileInfo.cs" />
|
||||
<Compile Include="Messages\Sftp\AceMasks.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<ChannelSessionSftp>();
|
||||
}
|
||||
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<uint, Action<SftpRequestMessage>> _requestActions;
|
||||
|
||||
private string _remoteCurrentDir;
|
||||
|
||||
public int OperationTimeout { get; set; }
|
||||
|
||||
internal Sftp(Session session)
|
||||
{
|
||||
this.OperationTimeout = -1;
|
||||
this._requestActions = new Dictionary<uint, Action<SftpRequestMessage>>();
|
||||
this._session = session;
|
||||
}
|
||||
this._session.ErrorOccured += Session_ErrorOccured;
|
||||
this._session.Disconnected += Session_Disconnected;
|
||||
this._channel = session.CreateChannel<ChannelSession>();
|
||||
this._channel.DataReceived += Channel_DataReceived;
|
||||
this._channel.Open();
|
||||
this._channel.SendSubsystemRequest("sftp");
|
||||
|
||||
public IEnumerable<FtpFileInfo> 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<FtpFileInfo> 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<SftpRequestMessage> 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);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FtpFileInfo> 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
|
||||
}
|
||||
}
|
||||
@@ -23,22 +23,22 @@ namespace Renci.SshClient
|
||||
}
|
||||
}
|
||||
|
||||
private Sftp _sftp;
|
||||
/// <summary>
|
||||
/// Gets the shell.
|
||||
/// </summary>
|
||||
/// <value>The shell.</value>
|
||||
public Sftp Sftp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._sftp == null)
|
||||
{
|
||||
this._sftp = new Sftp(this._session);
|
||||
}
|
||||
return this._sftp;
|
||||
}
|
||||
}
|
||||
//private Sftp _sftp;
|
||||
///// <summary>
|
||||
///// Gets the shell.
|
||||
///// </summary>
|
||||
///// <value>The shell.</value>
|
||||
//public Sftp Sftp
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// if (this._sftp == null)
|
||||
// {
|
||||
// this._sftp = new Sftp(this._session);
|
||||
// }
|
||||
// return this._sftp;
|
||||
// }
|
||||
//}
|
||||
|
||||
public IEnumerable<ForwardedPort> 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<T>(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);
|
||||
|
||||
Reference in New Issue
Block a user