فهرست منبع

Add interface to SftpFile #120 (#812)

* Create ISftpFile interface. SftpFile sealed. Return ISftpFile from SftpClient instead of SftpFile. Make ISftpClient interface disposable.

Co-authored-by: Wojciech Swieboda <wswieboda@chathamfinancial.com>
wxtsxt 4 سال پیش
والد
کامیت
583a9cea6a

+ 3 - 0
src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj

@@ -1358,6 +1358,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
       <Link>Sftp\SftpFile.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
+      <Link>Sftp\ISftpFile.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
       <Link>Sftp\SftpFileAttributes.cs</Link>
     </Compile>

+ 3 - 0
src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj

@@ -1364,6 +1364,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
       <Link>Sftp\SftpFile.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
+      <Link>Sftp\ISftpFile.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
       <Link>Sftp\SftpFileAttributes.cs</Link>
     </Compile>

+ 6 - 6
src/Renci.SshNet.Tests/Classes/SftpClientTest.cs

@@ -562,8 +562,8 @@ namespace Renci.SshNet.Tests.Classes
             ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
             SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
             IAsyncResult asyncResult = null; // TODO: Initialize to an appropriate value
-            IEnumerable<SftpFile> expected = null; // TODO: Initialize to an appropriate value
-            IEnumerable<SftpFile> actual;
+            IEnumerable<ISftpFile> expected = null; // TODO: Initialize to an appropriate value
+            IEnumerable<ISftpFile> actual;
             actual = target.EndListDirectory(asyncResult);
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
@@ -702,8 +702,8 @@ namespace Renci.SshNet.Tests.Classes
             ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
             SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
             string path = string.Empty; // TODO: Initialize to an appropriate value
-            SftpFile expected = null; // TODO: Initialize to an appropriate value
-            SftpFile actual;
+            ISftpFile expected = null; // TODO: Initialize to an appropriate value
+            ISftpFile actual;
             actual = target.Get(path);
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
@@ -802,8 +802,8 @@ namespace Renci.SshNet.Tests.Classes
             SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
             string path = string.Empty; // TODO: Initialize to an appropriate value
             Action<int> listCallback = null; // TODO: Initialize to an appropriate value
-            IEnumerable<SftpFile> expected = null; // TODO: Initialize to an appropriate value
-            IEnumerable<SftpFile> actual;
+            IEnumerable<ISftpFile> expected = null; // TODO: Initialize to an appropriate value
+            IEnumerable<ISftpFile> actual;
             actual = target.ListDirectory(path, listCallback);
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");

+ 3 - 0
src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj

@@ -1440,6 +1440,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
       <Link>Sftp\SftpFile.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
+      <Link>Sftp\ISftpFile.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
       <Link>Sftp\SftpFileAttributes.cs</Link>
     </Compile>

+ 3 - 0
src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj

@@ -1343,6 +1343,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
       <Link>Sftp\SftpFile.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
+      <Link>Sftp\ISftpFile.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
       <Link>Sftp\SftpFileAttributes.cs</Link>
     </Compile>

+ 3 - 0
src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj

@@ -1396,6 +1396,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
       <Link>Sftp\SftpFile.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
+      <Link>Sftp\ISftpFile.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
       <Link>Sftp\SftpFileAttributes.cs</Link>
     </Compile>

+ 5 - 5
src/Renci.SshNet/ISftpClient.cs

@@ -10,7 +10,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public interface ISftpClient
+    public interface ISftpClient : IDisposable
     {
         /// <summary>
         /// Gets or sets the maximum size of the buffer in bytes.
@@ -525,7 +525,7 @@ namespace Renci.SshNet
         /// A list of files.
         /// </returns>
         /// <exception cref="ArgumentException">The <see cref="IAsyncResult"/> object did not come from the corresponding async method on this type.<para>-or-</para><see cref="EndListDirectory(IAsyncResult)"/> was called multiple times with the same <see cref="IAsyncResult"/>.</exception>
-        IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult);
+        IEnumerable<ISftpFile> EndListDirectory(IAsyncResult asyncResult);
 
         /// <summary>
         /// Ends the synchronize directories.
@@ -568,13 +568,13 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>
-        /// A reference to <see cref="SftpFile"/> file object.
+        /// A reference to <see cref="ISftpFile"/> file object.
         /// </returns>
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        SftpFile Get(string path);
+        ISftpFile Get(string path);
 
         /// <summary>
         /// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
@@ -666,7 +666,7 @@ namespace Renci.SshNet
         /// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
         /// <exception cref="SshException">A SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        IEnumerable<SftpFile> ListDirectory(string path, Action<int> listCallback = null);
+        IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);
 
         /// <summary>
         /// Opens a <see cref="SftpFileStream"/> on the specified path with read/write access.

+ 233 - 0
src/Renci.SshNet/Sftp/ISftpFile.cs

@@ -0,0 +1,233 @@
+using System;
+
+namespace Renci.SshNet.Sftp
+{
+    /// <summary>
+    /// Represents SFTP file information
+    /// </summary>
+    public interface ISftpFile
+    {
+        /// <summary>
+        /// Gets the file attributes.
+        /// </summary>
+        SftpFileAttributes Attributes { get; }
+
+        /// <summary>
+        /// Gets the full path of the directory or file.
+        /// </summary>
+        string FullName { get; }
+
+        /// <summary>
+        /// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists. 
+        /// Otherwise, the Name property gets the name of the directory.
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Gets or sets the time the current file or directory was last accessed.
+        /// </summary>
+        /// <value>
+        /// The time that the current file or directory was last accessed.
+        /// </value>
+        DateTime LastAccessTime { get; set; }
+
+        /// <summary>
+        /// Gets or sets the time when the current file or directory was last written to.
+        /// </summary>
+        /// <value>
+        /// The time the current file was last written.
+        /// </value>
+        DateTime LastWriteTime { get; set; }
+
+        /// <summary>
+        /// Gets or sets the time, in coordinated universal time (UTC), the current file or directory was last accessed.
+        /// </summary>
+        /// <value>
+        /// The time that the current file or directory was last accessed.
+        /// </value>
+        DateTime LastAccessTimeUtc { get; set; }
+
+        /// <summary>
+        /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to.
+        /// </summary>
+        /// <value>
+        /// The time the current file was last written.
+        /// </value>
+        DateTime LastWriteTimeUtc { get; set; }
+
+        /// <summary>
+        /// Gets or sets the size, in bytes, of the current file.
+        /// </summary>
+        /// <value>
+        /// The size of the current file in bytes.
+        /// </value>
+        long Length { get; }
+
+        /// <summary>
+        /// Gets or sets file user id.
+        /// </summary>
+        /// <value>
+        /// File user id.
+        /// </value>
+        int UserId { get; set; }
+
+        /// <summary>
+        /// Gets or sets file group id.
+        /// </summary>
+        /// <value>
+        /// File group id.
+        /// </value>
+        int GroupId { get; set; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a socket.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if file represents a socket; otherwise, <c>false</c>.
+        /// </value>
+        bool IsSocket { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a symbolic link.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a symbolic link; otherwise, <c>false</c>.
+        /// </value>
+        bool IsSymbolicLink { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a regular file.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a regular file; otherwise, <c>false</c>.
+        /// </value>
+        bool IsRegularFile { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a block device.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a block device; otherwise, <c>false</c>.
+        /// </value>
+        bool IsBlockDevice { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a directory.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a directory; otherwise, <c>false</c>.
+        /// </value>
+        bool IsDirectory { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a character device.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a character device; otherwise, <c>false</c>.
+        /// </value>
+        bool IsCharacterDevice { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether file represents a named pipe.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if file represents a named pipe; otherwise, <c>false</c>.
+        /// </value>
+        bool IsNamedPipe { get; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the owner can read from this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if owner can read from this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OwnerCanRead { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the owner can write into this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if owner can write into this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OwnerCanWrite { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the owner can execute this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if owner can execute this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OwnerCanExecute { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the group members can read from this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if group members can read from this file; otherwise, <c>false</c>.
+        /// </value>
+        bool GroupCanRead { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the group members can write into this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if group members can write into this file; otherwise, <c>false</c>.
+        /// </value>
+        bool GroupCanWrite { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the group members can execute this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if group members can execute this file; otherwise, <c>false</c>.
+        /// </value>
+        bool GroupCanExecute { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the others can read from this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if others can read from this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OthersCanRead { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the others can write into this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if others can write into this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OthersCanWrite { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the others can execute this file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if others can execute this file; otherwise, <c>false</c>.
+        /// </value>
+        bool OthersCanExecute { get; set; }
+
+        /// <summary>
+        /// Sets file  permissions.
+        /// </summary>
+        /// <param name="mode">The mode.</param>
+        void SetPermissions(short mode);
+
+        /// <summary>
+        /// Permanently deletes a file on remote machine.
+        /// </summary>
+        void Delete();
+
+        /// <summary>
+        /// Moves a specified file to a new location on remote machine, providing the option to specify a new file name.
+        /// </summary>
+        /// <param name="destFileName">The path to move the file to, which can specify a different file name.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="destFileName"/> is <c>null</c>.</exception>
+        void MoveTo(string destFileName);
+
+        /// <summary>
+        /// Updates file status on the server.
+        /// </summary>
+        void UpdateStatus();
+    }
+}

+ 1 - 1
src/Renci.SshNet/Sftp/SftpFile.cs

@@ -7,7 +7,7 @@ namespace Renci.SshNet.Sftp
     /// <summary>
     /// Represents SFTP file information
     /// </summary>
-    public class SftpFile
+    public sealed class SftpFile : ISftpFile
     {
         private readonly ISftpSession _sftpSession;
 

+ 1 - 1
src/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs

@@ -7,7 +7,7 @@ namespace Renci.SshNet.Sftp
     /// <summary>
     /// Encapsulates the results of an asynchronous directory list operation.
     /// </summary>
-    public class SftpListDirectoryAsyncResult : AsyncResult<IEnumerable<SftpFile>>
+    public class SftpListDirectoryAsyncResult : AsyncResult<IEnumerable<ISftpFile>>
     {
         /// <summary>
         /// Gets the number of files read so far.

+ 14 - 9
src/Renci.SshNet/SftpClient.cs

@@ -469,7 +469,7 @@ namespace Renci.SshNet
         /// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
         /// <exception cref="SshException">A SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public IEnumerable<SftpFile> ListDirectory(string path, Action<int> listCallback = null)
+        public IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null)
         {
             CheckDisposed();
 
@@ -526,7 +526,7 @@ namespace Renci.SshNet
         /// A list of files.
         /// </returns>
         /// <exception cref="ArgumentException">The <see cref="IAsyncResult"/> object did not come from the corresponding async method on this type.<para>-or-</para><see cref="EndListDirectory(IAsyncResult)"/> was called multiple times with the same <see cref="IAsyncResult"/>.</exception>
-        public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult)
+        public IEnumerable<ISftpFile> EndListDirectory(IAsyncResult asyncResult)
         {
             var ar = asyncResult as SftpListDirectoryAsyncResult;
 
@@ -542,13 +542,13 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>
-        /// A reference to <see cref="SftpFile"/> file object.
+        /// A reference to <see cref="ISftpFile"/> file object.
         /// </returns>
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public SftpFile Get(string path)
+        public ISftpFile Get(string path)
         {
             CheckDisposed();
 
@@ -1920,7 +1920,7 @@ namespace Renci.SshNet
             #region Existing Files at The Destination
 
             var destFiles = InternalListDirectory(destinationPath, null);
-            var destDict = new Dictionary<string, SftpFile>();
+            var destDict = new Dictionary<string, ISftpFile>();
             foreach (var destFile in destFiles)
             {
                 if (destFile.IsDirectory)
@@ -1986,7 +1986,7 @@ namespace Renci.SshNet
         /// </returns>
         /// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
         /// <exception cref="SshConnectionException">Client not connected.</exception>
-        private IEnumerable<SftpFile> InternalListDirectory(string path, Action<int> listCallback)
+        private IEnumerable<ISftpFile> InternalListDirectory(string path, Action<int> listCallback)
         {
             if (path == null)
                 throw new ArgumentNullException("path");
@@ -2003,14 +2003,19 @@ namespace Renci.SshNet
             if (!basePath.EndsWith("/"))
                 basePath = string.Format("{0}/", fullPath);
 
-            var result = new List<SftpFile>();
+            var result = new List<ISftpFile>();
 
             var files = _sftpSession.RequestReadDir(handle);
 
             while (files != null)
             {
-                result.AddRange(from f in files
-                                select new SftpFile(_sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value));
+                foreach (var f in files)
+                {
+                    result.Add(new SftpFile(
+                        _sftpSession,
+                        string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key),
+                        f.Value));
+                }
 
                 //  Call callback to report number of files read
                 if (listCallback != null)