Reorganize SFTP functionality and add some methods to replicate sftp utility as close as possible

Add SFTP Tests
Other minor fixes
This commit is contained in:
olegkap_cp
2010-12-29 21:56:21 +00:00
parent 9f7a13a24f
commit a9c7addecd
22 changed files with 1356 additions and 389 deletions
@@ -60,6 +60,12 @@
<Compile Include="Security\TestHostKey.cs" />
<Compile Include="Security\TestKeyExchange.cs" />
<Compile Include="Security\TestPrivateKeyFile.cs" />
<Compile Include="SftpClientTests\CreateDirectoryTest.cs" />
<Compile Include="SftpClientTests\DeleteDirectoryTest.cs" />
<Compile Include="SftpClientTests\DeleteFileTest.cs" />
<Compile Include="SftpClientTests\ListDirectoryTest.cs" />
<Compile Include="SftpClientTests\RenameFileTest.cs" />
<Compile Include="SftpClientTests\UploadDownloadFileTest.cs" />
<Compile Include="SshClientTests\TestPortForwarding.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
@@ -67,7 +73,6 @@
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="SftpClientTests\SftpClientTest.cs" />
<Compile Include="SshClientTests\TestShell.cs" />
<Compile Include="SshClientTests\TestSshCommand.cs" />
</ItemGroup>
@@ -0,0 +1,104 @@
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Common;
using Renci.SshClient.Tests.Properties;
namespace Renci.SshClient.Tests.SftpClientTests
{
/// <summary>
/// Summary description for CreateDirectoryTest
/// </summary>
[TestClass]
public class CreateDirectoryTest
{
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshConnectionException))]
public void Test_Sftp_CreateDirectory_Without_Connecting()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.CreateDirectory("test");
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_CreateDirectory_In_Current_Location()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.CreateDirectory("test");
sftp.DeleteDirectory("test");
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshPermissionDeniedException))]
public void Test_Sftp_CreateDirectory_In_Forbidden_Directory()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.CreateDirectory("/sbin/test");
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshPermissionDeniedException))]
public void Test_Sftp_CreateDirectory_Invalid_Path()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.CreateDirectory("/abcdefg/abcefg");
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_CreateDirectory_Already_Exists()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.CreateDirectory("test");
var exceptionThrown = false;
try
{
sftp.CreateDirectory("test");
}
catch (SshException)
{
exceptionThrown = true;
}
Assert.IsTrue(exceptionThrown);
sftp.DeleteDirectory("test");
sftp.Disconnect();
}
}
}
}
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Common;
using Renci.SshClient.Tests.Properties;
namespace Renci.SshClient.Tests.SftpClientTests
{
[TestClass]
public class DeleteDirectoryTest
{
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshConnectionException))]
public void Test_Sftp_DeleteDirectory_Without_Connecting()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.DeleteDirectory("test");
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshFileNotFoundException))]
public void Test_Sftp_DeleteDirectory_Which_Doesnt_Exists()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.DeleteDirectory("abcdef");
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshPermissionDeniedException))]
public void Test_Sftp_DeleteDirectory_Which_No_Permissions()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.DeleteDirectory("/usr");
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_DeleteDirectory()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
sftp.CreateDirectory("abcdef");
sftp.DeleteDirectory("abcdef");
sftp.Disconnect();
}
}
}
}
@@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Renci.SshClient.Tests
namespace Renci.SshClient.Tests.SftpClientTests
{
class SftpClientTest
class DeleteFileTest
{
}
}
@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Common;
using Renci.SshClient.Tests.Properties;
using System.Diagnostics;
namespace Renci.SshClient.Tests.SftpClientTests
{
[TestClass]
public class ListDirectoryTest
{
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshConnectionException))]
public void Test_Sftp_ListDirectory_Without_Connecting()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
var files = sftp.ListDirectory(".");
foreach (var file in files)
{
Debug.WriteLine(file.AbsolutePath);
}
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshPermissionDeniedException))]
public void Test_Sftp_ListDirectory_Permission_Denied()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
var files = sftp.ListDirectory("/etc/audit");
foreach (var file in files)
{
Debug.WriteLine(file.AbsolutePath);
}
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SshFileNotFoundException))]
public void Test_Sftp_ListDirectory_Not_Exists()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
var files = sftp.ListDirectory("/asdfgh");
foreach (var file in files)
{
Debug.WriteLine(file.AbsolutePath);
}
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_ListDirectory_Current()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
var files = sftp.ListDirectory(".");
Assert.IsTrue(files.Count() > 0);
foreach (var file in files)
{
Debug.WriteLine(file.AbsolutePath);
}
sftp.Disconnect();
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_ListDirectory_HugeDirectory()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
// Create 30000 directory items
for (int i = 0; i < 30000; i++)
{
sftp.CreateDirectory(string.Format("test_{0}", i));
}
var files = sftp.ListDirectory(".");
// Ensure that directory has at least 30000 items
Assert.IsTrue(files.Count() > 30000);
// Delete 10000 directory items
for (int i = 0; i < 30000; i++)
{
sftp.DeleteDirectory(string.Format("test_{0}", i));
}
sftp.Disconnect();
}
}
}
}
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Tests.Properties;
using System.IO;
namespace Renci.SshClient.Tests.SftpClientTests
{
[TestClass]
public class RenameFileTest
{
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_Rename_File()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
string uploadedFileName = Path.GetTempFileName();
string remoteFileName1 = Path.GetRandomFileName();
string remoteFileName2 = Path.GetRandomFileName();
this.CreateTestFile(uploadedFileName, 1);
using (var file = File.OpenRead(uploadedFileName))
{
sftp.UploadFile(file, remoteFileName1);
}
sftp.RenameFile(remoteFileName1, remoteFileName2);
sftp.DeleteFile(remoteFileName2);
File.Delete(uploadedFileName);
sftp.Disconnect();
}
}
/// <summary>
/// Creates the test file.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="size">Size in megabytes.</param>
private void CreateTestFile(string fileName, int size)
{
using (var testFile = File.Create(fileName))
{
var random = new Random();
for (int i = 0; i < 1024 * size; i++)
{
var buffer = new byte[1024];
random.NextBytes(buffer);
testFile.Write(buffer, 0, buffer.Length);
}
}
}
}
}
@@ -0,0 +1,338 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Common;
using Renci.SshClient.Tests.Properties;
using System.IO;
using System.Security.Cryptography;
using Renci.SshClient.Sftp;
using System.Threading;
namespace Renci.SshClient.Tests.SftpClientTests
{
[TestClass]
public class UploadDownloadFileTest
{
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_Upload_And_Download_1MB_File()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
string uploadedFileName = Path.GetTempFileName();
string remoteFileName = Path.GetRandomFileName();
this.CreateTestFile(uploadedFileName, 1);
// Calculate has value
var uploadedHash = CalculateMD5(uploadedFileName);
using (var file = File.OpenRead(uploadedFileName))
{
sftp.UploadFile(file, remoteFileName);
}
string downloadedFileName = Path.GetTempFileName();
using (var file = File.OpenWrite(downloadedFileName))
{
sftp.DownloadFile(remoteFileName, file);
}
var downloadedHash = CalculateMD5(downloadedFileName);
sftp.DeleteFile(remoteFileName);
File.Delete(uploadedFileName);
File.Delete(downloadedFileName);
sftp.Disconnect();
Assert.AreEqual(uploadedHash, downloadedHash);
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_Upload_Forbidden()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
string uploadedFileName = Path.GetTempFileName();
string remoteFileName = "/usr/";
this.CreateTestFile(uploadedFileName, 1);
var exceptionOccured = false;
try
{
using (var file = File.OpenRead(uploadedFileName))
{
sftp.UploadFile(file, remoteFileName);
}
}
catch (SshPermissionDeniedException)
{
exceptionOccured = true;
}
sftp.DeleteFile(remoteFileName);
File.Delete(uploadedFileName);
sftp.Disconnect();
Assert.IsTrue(exceptionOccured);
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_Download_Forbidden()
{
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
string remoteFileName = "/root/install.log";
var exceptionOccured = false;
try
{
using (var ms = new MemoryStream())
{
sftp.UploadFile(ms, remoteFileName);
}
}
catch (SshPermissionDeniedException)
{
exceptionOccured = true;
}
sftp.Disconnect();
Assert.IsTrue(exceptionOccured);
}
}
[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
{
var maxFiles = 10;
var maxSize = 5;
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
{
sftp.Connect();
var testInfoList = new Dictionary<string, TestInfo>();
for (int i = 0; i < maxFiles; i++)
{
var testInfo = new TestInfo();
testInfo.UploadedFileName = Path.GetTempFileName();
testInfo.DownloadedFileName = Path.GetTempFileName();
testInfo.RemoteFileName = Path.GetRandomFileName();
this.CreateTestFile(testInfo.UploadedFileName, maxSize);
// Calculate hash value
testInfo.UploadedHash = CalculateMD5(testInfo.UploadedFileName);
testInfoList.Add(testInfo.RemoteFileName, testInfo);
}
var uploadWaitHandles = new List<WaitHandle>();
// Start file uploads
foreach (var remoteFile in testInfoList.Keys)
{
var testInfo = testInfoList[remoteFile];
testInfo.UploadedFile = File.OpenRead(testInfo.UploadedFileName);
testInfo.UploadResult = sftp.BeginUploadFile(testInfo.UploadedFile, remoteFile, null, null);
uploadWaitHandles.Add(testInfo.UploadResult.AsyncWaitHandle);
}
// Wait for upload to finish
bool uploadCompleted = false;
while (!uploadCompleted)
{
// Assume upload completed
uploadCompleted = true;
foreach (var testInfo in testInfoList.Values)
{
SftpAsyncResult sftpResult = testInfo.UploadResult as SftpAsyncResult;
testInfo.UploadedBytes = sftpResult.UploadedBytes;
if (!testInfo.UploadResult.IsCompleted)
{
uploadCompleted = false;
}
}
Thread.Sleep(500);
}
// End file uploads
foreach (var remoteFile in testInfoList.Keys)
{
var testInfo = testInfoList[remoteFile];
sftp.EndUploadFile(testInfo.UploadResult);
testInfo.UploadedFile.Dispose();
}
// Start file downloads
var downloadWaitHandles = new List<WaitHandle>();
foreach (var remoteFile in testInfoList.Keys)
{
var testInfo = testInfoList[remoteFile];
testInfo.DownloadedFile = File.OpenWrite(testInfo.DownloadedFileName);
testInfo.DownloadResult = sftp.BeginDownloadFile(remoteFile, testInfo.DownloadedFile, null, null);
downloadWaitHandles.Add(testInfo.DownloadResult.AsyncWaitHandle);
}
// Wait for download to finish
bool downloadCompleted = false;
while (!downloadCompleted)
{
// Assume download completed
downloadCompleted = true;
foreach (var testInfo in testInfoList.Values)
{
SftpAsyncResult sftpResult = testInfo.DownloadResult as SftpAsyncResult;
testInfo.DownloadedBytes = sftpResult.DownloadedBytes;
if (!testInfo.DownloadResult.IsCompleted)
{
downloadCompleted = false;
}
}
Thread.Sleep(500);
}
var hashMatches = true;
var uploadDownloadSizeOk = true;
// End file downloads
foreach (var remoteFile in testInfoList.Keys)
{
var testInfo = testInfoList[remoteFile];
sftp.EndDownloadFile(testInfo.DownloadResult);
testInfo.DownloadedFile.Dispose();
testInfo.DownloadedHash = CalculateMD5(testInfo.DownloadedFileName);
if (!(testInfo.UploadedBytes > 0 && testInfo.DownloadedBytes > 0 && testInfo.DownloadedBytes == testInfo.UploadedBytes))
{
uploadDownloadSizeOk = false;
}
if (!testInfo.DownloadedHash.Equals(testInfo.UploadedHash))
{
hashMatches = false;
}
}
// Clean up after test
foreach (var remoteFile in testInfoList.Keys)
{
var testInfo = testInfoList[remoteFile];
sftp.DeleteFile(remoteFile);
File.Delete(testInfo.UploadedFileName);
File.Delete(testInfo.DownloadedFileName);
}
sftp.Disconnect();
Assert.IsTrue(hashMatches, "Hash does not match");
Assert.IsTrue(uploadDownloadSizeOk, "Uploaded and downloaded bytes does not match");
}
}
/// <summary>
/// Creates the test file.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="size">Size in megabytes.</param>
private void CreateTestFile(string fileName, int size)
{
using (var testFile = File.Create(fileName))
{
var random = new Random();
for (int i = 0; i < 1024 * size; i++)
{
var buffer = new byte[1024];
random.NextBytes(buffer);
testFile.Write(buffer, 0, buffer.Length);
}
}
}
protected static string CalculateMD5(string fileName)
{
using (FileStream file = new FileStream(fileName, FileMode.Open))
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
}
/// <summary>
/// Helper class to help with upload and download testing
/// </summary>
private class TestInfo
{
public string RemoteFileName { get; set; }
public string UploadedFileName { get; set; }
public string DownloadedFileName { get; set; }
public ulong UploadedBytes { get; set; }
public ulong DownloadedBytes { get; set; }
public FileStream UploadedFile { get; set; }
public FileStream DownloadedFile { get; set; }
public string UploadedHash { get; set; }
public string DownloadedHash { get; set; }
public IAsyncResult UploadResult { get; set; }
public IAsyncResult DownloadResult { get; set; }
}
}
}
@@ -159,6 +159,16 @@ namespace Renci.SshClient
{
}
/// <summary>
/// Ensures that client is connected.
/// </summary>
/// <exception cref="Renci.SshClient.Common.SshConnectionException">When client not connected.</exception>
protected void EnsureConnection()
{
if (!this.Session.IsConnected)
throw new SshConnectionException("Client not connected.");
}
#region IDisposable Members
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Renci.SshClient.Common
{
/// <summary>
/// The exception that is thrown when file or directory is not found.
/// </summary>
[Serializable]
public class SshFileNotFoundException : SshException
{
/// <summary>
/// Initializes a new instance of the <see cref="SshFileNotFoundException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public SshFileNotFoundException(string message)
: base(message)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Renci.SshClient.Common
{
/// <summary>
/// The exception that is thrown when operation permission is denied.
/// </summary>
[Serializable]
public class SshPermissionDeniedException : SshException
{
/// <summary>
/// Initializes a new instance of the <see cref="SshPermissionDeniedException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public SshPermissionDeniedException(string message)
: base(message)
{
}
}
}
@@ -75,7 +75,9 @@
<Compile Include="Common\PortForwardEventArgs.cs" />
<Compile Include="Common\SshAuthenticationException.cs" />
<Compile Include="Common\SshConnectionException.cs" />
<Compile Include="Common\SshFileNotFoundException.cs" />
<Compile Include="Common\SshOperationTimeoutException.cs" />
<Compile Include="Common\SshPermissionDeniedException.cs" />
<Compile Include="KeyboardInteractiveConnectionInfo.cs" />
<Compile Include="Messages\Authentication\RequestMessageKeyboardInteractive.cs" />
<Compile Include="Messages\Connection\ChannelOpen\ChannelOpenInfo.cs" />
@@ -96,6 +98,9 @@
<Compile Include="Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs" />
<Compile Include="Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs" />
<Compile Include="Messages\MessageAttribute.cs" />
<Compile Include="Sftp\FileStatusCommand.cs" />
<Compile Include="Sftp\SetFileStatusCommand.cs" />
<Compile Include="Sftp\SymbolicLinkCommand.cs" />
<Compile Include="Sftp\Messages\SftpRequestMessage.cs" />
<Compile Include="Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs" />
<Compile Include="Messages\Transport\KeyExchangeDhGroupExchangeInit.cs" />
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshClient.Sftp.Messages;
namespace Renci.SshClient.Sftp
{
internal class FileStatusCommand : SftpCommand
{
private string _path;
public SftpFile SftpFile { get; private set; }
public FileStatusCommand(SftpSession sftpSession, string path)
: base(sftpSession)
{
this._path = path;
}
protected override void OnExecute()
{
this.SendStatMessage(this._path);
}
protected override void OnAttributes(Attributes attributes)
{
base.OnAttributes(attributes);
this.SftpFile = new SftpFile(this._path, attributes);
this.CompleteExecution();
}
}
}
@@ -27,10 +27,10 @@ namespace Renci.SshClient.Sftp.Messages
for (int i = 0; i < this.Count; i++)
{
var fileName = this.ReadString();
var fullName = this.ReadString();
var fullName = this.ReadString(); // This field value has meaningless information
var attributes = this.ReadAttributes();
files.Add(new SftpFile(fileName, fullName, attributes));
files.Add(new SftpFile(fileName, attributes));
}
this.Files = files;
}
@@ -13,14 +13,11 @@ namespace Renci.SshClient.Sftp.Messages
public string ExistingPath { get; set; }
public bool IsSymLink { get; set; }
protected override void LoadData()
{
base.LoadData();
this.NewLinkPath = this.ReadString();
this.ExistingPath = this.ReadString();
this.IsSymLink = this.ReadBoolean();
}
protected override void SaveData()
@@ -28,7 +25,6 @@ namespace Renci.SshClient.Sftp.Messages
base.SaveData();
this.Write(this.NewLinkPath, Encoding.UTF8);
this.Write(this.ExistingPath, Encoding.UTF8);
this.Write(this.IsSymLink);
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshClient.Sftp.Messages;
namespace Renci.SshClient.Sftp
{
internal class SetFileStatusCommand : SftpCommand
{
private string _path;
private Attributes _attributes;
public SftpFile SftpFile { get; private set; }
public SetFileStatusCommand(SftpSession sftpSession, string path, Attributes attributes)
: base(sftpSession)
{
this._path = path;
this._attributes = attributes;
}
protected override void OnExecute()
{
this.SendSetStatMessage(this._path, this._attributes);
}
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
{
if (statusCode == StatusCodes.Ok)
{
this.CompleteExecution();
}
}
}
}
@@ -249,13 +249,12 @@ namespace Renci.SshClient.Sftp
});
}
protected void SendSymLinkMessage(string existingPath, string newLinkPath, bool isSymLink)
protected void SendSymLinkMessage(string linkPath, string path)
{
this.SendMessage(new SymLinkMessage
{
ExistingPath = existingPath,
NewLinkPath = newLinkPath,
IsSymLink = isSymLink,
NewLinkPath = linkPath,
ExistingPath = path,
});
}
@@ -276,14 +275,20 @@ namespace Renci.SshClient.Sftp
{
this.OnStatus(e.Message.StatusCode, e.Message.ErrorMessage, e.Message.Language);
if (e.Message.StatusCode == StatusCodes.NoSuchFile ||
e.Message.StatusCode == StatusCodes.PermissionDenied ||
e.Message.StatusCode == StatusCodes.Failure ||
e.Message.StatusCode == StatusCodes.BadMessage ||
e.Message.StatusCode == StatusCodes.NoConnection ||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
e.Message.StatusCode == StatusCodes.OperationUnsupported
)
if (e.Message.StatusCode == StatusCodes.PermissionDenied)
{
throw new SshPermissionDeniedException(e.Message.ErrorMessage);
}
else if (e.Message.StatusCode == StatusCodes.NoSuchFile)
{
throw new SshFileNotFoundException(e.Message.ErrorMessage);
}
else if (e.Message.StatusCode == StatusCodes.Failure ||
e.Message.StatusCode == StatusCodes.BadMessage ||
e.Message.StatusCode == StatusCodes.NoConnection ||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
e.Message.StatusCode == StatusCodes.OperationUnsupported
)
{
// Throw an exception if it was not handled by the command
throw new SshException(e.Message.ErrorMessage);
+130 -53
View File
@@ -12,62 +12,27 @@ namespace Renci.SshClient.Sftp
/// <summary>
/// Initializes a new instance of the <see cref="SftpFile"/> class.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="fullName">The full name.</param>
/// <param name="absolutePath">Name of the file.</param>
/// <param name="attributes">The attributes.</param>
public SftpFile(string fileName, string fullName, Attributes attributes)
public SftpFile(string absolutePath, Attributes attributes)
{
this.Name = fileName;
this.AbsolutePath = absolutePath;
if (attributes.Size.HasValue)
this.Size = (int)attributes.Size.Value;
else
this.Size = -1;
if (attributes.UserId.HasValue)
this.UserId = (int)attributes.UserId.Value;
else
this.UserId = -1;
if (attributes.GroupId.HasValue)
this.GroupId = (int)attributes.GroupId.Value;
else
this.GroupId = -1;
if (attributes.Permissions.HasValue)
this.Permissions = (int)attributes.Permissions.Value;
else
this.Permissions = -1;
if (attributes.AccessTime.HasValue)
this.AccessedTime = attributes.AccessTime.Value;
else
this.AccessedTime = DateTime.MinValue;
if (attributes.ModifyTime.HasValue)
this.ModifiedTime = attributes.ModifyTime.Value;
else
this.ModifiedTime = DateTime.MinValue;
if (attributes.Extensions != null)
this.Extensions = new Dictionary<string, string>(attributes.Extensions);
this.Attributes = attributes;
}
/// <summary>
/// Gets file status information attributes.
/// </summary>
public Attributes Attributes { get; private set; }
/// <summary>
/// Gets or sets file name.
/// </summary>
/// <value>
/// File name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets file filename.
/// </summary>
/// <value>
/// File filename.
/// </value>
public string Filename { get; set; }
public string AbsolutePath { get; set; }
/// <summary>
/// Gets or sets file accessed time.
@@ -75,7 +40,20 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File accessed time.
/// </value>
public DateTime AccessedTime { get; set; }
public DateTime AccessedTime
{
get
{
if (this.Attributes.AccessTime.HasValue)
return this.Attributes.AccessTime.Value;
else
return DateTime.MinValue;
}
set
{
this.Attributes.AccessTime = value;
}
}
/// <summary>
/// Gets or sets file modified time.
@@ -83,7 +61,20 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File modified time.
/// </value>
public DateTime ModifiedTime { get; set; }
public DateTime ModifiedTime
{
get
{
if (this.Attributes.ModifyTime.HasValue)
return this.Attributes.ModifyTime.Value;
else
return DateTime.MinValue;
}
set
{
this.Attributes.ModifyTime = value;
}
}
/// <summary>
/// Gets or sets file size.
@@ -91,7 +82,27 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File size.
/// </value>
public long Size { get; set; }
public long Size
{
get
{
if (this.Attributes.Size.HasValue)
return (long)this.Attributes.Size.Value;
else
return -1;
}
set
{
if (value > -1)
{
this.Attributes.Size = new Nullable<ulong>((ulong)value);
}
else
{
this.Attributes.Size = null;
}
}
}
/// <summary>
/// Gets or sets file user id.
@@ -99,7 +110,27 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File user id.
/// </value>
public int UserId { get; set; }
public int UserId
{
get
{
if (this.Attributes.UserId.HasValue)
return (int)this.Attributes.UserId.Value;
else
return -1;
}
set
{
if (value > -1)
{
this.Attributes.UserId = new Nullable<uint>((uint)value);
}
else
{
this.Attributes.UserId = null;
}
}
}
/// <summary>
/// Gets or sets file group id.
@@ -107,7 +138,27 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File group id.
/// </value>
public int GroupId { get; set; }
public int GroupId
{
get
{
if (this.Attributes.GroupId.HasValue)
return (int)this.Attributes.GroupId.Value;
else
return -1;
}
set
{
if (value > -1)
{
this.Attributes.GroupId = new Nullable<uint>((uint)value);
}
else
{
this.Attributes.GroupId = null;
}
}
}
/// <summary>
/// Gets or sets file permissions.
@@ -115,7 +166,27 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File permissions.
/// </value>
public int Permissions { get; set; }
public int Permissions
{
get
{
if (this.Attributes.Permissions.HasValue)
return (int)this.Attributes.Permissions.Value;
else
return -1;
}
set
{
if (value > -1)
{
this.Attributes.Permissions = new Nullable<uint>((uint)value);
}
else
{
this.Attributes.Permissions = null;
}
}
}
/// <summary>
/// Gets or sets file extensions attributes.
@@ -123,7 +194,13 @@ namespace Renci.SshClient.Sftp
/// <value>
/// File extensions.
/// </value>
public IDictionary<string, string> Extensions { get; set; }
public IDictionary<string, string> Extensions
{
get
{
return this.Attributes.Extensions;
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
@@ -133,7 +210,7 @@ namespace Renci.SshClient.Sftp
/// </returns>
public override string ToString()
{
return string.Format("Name {0}, Size {1}, User ID {2}, Group ID {3}, Permissions {4:X}, Accessed {5}, Modified {6}", this.Name, this.Size, this.UserId, this.GroupId, this.Permissions, this.AccessedTime, this.ModifiedTime);
return string.Format("Name {0}, Size {1}, User ID {2}, Group ID {3}, Permissions {4:X}, Accessed {5}, Modified {6}", this.AbsolutePath, this.Size, this.UserId, this.GroupId, this.Permissions, this.AccessedTime, this.ModifiedTime);
}
}
}
@@ -25,10 +25,12 @@ namespace Renci.SshClient.Sftp
private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false);
public int _operationTimeout;
private int _operationTimeout;
public event EventHandler<ErrorEventArgs> ErrorOccured;
public int ProtocolVersion { get; private set; }
#region SFTP messages
internal event EventHandler<MessageEventArgs<StatusMessage>> StatusMessageReceived;
@@ -67,6 +69,8 @@ namespace Renci.SshClient.Sftp
});
this.WaitHandle(this._sftpVersionConfirmed, this._operationTimeout);
this.ProtocolVersion = 3;
}
public void Disconnect()
@@ -295,6 +299,5 @@ namespace Renci.SshClient.Sftp
}
#endregion
}
}
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshClient.Sftp.Messages;
namespace Renci.SshClient.Sftp
{
internal class SymbolicLinkCommand : SftpCommand
{
private string _linkPath;
private string _path;
public SymbolicLinkCommand(SftpSession sftpSession, string linkPath, string path)
: base(sftpSession)
{
this._linkPath = linkPath;
this._path = path;
}
protected override void OnExecute()
{
this.SendSymLinkMessage(this._linkPath, this._path);
}
protected override void OnStatus(StatusCodes statusCode, string errorMessage, string language)
{
if (statusCode == StatusCodes.Ok)
{
this.CompleteExecution();
}
}
}
}
+311 -298
View File
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using Renci.SshClient.Sftp;
@@ -28,6 +29,16 @@ namespace Renci.SshClient
/// <value>The size of the buffer.</value>
public uint BufferSize { get; set; }
/// <summary>
/// Gets remote working directory.
/// </summary>
public string WorkingDirectory { get; private set; }
/// <summary>
/// Gets sftp protocol version.
/// </summary>
public int ProtocolVersion { get; private set; }
#region Constructors
/// <summary>
@@ -89,18 +100,232 @@ namespace Renci.SshClient
#endregion
#region List Directory
/// <summary>
/// Changes remote directory to path.
/// </summary>
/// <param name="path">New directory path.</param>
public void ChangeDirectory(string path)
{
// TODO: Check if change directory should be improved based on "6.10.1 Best Practice for Dealing with Paths" paragraph.
// Ensure that connection is established.
this.EnsureConnection();
this.WorkingDirectory = this.ValidatePath(this.ResolvePath(path));
}
/// <summary>
/// Begins the list directory operation.
/// Changes group of file(s)to specified group id.
/// </summary>
/// <param name="path">File(s) path, may match multiple files.</param>
/// <param name="groupId">Numeric GID.</param>
public void ChangeGroup(string path, ushort groupId)
{
// TODO: Need to be tested
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ResolvePath(path);
var cmd = new FileStatusCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
cmd.SftpFile.GroupId = groupId;
var setCmd = new SetFileStatusCommand(this._sftpSession, fullPath, cmd.SftpFile.Attributes);
setCmd.CommandTimeout = this.OperationTimeout;
setCmd.Execute();
}
/// <summary>
/// Changes permissions of file(s) to specified mode.
/// </summary>
/// <param name="path">File(s) path, may match multiple files.</param>
/// <param name="mode">The mode.</param>
public void ChangePermissions(string path, int mode)
{
// TODO: Need to be tested
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ResolvePath(path);
var cmd = new FileStatusCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
cmd.SftpFile.Permissions = mode;
var setCmd = new SetFileStatusCommand(this._sftpSession, fullPath, cmd.SftpFile.Attributes);
setCmd.CommandTimeout = this.OperationTimeout;
setCmd.Execute();
}
/// <summary>
/// Changes the owner of file(s) to specified owner.
/// </summary>
/// <param name="path">File(s) path, may match multiple files.</param>
/// <param name="owner">Numeric UID.</param>
public void ChangeOwner(string path, ushort owner)
{
// TODO: Need to be tested
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ResolvePath(path);
var cmd = new FileStatusCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
cmd.SftpFile.UserId = owner;
var setCmd = new SetFileStatusCommand(this._sftpSession, fullPath, cmd.SftpFile.Attributes);
setCmd.CommandTimeout = this.OperationTimeout;
setCmd.Execute();
}
/// <summary>
/// Creates remote directory specified by path.
/// </summary>
/// <param name="path">Directory path to create.</param>
/// <exception cref="Renci.SshClient.Common.SshPermissionDeniedException"></exception>
/// <exception cref="Renci.SshClient.Common.SshException"></exception>
public void CreateDirectory(string path)
{
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ResolvePath(path);
var cmd = new CreateDirectoryCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
}
/// <summary>
/// Deletes remote directory specified by path.
/// </summary>
/// <param name="path">Directory to be deleted path.</param>
public void DeleteDirectory(string path)
{
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ValidatePath(this.ResolvePath(path));
var cmd = new RemoveDirectoryCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
}
/// <summary>
/// Deletes remote file specified by path.
/// </summary>
/// <param name="path">File to be deleted path.</param>
public void DeleteFile(string path)
{
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ValidatePath(this.ResolvePath(path));
var cmd = new RemoveFileCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
}
/// <summary>
/// Renames remote file from old path to new path.
/// </summary>
/// <param name="oldPath">Path to the old file location.</param>
/// <param name="newPath">Path to the new file location.</param>
public void RenameFile(string oldPath, string newPath)
{
// Ensure that connection is established.
this.EnsureConnection();
var oldFullPath = this.ValidatePath(this.ResolvePath(oldPath));
var newFullPath = this.ResolvePath(newPath);
var cmd = new RenameFileCommand(this._sftpSession, oldFullPath, newFullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
}
/// <summary>
/// Creates a symbolic link from old path to new path.
/// </summary>
/// <param name="linkPath">The old path.</param>
/// <param name="path">The new path.</param>
public void SymbolicLink(string linkPath, string path)
{
// TODO: Need to be tested, currently does not work
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ValidatePath(this.ResolvePath(path));
var linkFullPath = this.ResolvePath(linkPath);
var cmd = new SymbolicLinkCommand(this._sftpSession, linkFullPath, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
}
/// <summary>
/// Retrieves list of files in remote directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>List of directory entries</returns>
public IEnumerable<SftpFile> ListDirectory(string path)
{
return this.EndListDirectory(this.BeginListDirectory(path, null, null));
}
/// <summary>
/// Begins an asynchronous operation of retrieving list of files in remote directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
/// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state)
{
var cmd = new ListDirectoryCommand(this._sftpSession, path);
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ValidatePath(this.ResolvePath(path));
var cmd = new ListDirectoryCommand(this._sftpSession, fullPath);
cmd.CommandTimeout = this.OperationTimeout;
@@ -108,9 +333,9 @@ namespace Renci.SshClient
}
/// <summary>
/// Ends the list directory operation.
/// Ends an asynchronous operation of retrieving list of files in remote directory.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
/// <param name="asyncResult">The pending asynchronous SFTP request.</param>
/// <returns>List of files</returns>
public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult)
{
@@ -129,30 +354,31 @@ namespace Renci.SshClient
}
/// <summary>
/// Lists the directory.
/// Downloads remote file specified by the path into the stream.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>List of files</returns>
public IEnumerable<SftpFile> ListDirectory(string path)
/// <param name="path">File to download.</param>
/// <param name="output">Stream to write the file into.</param>
public void DownloadFile(string path, Stream output)
{
return this.EndListDirectory(this.BeginListDirectory(path, null, null));
this.EndDownloadFile(this.BeginDownloadFile(path, output, null, null));
}
#endregion
#region Rename file
/// <summary>
/// Begins the rename file.
/// Begins an asynchronous file downloading into the stream.
/// </summary>
/// <param name="oldFilename">The old filename.</param>
/// <param name="newFileName">New name of the file.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <param name="path">The path.</param>
/// <param name="output">The output.</param>
/// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
/// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginRenameFile(string oldFilename, string newFileName, AsyncCallback asyncCallback, object state)
public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state)
{
var cmd = new RenameFileCommand(this._sftpSession, oldFilename, newFileName);
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ValidatePath(this.ResolvePath(path));
var cmd = new DownloadFileCommand(this._sftpSession, this.BufferSize, fullPath, output);
cmd.CommandTimeout = this.OperationTimeout;
@@ -160,10 +386,10 @@ namespace Renci.SshClient
}
/// <summary>
/// Ends the rename file.
/// Ends an asynchronous file downloading into the stream.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void EndRenameFile(IAsyncResult asyncResult)
/// <param name="asyncResult">The pending asynchronous SFTP request.</param>
public void EndDownloadFile(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
@@ -172,35 +398,37 @@ namespace Renci.SshClient
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<RenameFileCommand>();
var cmd = sftpAsyncResult.GetCommand<DownloadFileCommand>();
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Renames the file.
/// Uploads stream into remote file..
/// </summary>
/// <param name="oldFilename">The old filename.</param>
/// <param name="newFileName">New name of the file.</param>
public void RenameFile(string oldFilename, string newFileName)
/// <param name="input">Data input stream.</param>
/// <param name="path">Remote file path.</param>
public void UploadFile(Stream input, string path)
{
this.EndRenameFile(this.BeginRenameFile(oldFilename, newFileName, null, null));
this.EndUploadFile(this.BeginUploadFile(input, path, null, null));
}
#endregion
#region Remove Directory
/// <summary>
/// Begins the remove directory.
/// Begins an asynchronous uploading the steam into remote file.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <param name="input">Data input stream.</param>
/// <param name="path">Remote file path.</param>
/// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
/// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginRemoveDirectory(string path, AsyncCallback asyncCallback, object state)
public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state)
{
var cmd = new RemoveDirectoryCommand(this._sftpSession, path);
// Ensure that connection is established.
this.EnsureConnection();
var fullPath = this.ResolvePath(path);
var cmd = new UploadFileCommand(this._sftpSession, this.BufferSize, fullPath, input);
cmd.CommandTimeout = this.OperationTimeout;
@@ -208,151 +436,9 @@ namespace Renci.SshClient
}
/// <summary>
/// Ends the remove directory.
/// Ends an asynchronous uploading the steam into remote file.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void EndRemoveDirectory(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
if (sftpAsyncResult == null)
{
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<RemoveDirectoryCommand>();
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Removes the directory.
/// </summary>
/// <param name="path">The path.</param>
public void RemoveDirectory(string path)
{
this.EndRemoveDirectory(this.BeginRemoveDirectory(path, null, null));
}
#endregion
#region Create Directory
/// <summary>
/// Begins the create directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginCreateDirectory(string path, AsyncCallback asyncCallback, object state)
{
var cmd = new CreateDirectoryCommand(this._sftpSession, path);
cmd.CommandTimeout = this.OperationTimeout;
return cmd.BeginExecute(asyncCallback, state);
}
/// <summary>
/// Ends the create directory.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void EndCreateDirectory(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
if (sftpAsyncResult == null)
{
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<CreateDirectoryCommand>();
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Creates the directory.
/// </summary>
/// <param name="path">The path.</param>
public void CreateDirectory(string path)
{
this.EndCreateDirectory(this.BeginCreateDirectory(path, null, null));
}
#endregion
#region Remove File
/// <summary>
/// Begins the remove file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginRemoveFile(string filename, AsyncCallback asyncCallback, object state)
{
var cmd = new RemoveFileCommand(this._sftpSession, filename);
cmd.CommandTimeout = this.OperationTimeout;
return cmd.BeginExecute(asyncCallback, state);
}
/// <summary>
/// Ends the remove file.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void EndRemoveFile(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
if (sftpAsyncResult == null)
{
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<RemoveFileCommand>();
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Removes the file.
/// </summary>
/// <param name="filename">The filename.</param>
public void RemoveFile(string filename)
{
this.EndRemoveFile(this.BeginRemoveFile(filename, null, null));
}
#endregion
#region Upload File
/// <summary>
/// Begins the upload file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="input">The input.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginUploadFile(string filename, Stream input, AsyncCallback asyncCallback, object state)
{
var cmd = new UploadFileCommand(this._sftpSession, this.BufferSize, filename, input);
cmd.CommandTimeout = this.OperationTimeout;
return cmd.BeginExecute(asyncCallback, state);
}
/// <summary>
/// Ends the upload file.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
/// <param name="asyncResult">The pending asynchronous SFTP request.</param>
public void EndUploadFile(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
@@ -367,118 +453,6 @@ namespace Renci.SshClient
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Uploads the file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="input">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void UploadFile(string filename, Stream input)
{
this.EndUploadFile(this.BeginUploadFile(filename, input, null, null));
}
#endregion
#region Download File
/// <summary>
/// Begins the download.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="output">The output.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginDownload(string filename, Stream output, AsyncCallback asyncCallback, object state)
{
var cmd = new DownloadFileCommand(this._sftpSession, this.BufferSize, filename, output);
cmd.CommandTimeout = this.OperationTimeout;
return cmd.BeginExecute(asyncCallback, state);
}
/// <summary>
/// Ends the download.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
public void EndDownload(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
if (sftpAsyncResult == null)
{
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<DownloadFileCommand>();
cmd.EndExecute(sftpAsyncResult);
}
/// <summary>
/// Downloads the specified filename.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="output">The output.</param>
public void Download(string filename, Stream output)
{
this.EndDownload(this.BeginDownload(filename, output, null, null));
}
#endregion
#region Get Real Path
/// <summary>
/// Begins the get real path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
/// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
public IAsyncResult BeginGetRealPath(string path, AsyncCallback asyncCallback, object state)
{
var cmd = new RealPathCommand(this._sftpSession, path);
cmd.CommandTimeout = this.OperationTimeout;
return cmd.BeginExecute(asyncCallback, state);
}
/// <summary>
/// Ends the get real path.
/// </summary>
/// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous operation.</param>
/// <returns></returns>
public IEnumerable<SftpFile> EndGetRealPath(IAsyncResult asyncResult)
{
var sftpAsyncResult = asyncResult as SftpAsyncResult;
if (sftpAsyncResult == null)
{
throw new InvalidOperationException("Not valid IAsyncResult object.");
}
var cmd = sftpAsyncResult.GetCommand<RealPathCommand>();
cmd.EndExecute(sftpAsyncResult);
return cmd.Files;
}
/// <summary>
/// Gets the real path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns></returns>
public IEnumerable<SftpFile> GetRealPath(string path)
{
return this.EndGetRealPath(this.BeginGetRealPath(path, null, null));
}
#endregion
/// <summary>
/// Called when client is connected to the server.
/// </summary>
@@ -489,6 +463,12 @@ namespace Renci.SshClient
this._sftpSession = new SftpSession(this.Session, this.OperationTimeout);
this._sftpSession.Connect();
// Resolve current directory
this.WorkingDirectory = this.ValidatePath(".");
// Resolve current running version
this.ProtocolVersion = this._sftpSession.ProtocolVersion;
}
/// <summary>
@@ -501,5 +481,38 @@ namespace Renci.SshClient
this._sftpSession.Disconnect();
}
/// <summary>
/// Resolves the path client side without server validation.
/// </summary>
/// <param name="path">Path to resolve.</param>
/// <returns>Resolved path</returns>
private string ResolvePath(string path)
{
// If path starts with "/" then its "absolute"
if (!path.StartsWith("/"))
{
return string.Format("{0}/{1}", this.WorkingDirectory, path);
}
else
return path;
}
/// <summary>
/// Resolves path into absolute path on the server.
/// </summary>
/// <param name="path">PAth to resolve..</param>
/// <returns>Absolute path</returns>
private string ValidatePath(string path)
{
var cmd = new RealPathCommand(this._sftpSession, path);
cmd.CommandTimeout = this.OperationTimeout;
cmd.Execute();
var file = cmd.Files.FirstOrDefault();
return file.AbsolutePath;
}
}
}
+10 -2
View File
@@ -112,6 +112,9 @@ namespace Renci.SshClient
/// </returns>
public T AddForwardedPort<T>(string boundHost, uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new()
{
// Ensure that connection is established.
this.EnsureConnection();
T port = new T();
port.Session = this.Session;
@@ -157,7 +160,7 @@ namespace Renci.SshClient
/// <returns><see cref="SshCommand"/> object.</returns>
public SshCommand CreateCommand(string commandText)
{
return new SshCommand(this.Session, commandText);
return this.CreateCommand(commandText, Encoding.ASCII);
}
/// <summary>
@@ -168,10 +171,12 @@ namespace Renci.SshClient
/// <returns><see cref="SshCommand"/> object which uses specified encoding.</returns>
public SshCommand CreateCommand(string commandText, Encoding encoding)
{
// Ensure that connection is established.
this.EnsureConnection();
return new SshCommand(this.Session, commandText, encoding);
}
/// <summary>
/// Creates and executes the command.
/// </summary>
@@ -199,6 +204,9 @@ namespace Renci.SshClient
/// <returns></returns>
public Shell CreateShell(Stream input, TextWriter output, TextWriter extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, string terminalMode)
{
// Ensure that connection is established.
this.EnsureConnection();
return new Shell(this.Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode);
}
}
@@ -86,16 +86,6 @@ namespace Renci.SshClient
}
}
/// <summary>
/// Initializes a new instance of the <see cref="SshCommand"/> class.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="commandText">The command text.</param>
internal SshCommand(Session session, string commandText)
: this(session, commandText, Encoding.ASCII)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SshCommand"/> class.
/// </summary>
@@ -123,9 +113,6 @@ namespace Renci.SshClient
/// <exception cref="Renci.SshClient.Common.SshOperationTimeoutException">Operation has timed out.</exception>
public IAsyncResult BeginExecute(AsyncCallback callback, object state)
{
if (!this._session.IsConnected)
throw new SshConnectionException("Not connected.");
// Prevent from executing BeginExecute before calling EndExecute
if (this._asyncResult != null)
{