ScpClient
Provides SCP client functionality.
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
using System;
using System.Collections.Generic;
using System.IO;
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 readonly byte[] SuccessConfirmationCode = new byte[1];
private static readonly byte[] ErrorConfirmationCode = new byte[1] {
1
};
private IRemotePathTransformation _remotePathTransformation;
public TimeSpan OperationTimeout { get; set; }
public uint BufferSize { get; set; }
public IRemotePathTransformation RemotePathTransformation {
get {
return _remotePathTransformation;
}
set {
if (value == null)
throw new ArgumentNullException("value");
_remotePathTransformation = value;
}
}
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;
_remotePathTransformation = serviceFactory.CreateRemotePathDoubleQuoteTransformation();
}
public void Upload(Stream source, string path)
{
PosixPath posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(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($"""{_remotePathTransformation.Transform(posixPath.Directory)}"))
throw SecureExecutionRequestRejectedException();
CheckReturnCode(input);
UploadFileModeAndName(channelSession, input, source.Length, posixPath.File);
UploadFileContent(channelSession, input, source, posixPath.File);
}
} 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();
if (!channelSession.SendExecRequest($"""{_remotePathTransformation.Transform(filename)}"))
throw SecureExecutionRequestRejectedException();
SendSuccessConfirmation(channelSession);
string text = ReadString(input);
Match match = FileInfoRe.Match(text);
if (match.Success) {
SendSuccessConfirmation(channelSession);
long length = long.Parse(match.Result("${length}"));
string filename2 = match.Result("${filename}");
InternalDownload(channelSession, input, destination, filename2, length);
} else
SendErrorConfirmation(channelSession, $"""{text}""");
}
} finally {
if (input != null)
((IDisposable)input).Dispose();
}
}
private void UploadFileModeAndName(IChannelSession channel, Stream input, long fileSize, string serverFileName)
{
SendData(channel, $"""{fileSize}""{serverFileName}""");
CheckReturnCode(input);
}
private void UploadFileContent(IChannelSession channel, Stream input, Stream source, string remoteFileName)
{
long length = source.Length;
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(remoteFileName, length, num2);
num = source.Read(array, 0, array.Length);
}
SendSuccessConfirmation(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);
SendSuccessConfirmation(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 SendSuccessConfirmation(IChannel channel)
{
SendData(channel, SuccessConfirmationCode);
}
private void SendErrorConfirmation(IChannel channel, string message)
{
SendData(channel, ErrorConfirmationCode);
SendData(channel, message + "\n");
}
private void CheckReturnCode(Stream input)
{
if (ReadByte(input) > 0)
throw new ScpException(ReadString(input));
}
private void SendData(IChannel channel, string command)
{
channel.SendData(base.ConnectionInfo.Encoding.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 string ReadString(Stream stream)
{
bool flag = false;
List<byte> list = new List<byte>();
int num = ReadByte(stream);
if (num == 1 || num == 2) {
flag = true;
num = ReadByte(stream);
}
while (num != 10) {
list.Add((byte)num);
num = ReadByte(stream);
}
byte[] array = list.ToArray();
if (flag)
throw new ScpException(base.ConnectionInfo.Encoding.GetString(array, 0, array.Length));
return base.ConnectionInfo.Encoding.GetString(array, 0, array.Length);
}
private static SshException SecureExecutionRequestRejectedException()
{
throw new SshException("Secure copy execution request was rejected by the server. Please consult the server logs.");
}
}
}