Преглед на файлове

Split SftpSession class into 2 to allow future extensability of other sub systems

olegkap_cp преди 14 години
родител
ревизия
097246c153
променени са 3 файла, в които са добавени 305 реда и са изтрити 222 реда
  1. 1 0
      Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj
  2. 95 222
      Renci.SshClient/Renci.SshNet/Sftp/SftpSession.cs
  3. 209 0
      Renci.SshClient/Renci.SshNet/SubsystemSession.cs

+ 1 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -182,6 +182,7 @@
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="Security\KeyHostAlgorithm.cs" />
+    <Compile Include="SubsystemSession.cs" />
     <Compile Include="Shell.NET40.cs" />
     <Compile Include="SftpClient.NET40.cs" />
     <Compile Include="PasswordConnectionInfo.NET40.cs" />

+ 95 - 222
Renci.SshClient/Renci.SshNet/Sftp/SftpSession.cs

@@ -13,26 +13,14 @@ using Renci.SshNet.Sftp.Requests;
 
 namespace Renci.SshNet.Sftp
 {
-    internal class SftpSession : IDisposable
+    internal class SftpSession : SubsystemSession
     {
-        private Session _session;
-
-        private ChannelSession _channel;
-
         private Dictionary<uint, SftpRequest> _requests = new Dictionary<uint, SftpRequest>();
 
         private List<byte> _data = new List<byte>(32 * 1024);
 
-        private Exception _exception;
-
-        private EventWaitHandle _errorOccuredWaitHandle = new AutoResetEvent(false);
-
         private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false);
 
-        private TimeSpan _operationTimeout;
-
-        public event EventHandler<ExceptionEventArgs> ErrorOccured;
-
         /// <summary>
         /// Gets remote working directory.
         /// </summary>
@@ -70,36 +58,8 @@ namespace Renci.SshNet.Sftp
         #endregion
 
         public SftpSession(Session session, TimeSpan operationTimeout)
+            : base(session, "sftp", operationTimeout)
         {
-            this._session = session;
-            this._operationTimeout = operationTimeout;
-        }
-
-        public void Connect()
-        {
-            this._channel = this._session.CreateChannel<ChannelSession>();
-
-            this._session.ErrorOccured += Session_ErrorOccured;
-            this._session.Disconnected += Session_Disconnected;
-            this._channel.DataReceived += Channel_DataReceived;
-
-            this._channel.Open();
-
-            this._channel.SendSubsystemRequest("sftp");
-
-            this.SendMessage(new SftpInitRequest(3));
-
-            this.WaitHandle(this._sftpVersionConfirmed, this._operationTimeout);
-
-            this.ProtocolVersion = 3;
-
-            //  Resolve current directory
-            this.WorkingDirectory = this.RequestRealPath(".").Keys.First();
-        }
-
-        public void Disconnect()
-        {
-            this.Dispose();
         }
 
         public void ChangeDirectory(string path)
@@ -113,96 +73,9 @@ namespace Renci.SshNet.Sftp
             this.WorkingDirectory = fullPath;
         }
 
-        private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e)
-        {
-            //  Add channel data to internal data holder
-            this._data.AddRange(e.Data);
-
-            while (this._data.Count > 4 + 1)
-            {
-                //  Extract packet length
-                var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]);
-
-                //  Check if complete packet data is available
-                if (this._data.Count < packetLength + 4)
-                {
-                    //  Wait for complete message to arrive first
-                    break;
-                }
-                this._data.RemoveRange(0, 4);
-
-                //  Create buffer to hold packet data
-                var packetData = new byte[packetLength];
-
-                //  Cope packet data to array
-                this._data.CopyTo(0, packetData, 0, packetLength);
-
-                //  Remove loaded data from _data holder
-                this._data.RemoveRange(0, packetLength);
-
-                //  Load SFTP Message and handle it
-                dynamic response = SftpMessage.Load(packetData);
-
-                try
-                {
-                    if (response is SftpVersionResponse)
-                    {
-                        if (response.Version == 3)
-                        {
-                            this._sftpVersionConfirmed.Set();
-                        }
-                        else
-                        {
-                            throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", response.Version));
-                        }
-                    }
-                    else
-                    {
-                        this.HandleResponse(response);
-                    }
-                }
-                catch (Exception exp)
-                {
-                    this.RaiseError(exp);
-                    break;
-                }
-            }
-        }
-
-        private void SendRequest(SftpRequest request)
-        {
-            lock (this._requests)
-            {
-                this._requests.Add(request.RequestId, request);
-            }
-
-            this._session.SendMessage(new SftpDataMessage(this._channel.RemoteChannelNumber, request));
-        }
-
         internal void SendMessage(SftpMessage sftpMessage)
         {
-            this._session.SendMessage(new SftpDataMessage(this._channel.RemoteChannelNumber, sftpMessage));
-        }
-
-        internal void WaitHandle(WaitHandle waitHandle, TimeSpan operationTimeout)
-        {
-            var waitHandles = new WaitHandle[]
-                {
-                    this._errorOccuredWaitHandle,
-                    waitHandle,
-                };
-
-            var index = EventWaitHandle.WaitAny(waitHandles, operationTimeout);
-
-            if (index < 1)
-            {
-                throw this._exception;
-            }
-            else if (index > 1)
-            {
-                //  throw time out error
-                throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Sftp operation has timed out."));
-            }
+            this.SendData(new SftpDataMessage(this.ChannelNumber, sftpMessage));
         }
 
         /// <summary>
@@ -288,6 +161,98 @@ namespace Renci.SshNet.Sftp
             }
         }
 
+        protected override void OnChannelOpen()
+        {
+            this.SendMessage(new SftpInitRequest(3));
+
+            this.WaitHandle(this._sftpVersionConfirmed, this._operationTimeout);
+
+            this.ProtocolVersion = 3;
+
+            //  Resolve current directory
+            this.WorkingDirectory = this.RequestRealPath(".").Keys.First();
+        }
+
+        protected override void OnDataReceived(uint dataTypeCode, byte[] data)
+        {
+            //  Add channel data to internal data holder
+            this._data.AddRange(data);
+
+            while (this._data.Count > 4 + 1)
+            {
+                //  Extract packet length
+                var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]);
+
+                //  Check if complete packet data is available
+                if (this._data.Count < packetLength + 4)
+                {
+                    //  Wait for complete message to arrive first
+                    break;
+                }
+                this._data.RemoveRange(0, 4);
+
+                //  Create buffer to hold packet data
+                var packetData = new byte[packetLength];
+
+                //  Cope packet data to array
+                this._data.CopyTo(0, packetData, 0, packetLength);
+
+                //  Remove loaded data from _data holder
+                this._data.RemoveRange(0, packetLength);
+
+                //  Load SFTP Message and handle it
+                dynamic response = SftpMessage.Load(packetData);
+
+                try
+                {
+                    if (response is SftpVersionResponse)
+                    {
+                        if (response.Version == 3)
+                        {
+                            this._sftpVersionConfirmed.Set();
+                        }
+                        else
+                        {
+                            throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", response.Version));
+                        }
+                    }
+                    else
+                    {
+                        this.HandleResponse(response);
+                    }
+                }
+                catch (Exception exp)
+                {
+                    this.RaiseError(exp);
+                    break;
+                }
+            }
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            base.Dispose(disposing);
+
+            if (disposing)
+            {
+                if (this._sftpVersionConfirmed != null)
+                {
+                    this._sftpVersionConfirmed.Dispose();
+                    this._sftpVersionConfirmed = null;
+                }
+            }
+        }
+
+        private void SendRequest(SftpRequest request)
+        {
+            lock (this._requests)
+            {
+                this._requests.Add(request.RequestId, request);
+            }
+
+            this.SendData(new SftpDataMessage(this.ChannelNumber, request));
+        }
+
         #region SFTP API functions
 
         //#define SSH_FXP_INIT                1
@@ -948,97 +913,5 @@ namespace Renci.SshNet.Sftp
 
             request.Complete(response);
         }
-
-        private void Session_Disconnected(object sender, EventArgs e)
-        {
-            this.RaiseError(new SshException("Connection was lost"));
-        }
-
-        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
-        {
-            this.RaiseError(e.Exception);
-        }
-
-        private void RaiseError(Exception error)
-        {
-            this._exception = error;
-
-            this._errorOccuredWaitHandle.Set();
-
-            if (this.ErrorOccured != null)
-            {
-                this.ErrorOccured(this, new ExceptionEventArgs(error));
-            }
-        }
-
-        #region IDisposable Members
-
-        private bool _isDisposed = false;
-
-        /// <summary>
-        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
-        /// </summary>
-        public void Dispose()
-        {
-            Dispose(true);
-
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and - optionally - managed resources
-        /// </summary>
-        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool disposing)
-        {
-            // Check to see if Dispose has already been called.
-            if (!this._isDisposed)
-            {
-                if (this._channel != null)
-                {
-                    this._channel.DataReceived -= Channel_DataReceived;
-
-                    this._channel.Dispose();
-                    this._channel = null;
-                }
-
-                this._session.ErrorOccured -= Session_ErrorOccured;
-                this._session.Disconnected -= Session_Disconnected;
-
-                // If disposing equals true, dispose all managed
-                // and unmanaged resources.
-                if (disposing)
-                {
-                    // Dispose managed resources.
-                    if (this._errorOccuredWaitHandle != null)
-                    {
-                        this._errorOccuredWaitHandle.Dispose();
-                        this._errorOccuredWaitHandle = null;
-                    }
-                    if (this._sftpVersionConfirmed != null)
-                    {
-                        this._sftpVersionConfirmed.Dispose();
-                        this._sftpVersionConfirmed = null;
-                    }
-                }
-
-                // Note disposing has been done.
-                _isDisposed = true;
-            }
-        }
-
-        /// <summary>
-        /// Releases unmanaged resources and performs other cleanup operations before the
-        /// <see cref="SftpSession"/> is reclaimed by garbage collection.
-        /// </summary>
-        ~SftpSession()
-        {
-            // Do not re-create Dispose clean-up code here.
-            // Calling Dispose(false) is optimal in terms of
-            // readability and maintainability.
-            Dispose(false);
-        }
-
-        #endregion
     }
 }

+ 209 - 0
Renci.SshClient/Renci.SshNet/SubsystemSession.cs

@@ -0,0 +1,209 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Globalization;
+using Renci.SshNet.Sftp.Responses;
+using Renci.SshNet.Sftp.Requests;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Sftp
+{
+    internal abstract class SubsystemSession : IDisposable
+    {
+        private Session _session;
+
+        private string _subsystemName;
+
+        private ChannelSession _channel;
+
+        private Exception _exception;
+
+        private EventWaitHandle _errorOccuredWaitHandle = new AutoResetEvent(false);
+
+        protected TimeSpan _operationTimeout;
+
+        public event EventHandler<ExceptionEventArgs> ErrorOccured;
+
+        protected uint ChannelNumber
+        {
+            get
+            {
+                return this._channel.RemoteChannelNumber;
+            }
+        }
+
+        #region SFTP messages
+
+        //internal event EventHandler<MessageEventArgs<StatusMessage>> StatusMessageReceived;
+
+        //internal event EventHandler<MessageEventArgs<DataMessage>> DataMessageReceived;
+
+        //internal event EventHandler<MessageEventArgs<HandleMessage>> HandleMessageReceived;
+
+        //internal event EventHandler<MessageEventArgs<NameMessage>> NameMessageReceived;
+
+        //internal event EventHandler<MessageEventArgs<AttributesMessage>> AttributesMessageReceived;
+
+        #endregion
+
+        public SubsystemSession(Session session, string subsystemName, TimeSpan operationTimeout)
+        {
+            this._session = session;
+            this._subsystemName = subsystemName;
+            this._operationTimeout = operationTimeout;
+        }
+
+        public void Connect()
+        {
+            this._channel = this._session.CreateChannel<ChannelSession>();
+
+            this._session.ErrorOccured += Session_ErrorOccured;
+            this._session.Disconnected += Session_Disconnected;
+            this._channel.DataReceived += Channel_DataReceived;
+
+            this._channel.Open();
+
+            this._channel.SendSubsystemRequest(_subsystemName);
+
+            this.OnChannelOpen();
+        }
+
+        public void Disconnect()
+        {
+            this.Dispose();
+        }
+
+        //public void SendData(byte[] data)
+        //{
+        //    this._session.SendMessage(new ChannelDataMessage(this._channel.RemoteChannelNumber, data));
+        //}
+
+        public void SendData(ChannelDataMessage message)
+        {
+            this._session.SendMessage(message);
+        }
+
+        protected abstract void OnChannelOpen();
+
+        protected abstract void OnDataReceived(uint dataTypeCode, byte[] data);
+
+        protected void RaiseError(Exception error)
+        {
+            this._exception = error;
+
+            this._errorOccuredWaitHandle.Set();
+
+            if (this.ErrorOccured != null)
+            {
+                this.ErrorOccured(this, new ExceptionEventArgs(error));
+            }
+        }
+
+        private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e)
+        {
+            this.OnDataReceived(e.DataTypeCode, e.Data);
+        }
+
+        internal void WaitHandle(WaitHandle waitHandle, TimeSpan operationTimeout)
+        {
+            var waitHandles = new WaitHandle[]
+                {
+                    this._errorOccuredWaitHandle,
+                    waitHandle,
+                };
+
+            var index = EventWaitHandle.WaitAny(waitHandles, operationTimeout);
+
+            if (index < 1)
+            {
+                throw this._exception;
+            }
+            else if (index > 1)
+            {
+                //  throw time out error
+                throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Sftp operation has timed out."));
+            }
+        }
+
+        private void Session_Disconnected(object sender, EventArgs e)
+        {
+            this.RaiseError(new SshException("Connection was lost"));
+        }
+
+        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
+        {
+            this.RaiseError(e.Exception);
+        }
+
+        #region IDisposable Members
+
+        private bool _isDisposed = false;
+
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and - optionally - managed resources
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            // Check to see if Dispose has already been called.
+            if (!this._isDisposed)
+            {
+                if (this._channel != null)
+                {
+                    this._channel.DataReceived -= Channel_DataReceived;
+
+                    this._channel.Dispose();
+                    this._channel = null;
+                }
+
+                this._session.ErrorOccured -= Session_ErrorOccured;
+                this._session.Disconnected -= Session_Disconnected;
+
+                // If disposing equals true, dispose all managed
+                // and unmanaged resources.
+                if (disposing)
+                {
+                    // Dispose managed resources.
+                    if (this._errorOccuredWaitHandle != null)
+                    {
+                        this._errorOccuredWaitHandle.Dispose();
+                        this._errorOccuredWaitHandle = null;
+                    }
+                }
+
+                // Note disposing has been done.
+                _isDisposed = true;
+            }
+        }
+
+        /// <summary>
+        /// Releases unmanaged resources and performs other cleanup operations before the
+        /// <see cref="SftpSession"/> is reclaimed by garbage collection.
+        /// </summary>
+        ~SubsystemSession()
+        {
+            // Do not re-create Dispose clean-up code here.
+            // Calling Dispose(false) is optimal in terms of
+            // readability and maintainability.
+            Dispose(false);
+        }
+
+        #endregion
+    }
+}