From e488bc516e9b7cd021d3e2ac463629ca37eb862f Mon Sep 17 00:00:00 2001 From: olegkap_cp Date: Wed, 5 Jan 2011 23:12:10 +0000 Subject: [PATCH] Add ErrorOccured to BaseClient to allow catching of exceptions that happened on the other thread Fix SymbolicLink method Minor changes --- .../Security/TestKeyExchange.cs | 20 ++-- Renci.SshClient/Renci.SshClient/BaseClient.cs | 26 +++++- .../Renci.SshClient/ChannelAsyncResult.cs | 17 +++- .../Channels/ChannelDirectTcpip.cs | 9 +- .../Renci.SshClient/ForwardedPortLocal.cs | 11 +-- .../Renci.SshClient/Security/HMac.cs | 36 ++----- .../Renci.SshClient/Security/HMacMD5.cs | 26 +++++- .../Renci.SshClient/Security/HMacSha1.cs | 25 ++++- .../Renci.SshClient/Security/KeyExchange.cs | 28 +++++- Renci.SshClient/Renci.SshClient/Session.cs | 93 ++++++++----------- Renci.SshClient/Renci.SshClient/SftpClient.cs | 12 +-- Renci.SshClient/Renci.SshClient/SshCommand.cs | 20 ++-- 12 files changed, 188 insertions(+), 135 deletions(-) diff --git a/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs b/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs index 4f13baa8..b0a8f191 100644 --- a/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs +++ b/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshClient.Security; using Renci.SshClient.Tests.Properties; @@ -68,6 +64,18 @@ namespace Renci.SshClient.Tests.Security } } - // TODO: Add test to test re-keying + [TestMethod] + public void Test_KeyExchange_Rekeying() + { + var connectionInfo = new PasswordConnectionInfo(Resources.HOST, 22, Resources.USERNAME, Resources.PASSWORD); + + using (var client = new SshClient(connectionInfo)) + { + client.Connect(); + // TODO: Add test to test re-keying + Assert.Inconclusive(); + client.Disconnect(); + } + } } } diff --git a/Renci.SshClient/Renci.SshClient/BaseClient.cs b/Renci.SshClient/Renci.SshClient/BaseClient.cs index 72642fa0..43f8ba17 100644 --- a/Renci.SshClient/Renci.SshClient/BaseClient.cs +++ b/Renci.SshClient/Renci.SshClient/BaseClient.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading; using Renci.SshClient.Common; @@ -68,6 +69,11 @@ namespace Renci.SshClient } } + /// + /// Occurs when an error occurred. + /// + public event EventHandler ErrorOccurred; + /// /// Initializes a new instance of the class. /// @@ -92,6 +98,7 @@ namespace Renci.SshClient this.Session = new Session(this.ConnectionInfo); this.Session.Connect(); + this.Session.ErrorOccured += Session_ErrorOccured; this.OnConnected(); } @@ -164,12 +171,20 @@ namespace Renci.SshClient throw new SshConnectionException("Client not connected."); } + private void Session_ErrorOccured(object sender, System.IO.ErrorEventArgs e) + { + if (this.ErrorOccurred != null) + { + this.ErrorOccurred(this, e); + } + } + #region IDisposable Members private bool _isDisposed = false; /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -181,17 +196,19 @@ namespace Renci.SshClient /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._isDisposed) { // If disposing equals true, dispose all managed - // and unmanaged resources. + // and unmanaged ResourceMessages. if (disposing) { - // Dispose managed resources. + // Dispose managed ResourceMessages. + this.Session.ErrorOccured -= Session_ErrorOccured; + if (this.Session != null) { this.Session.Dispose(); @@ -202,7 +219,6 @@ namespace Renci.SshClient this._keepAliveTimer.Dispose(); this._keepAliveTimer = null; } - } // Note disposing has been done. diff --git a/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs b/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs index f3f9b98d..3ddc256e 100644 --- a/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs +++ b/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs @@ -12,7 +12,7 @@ namespace Renci.SshClient /// Gets or sets the command that async result was created for. /// /// The channel. - internal SshCommand Command { get; private set; } + private SshCommand _command; /// /// Gets or sets the bytes received. If SFTP only file bytes are counted. @@ -60,7 +60,20 @@ namespace Renci.SshClient /// The command. internal CommandAsyncResult(SshCommand command) { - this.Command = command; + this._command = command; + } + + /// + /// Validates that command object is valid to be used with this instance. + /// + /// The command. + public void ValidateCommand(SshCommand command) + { + if (this._command != command) + { + throw new InvalidOperationException("Invalid IAsyncResult parameter"); + } + } } } diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs index a94674f5..8d18fb2a 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs @@ -120,7 +120,8 @@ namespace Renci.SshClient.Channels // data transfer can take longer then connection specified timeout System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskError }); - this._socket.Close(); + this._socket.Dispose(); + this._socket = null; // Wait for task to finish and will throw any errors if any readerTask.Wait(); @@ -162,6 +163,12 @@ namespace Renci.SshClient.Channels protected override void Dispose(bool disposing) { + if (this._socket != null) + { + this._socket.Dispose(); + this._socket = null; + } + if (this._channelEof != null) { this._channelEof.Dispose(); diff --git a/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs b/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs index 4dd50587..08aedd7e 100644 --- a/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs +++ b/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs @@ -3,8 +3,6 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using Renci.SshClient.Channels; -using Renci.SshClient.Common; -using System.Diagnostics; namespace Renci.SshClient { @@ -37,7 +35,6 @@ namespace Renci.SshClient { while (true) { - // TODO: Check what the best place to call dispose for this socket. var socket = this._listener.AcceptSocket(); Task.Factory.StartNew(() => @@ -99,7 +96,7 @@ namespace Renci.SshClient private bool _isDisposed = false; /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -111,17 +108,17 @@ namespace Renci.SshClient /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._isDisposed) { // If disposing equals true, dispose all managed - // and unmanaged resources. + // and unmanaged ResourceMessages. if (disposing) { - // Dispose managed resources. + // Dispose managed ResourceMessages. if (this._listenerTask != null) { this._listenerTask.Dispose(); diff --git a/Renci.SshClient/Renci.SshClient/Security/HMac.cs b/Renci.SshClient/Renci.SshClient/Security/HMac.cs index 80a2a7ad..ee77f9e9 100644 --- a/Renci.SshClient/Renci.SshClient/Security/HMac.cs +++ b/Renci.SshClient/Renci.SshClient/Security/HMac.cs @@ -1,20 +1,18 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Security.Cryptography; namespace Renci.SshClient.Security { /// - /// Represents base class for hash algoritm classes + /// Represents base class for hash algorithm classes /// public abstract class HMac : Algorithm, IDisposable { /// /// Instance of initialized hash algorithm that being used /// - protected System.Security.Cryptography.HMAC _hmac; - // TODO: Refactor code so this member will not be protected + protected abstract HMAC Hash { get; } /// /// Initializes algorithm with specified key. @@ -29,7 +27,7 @@ namespace Renci.SshClient.Security /// The hash internal byte[] ComputeHash(byte[] hashData) { - return this._hmac.ComputeHash(hashData); + return this.Hash.ComputeHash(hashData); } /// @@ -42,16 +40,14 @@ namespace Renci.SshClient.Security { get { - return this._hmac.HashSize; + return this.Hash.HashSize; } } #region IDisposable Members - private bool _disposed = false; - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -63,27 +59,9 @@ namespace Renci.SshClient.Security /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._hmac != null) - { - this._hmac.Dispose(); - this._hmac = null; - } - } - - // Note disposing has been done. - this._disposed = true; - } } /// diff --git a/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs b/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs index 20df3210..ea33cad3 100644 --- a/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs +++ b/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Security.Cryptography; namespace Renci.SshClient.Security { @@ -10,6 +9,13 @@ namespace Renci.SshClient.Security /// internal class HMacMD5 : HMac { + private HMACMD5 _hash; + + protected override HMAC Hash + { + get { return this._hash; } + } + /// /// Gets algorithm name. /// @@ -24,7 +30,19 @@ namespace Renci.SshClient.Security /// The hash key. public override void Init(IEnumerable key) { - this._hmac = new System.Security.Cryptography.HMACMD5(key.Take(16).ToArray()); + this._hash = new HMACMD5(key.Take(16).ToArray()); + } + + protected override void Dispose(bool disposing) + { + // Dispose managed ResourceMessages. + if (this._hash != null) + { + this._hash.Dispose(); + this._hash = null; + } + + base.Dispose(disposing); } } } diff --git a/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs b/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs index 3ef8f9f6..d83db0d8 100644 --- a/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs +++ b/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; using System.Security.Cryptography; namespace Renci.SshClient.Security @@ -11,6 +9,13 @@ namespace Renci.SshClient.Security /// internal class HMacSha1 : HMac { + private HMACSHA1 _hash; + + protected override HMAC Hash + { + get { return this._hash; } + } + /// /// Gets algorithm name. /// @@ -25,7 +30,19 @@ namespace Renci.SshClient.Security /// The hash key. public override void Init(IEnumerable key) { - this._hmac = new System.Security.Cryptography.HMACSHA1(key.Take(20).ToArray()); + this._hash = new HMACSHA1(key.Take(20).ToArray()); + } + + protected override void Dispose(bool disposing) + { + // Dispose managed ResourceMessages. + if (this._hash != null) + { + this._hash.Dispose(); + this._hash = null; + } + + base.Dispose(disposing); } } } diff --git a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs index 6cd4dfbd..7a9e0e75 100644 --- a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs +++ b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs @@ -156,6 +156,10 @@ namespace Renci.SshClient.Security } } + /// + /// Creates the server side cipher to use. + /// + /// public Cipher CreateServerCipher() { // Resolve Session ID @@ -177,6 +181,10 @@ namespace Renci.SshClient.Security return serverCipher; } + /// + /// Creates the client side cipher to use. + /// + /// public Cipher CreateClientCipher() { // Resolve Session ID @@ -198,6 +206,10 @@ namespace Renci.SshClient.Security return clientCipher; } + /// + /// Creates the server side hash algorithm to use. + /// + /// public HMac CreateServerHash() { // Resolve Session ID @@ -211,6 +223,10 @@ namespace Renci.SshClient.Security return serverHMac; } + /// + /// Creates the client side hash algorithm to use. + /// + /// public HMac CreateClientHash() { // Resolve Session ID @@ -224,6 +240,10 @@ namespace Renci.SshClient.Security return clientHMac; } + /// + /// Creates the compression algorithm to use to deflate data. + /// + /// public Compressor CreateCompressor() { if (this._compressionType == null) @@ -236,6 +256,10 @@ namespace Renci.SshClient.Security return compressor; } + /// + /// Creates the compression algorithm to use to inflate data. + /// + /// public Compressor CreateDecompressor() { if (this._compressionType == null) @@ -374,7 +398,7 @@ namespace Renci.SshClient.Security #region IDisposable Members /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -386,7 +410,7 @@ namespace Renci.SshClient.Security /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { } diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index 2817221d..db6cc2fb 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -430,6 +430,7 @@ namespace Renci.SshClient Name = messageAttribute.Name, Number = messageAttribute.Number, Enabled = false, + Activated = false, Type = type, }).ToList(); @@ -440,11 +441,8 @@ namespace Renci.SshClient Match versionMatch = null; // Get server version from the server, // ignore text lines which are sent before if any - NetworkStream ns = null; - try + using (var ns = new NetworkStream(this._socket)) { - - ns = new NetworkStream(this._socket); using (var sr = new StreamReader(ns)) { while (true) @@ -464,14 +462,6 @@ namespace Renci.SshClient } } } - finally - { - if (ns != null) - { - ns.Dispose(); - ns = null; - } - } // Get server SSH version var version = versionMatch.Result("${protoversion}"); @@ -528,6 +518,21 @@ namespace Renci.SshClient throw new SshAuthenticationException("User cannot be authenticated."); } + // Register Connection messages + this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); + this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); + this.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); + this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); + this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); + this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); + this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); + this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_DATA"); + this.RegisterMessage("SSH_MSG_CHANNEL_EOF"); + this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); + Monitor.Pulse(this); } } @@ -1025,22 +1030,12 @@ namespace Renci.SshClient { this._keyExchangeCompletedWaitHandle.Reset(); - // Connection type messages are not allowed during key exchange phase - this.UnRegisterMessage("SSH_MSG_GLOBAL_REQUEST"); - this.UnRegisterMessage("SSH_MSG_REQUEST_SUCCESS"); - this.UnRegisterMessage("SSH_MSG_REQUEST_FAILURE"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_REQUEST"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_FAILURE"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_DATA"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_EOF"); - this.UnRegisterMessage("SSH_MSG_CHANNEL_CLOSE"); - // TODO: Replace it with algorithm which disables all messages but relevant for key exchange - + // Disable all registered messages except key exchange related + foreach (var messageMetadata in this._messagesMetadata) + { + if (messageMetadata.Activated == true && messageMetadata.Number < 20 || messageMetadata.Number > 30) + messageMetadata.Enabled = false; + } var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys from s in message.KeyExchangeAlgorithms @@ -1116,20 +1111,12 @@ namespace Renci.SshClient this._keyExchange = null; } - // Register Connection messages - this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); - this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); - this.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); - this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); - this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); - this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_EOF"); - this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); + // Enable all active registered messages + foreach (var messageMetadata in this._messagesMetadata) + { + if (messageMetadata.Activated == true) + messageMetadata.Enabled = true; + } if (this.NewKeysReceived != null) { @@ -1460,7 +1447,7 @@ namespace Renci.SshClient { Parallel.ForEach( from m in this._messagesMetadata where m.Name == messageName select m, - (item) => { item.Enabled = true; }); + (item) => { item.Enabled = true; item.Activated = true; }); } } @@ -1474,7 +1461,7 @@ namespace Renci.SshClient { Parallel.ForEach( from m in this._messagesMetadata where m.Name == messageName select m, - (item) => { item.Enabled = false; }); + (item) => { item.Enabled = false; item.Activated = false; }); } } @@ -1486,8 +1473,7 @@ namespace Renci.SshClient private Message LoadMessage(IEnumerable data) { var messageType = data.FirstOrDefault(); - - var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled == true select m).SingleOrDefault(); + var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault(); if (messageMetadata == null) throw new SshException(string.Format("Message type {0} is not valid.", messageType)); @@ -1512,8 +1498,6 @@ namespace Renci.SshClient { var message = this.ReceiveMessage(); - //Debug.WriteLine(string.Format("{0} : {1}", DateTime.Now, message)); - if (message == null) { throw new NullReferenceException("The 'message' variable cannot be null"); @@ -1527,10 +1511,6 @@ namespace Renci.SshClient catch (Exception exp) { this.RaiseError(exp); - // TODO: This exception can be swallowed if it occurs while running in the background, look for possible solutions - // It can be swollen if call async, when asynch method is finished ensure all async method check for exception to be raised - // Or if it is running in port forwarding mode, some event should be raised at this case to handle and error and if not handle then throw - // throw an exception and let it be swallowed then if not handled by the event handler } } @@ -1568,7 +1548,7 @@ namespace Renci.SshClient private bool _disposed = false; /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -1580,14 +1560,14 @@ namespace Renci.SshClient /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._disposed) { // If disposing equals true, dispose all managed - // and unmanaged resources. + // and unmanaged ResourceMessages. if (disposing) { this._isDisconnecting = true; @@ -1690,8 +1670,9 @@ namespace Renci.SshClient public bool Enabled { get; set; } + public bool Activated { get; set; } + public Type Type { get; set; } } - } } diff --git a/Renci.SshClient/Renci.SshClient/SftpClient.cs b/Renci.SshClient/Renci.SshClient/SftpClient.cs index 9a42999e..93fea58f 100644 --- a/Renci.SshClient/Renci.SshClient/SftpClient.cs +++ b/Renci.SshClient/Renci.SshClient/SftpClient.cs @@ -213,12 +213,10 @@ namespace Renci.SshClient /// /// Creates a symbolic link from old path to new path. /// - /// The old path. - /// The new path. - public void SymbolicLink(string linkPath, string path) + /// The old path. + /// The new path. + public void SymbolicLink(string path, string linkPath) { - // TODO: Need to be tested, currently does not work - // Ensure that connection is established. this.EnsureConnection(); @@ -226,7 +224,7 @@ namespace Renci.SshClient var linkFullPath = this._sftpSession.ResolvePath(linkPath); - var cmd = new SymbolicLinkCommand(this._sftpSession, linkFullPath, fullPath); + var cmd = new SymbolicLinkCommand(this._sftpSession, fullPath, linkFullPath); cmd.CommandTimeout = this.OperationTimeout; @@ -413,7 +411,7 @@ namespace Renci.SshClient /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected override void Dispose(bool disposing) { if (this._sftpSession != null) diff --git a/Renci.SshClient/Renci.SshClient/SshCommand.cs b/Renci.SshClient/Renci.SshClient/SshCommand.cs index 1dc86513..858f7557 100644 --- a/Renci.SshClient/Renci.SshClient/SshCommand.cs +++ b/Renci.SshClient/Renci.SshClient/SshCommand.cs @@ -172,11 +172,7 @@ namespace Renci.SshClient { CommandAsyncResult channelAsyncResult = asynchResult as CommandAsyncResult; - // TODO: Create a method to perform this check in the method - if (channelAsyncResult.Command != this) - { - throw new InvalidOperationException("Invalid IAsyncResult parameter"); - } + channelAsyncResult.ValidateCommand(this); // Make sure that operation completed if not wait for it to finish this.WaitHandle(this._asyncResult.AsyncWaitHandle); @@ -334,7 +330,7 @@ namespace Renci.SshClient private bool _isDisposed = false; /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. /// public void Dispose() { @@ -346,42 +342,42 @@ namespace Renci.SshClient /// /// Releases unmanaged and - optionally - managed resources /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._isDisposed) { // If disposing equals true, dispose all managed - // and unmanaged resources. + // and unmanaged ResourceMessages. if (disposing) { this._session.Disconnected -= Session_Disconnected; this._session.ErrorOccured -= Session_ErrorOccured; - // Dispose managed resources. + // Dispose managed ResourceMessages. if (this.OutputStream != null) { this.OutputStream.Dispose(); this.OutputStream = null; } - // Dispose managed resources. + // Dispose managed ResourceMessages. if (this.ExtendedOutputStream != null) { this.ExtendedOutputStream.Dispose(); this.ExtendedOutputStream = null; } - // Dispose managed resources. + // Dispose managed ResourceMessages. if (this._sessionErrorOccuredWaitHandle != null) { this._sessionErrorOccuredWaitHandle.Dispose(); this._sessionErrorOccuredWaitHandle = null; } - // Dispose managed resources. + // Dispose managed ResourceMessages. if (this._channel != null) { this._channel.DataReceived -= Channel_DataReceived;