ScpClient
Provides SCP client functionality.
using Renci.SshNet.Abstractions;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Renci.SshNet
{
public class ScpClient : BaseClient
{
private static readonly Regex FileInfoRe = new Regex("C(?<mode>\\d{4}) (?<length>\\d+) (?<filename>.+)");
private static char[] _byteToChar;
public TimeSpan OperationTimeout { get; set; }
public uint BufferSize { get; set; }
public event EventHandler<ScpDownloadEventArgs> Downloading;
public event EventHandler<ScpUploadEventArgs> Uploading;
public ScpClient(ConnectionInfo connectionInfo)
: this(connectionInfo, false)
{
}
public ScpClient(string host, int port, string username, string password)
: this(new PasswordConnectionInfo(host, port, username, password), true)
{
}
public ScpClient(string host, string username, string password)
: this(host, ConnectionInfo.DefaultPort, username, password)
{
}
public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
private ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
: this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
{
}
internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
: base(connectionInfo, ownsConnectionInfo, serviceFactory)
{
OperationTimeout = Renci.SshNet.Session.InfiniteTimeSpan;
BufferSize = 16384;
if (_byteToChar == null) {
_byteToChar = new char[128];
char c = ' ';
for (int i = 0; i < 128; i++) {
char[] byteToChar = _byteToChar;
int num = i;
char num2 = c;
c = (char)(num2 + 1);
byteToChar[num] = num2;
}
}
}
public void Upload(Stream source, string path)
{
PipeStream input = base.ServiceFactory.CreatePipeStream();
try {
using (IChannelSession channelSession = base.Session.CreateChannelSession()) {
channelSession.DataReceived += delegate(object sender, ChannelDataEventArgs e) {
input.Write(e.Data, 0, e.Data.Length);
input.Flush();
};
channelSession.Open();
int num = path.LastIndexOfAny(new char[2] {
'\\',
'/'
});
if (num != -1) {
string arg = path.Substring(0, num);
string text = path.Substring(num + 1);
channelSession.SendExecRequest($"""{arg}""");
CheckReturnCode(input);
path = text;
}
InternalUpload(channelSession, input, source, path);
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
public void Download(string filename, Stream destination)
{
if (filename.IsNullOrWhiteSpace())
throw new ArgumentException("filename");
if (destination == null)
throw new ArgumentNullException("destination");
PipeStream input = new PipeStream();
try {
using (IChannelSession channelSession = base.Session.CreateChannelSession()) {
channelSession.DataReceived += delegate(object sender, ChannelDataEventArgs e) {
input.Write(e.Data, 0, e.Data.Length);
input.Flush();
};
channelSession.Open();
channelSession.SendExecRequest($"""{filename}""");
SendConfirmation(channelSession);
string text = ReadString(input);
Match match = FileInfoRe.Match(text);
if (match.Success) {
SendConfirmation(channelSession);
match.Result("${mode}");
long length = long.Parse(match.Result("${length}"));
string filename2 = match.Result("${filename}");
InternalDownload(channelSession, input, destination, filename2, length);
} else
SendConfirmation(channelSession, 1, $"""{text}""");
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
private void InternalSetTimestamp(IChannelSession channel, Stream input, DateTime lastWriteTime, DateTime lastAccessime)
{
DateTime d = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
TimeSpan timeSpan = lastWriteTime - d;
long num = (long)timeSpan.TotalSeconds;
timeSpan = lastAccessime - d;
long num2 = (long)timeSpan.TotalSeconds;
SendData(channel, $"""{num}""{num2}""");
CheckReturnCode(input);
}
private void InternalUpload(IChannelSession channel, Stream input, Stream source, string filename)
{
long length = source.Length;
SendData(channel, $"""{length}""{Path.GetFileName(filename)}""");
CheckReturnCode(input);
byte[] array = new byte[BufferSize];
int num = source.Read(array, 0, array.Length);
long num2 = 0;
while (num > 0) {
SendData(channel, array, num);
num2 += num;
RaiseUploadingEvent(filename, length, num2);
num = source.Read(array, 0, array.Length);
}
SendConfirmation(channel);
CheckReturnCode(input);
}
private void InternalDownload(IChannel channel, Stream input, Stream output, string filename, long length)
{
byte[] buffer = new byte[Math.Min(length, BufferSize)];
long num = length;
do {
int num2 = input.Read(buffer, 0, (int)Math.Min(num, BufferSize));
output.Write(buffer, 0, num2);
RaiseDownloadingEvent(filename, length, length - num);
num -= num2;
} while (num > 0);
output.Flush();
RaiseDownloadingEvent(filename, length, length - num);
SendConfirmation(channel);
CheckReturnCode(input);
}
private void RaiseDownloadingEvent(string filename, long size, long downloaded)
{
if (this.Downloading != null)
this.Downloading(this, new ScpDownloadEventArgs(filename, size, downloaded));
}
private void RaiseUploadingEvent(string filename, long size, long uploaded)
{
if (this.Uploading != null)
this.Uploading(this, new ScpUploadEventArgs(filename, size, uploaded));
}
private void SendConfirmation(IChannel channel)
{
SendData(channel, new byte[1]);
}
private void SendConfirmation(IChannel channel, byte errorCode, string message)
{
SendData(channel, new byte[1] {
errorCode
});
SendData(channel, $"{message}""");
}
private void CheckReturnCode(Stream input)
{
if (ReadByte(input) > 0)
throw new ScpException(ReadString(input));
}
private void SendData(IChannel channel, string command)
{
channel.SendData(SshData.Utf8.GetBytes(command));
}
private void SendData(IChannel channel, byte[] buffer, int length)
{
channel.SendData(buffer, 0, length);
}
private void SendData(IChannel channel, byte[] buffer)
{
channel.SendData(buffer);
}
private static int ReadByte(Stream stream)
{
int num;
for (num = stream.ReadByte(); num < 0; num = stream.ReadByte()) {
ThreadAbstraction.Sleep(100);
}
return num;
}
private static string ReadString(Stream stream)
{
bool flag = false;
StringBuilder stringBuilder = new StringBuilder();
int num = ReadByte(stream);
if (num == 1 || num == 2) {
flag = true;
num = ReadByte(stream);
}
for (char c = _byteToChar[num]; c != '\n'; c = _byteToChar[num]) {
stringBuilder.Append(c);
num = ReadByte(stream);
}
if (flag)
throw new ScpException(stringBuilder.ToString());
return stringBuilder.ToString();
}
}
}