mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-10-17 05:21:15 +08:00
Partially implemented SftpClient, still missing few public methods to finish implementation
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
public class FtpFileInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public DateTime? CreationTime { get; set; }
|
||||
|
||||
public DateTime? LastAccessTime { get; set; }
|
||||
|
||||
public DateTime? LastModifyTime { get; set; }
|
||||
|
||||
public ulong? Size { get; set; }
|
||||
|
||||
public uint? UserId { get; set; }
|
||||
|
||||
public uint? GroupId { get; set; }
|
||||
|
||||
public uint? Permissions { get; set; }
|
||||
|
||||
public IDictionary<string, string> Extentions { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class AttrsMessage : SftpRequestMessage
|
||||
internal class AttributesMessage : SftpRequestMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
@@ -13,35 +13,37 @@ namespace Renci.SshClient.Messages.Sftp
|
||||
|
||||
public uint Count { get; set; }
|
||||
|
||||
public IList<FtpFileInfo> Files { get; set; }
|
||||
public IEnumerable<SftpFile> Files { get; set; }
|
||||
|
||||
public NameMessage()
|
||||
{
|
||||
this.Files = new List<FtpFileInfo>();
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Count = this.ReadUInt32();
|
||||
var files = new List<SftpFile>();
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var fileName = this.ReadString();
|
||||
var fullName = this.ReadString();
|
||||
var attribute = this.ReadAttributes();
|
||||
|
||||
this.Files.Add(new FtpFileInfo
|
||||
// TODO: Complete files info population
|
||||
files.Add(new SftpFile
|
||||
{
|
||||
Name = fileName,
|
||||
FullName = fullName,
|
||||
Size = attribute.Size,
|
||||
UserId = attribute.UserId,
|
||||
GroupId = attribute.GroupId,
|
||||
LastAccessTime = attribute.AccessTime,
|
||||
LastModifyTime = attribute.ModifyTime,
|
||||
AbsolutePath = fullName,
|
||||
//Size = attribute.Size,
|
||||
//UserId = attribute.UserId,
|
||||
//GroupId = attribute.GroupId,
|
||||
//AccessedTime = attribute.AccessTime,
|
||||
//ModifiedTime = attribute.ModifyTime,
|
||||
Extentions = attribute.Extentions,
|
||||
});
|
||||
}
|
||||
this.Files = files;
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Renci.SshClient.Messages.Sftp
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Handle, new LoadFunc<SftpMessage>(Load<HandleMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Data, new LoadFunc<SftpMessage>(Load<DataMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Name, new LoadFunc<SftpMessage>(Load<NameMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Attrs, new LoadFunc<SftpMessage>(Load<AttrsMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Attrs, new LoadFunc<SftpMessage>(Load<AttributesMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Extended, new LoadFunc<SftpMessage>(Load<ExtendedMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.ExtendedReply, new LoadFunc<SftpMessage>(Load<ExtendedReplyMessage>));
|
||||
}
|
||||
|
||||
@@ -84,7 +84,19 @@
|
||||
<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="SftpClient.cs" />
|
||||
<Compile Include="Sftp\CreateRemoteDirectoryCommand.cs" />
|
||||
<Compile Include="Sftp\DownloadFileCommand.cs" />
|
||||
<Compile Include="Sftp\ListDirectoryCommand.cs" />
|
||||
<Compile Include="Sftp\RemoveDirectoryCommand.cs" />
|
||||
<Compile Include="Sftp\RemoveFileCommand.cs" />
|
||||
<Compile Include="Sftp\RenameFileCommand.cs" />
|
||||
<Compile Include="Sftp\SftpAsyncResult.cs" />
|
||||
<Compile Include="Sftp\SftpCommand.cs" />
|
||||
<Compile Include="Sftp\SftpFile.cs" />
|
||||
<Compile Include="Sftp\SftpFileAttributes.cs" />
|
||||
<Compile Include="Sftp\SftpSession.cs" />
|
||||
<Compile Include="Sftp\UploadFileCommand.cs" />
|
||||
<Compile Include="SshCommand.cs" />
|
||||
<Compile Include="MessageEventArgs.cs" />
|
||||
<Compile Include="ChannelAsyncResult.cs" />
|
||||
@@ -104,7 +116,6 @@
|
||||
<Compile Include="Security\CipherTripleDES.cs" />
|
||||
<Compile Include="Compression\Compressor.cs" />
|
||||
<Compile Include="Common\DataReceivedEventArgs.cs" />
|
||||
<Compile Include="Common\FtpFileInfo.cs" />
|
||||
<Compile Include="Messages\Sftp\AceMasks.cs" />
|
||||
<Compile Include="Messages\Sftp\Attributes.cs" />
|
||||
<Compile Include="Messages\Sftp\AttrsMessage.cs" />
|
||||
@@ -229,9 +240,7 @@
|
||||
<Compile Include="Session.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Common\SshData.cs" />
|
||||
<Compile Include="Sftp.cs" />
|
||||
<Compile Include="Shell.cs" />
|
||||
<Compile Include="SshBase.cs" />
|
||||
<Compile Include="SshClient.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
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 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");
|
||||
|
||||
this.SendMessage(new InitMessage
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(SftpRequestMessage message)
|
||||
{
|
||||
if (this._requestActions.ContainsKey(message.RequestId))
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void Session_Disconnected(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void Session_ErrorOccured(object sender, ErrorEventArgs e)
|
||||
{
|
||||
this._exception = e.GetException();
|
||||
|
||||
this._sessionErrorOccuredWaitHandle.Set();
|
||||
}
|
||||
|
||||
private void SendMessage(SftpMessage sftpMessage)
|
||||
{
|
||||
var message = new SftpDataMessage
|
||||
{
|
||||
LocalChannelNumber = this._channel.RemoteChannelNumber,
|
||||
Message = sftpMessage,
|
||||
};
|
||||
|
||||
this._session.SendMessage(message);
|
||||
}
|
||||
|
||||
private void SendMessage(SftpRequestMessage sftpMessage, Action<SftpRequestMessage> action)
|
||||
{
|
||||
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,28 @@
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class CreateRemoteDirectoryCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
|
||||
public CreateRemoteDirectoryCommand(SftpSession sftpSession, string path)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendMkDirMessage(this._path);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (statusCode == StatusCodes.Ok)
|
||||
{
|
||||
this.CompleteExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class DownloadFileCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
private Stream _output;
|
||||
private string _handle;
|
||||
private ulong _offset = 0;
|
||||
private uint _bufferSize = 1024;
|
||||
private string _data;
|
||||
|
||||
public DownloadFileCommand(SftpSession sftpSession, string path, Stream output)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
this._output = output;
|
||||
// TODO: Make this field to be customizable
|
||||
this._bufferSize = 1024;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendOpenMessage(this._path, Flags.Read);
|
||||
}
|
||||
|
||||
protected override void OnHandle(string handle)
|
||||
{
|
||||
base.OnHandle(handle);
|
||||
|
||||
this._handle = handle;
|
||||
|
||||
this.SendReadMessage(this._handle, this._offset, this._bufferSize);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
base.OnStatus(statusCode, errorMessage, language);
|
||||
|
||||
if (statusCode == StatusCodes.Eof)
|
||||
{
|
||||
this.CompleteExecution();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnData(string data, bool isEof)
|
||||
{
|
||||
base.OnData(data, isEof);
|
||||
|
||||
var fileData = data.GetSshBytes().ToArray();
|
||||
this._output.Write(fileData, 0, fileData.Length);
|
||||
this._output.Flush();
|
||||
this._offset += (ulong)fileData.Length;
|
||||
|
||||
this.SendReadMessage(this._handle, this._offset, this._bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class ListDirectoryCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
|
||||
private string _handle;
|
||||
|
||||
public IEnumerable<SftpFile> Files { get; private set; }
|
||||
|
||||
public ListDirectoryCommand(SftpSession sftpSession, string path)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendOpenDirMessage(this._path);
|
||||
}
|
||||
|
||||
protected override void OnHandle(string handle)
|
||||
{
|
||||
base.OnHandle(handle);
|
||||
|
||||
this._handle = handle;
|
||||
|
||||
this.SendReadDirMessage(this._handle);
|
||||
}
|
||||
|
||||
protected override void OnName(IEnumerable<SftpFile> files)
|
||||
{
|
||||
base.OnName(files);
|
||||
|
||||
this.Files = files;
|
||||
|
||||
this.CompleteExecution();
|
||||
|
||||
this.SendCloseMessage(this._handle);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (statusCode == StatusCodes.NoSuchFile)
|
||||
{
|
||||
throw new SshException(string.Format("Path '{0}' is not found.", this._path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class RemoveDirectoryCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
|
||||
public RemoveDirectoryCommand(SftpSession sftpSession, string path)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendRmDirMessage(this._path);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (statusCode == StatusCodes.Ok)
|
||||
{
|
||||
this.CompleteExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class RemoveFileCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
|
||||
public RemoveFileCommand(SftpSession sftpSession, string path)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendRemoveMessage(this._path);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (statusCode == StatusCodes.Ok)
|
||||
{
|
||||
this.CompleteExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class RenameFileCommand : SftpCommand
|
||||
{
|
||||
private string _oldPath;
|
||||
private string _newPath;
|
||||
|
||||
public RenameFileCommand(SftpSession sftpSession, string oldPath, string newPath)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._oldPath = oldPath;
|
||||
this._newPath = newPath;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendRenameMessage(this._oldPath, this._newPath);
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (statusCode == StatusCodes.Ok)
|
||||
{
|
||||
this.CompleteExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-9
@@ -1,30 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Common;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Renci.SshClient
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
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 SftpCommand Command { get; private set; }
|
||||
|
||||
internal SftpAsyncResult(Channels.ChannelSession channelSession, AsyncCallback callback, object state)
|
||||
internal SftpAsyncResult(SftpCommand command, AsyncCallback callback, object state)
|
||||
{
|
||||
this._channelSession = channelSession;
|
||||
this.Command = command;
|
||||
this._callback = callback;
|
||||
this.AsyncState = state;
|
||||
this.AsyncWaitHandle = _completedWaitHandle;
|
||||
}
|
||||
|
||||
internal T GetCommand<T>() where T : SftpCommand
|
||||
{
|
||||
return this.Command as T;
|
||||
}
|
||||
|
||||
#region IAsyncResult Members
|
||||
|
||||
public object AsyncState { get; private set; }
|
||||
@@ -39,14 +41,20 @@ namespace Renci.SshClient
|
||||
{
|
||||
return this._isCompleted;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
this._isCompleted = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (this._callback != null)
|
||||
{
|
||||
this._callback(this);
|
||||
// Execute callback on new pool thread
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
this._callback(this);
|
||||
});
|
||||
}
|
||||
this._completedWaitHandle.Set();
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal abstract class SftpCommand
|
||||
{
|
||||
private SftpSession _sftpSession;
|
||||
|
||||
private uint _requestId;
|
||||
|
||||
private SftpAsyncResult _asyncResult;
|
||||
|
||||
public SftpCommand(SftpSession sftpSession)
|
||||
{
|
||||
this._sftpSession = sftpSession;
|
||||
this._sftpSession.AttributesMessageReceived += SftpSession_AttributesMessageReceived;
|
||||
this._sftpSession.DataMessageReceived += SftpSession_DataMessageReceived;
|
||||
this._sftpSession.HandleMessageReceived += SftpSession_HandleMessageReceived;
|
||||
this._sftpSession.NameMessageReceived += SftpSession_NameMessageReceived;
|
||||
this._sftpSession.StatusMessageReceived += SftpSession_StatusMessageReceived;
|
||||
}
|
||||
|
||||
public SftpAsyncResult BeginExecute(AsyncCallback callback, object state)
|
||||
{
|
||||
this._asyncResult = new SftpAsyncResult(this, callback, state);
|
||||
|
||||
this.OnExecute();
|
||||
|
||||
return this._asyncResult;
|
||||
}
|
||||
|
||||
public void EndExecute(SftpAsyncResult result)
|
||||
{
|
||||
// TODO: Add timeout info here
|
||||
this._sftpSession.WaitHandle(result.AsyncWaitHandle);
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
this.EndExecute(this.BeginExecute(null, null));
|
||||
}
|
||||
|
||||
protected abstract void OnExecute();
|
||||
|
||||
protected virtual void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnName(IEnumerable<SftpFile> files)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnHandle(string handle)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnData(string data, bool isEof)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnAttributes(Attributes attributes)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnHandleClosed()
|
||||
{
|
||||
}
|
||||
|
||||
protected void SendOpenMessage(string path, Flags flags)
|
||||
{
|
||||
this.SendMessage(new OpenMessage
|
||||
{
|
||||
Filename = path,
|
||||
Flags = flags,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendCloseMessage(string handle)
|
||||
{
|
||||
this.SendMessage(new CloseMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendReadMessage(string handle, ulong offset, uint bufferSize)
|
||||
{
|
||||
this.SendMessage(new ReadMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Offset = offset,
|
||||
Length = bufferSize,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendWriteMessage(string handle, ulong offset, string data)
|
||||
{
|
||||
this.SendMessage(new WriteMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Offset = offset,
|
||||
Data = data,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendLStatMessage(string path)
|
||||
{
|
||||
this.SendMessage(new LStatMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendFStatMessage(string handle)
|
||||
{
|
||||
this.SendMessage(new FStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendSetStatMessage(string path, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new SetStatMessage
|
||||
{
|
||||
Path = path,
|
||||
Attributes = attributes,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendFSetStatMessage(string handle, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new FSetStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Attributes = attributes,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendOpenDirMessage(string path)
|
||||
{
|
||||
this.SendMessage(new OpenDirMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendReadDirMessage(string handle)
|
||||
{
|
||||
this.SendMessage(new ReadDirMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendRemoveMessage(string filename)
|
||||
{
|
||||
this.SendMessage(new RemoveMessage
|
||||
{
|
||||
Filename = filename,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendMkDirMessage(string path)
|
||||
{
|
||||
this.SendMessage(new MkDirMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendRmDirMessage(string path)
|
||||
{
|
||||
this.SendMessage(new RmDirMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendRealPathMessage(string path)
|
||||
{
|
||||
this.SendMessage(new RealPathMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendStatMessage(string path)
|
||||
{
|
||||
this.SendMessage(new StatMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendRenameMessage(string oldPath, string newPath)
|
||||
{
|
||||
this.SendMessage(new RenameMessage
|
||||
{
|
||||
OldPath = oldPath,
|
||||
NewPath = newPath,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendReadLinkMessage(string path)
|
||||
{
|
||||
this.SendMessage(new ReadLinkMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
}
|
||||
|
||||
protected void SendSymLinkMessage(string existingPath, string newLinkPath, bool isSymLink)
|
||||
{
|
||||
this.SendMessage(new SymLinkMessage
|
||||
{
|
||||
ExistingPath = existingPath,
|
||||
NewLinkPath = newLinkPath,
|
||||
IsSymLink = isSymLink,
|
||||
});
|
||||
}
|
||||
|
||||
protected void CompleteExecution()
|
||||
{
|
||||
this._asyncResult.IsCompleted = true;
|
||||
|
||||
this._sftpSession.AttributesMessageReceived -= SftpSession_AttributesMessageReceived;
|
||||
this._sftpSession.DataMessageReceived -= SftpSession_DataMessageReceived;
|
||||
this._sftpSession.HandleMessageReceived -= SftpSession_HandleMessageReceived;
|
||||
this._sftpSession.NameMessageReceived -= SftpSession_NameMessageReceived;
|
||||
this._sftpSession.StatusMessageReceived -= SftpSession_StatusMessageReceived;
|
||||
}
|
||||
|
||||
private void SftpSession_StatusMessageReceived(object sender, MessageEventArgs<StatusMessage> e)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void SftpSession_NameMessageReceived(object sender, MessageEventArgs<NameMessage> e)
|
||||
{
|
||||
if (this._requestId == e.Message.RequestId)
|
||||
{
|
||||
this.OnName(e.Message.Files);
|
||||
}
|
||||
}
|
||||
|
||||
private void SftpSession_HandleMessageReceived(object sender, MessageEventArgs<HandleMessage> e)
|
||||
{
|
||||
if (this._requestId == e.Message.RequestId)
|
||||
{
|
||||
this.OnHandle(e.Message.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SftpSession_DataMessageReceived(object sender, MessageEventArgs<DataMessage> e)
|
||||
{
|
||||
if (this._requestId == e.Message.RequestId)
|
||||
{
|
||||
this.OnData(e.Message.Data, e.Message.IsEof);
|
||||
}
|
||||
}
|
||||
|
||||
private void SftpSession_AttributesMessageReceived(object sender, MessageEventArgs<AttributesMessage> e)
|
||||
{
|
||||
if (this._requestId == e.Message.RequestId)
|
||||
{
|
||||
this.OnAttributes(e.Message.Attributes);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendMessage(SftpRequestMessage message)
|
||||
{
|
||||
this._sftpSession.SendMessage(message);
|
||||
|
||||
// Remeber command request id that was sent
|
||||
this._requestId = message.RequestId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
public class SftpFile
|
||||
{
|
||||
public SftpFile()
|
||||
{
|
||||
this.Attributes = new SftpFileAttributes();
|
||||
this.CreatedTime = DateTime.MinValue;
|
||||
this.AccessedTime = DateTime.MinValue;
|
||||
this.ModifiedTime = DateTime.MinValue;
|
||||
this.Size = -1;
|
||||
this.UserId = -1;
|
||||
this.GroupId = -1;
|
||||
this.Permissions = -1;
|
||||
this.Extentions = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string AbsolutePath { get; set; }
|
||||
|
||||
public SftpFileAttributes Attributes { get; set; }
|
||||
|
||||
public string Filename { get; set; }
|
||||
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
public DateTime AccessedTime { get; set; }
|
||||
|
||||
public DateTime ModifiedTime { get; set; }
|
||||
|
||||
public long Size { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public int GroupId { get; set; }
|
||||
|
||||
public int Permissions { get; set; }
|
||||
|
||||
public IDictionary<string, string> Extentions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
public class SftpFileAttributes
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
using System;
|
||||
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.Sftp
|
||||
{
|
||||
internal class SftpSession
|
||||
{
|
||||
private Session _session;
|
||||
|
||||
private uint _requestId;
|
||||
|
||||
private ChannelSession _channel;
|
||||
|
||||
private StringBuilder _packetData;
|
||||
|
||||
private Exception _exception;
|
||||
|
||||
private EventWaitHandle _errorOccuredWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false);
|
||||
|
||||
#region SFTP messages
|
||||
|
||||
internal event EventHandler<MessageEventArgs<StatusMessage>> StatusMessageReceived;
|
||||
|
||||
internal event EventHandler<MessageEventArgs<DataMessage>> DataMessageReceived;
|
||||
|
||||
internal event EventHandler<MessageEventArgs<HandleMessage>> HandleMessageReceived;
|
||||
|
||||
internal event EventHandler<MessageEventArgs<NameMessage>> NameMessageReceived;
|
||||
|
||||
internal event EventHandler<MessageEventArgs<AttributesMessage>> AttributesMessageReceived;
|
||||
|
||||
#endregion
|
||||
|
||||
public int OperationTimeout { get; private set; }
|
||||
|
||||
public SftpSession(Session session, int operationTimeout)
|
||||
{
|
||||
this._session = session;
|
||||
this.OperationTimeout = operationTimeout;
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this._channel = this._session.CreateChannel<ChannelSession>();
|
||||
|
||||
this._session.ErrorOccured += Session_ErrorOccured;
|
||||
this._session.Disconnected += Session_Disconnected;
|
||||
this._channel.DataReceived += Channel_DataReceived;
|
||||
|
||||
this._channel.Open();
|
||||
|
||||
this._channel.SendSubsystemRequest("sftp");
|
||||
|
||||
this.SendMessage(new InitMessage
|
||||
{
|
||||
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 void Disconnect()
|
||||
{
|
||||
}
|
||||
|
||||
internal void SendMessage(SftpRequestMessage sftpMessage)
|
||||
{
|
||||
sftpMessage.RequestId = this._requestId++;
|
||||
|
||||
var message = new SftpDataMessage
|
||||
{
|
||||
LocalChannelNumber = this._channel.RemoteChannelNumber,
|
||||
Message = sftpMessage,
|
||||
};
|
||||
this._session.SendMessage(message);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Check to run on different thread
|
||||
this.HandleMessage(sftpMessage);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this._exception = exp;
|
||||
this._errorOccuredWaitHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
#region Handle SFTP incoming messages and raise appropriate events
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(StatusMessage message)
|
||||
{
|
||||
if (this.StatusMessageReceived != null)
|
||||
{
|
||||
this.StatusMessageReceived(this, new MessageEventArgs<StatusMessage>(message));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(DataMessage message)
|
||||
{
|
||||
if (this.DataMessageReceived != null)
|
||||
{
|
||||
this.DataMessageReceived(this, new MessageEventArgs<DataMessage>(message));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(HandleMessage message)
|
||||
{
|
||||
if (this.HandleMessageReceived != null)
|
||||
{
|
||||
this.HandleMessageReceived(this, new MessageEventArgs<HandleMessage>(message));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(NameMessage message)
|
||||
{
|
||||
if (this.NameMessageReceived != null)
|
||||
{
|
||||
this.NameMessageReceived(this, new MessageEventArgs<NameMessage>(message));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(AttributesMessage message)
|
||||
{
|
||||
if (this.AttributesMessageReceived != null)
|
||||
{
|
||||
this.AttributesMessageReceived(this, new MessageEventArgs<AttributesMessage>(message));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Session_Disconnected(object sender, EventArgs e)
|
||||
{
|
||||
// TODO: Do something when session is closed and operation still in progress
|
||||
}
|
||||
|
||||
private void Session_ErrorOccured(object sender, ErrorEventArgs e)
|
||||
{
|
||||
this._exception = e.GetException();
|
||||
|
||||
this._errorOccuredWaitHandle.Set();
|
||||
}
|
||||
|
||||
private void SendMessage(SftpMessage sftpMessage)
|
||||
{
|
||||
var message = new SftpDataMessage
|
||||
{
|
||||
LocalChannelNumber = this._channel.RemoteChannelNumber,
|
||||
Message = sftpMessage,
|
||||
};
|
||||
|
||||
this._session.SendMessage(message);
|
||||
}
|
||||
|
||||
internal void WaitHandle(WaitHandle waitHandle)
|
||||
{
|
||||
var waitHandles = new WaitHandle[]
|
||||
{
|
||||
this._errorOccuredWaitHandle,
|
||||
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."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
internal class UploadFileCommand : SftpCommand
|
||||
{
|
||||
private string _path;
|
||||
private Stream _input;
|
||||
private string _handle;
|
||||
private byte[] _buffer;
|
||||
private ulong _offset;
|
||||
private bool _hasMoreData;
|
||||
|
||||
public UploadFileCommand(SftpSession sftpSession, string path, Stream input)
|
||||
: base(sftpSession)
|
||||
{
|
||||
this._path = path;
|
||||
this._input = input;
|
||||
// TODO: Make this field to be customizable
|
||||
this._buffer = new byte[1024];
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
this.SendOpenMessage(this._path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate);
|
||||
}
|
||||
|
||||
protected override void OnHandle(string handle)
|
||||
{
|
||||
base.OnHandle(handle);
|
||||
|
||||
this._handle = handle;
|
||||
|
||||
this.SendData();
|
||||
}
|
||||
|
||||
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
base.OnStatus(statusCode, errorMessage, language);
|
||||
|
||||
if (statusCode == StatusCodes.Ok)
|
||||
{
|
||||
if (this._hasMoreData)
|
||||
{
|
||||
this.SendData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHandleClosed()
|
||||
{
|
||||
base.OnHandleClosed();
|
||||
|
||||
this.CompleteExecution();
|
||||
}
|
||||
|
||||
private void SendData()
|
||||
{
|
||||
var bytesRead = this._input.Read(this._buffer, 0, this._buffer.Length);
|
||||
|
||||
this._hasMoreData = (bytesRead > 0);
|
||||
|
||||
if (this._hasMoreData)
|
||||
{
|
||||
this.SendWriteMessage(this._handle, this._offset, this._buffer.Take(bytesRead).GetSshString());
|
||||
|
||||
this._offset += (ulong)bytesRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SendCloseMessage(this._handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Renci.SshClient.Sftp;
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class SftpClient
|
||||
{
|
||||
private SftpSession _sftpSession;
|
||||
|
||||
private Session _session;
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; private set; }
|
||||
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._session == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this._session.IsConnected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int OperationTimeout { get; set; }
|
||||
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
public SftpClient(ConnectionInfo connectionInfo)
|
||||
{
|
||||
this.OperationTimeout = -1;
|
||||
|
||||
this.ConnectionInfo = connectionInfo;
|
||||
}
|
||||
|
||||
public SftpClient(string host, int port, string username, string password)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
Username = username,
|
||||
Password = password,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SftpClient(string host, string username, string password)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Username = username,
|
||||
Password = password,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SftpClient(string host, int port, string username, PrivateKeyFile keyFile)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
Username = username,
|
||||
KeyFile = keyFile,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SftpClient(string host, string username, PrivateKeyFile keyFile)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Username = username,
|
||||
KeyFile = keyFile,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
if (this._session == null)
|
||||
{
|
||||
this._session = new Session(this.ConnectionInfo);
|
||||
}
|
||||
this._session.Connect();
|
||||
|
||||
this._sftpSession = new SftpSession(this._session, this.OperationTimeout);
|
||||
|
||||
this._sftpSession.Connect();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
this._sftpSession.Disconnect();
|
||||
|
||||
this._session.Disconnect();
|
||||
|
||||
this._session = null;
|
||||
}
|
||||
|
||||
#region List Directory
|
||||
|
||||
public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state)
|
||||
{
|
||||
var cmd = new ListDirectoryCommand(this._sftpSession, path);
|
||||
return cmd.BeginExecute(asyncCallback, state);
|
||||
}
|
||||
|
||||
public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult)
|
||||
{
|
||||
var sftpAsyncResult = asyncResult as SftpAsyncResult;
|
||||
|
||||
if (sftpAsyncResult == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
|
||||
sftpAsyncResult.Command.EndExecute(sftpAsyncResult);
|
||||
|
||||
var cmd = sftpAsyncResult.Command as ListDirectoryCommand;
|
||||
|
||||
if (cmd == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
return cmd.Files;
|
||||
}
|
||||
|
||||
public IEnumerable<SftpFile> ListDirectory(string path)
|
||||
{
|
||||
return this.EndListDirectory(this.BeginListDirectory(path, null, null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void RenameFile(string oldFilename, string newFileName)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveFile(string filename)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveDirectory(string path)
|
||||
{
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
}
|
||||
|
||||
#region Upload File
|
||||
|
||||
public IAsyncResult BeginUploadFile(string filename, Stream input, AsyncCallback asyncCallback, object state)
|
||||
{
|
||||
var cmd = new UploadFileCommand(this._sftpSession, filename, input);
|
||||
|
||||
return cmd.BeginExecute(asyncCallback, state);
|
||||
}
|
||||
|
||||
public void EndUploadFile(IAsyncResult asyncResult)
|
||||
{
|
||||
var sftpAsyncResult = asyncResult as SftpAsyncResult;
|
||||
|
||||
if (sftpAsyncResult == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
|
||||
sftpAsyncResult.Command.EndExecute(sftpAsyncResult);
|
||||
|
||||
var cmd = sftpAsyncResult.Command as UploadFileCommand;
|
||||
|
||||
if (cmd == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
}
|
||||
|
||||
public void UploadFile(string filename, Stream input)
|
||||
{
|
||||
this.EndUploadFile(this.BeginUploadFile(filename, input, null, null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Download File
|
||||
|
||||
public IAsyncResult BeginDownload(string filename, Stream output, AsyncCallback asyncCallback, object state)
|
||||
{
|
||||
var cmd = new DownloadFileCommand(this._sftpSession, filename, output);
|
||||
|
||||
return cmd.BeginExecute(asyncCallback, state);
|
||||
}
|
||||
|
||||
public void EndDownload(IAsyncResult asyncResult)
|
||||
{
|
||||
var sftpAsyncResult = asyncResult as SftpAsyncResult;
|
||||
|
||||
if (sftpAsyncResult == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
|
||||
sftpAsyncResult.Command.EndExecute(sftpAsyncResult);
|
||||
|
||||
var cmd = sftpAsyncResult.Command as DownloadFileCommand;
|
||||
|
||||
if (cmd == null)
|
||||
{
|
||||
throw new InvalidOperationException("Not valid IAsyncResult object.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Download(string filename, Stream output)
|
||||
{
|
||||
this.EndDownload(this.BeginDownload(filename, output, null, null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void ChangeDirectory(string path)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public abstract class SshBase
|
||||
{
|
||||
public ConnectionInfo ConnectionInfo { get; private set; }
|
||||
|
||||
internal Session Session { get; private set; }
|
||||
|
||||
public SshBase(ConnectionInfo connectionInfo)
|
||||
{
|
||||
this.ConnectionInfo = connectionInfo;
|
||||
this.Session = new Session(this.ConnectionInfo);
|
||||
}
|
||||
|
||||
public SshBase(string host, int port, string username, string password)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
Username = username,
|
||||
Password = password,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SshBase(string host, string username, string password)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Username = username,
|
||||
Password = password,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SshBase(string host, int port, string username, PrivateKeyFile keyFile)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
Username = username,
|
||||
KeyFile = keyFile,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public SshBase(string host, string username, PrivateKeyFile keyFile)
|
||||
: this(new ConnectionInfo
|
||||
{
|
||||
Host = host,
|
||||
Username = username,
|
||||
KeyFile = keyFile,
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this.Session.Connect();
|
||||
}
|
||||
|
||||
public virtual void Disconnect()
|
||||
{
|
||||
this.Session.Disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user