mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
Pass full remote file path to scp command when uploading or downloading a file, and use zero-length pass in C directive. Combined, this ensure the SCP server will reject a remote file path that points to a directory.
Fixes issue #286.
This commit is contained in:
@@ -197,6 +197,9 @@
|
||||
<Compile Include="..\Renci.SshNet\Common\PortForwardEventArgs.cs">
|
||||
<Link>Common\PortForwardEventArgs.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\Common\PosixPath.cs">
|
||||
<Link>Common\PosixPath.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\Common\ProxyException.cs">
|
||||
<Link>Common\ProxyException.cs</Link>
|
||||
</Compile>
|
||||
@@ -953,7 +956,7 @@
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_fileName = CreateTemporaryFile(new byte[] {1});
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_fileInfo = new FileInfo(_fileName);
|
||||
_path = random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_fileName = CreateTemporaryFile(_fileContent);
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_fileInfo = new FileInfo(_fileName);
|
||||
_path = random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
@@ -86,11 +86,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendData(It.Is<byte[]>(b => b.SequenceEqual(CreateData(
|
||||
string.Format("C0644 {0} {1}\n",
|
||||
_fileInfo.Length,
|
||||
Path.GetFileName(_fileName)
|
||||
)
|
||||
)))));
|
||||
string.Format("C0644 {0} {1}\n", _fileInfo.Length, string.Empty))))));
|
||||
_pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Renci.SshNet.Common
|
||||
{
|
||||
internal class PosixPath
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the file name part of a given POSIX path.
|
||||
/// </summary>
|
||||
/// <param name="path">The POSIX path to get the file name for.</param>
|
||||
/// <returns>
|
||||
/// The file name part of <paramref name="path"/>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If <paramref name="path"/> contains no forward slash or has a trailing
|
||||
/// forward slash, then <paramref name="path"/> is returned.
|
||||
/// </remarks>
|
||||
public static string GetFileName(string path)
|
||||
{
|
||||
var pathEnd = path.LastIndexOf('/');
|
||||
if (pathEnd == -1 || pathEnd == path.Length - 1)
|
||||
return path;
|
||||
return path.Substring(pathEnd + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,7 @@
|
||||
<Compile Include="Common\ChannelRequestEventArgs.cs" />
|
||||
<Compile Include="Common\CountdownEvent.cs" />
|
||||
<Compile Include="Common\Pack.cs" />
|
||||
<Compile Include="Common\PosixPath.cs" />
|
||||
<Compile Include="Common\ProxyException.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
||||
@@ -18,9 +18,10 @@ namespace Renci.SshNet
|
||||
/// Uploads the specified file to the remote host.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">The file system info.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="path">A relative or absolute path for the remote file.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path"/> is <c>null</c> or empty.</exception>
|
||||
/// <exception cref="ScpException">A directory with the specified path exists on the remote host.</exception>
|
||||
public void Upload(FileInfo fileInfo, string path)
|
||||
{
|
||||
if (fileInfo == null)
|
||||
@@ -36,10 +37,14 @@ namespace Renci.SshNet
|
||||
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", path.ShellQuote())))
|
||||
throw new SshException("Secure copy execution request was rejected by the server. Please consult the server logs.");
|
||||
|
||||
CheckReturnCode(input);
|
||||
|
||||
InternalUpload(channel, input, fileInfo);
|
||||
using (var source = fileInfo.OpenRead())
|
||||
{
|
||||
UploadTimes(channel, input, fileInfo);
|
||||
UploadFileModeAndName(channel, input, source.Length, string.Empty);
|
||||
UploadFileContent(channel, input, source, fileInfo.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +52,10 @@ namespace Renci.SshNet
|
||||
/// Uploads the specified directory to the remote host.
|
||||
/// </summary>
|
||||
/// <param name="directoryInfo">The directory info.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="path">A relative or absolute path for the remote directory.</param>
|
||||
/// <exception cref="ArgumentNullException">fileSystemInfo</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path"/> is <c>null</c> or empty.</exception>
|
||||
/// <exception cref="ScpException"><paramref name="path"/> exists on the remote host, and is not a directory.</exception>
|
||||
public void Upload(DirectoryInfo directoryInfo, string path)
|
||||
{
|
||||
if (directoryInfo == null)
|
||||
@@ -67,17 +73,9 @@ namespace Renci.SshNet
|
||||
channel.SendExecRequest(string.Format("scp -rt {0}", path.ShellQuote()));
|
||||
CheckReturnCode(input);
|
||||
|
||||
// set last write and last access time on specified remote path
|
||||
InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc);
|
||||
SendData(channel, string.Format("D0755 0 {0}\n", "."));
|
||||
CheckReturnCode(input);
|
||||
|
||||
// recursively upload files and directories in specified remote path
|
||||
InternalUpload(channel, input, directoryInfo);
|
||||
|
||||
// terminate upload of specified remote path
|
||||
SendData(channel, "E\n");
|
||||
CheckReturnCode(input);
|
||||
UploadTimes(channel, input, directoryInfo);
|
||||
UploadDirectoryModeAndName(channel, input, ".");
|
||||
UploadDirectoryContent(channel, input, directoryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +86,7 @@ namespace Renci.SshNet
|
||||
/// <param name="fileInfo">Local file information.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="fileInfo"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="filename"/> is <c>null</c> or empty.</exception>
|
||||
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
|
||||
public void Download(string filename, FileInfo fileInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
@@ -104,7 +103,7 @@ namespace Renci.SshNet
|
||||
// Send channel command request
|
||||
channel.SendExecRequest(string.Format("scp -pf {0}", filename.ShellQuote()));
|
||||
// Send reply
|
||||
SendConfirmation(channel);
|
||||
SendSuccessConfirmation(channel);
|
||||
|
||||
InternalDownload(channel, input, fileInfo);
|
||||
}
|
||||
@@ -117,6 +116,7 @@ namespace Renci.SshNet
|
||||
/// <param name="directoryInfo">Local directory information.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="directoryName"/> is <c>null</c> or empty.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="directoryInfo"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ScpException">File or directory with the specified path does not exist on the remote host.</exception>
|
||||
public void Download(string directoryName, DirectoryInfo directoryInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directoryName))
|
||||
@@ -133,51 +133,53 @@ namespace Renci.SshNet
|
||||
// Send channel command request
|
||||
channel.SendExecRequest(string.Format("scp -prf {0}", directoryName.ShellQuote()));
|
||||
// Send reply
|
||||
SendConfirmation(channel);
|
||||
SendSuccessConfirmation(channel);
|
||||
|
||||
InternalDownload(channel, input, directoryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the file in the active directory context, and set
|
||||
/// Upload the files and subdirectories in the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to perform the upload in.</param>
|
||||
/// <param name="input">A <see cref="Stream"/> from which any feedback from the server can be read.</param>
|
||||
/// <param name="fileInfo">The file to upload.</param>
|
||||
private void InternalUpload(IChannelSession channel, Stream input, FileInfo fileInfo)
|
||||
{
|
||||
using (var source = fileInfo.OpenRead())
|
||||
{
|
||||
// set the last write and last access time for the next file uploaded
|
||||
InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc);
|
||||
// upload the actual file
|
||||
InternalUpload(channel, input, source, fileInfo.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void InternalUpload(IChannelSession channel, Stream input, DirectoryInfo directoryInfo)
|
||||
/// <param name="directoryInfo">The directory to upload.</param>
|
||||
private void UploadDirectoryContent(IChannelSession channel, Stream input, DirectoryInfo directoryInfo)
|
||||
{
|
||||
// Upload files
|
||||
var files = directoryInfo.GetFiles();
|
||||
foreach (var file in files)
|
||||
{
|
||||
InternalUpload(channel, input, file);
|
||||
using (var source = file.OpenRead())
|
||||
{
|
||||
UploadTimes(channel, input, file);
|
||||
UploadFileModeAndName(channel, input, source.Length, file.Name);
|
||||
UploadFileContent(channel, input, source, file.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload directories
|
||||
var directories = directoryInfo.GetDirectories();
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
InternalSetTimestamp(channel, input, directory.LastWriteTimeUtc, directory.LastAccessTimeUtc);
|
||||
SendData(channel, string.Format("D0755 0 {0}\n", directory.Name));
|
||||
CheckReturnCode(input);
|
||||
|
||||
InternalUpload(channel, input, directory);
|
||||
|
||||
SendData(channel, "E\n");
|
||||
CheckReturnCode(input);
|
||||
UploadTimes(channel, input, directory);
|
||||
UploadDirectoryModeAndName(channel, input, directory.Name);
|
||||
UploadDirectoryContent(channel, input, directory);
|
||||
}
|
||||
|
||||
// Mark upload of current directory complete
|
||||
SendData(channel, "E\n");
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets mode and name of the directory being upload.
|
||||
/// </summary>
|
||||
private void UploadDirectoryModeAndName(IChannelSession channel, Stream input, string directoryName)
|
||||
{
|
||||
SendData(channel, string.Format("D0755 0 {0}\n", directoryName));
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
|
||||
private void InternalDownload(IChannelSession channel, Stream input, FileSystemInfo fileSystemInfo)
|
||||
@@ -195,7 +197,7 @@ namespace Renci.SshNet
|
||||
|
||||
if (message == "E")
|
||||
{
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
directoryCounter--;
|
||||
|
||||
@@ -209,7 +211,7 @@ namespace Renci.SshNet
|
||||
var match = DirectoryInfoRe.Match(message);
|
||||
if (match.Success)
|
||||
{
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
// Read directory
|
||||
var filename = match.Result("${filename}");
|
||||
@@ -217,7 +219,7 @@ namespace Renci.SshNet
|
||||
DirectoryInfo newDirectoryInfo;
|
||||
if (directoryCounter > 0)
|
||||
{
|
||||
newDirectoryInfo = Directory.CreateDirectory(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, filename));
|
||||
newDirectoryInfo = Directory.CreateDirectory(Path.Combine(currentDirectoryFullName, filename));
|
||||
newDirectoryInfo.LastAccessTime = accessedTime;
|
||||
newDirectoryInfo.LastWriteTime = modifiedTime;
|
||||
}
|
||||
@@ -237,7 +239,7 @@ namespace Renci.SshNet
|
||||
if (match.Success)
|
||||
{
|
||||
// Read file
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
var length = long.Parse(match.Result("${length}"));
|
||||
var fileName = match.Result("${filename}");
|
||||
@@ -245,7 +247,7 @@ namespace Renci.SshNet
|
||||
var fileInfo = fileSystemInfo as FileInfo;
|
||||
|
||||
if (fileInfo == null)
|
||||
fileInfo = new FileInfo(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, fileName));
|
||||
fileInfo = new FileInfo(Path.Combine(currentDirectoryFullName, fileName));
|
||||
|
||||
using (var output = fileInfo.OpenWrite())
|
||||
{
|
||||
@@ -264,7 +266,7 @@ namespace Renci.SshNet
|
||||
if (match.Success)
|
||||
{
|
||||
// Read timestamp
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
var mtime = long.Parse(match.Result("${mtime}"));
|
||||
var atime = long.Parse(match.Result("${atime}"));
|
||||
@@ -275,7 +277,7 @@ namespace Renci.SshNet
|
||||
continue;
|
||||
}
|
||||
|
||||
SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message));
|
||||
SendErrorConfirmation(channel, string.Format("\"{0}\" is not valid protocol message.", message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Renci.SshNet
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description>Recursive download (-prf) does not deal well with specific UTF-8 and newline characters.</description>
|
||||
/// <description>Recursive update does not support empty path for uploading to home directorydeal well with specific UTF-8 and newline characters.</description>
|
||||
/// <description>Recursive update does not support empty path for uploading to home directory.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
@@ -30,6 +30,8 @@ namespace Renci.SshNet
|
||||
public partial class ScpClient : BaseClient
|
||||
{
|
||||
private static readonly Regex FileInfoRe = new Regex(@"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)");
|
||||
private static readonly byte[] SuccessConfirmationCode = {0};
|
||||
private static readonly byte[] ErrorConfirmationCode = { 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the operation timeout.
|
||||
@@ -167,8 +169,9 @@ namespace Renci.SshNet
|
||||
/// <summary>
|
||||
/// Uploads the specified stream to the remote host.
|
||||
/// </summary>
|
||||
/// <param name="source">Stream to upload.</param>
|
||||
/// <param name="path">Remote host file name.</param>
|
||||
/// <param name="source">The <see cref="Stream"/> to upload.</param>
|
||||
/// <param name="path">A relative or absolute path for the remote file.</param>
|
||||
/// <exception cref="ScpException">A directory with the specified path exists on the remote host.</exception>
|
||||
public void Upload(Stream source, string path)
|
||||
{
|
||||
using (var input = ServiceFactory.CreatePipeStream())
|
||||
@@ -177,30 +180,27 @@ namespace Renci.SshNet
|
||||
channel.DataReceived += (sender, e) => input.Write(e.Data, 0, e.Data.Length);
|
||||
channel.Open();
|
||||
|
||||
var pathEnd = path.LastIndexOfAny(new[] { '\\', '/' });
|
||||
if (pathEnd != -1)
|
||||
{
|
||||
// split the path from the file
|
||||
var pathOnly = path.Substring(0, pathEnd);
|
||||
var fileOnly = path.Substring(pathEnd + 1);
|
||||
// Send channel command request
|
||||
channel.SendExecRequest(string.Format("scp -t \"{0}\"", pathOnly));
|
||||
CheckReturnCode(input);
|
||||
// pass the full path to ensure the server does not create the directory part
|
||||
// as a file in case the directory does not exist
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", path.ShellQuote())))
|
||||
return;
|
||||
CheckReturnCode(input);
|
||||
|
||||
path = fileOnly;
|
||||
}
|
||||
|
||||
InternalUpload(channel, input, source, path);
|
||||
// specify a zero-length file name to avoid creating a file with absolute
|
||||
// path '<path>/<filename part of path>' if directory '<path>' already exists
|
||||
UploadFileModeAndName(channel, input, source.Length, string.Empty);
|
||||
UploadFileContent(channel, input, source, PosixPath.GetFileName(path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the specified file from the remote host to the stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">Remote host file name.</param>
|
||||
/// <param name="destination">The stream where to download remote file.</param>
|
||||
/// <param name="filename">A relative or absolute path for the remote file.</param>
|
||||
/// <param name="destination">The <see cref="Stream"/> to download the remote file to.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="filename"/> is <c>null</c> or contains only whitespace characters.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="destination"/> is <c>null</c>.</exception>
|
||||
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
|
||||
/// <remarks>
|
||||
/// Method calls made by this method to <paramref name="destination"/>, may under certain conditions result
|
||||
/// in exceptions thrown by the stream.
|
||||
@@ -221,7 +221,7 @@ namespace Renci.SshNet
|
||||
|
||||
// Send channel command request
|
||||
channel.SendExecRequest(string.Format("scp -f {0}", filename.ShellQuote()));
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
var message = ReadString(input);
|
||||
var match = FileInfoRe.Match(message);
|
||||
@@ -229,7 +229,7 @@ namespace Renci.SshNet
|
||||
if (match.Success)
|
||||
{
|
||||
// Read file
|
||||
SendConfirmation(channel); // Send reply
|
||||
SendSuccessConfirmation(channel); // Send reply
|
||||
|
||||
var length = long.Parse(match.Result("${length}"));
|
||||
var fileName = match.Result("${filename}");
|
||||
@@ -238,28 +238,61 @@ namespace Renci.SshNet
|
||||
}
|
||||
else
|
||||
{
|
||||
SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message));
|
||||
SendErrorConfirmation(channel, string.Format("\"{0}\" is not valid protocol message.", message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InternalSetTimestamp(IChannelSession channel, Stream input, DateTime lastWriteTime, DateTime lastAccessime)
|
||||
/// <summary>
|
||||
/// Uploads the <see cref="FileSystemInfo.LastWriteTimeUtc"/> and <see cref="FileSystemInfo.LastAccessTimeUtc"/>
|
||||
/// of the next file or directory to upload.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to perform the upload in.</param>
|
||||
/// <param name="input">A <see cref="Stream"/> from which any feedback from the server can be read.</param>
|
||||
/// <param name="fileOrDirectory">The file or directory to upload.</param>
|
||||
private void UploadTimes(IChannelSession channel, Stream input, FileSystemInfo fileOrDirectory)
|
||||
{
|
||||
var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
var modificationSeconds = (long) (lastWriteTime - zeroTime).TotalSeconds;
|
||||
var accessSeconds = (long) (lastAccessime - zeroTime).TotalSeconds;
|
||||
var modificationSeconds = (long) (fileOrDirectory.LastWriteTimeUtc - zeroTime).TotalSeconds;
|
||||
var accessSeconds = (long) (fileOrDirectory.LastAccessTimeUtc - zeroTime).TotalSeconds;
|
||||
SendData(channel, string.Format("T{0} 0 {1} 0\n", modificationSeconds, accessSeconds));
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
|
||||
private void InternalUpload(IChannelSession channel, Stream input, Stream source, string filename)
|
||||
/// <summary>
|
||||
/// Sets mode, size and name of file being upload.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to perform the upload in.</param>
|
||||
/// <param name="input">A <see cref="Stream"/> from which any feedback from the server can be read.</param>
|
||||
/// <param name="fileSize">The size of the content to upload.</param>
|
||||
/// <param name="serverFileName">The name of the file, without path, to which the content is to be uploaded.</param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When the SCP transfer is already initiated for a file, a zero-length <see cref="string"/> should
|
||||
/// be specified for <paramref name="serverFileName"/>. This prevents the server from uploading the
|
||||
/// content to a file with path <c><file path>/<paramref name="serverFileName"/></c> if there's
|
||||
/// already a directory with this path, and allows us to receive an error response.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private void UploadFileModeAndName(IChannelSession channel, Stream input, long fileSize, string serverFileName)
|
||||
{
|
||||
var length = source.Length;
|
||||
|
||||
// specify permissions, length and name of file being upload
|
||||
SendData(channel, string.Format("C0644 {0} {1}\n", length, filename));
|
||||
SendData(channel, string.Format("C0644 {0} {1}\n", fileSize, serverFileName));
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the content of a file.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to perform the upload in.</param>
|
||||
/// <param name="input">A <see cref="Stream"/> from which any feedback from the server can be read.</param>
|
||||
/// <param name="source">The content to upload.</param>
|
||||
/// <param name="remoteFileName">The name of the remote file, without path, to which the content is uploaded.</param>
|
||||
/// <remarks>
|
||||
/// <paramref name="remoteFileName"/> is only used for raising the <see cref="Uploading"/> event.
|
||||
/// </remarks>
|
||||
private void UploadFileContent(IChannelSession channel, Stream input, Stream source, string remoteFileName)
|
||||
{
|
||||
var totalLength = source.Length;
|
||||
var buffer = new byte[BufferSize];
|
||||
|
||||
var read = source.Read(buffer, 0, buffer.Length);
|
||||
@@ -272,12 +305,12 @@ namespace Renci.SshNet
|
||||
|
||||
totalRead += read;
|
||||
|
||||
RaiseUploadingEvent(filename, length, totalRead);
|
||||
RaiseUploadingEvent(remoteFileName, totalLength, totalRead);
|
||||
|
||||
read = source.Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
SendConfirmation(channel);
|
||||
SendSuccessConfirmation(channel);
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
|
||||
@@ -304,7 +337,7 @@ namespace Renci.SshNet
|
||||
RaiseDownloadingEvent(filename, length, length - needToRead);
|
||||
|
||||
// Send confirmation byte after last data byte was read
|
||||
SendConfirmation(channel);
|
||||
SendSuccessConfirmation(channel);
|
||||
|
||||
CheckReturnCode(input);
|
||||
}
|
||||
@@ -325,15 +358,15 @@ namespace Renci.SshNet
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendConfirmation(IChannel channel)
|
||||
private static void SendSuccessConfirmation(IChannel channel)
|
||||
{
|
||||
SendData(channel, new byte[] { 0 });
|
||||
SendData(channel, SuccessConfirmationCode);
|
||||
}
|
||||
|
||||
private void SendConfirmation(IChannel channel, byte errorCode, string message)
|
||||
private void SendErrorConfirmation(IChannel channel, string message)
|
||||
{
|
||||
SendData(channel, new[] { errorCode });
|
||||
SendData(channel, string.Format("{0}\n", message));
|
||||
SendData(channel, ErrorConfirmationCode);
|
||||
SendData(channel, string.Concat(message, "\n"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user