diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj
index 124ce9d4b..839118252 100644
--- a/src/Renci.SshNet/Renci.SshNet.csproj
+++ b/src/Renci.SshNet/Renci.SshNet.csproj
@@ -36,12 +36,12 @@
FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA
- FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA
+ FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA;FEATURE_TPL
- FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_TAP;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512
+ FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_TAP;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_TPL
- FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_ECDSA
+ FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_ECDSA;FEATURE_TPL
diff --git a/src/Renci.SshNet/ScpClient.cs b/src/Renci.SshNet/ScpClient.cs
index 8a252cac9..c0a448fd4 100644
--- a/src/Renci.SshNet/ScpClient.cs
+++ b/src/Renci.SshNet/ScpClient.cs
@@ -7,6 +7,10 @@
using System.Net;
using System.Collections.Generic;
+#if FEATURE_TPL
+using System.Threading;
+#endif
+
namespace Renci.SshNet
{
///
@@ -90,7 +94,7 @@ public IRemotePathTransformation RemotePathTransformation
///
public event EventHandler Uploading;
- #region Constructors
+#region Constructors
///
/// Initializes a new instance of the class.
@@ -195,8 +199,10 @@ internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServ
_remotePathTransformation = serviceFactory.CreateRemotePathDoubleQuoteTransformation();
}
- #endregion
+#endregion
+
+#if FEATURE_TPL
///
/// Uploads the specified stream to the remote host.
///
@@ -208,7 +214,71 @@ internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServ
/// The secure copy execution request was rejected by the server.
public void Upload(Stream source, string path)
{
- var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);
+ Upload(source, path, CancellationToken.None);
+ }
+
+#endif
+
+#if FEATURE_TPL
+ ///
+ /// Uploads the specified stream to the remote host.
+ ///
+ /// The to upload.
+ /// A relative or absolute path for the remote file.
+ /// cancellation token
+ /// is null.
+ /// is a zero-length .
+ /// A directory with the specified path exists on the remote host.
+ /// The secure copy execution request was rejected by the server.
+ /// Download cancelled exception
+ public void Upload(Stream source, string path,CancellationToken cancellationToken )
+ {
+ try
+ {
+ var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);
+
+ using (var input = ServiceFactory.CreatePipeStream())
+ using (var channel = Session.CreateChannelSession())
+ using (cancellationToken.Register(() => { channel.Dispose(); input.Dispose(); }))
+ {
+ channel.DataReceived += (sender, e) => input.Write(e.Data, 0, e.Data.Length);
+ channel.Open();
+
+ // Pass only the directory part of the path to the server, and use the (hidden) -d option to signal
+ // that we expect the target to be a directory.
+ if (!channel.SendExecRequest(string.Format("scp -t -d {0}",
+ _remotePathTransformation.Transform(posixPath.Directory))))
+ {
+ throw SecureExecutionRequestRejectedException();
+ }
+
+ CheckReturnCode(input);
+
+ UploadFileModeAndName(channel, input, source.Length, posixPath.File);
+ UploadFileContent(channel, input, source, posixPath.File);
+ }
+ }
+ catch(Exception)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ throw new OperationCanceledException();
+ else
+ throw;
+ }
+ }
+#else
+ ///
+ /// Uploads the specified stream to the remote host.
+ ///
+ /// The to upload.
+ /// A relative or absolute path for the remote file.
+ /// is null.
+ /// is a zero-length .
+ /// A directory with the specified path exists on the remote host.
+ /// The secure copy execution request was rejected by the server.
+ public void Upload(Stream source, string path)
+ {
+ var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);
using (var input = ServiceFactory.CreatePipeStream())
using (var channel = Session.CreateChannelSession())
@@ -227,8 +297,98 @@ public void Upload(Stream source, string path)
UploadFileModeAndName(channel, input, source.Length, posixPath.File);
UploadFileContent(channel, input, source, posixPath.File);
}
+
+ }
+#endif
+
+#if FEATURE_TPL
+ ///
+ /// Downloads the specified file from the remote host to the stream.
+ ///
+ /// A relative or absolute path for the remote file.
+ /// The to download the remote file to.
+ /// is null or contains only whitespace characters.
+ /// is null.
+ /// exists on the remote host, and is not a regular file.
+ /// The secure copy execution request was rejected by the server.
+ public void Download(string filename, Stream destination)
+ {
+ Download(filename,destination,CancellationToken.None);
+ }
+#endif
+
+#if FEATURE_TPL
+ ///
+ /// Downloads the specified file from the remote host to the stream.
+ ///
+ /// A relative or absolute path for the remote file.
+ /// The to download the remote file to.
+ /// cancellation token
+ /// is null or contains only whitespace characters.
+ /// is null.
+ /// exists on the remote host, and is not a regular file.
+ /// The secure copy execution request was rejected by the server.
+ /// Download cancelled exception
+ public void Download(string filename, Stream destination, CancellationToken cancellationToken)
+ {
+ if (filename.IsNullOrWhiteSpace())
+ throw new ArgumentException("filename");
+
+ if (destination == null)
+ throw new ArgumentNullException("destination");
+
+ try
+ {
+ using (var input = ServiceFactory.CreatePipeStream())
+ using (var channel = Session.CreateChannelSession())
+ using (cancellationToken.Register(() =>
+ {
+ channel.Dispose();
+ input.Dispose();
+ }))
+ {
+ channel.DataReceived += (sender, e) => input.Write(e.Data, 0, e.Data.Length);
+ channel.Open();
+
+ // Send channel command request
+ if (!channel.SendExecRequest(string.Format("scp -f {0}",
+ _remotePathTransformation.Transform(filename))))
+ {
+ throw SecureExecutionRequestRejectedException();
+ }
+
+ SendSuccessConfirmation(channel); // Send reply
+
+ var message = ReadString(input);
+ var match = FileInfoRe.Match(message);
+
+ if (match.Success)
+ {
+ // Read file
+ SendSuccessConfirmation(channel); // Send reply
+
+ var length = long.Parse(match.Result("${length}"));
+ var fileName = match.Result("${filename}");
+
+ InternalDownload(channel, input, destination, fileName, length);
+ }
+ else
+ {
+ SendErrorConfirmation(channel,
+ string.Format("\"{0}\" is not valid protocol message.", message));
+ }
+ }
+ }
+ catch (Exception)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ throw new OperationCanceledException();
+ else
+ throw;
+ }
}
+#else
///
/// Downloads the specified file from the remote host to the stream.
///
@@ -278,6 +438,7 @@ public void Download(string filename, Stream destination)
}
}
}
+#endif
///
/// Sets mode, size and name of file being upload.