ScpClient
Provides SCP client functionality.
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;
private static readonly Regex DirectoryInfoRe = new Regex("D(?<mode>\\d{4}) (?<length>\\d+) (?<filename>.+)");
private static readonly Regex TimestampRe = new Regex("T(?<mtime>\\d+) 0 (?<atime>\\d+) 0");
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);
};
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 = 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);
};
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 static 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 static void SendConfirmation(IChannel channel)
{
SendData(channel, new byte[1]);
}
private static void SendConfirmation(IChannel channel, byte errorCode, string message)
{
SendData(channel, new byte[1] {
errorCode
});
SendData(channel, $"{message}""");
}
private static void CheckReturnCode(Stream input)
{
if (ReadByte(input) > 0)
throw new ScpException(ReadString(input));
}
private static void SendData(IChannel channel, string command)
{
channel.SendData(SshData.Utf8.GetBytes(command));
}
private static void SendData(IChannel channel, byte[] buffer, int length)
{
channel.SendData(buffer, 0, length);
}
private static void SendData(IChannel channel, byte[] buffer)
{
channel.SendData(buffer);
}
private static int ReadByte(Stream stream)
{
int num = stream.ReadByte();
if (num == -1)
throw new SshException("Stream has been closed.");
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();
}
public void Upload(FileInfo fileInfo, string path)
{
if (fileInfo == null)
throw new ArgumentNullException("fileInfo");
if (string.IsNullOrEmpty(path))
throw new ArgumentException("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);
};
channelSession.Open();
if (!channelSession.SendExecRequest($"""{path}"""))
throw new SshException("Secure copy execution request was rejected by the server. Please consult the server logs.");
CheckReturnCode(input);
InternalUpload(channelSession, input, fileInfo, fileInfo.Name);
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
public void Upload(DirectoryInfo directoryInfo, string path)
{
if (directoryInfo == null)
throw new ArgumentNullException("directoryInfo");
if (string.IsNullOrEmpty(path))
throw new ArgumentException("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);
};
channelSession.Open();
channelSession.SendExecRequest($"""{path}""");
CheckReturnCode(input);
InternalSetTimestamp(channelSession, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc);
SendData(channelSession, $"""{Path.GetFileName(path)}""");
CheckReturnCode(input);
InternalUpload(channelSession, input, directoryInfo);
SendData(channelSession, "E\n");
CheckReturnCode(input);
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
public void Download(string filename, FileInfo fileInfo)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException("filename");
if (fileInfo == null)
throw new ArgumentNullException("fileInfo");
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);
};
channelSession.Open();
channelSession.SendExecRequest($"""{filename}""");
SendConfirmation(channelSession);
InternalDownload(channelSession, input, fileInfo);
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
public void Download(string directoryName, DirectoryInfo directoryInfo)
{
if (string.IsNullOrEmpty(directoryName))
throw new ArgumentException("directoryName");
if (directoryInfo == null)
throw new ArgumentNullException("directoryInfo");
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);
};
channelSession.Open();
channelSession.SendExecRequest($"""{directoryName}""");
SendConfirmation(channelSession);
InternalDownload(channelSession, input, directoryInfo);
channelSession.Close();
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
private void InternalUpload(IChannelSession channel, Stream input, FileInfo fileInfo, string filename)
{
InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc);
using (FileStream source = fileInfo.OpenRead())
InternalUpload(channel, input, source, filename);
}
private void InternalUpload(IChannelSession channel, Stream input, DirectoryInfo directoryInfo)
{
FileInfo[] files = directoryInfo.GetFiles();
foreach (FileInfo fileInfo in files) {
InternalUpload(channel, input, fileInfo, fileInfo.Name);
}
DirectoryInfo[] directories = directoryInfo.GetDirectories();
foreach (DirectoryInfo directoryInfo2 in directories) {
InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc);
SendData(channel, $"""{directoryInfo2.Name}""");
CheckReturnCode(input);
InternalUpload(channel, input, directoryInfo2);
SendData(channel, "E\n");
CheckReturnCode(input);
}
}
private void InternalDownload(IChannelSession channel, Stream input, FileSystemInfo fileSystemInfo)
{
DateTime lastWriteTime = DateTime.Now;
DateTime lastAccessTime = DateTime.Now;
string fullName = fileSystemInfo.FullName;
int num = 0;
while (true) {
string text = ReadString(input);
if (text == "E") {
SendConfirmation(channel);
num--;
fullName = new DirectoryInfo(fullName).Parent.FullName;
if (num == 0)
break;
} else {
Match match = DirectoryInfoRe.Match(text);
if (match.Success) {
SendConfirmation(channel);
long.Parse(match.Result("${mode}"));
string arg = match.Result("${filename}");
DirectoryInfo directoryInfo;
if (num > 0) {
directoryInfo = Directory.CreateDirectory($"{fullName}{Path.DirectorySeparatorChar}{arg}");
directoryInfo.LastAccessTime = lastAccessTime;
directoryInfo.LastWriteTime = lastWriteTime;
} else
directoryInfo = (fileSystemInfo as DirectoryInfo);
num++;
fullName = directoryInfo.FullName;
} else {
match = FileInfoRe.Match(text);
if (match.Success) {
SendConfirmation(channel);
match.Result("${mode}");
long length = long.Parse(match.Result("${length}"));
string text2 = match.Result("${filename}");
FileInfo fileInfo = fileSystemInfo as FileInfo;
if (fileInfo == null)
fileInfo = new FileInfo($"{fullName}{Path.DirectorySeparatorChar}{text2}");
using (FileStream output = fileInfo.OpenWrite())
InternalDownload(channel, input, output, text2, length);
fileInfo.LastAccessTime = lastAccessTime;
fileInfo.LastWriteTime = lastWriteTime;
if (num == 0)
break;
} else {
match = TimestampRe.Match(text);
if (match.Success) {
SendConfirmation(channel);
long num2 = long.Parse(match.Result("${mtime}"));
long num3 = long.Parse(match.Result("${atime}"));
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
lastWriteTime = dateTime.AddSeconds((double)num2);
lastAccessTime = dateTime.AddSeconds((double)num3);
} else
SendConfirmation(channel, 1, $"""{text}""");
}
}
}
}
}
}
}