From 7bf04249e4a79d03343b99e89e2a46f9d9cdd4c1 Mon Sep 17 00:00:00 2001 From: Val Date: Sun, 4 Sep 2016 01:35:14 -0400 Subject: [PATCH] Tested FtpTransfer Bumped the version published on NuGet --- .../FtpFileDescriptor.cs} | 32 +-- .../FtpParseMSDosListStreams.cs | 8 +- .../FtpParseUnixListStreams.cs | 10 +- .../FtpUserCredentials.cs | 9 +- .../IFileListParser.cs | 10 +- .../ITransferClientConfiguration.cs | 54 +++++ .../ITransferFiles.cs} | 10 +- .../Properties/AssemblyInfo.cs | 6 +- .../TransferClient.cs} | 187 +++++++----------- .../TransferClientConfiguration.cs | 58 ++++++ .../vm.Aspects.FtpTransfer.csproj | 11 +- .../NuGet/ExpressionSerialization.nuspec | 2 +- .../Serialization/Properties/AssemblyInfo.cs | 6 +- Aspects/Model/Properties/AssemblyInfo.cs | 6 +- Aspects/NuGet/PublishAspects.cmd | 5 +- Aspects/NuGet/vm.Aspects.nuspec | 8 +- Aspects/Parsers/Properties/AssemblyInfo.cs | 6 +- Aspects/Properties/AssemblyInfo.cs | 6 +- .../Cryptography/Ciphers/KeyedHasher .cs | 4 +- Aspects/Test/TestFtpClient/App.config | 6 +- Aspects/Test/TestFtpClient/Program.cs | 73 +++++-- .../Test/TestFtpClient/TestFtpClient.csproj | 5 +- Aspects/Wcf/Properties/AssemblyInfo.cs | 6 +- .../vm.Aspects.FtpTransfer/CertificateData.cs | 54 ----- .../TransmissionDirection.cs | 23 --- vm.Aspects.sln | 19 +- 26 files changed, 326 insertions(+), 298 deletions(-) rename Aspects/{vm.Aspects.FtpTransfer/FileDescriptor.cs => FtpTransfer/FtpFileDescriptor.cs} (81%) rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/FtpParseMSDosListStreams.cs (94%) rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/FtpParseUnixListStreams.cs (90%) rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/FtpUserCredentials.cs (59%) rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/IFileListParser.cs (70%) create mode 100644 Aspects/FtpTransfer/ITransferClientConfiguration.cs rename Aspects/{vm.Aspects.FtpTransfer/ITransferFile.cs => FtpTransfer/ITransferFiles.cs} (96%) rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/Properties/AssemblyInfo.cs (83%) rename Aspects/{vm.Aspects.FtpTransfer/TransferSite.cs => FtpTransfer/TransferClient.cs} (59%) create mode 100644 Aspects/FtpTransfer/TransferClientConfiguration.cs rename Aspects/{vm.Aspects.FtpTransfer => FtpTransfer}/vm.Aspects.FtpTransfer.csproj (96%) delete mode 100644 Aspects/vm.Aspects.FtpTransfer/CertificateData.cs delete mode 100644 Aspects/vm.Aspects.FtpTransfer/TransmissionDirection.cs diff --git a/Aspects/vm.Aspects.FtpTransfer/FileDescriptor.cs b/Aspects/FtpTransfer/FtpFileDescriptor.cs similarity index 81% rename from Aspects/vm.Aspects.FtpTransfer/FileDescriptor.cs rename to Aspects/FtpTransfer/FtpFileDescriptor.cs index 5b7f1ae..b6c3d82 100644 --- a/Aspects/vm.Aspects.FtpTransfer/FileDescriptor.cs +++ b/Aspects/FtpTransfer/FtpFileDescriptor.cs @@ -6,7 +6,7 @@ namespace vm.Aspects.FtpTransfer /// Represents a file list entry in the list of file (Unix or DOS style) coming from the FTP site. /// /// - public class FileListEntry : IEquatable + public class FtpFileListEntry : IEquatable { /// /// Gets or sets a value indicating whether this instance is folder or a file. @@ -48,13 +48,13 @@ public class FileListEntry : IEquatable /// public string Name { get; set; } - #region IEquatable Members + #region IEquatable Members /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. - public virtual bool Equals(FileListEntry other) + public virtual bool Equals(FtpFileListEntry other) { if (ReferenceEquals(other, null)) return false; @@ -78,7 +78,7 @@ public virtual bool Equals(FileListEntry other) /// The object to compare with the current object. /// if the specified is equal to this instance; otherwise, . public override bool Equals(object obj) - => Equals(obj as FileListEntry); + => Equals(obj as FtpFileListEntry); /// /// Returns a hash code for this instance. @@ -89,45 +89,45 @@ public override int GetHashCode() var hashCode = Constants.HashInitializer; hashCode = Constants.HashMultiplier * hashCode + IsFolder.GetHashCode(); - hashCode = Constants.HashMultiplier * hashCode + AccessRights.GetHashCode(); + hashCode = Constants.HashMultiplier * hashCode + (AccessRights!=null ? AccessRights.GetHashCode() : 0); hashCode = Constants.HashMultiplier * hashCode + Number.GetHashCode(); - hashCode = Constants.HashMultiplier * hashCode + Owner.GetHashCode(); - hashCode = Constants.HashMultiplier * hashCode + Group.GetHashCode(); + hashCode = Constants.HashMultiplier * hashCode + (Owner!=null ? Owner.GetHashCode() : 0); + hashCode = Constants.HashMultiplier * hashCode + (Group!=null ? Group.GetHashCode() : 0); hashCode = Constants.HashMultiplier * hashCode + FileSize.GetHashCode(); hashCode = Constants.HashMultiplier * hashCode + Created.GetHashCode(); - hashCode = Constants.HashMultiplier * hashCode + Name.GetHashCode(); + hashCode = Constants.HashMultiplier * hashCode + (Name!=null ? Name.GetHashCode() : 0); return hashCode; } /// - /// Compares two objects. + /// Compares two objects. /// /// The left operand. /// The right operand. /// - /// true if the objects are considered to be equal (); + /// true if the objects are considered to be equal (); /// otherwise false. /// public static bool operator ==( - FileListEntry left, - FileListEntry right) + FtpFileListEntry left, + FtpFileListEntry right) => ReferenceEquals(left, null) ? ReferenceEquals(right, null) : left.Equals(right); /// - /// Compares two objects. + /// Compares two objects. /// /// The left operand. /// The right operand. /// - /// true if the objects are not considered to be equal (); + /// true if the objects are not considered to be equal (); /// otherwise false. /// public static bool operator !=( - FileListEntry left, - FileListEntry right) + FtpFileListEntry left, + FtpFileListEntry right) => !(left==right); } } diff --git a/Aspects/vm.Aspects.FtpTransfer/FtpParseMSDosListStreams.cs b/Aspects/FtpTransfer/FtpParseMSDosListStreams.cs similarity index 94% rename from Aspects/vm.Aspects.FtpTransfer/FtpParseMSDosListStreams.cs rename to Aspects/FtpTransfer/FtpParseMSDosListStreams.cs index 9a679d0..48f1dab 100644 --- a/Aspects/vm.Aspects.FtpTransfer/FtpParseMSDosListStreams.cs +++ b/Aspects/FtpTransfer/FtpParseMSDosListStreams.cs @@ -32,10 +32,10 @@ public class FtpParseMSDosListStreams : IFileListParser /// /// The list streams. /// Sequence FileListEntry. - public IEnumerable Parse( + public IEnumerable Parse( Stream fileListStream) { - var reader = new StreamReader(fileListStream, Encoding.Unicode); + var reader = new StreamReader(fileListStream, Encoding.ASCII); while (!reader.EndOfStream) { @@ -50,12 +50,12 @@ public IEnumerable Parse( } #endregion - static FileListEntry StreamDescriptorFactory(Match match) + static FtpFileListEntry StreamDescriptorFactory(Match match) { int num; DateTime dt; - return new FileListEntry + return new FtpFileListEntry { IsFolder = !string.IsNullOrWhiteSpace(match.Groups["isDir"].Value), Name = match.Groups["name"].Value, diff --git a/Aspects/vm.Aspects.FtpTransfer/FtpParseUnixListStreams.cs b/Aspects/FtpTransfer/FtpParseUnixListStreams.cs similarity index 90% rename from Aspects/vm.Aspects.FtpTransfer/FtpParseUnixListStreams.cs rename to Aspects/FtpTransfer/FtpParseUnixListStreams.cs index 4f04ef8..f82dc8e 100644 --- a/Aspects/vm.Aspects.FtpTransfer/FtpParseUnixListStreams.cs +++ b/Aspects/FtpTransfer/FtpParseUnixListStreams.cs @@ -27,11 +27,11 @@ public class FtpParseUnixListStreams : IFileListParser /// /// Parses the specified stream returned by the FTP site to the command ls or dir - /// and produces a sequence of -s. + /// and produces a sequence of -s. /// /// The stream whose contents will be parsed to produce the list of entries. - /// A sequence of -s - public IEnumerable Parse( + /// A sequence of -s + public IEnumerable Parse( Stream fileListStream) { var reader = new StreamReader(fileListStream, Encoding.ASCII); @@ -48,12 +48,12 @@ public IEnumerable Parse( } } - static FileListEntry StreamDescriptorFactory(Match match) + static FtpFileListEntry StreamDescriptorFactory(Match match) { int num; DateTime dt; - return new FileListEntry + return new FtpFileListEntry { IsFolder = string.Compare(match.Groups["dir"].Value, "d", StringComparison.OrdinalIgnoreCase) == 0, AccessRights = match.Groups["access"].Value, diff --git a/Aspects/vm.Aspects.FtpTransfer/FtpUserCredentials.cs b/Aspects/FtpTransfer/FtpUserCredentials.cs similarity index 59% rename from Aspects/vm.Aspects.FtpTransfer/FtpUserCredentials.cs rename to Aspects/FtpTransfer/FtpUserCredentials.cs index 5e590a8..d2c043f 100644 --- a/Aspects/vm.Aspects.FtpTransfer/FtpUserCredentials.cs +++ b/Aspects/FtpTransfer/FtpUserCredentials.cs @@ -10,14 +10,7 @@ public class FtpUserCredentials /// /// Needed in case the remote site requires user credentials. /// The user name - public virtual string UserName { get; set; } - - /// - /// The encrypted password part of the credentials if required by the remote site. - /// - /// Needed in case the remote site requires user credentials. - /// The encrypted password. - public virtual string Password64 { get; set; } + public string UserName { get; set; } /// /// Gets the password. diff --git a/Aspects/vm.Aspects.FtpTransfer/IFileListParser.cs b/Aspects/FtpTransfer/IFileListParser.cs similarity index 70% rename from Aspects/vm.Aspects.FtpTransfer/IFileListParser.cs rename to Aspects/FtpTransfer/IFileListParser.cs index c168cff..2233c1f 100644 --- a/Aspects/vm.Aspects.FtpTransfer/IFileListParser.cs +++ b/Aspects/FtpTransfer/IFileListParser.cs @@ -13,21 +13,21 @@ public interface IFileListParser { /// /// Parses the specified stream returned by the FTP site to the command ls or dir - /// and produces a sequence of -s. + /// and produces a sequence of -s. /// /// The stream whose contents will be parsed to produce the list of entries. - /// A sequence of -s - IEnumerable Parse(Stream fileListStream); + /// A sequence of -s + IEnumerable Parse(Stream fileListStream); } #region IFileListParser contracts [ContractClassFor(typeof(IFileListParser))] abstract class IFileListParserContract : IFileListParser { - public IEnumerable Parse(Stream fileListStream) + public IEnumerable Parse(Stream fileListStream) { Contract.Requires(fileListStream != null, nameof(fileListStream)); - Contract.Ensures(Contract.Result>() != null); + Contract.Ensures(Contract.Result>() != null); throw new NotImplementedException(); } diff --git a/Aspects/FtpTransfer/ITransferClientConfiguration.cs b/Aspects/FtpTransfer/ITransferClientConfiguration.cs new file mode 100644 index 0000000..5265d84 --- /dev/null +++ b/Aspects/FtpTransfer/ITransferClientConfiguration.cs @@ -0,0 +1,54 @@ +using System; + +namespace vm.Aspects.FtpTransfer +{ + /// + /// Represents the configuration properties of the FTP transfer site objects represented by . + /// Note that the interface inherits from which should be used by the transfer object to clone the properties internally for multiple use. + /// + /// + public interface ITransferClientConfiguration : ICloneable + { + #region Properties + /// + /// URL string of the target. + /// + /// This is the URL string of the resource to be downloaded (from) or uploaded (to) + /// The URL of the resource. + string Link { get; } + + /// + /// Gets or sets a value that specifies that an SSL connection should be used. + /// + /// + /// true if SSL is enabled; otherwise, false. + /// + bool EnableSsl { get; } + + /// + /// Gets or sets a value which specifies the data type for file transfers. + /// + bool UseBinary { get; } + + /// + /// Gets or sets a value indicating whether the control connection to the FTP server is closed after the request completes. + /// + /// + /// true if keep-alive-s are enabled; otherwise, false. + /// + bool KeepAlive { get; } + + /// + /// The user name part of the credentials if required by the remote site. + /// + /// Needed in case the remote site requires user credentials. + /// The user name + string UserName { get; } + + /// + /// Gets the password. + /// + string Password { get; } + #endregion + } +} diff --git a/Aspects/vm.Aspects.FtpTransfer/ITransferFile.cs b/Aspects/FtpTransfer/ITransferFiles.cs similarity index 96% rename from Aspects/vm.Aspects.FtpTransfer/ITransferFile.cs rename to Aspects/FtpTransfer/ITransferFiles.cs index c783e0a..3b51367 100644 --- a/Aspects/vm.Aspects.FtpTransfer/ITransferFile.cs +++ b/Aspects/FtpTransfer/ITransferFiles.cs @@ -9,10 +9,9 @@ namespace vm.Aspects.FtpTransfer /// /// Abstracts a object that can list download and upload files (e.g. from an FTP site). /// - [ContractClass(typeof(ITransferFileContract))] - public interface ITransferFile + [ContractClass(typeof(ITransferFilesContract))] + public interface ITransferFiles { - /// /// Lists the files available for receiving at the target (something like dir or ls). /// @@ -35,7 +34,6 @@ public interface ITransferFile // ----------------------------------------- - /// /// Asynchronously lists the files available for receiving at the target (something like dir or ls). /// @@ -59,8 +57,8 @@ public interface ITransferFile } #region ITransferFile contract binding - [ContractClassFor(typeof(ITransferFile))] - abstract class ITransferFileContract : ITransferFile + [ContractClassFor(typeof(ITransferFiles))] + abstract class ITransferFilesContract : ITransferFiles { public Stream DownloadFile(string name) { diff --git a/Aspects/vm.Aspects.FtpTransfer/Properties/AssemblyInfo.cs b/Aspects/FtpTransfer/Properties/AssemblyInfo.cs similarity index 83% rename from Aspects/vm.Aspects.FtpTransfer/Properties/AssemblyInfo.cs rename to Aspects/FtpTransfer/Properties/AssemblyInfo.cs index bbd6423..c474810 100644 --- a/Aspects/vm.Aspects.FtpTransfer/Properties/AssemblyInfo.cs +++ b/Aspects/FtpTransfer/Properties/AssemblyInfo.cs @@ -2,9 +2,9 @@ [assembly: AssemblyTitle("vm.Aspects.FtpTransfer")] [assembly: AssemblyDescription("A set of classes for FTP file transfer.")] -[assembly: AssemblyVersion("1.0.67")] -[assembly: AssemblyFileVersion("1.0.67")] -[assembly: AssemblyInformationalVersion("1.0.67")] +[assembly: AssemblyVersion("1.0.68")] +[assembly: AssemblyFileVersion("1.0.68")] +[assembly: AssemblyInformationalVersion("1.0.68")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo( "vm.Aspects.FtpTransfer.Test, " + diff --git a/Aspects/vm.Aspects.FtpTransfer/TransferSite.cs b/Aspects/FtpTransfer/TransferClient.cs similarity index 59% rename from Aspects/vm.Aspects.FtpTransfer/TransferSite.cs rename to Aspects/FtpTransfer/TransferClient.cs index 10fd006..43515b8 100644 --- a/Aspects/vm.Aspects.FtpTransfer/TransferSite.cs +++ b/Aspects/FtpTransfer/TransferClient.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.Contracts; using System.IO; using System.Net; using System.Net.Security; @@ -14,121 +15,45 @@ namespace vm.Aspects.FtpTransfer /// /// Represents an FTP site. /// - /// - public class TransferSite : ITransferFile + /// + public class TransferClient : ITransferFiles { #region Properties /// - /// URL string of the target. + /// Gets the FTP configuration. /// - /// This is the URL string of the resource to be downloaded (from) or uploaded (to) - /// The URL of the resource. - public string Link { get; set; } + ITransferClientConfiguration Configuration { get; } /// - /// Gets or sets the enum value of the property TransmissionDirection. + /// Gets the client certificate identifying this owner to the FTP server. /// - public TransmissionDirection TransmissionDirection { get; set; } - - /// - /// Gets or sets the status code from the last transfer operation. - /// - /// - /// The status code. - /// - public int StatusCode { get; set; } - - /// - /// Gets or sets the status description from the FTP operation. - /// - /// - /// The status code. - /// - public string StatusDescription { get; set; } - - /// - /// Gets or sets the name of the list parser for this (inbound) site which will parse the text list of available streams for download to a list of objects. - /// - /// - /// The name of the list parser. - /// - public string ListParser { get; set; } - - /// - /// Gets or sets the credentials which might be needed at the target site. - /// - /// - /// The credentials. - /// - public FtpUserCredentials Credentials { get; set; } - - /// - /// Gets or sets the data by which to find the corresponding client certificate in the Windows certificate stores. - /// - /// - /// The certificate data. - /// - public CertificateData ClientCertificate { get; set; } - - /// - /// Gets or sets a value indicating whether the control connection to the FTP server is closed after the request completes. - /// - /// - /// true if keep-alive-s are enabled; otherwise, false. - /// - public bool KeepAlive { get; set; } - - /// - /// Gets or sets a value that specifies that an SSL connection should be used. - /// - /// - /// true if SSL is enabled; otherwise, false. - /// - public bool EnableSsl { get; set; } - - /// - /// Gets or sets a value which specifies the data type for file transfers. - /// - public bool UseBinary { get; set; } + X509Certificate2 ClientCertificate { get; } #endregion /// - /// Callback to validate a server certificate. + /// Initializes a new instance of the class. /// - /// - /// The default implementation here allows for self-signed certificate within the intranet and - /// the local computer otherwise it fails if the server certificate is not kosher. - /// - bool ServerCertificateValidation( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors) + /// The FTP client configuration. + /// The FTP client certificate. + public TransferClient( + ITransferClientConfiguration configuration, + X509Certificate2 clientCertificate = null) { - if (sslPolicyErrors == SslPolicyErrors.None) - return true; - - if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || - sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) - { - // allow self-signed certificates - Zone z = Zone.CreateFromUrl(((WebRequest)sender).RequestUri.ToString()); + Contract.Requires(configuration != null, nameof(configuration)); - if (z.SecurityZone == SecurityZone.Intranet || - z.SecurityZone == SecurityZone.MyComputer) - return true; - } - - return false; + Configuration = (ITransferClientConfiguration)configuration.Clone(); + ClientCertificate = clientCertificate; } + // --------------------------------------------------------------------------- + /// /// Lists the files available for receiving at the target (something like dir). /// /// The stream that will receive the files list text. The interpretation of the list text is up to the consumer. public Stream ListFiles() { - var request = PrepareFileRequest(WebRequestMethods.Ftp.ListDirectoryDetails, null); + var request = PrepareFileRequest(WebRequestMethods.Ftp.ListDirectoryDetails, null); var response = request.GetResponse(); return response.GetResponseStream(); @@ -141,7 +66,7 @@ public Stream ListFiles() /// Task{Stream}. public Stream DownloadFile(string name) { - var request = PrepareFileRequest(WebRequestMethods.Ftp.DownloadFile, name); + var request = PrepareFileRequest(WebRequestMethods.Ftp.DownloadFile, name); var response = request.GetResponse(); return response.GetResponseStream(); @@ -156,9 +81,12 @@ public Stream DownloadFile(string name) public void UploadFile(Stream stream, string name) { var request = PrepareFileRequest(WebRequestMethods.Ftp.UploadFile, name); - var targetStream = request.GetRequestStream(); - stream.CopyTo(targetStream); + using (var requestStream = request.GetRequestStream()) + stream.CopyTo(requestStream); + + using (var response = request.GetResponse()) + response.Close(); } // --------------------------------------------------------------------------- @@ -169,7 +97,7 @@ public void UploadFile(Stream stream, string name) /// The stream that will receive the files list text. The interpretation of the list text is up to the consumer. public async Task ListFilesAsync() { - var request = PrepareFileRequest(WebRequestMethods.Ftp.ListDirectoryDetails, null); + var request = PrepareFileRequest(WebRequestMethods.Ftp.ListDirectoryDetails, null); var response = await request.GetResponseAsync(); return response.GetResponseStream(); @@ -182,7 +110,7 @@ public async Task ListFilesAsync() /// Task{Stream}. public async Task DownloadFileAsync(string name) { - var request = PrepareFileRequest(WebRequestMethods.Ftp.DownloadFile, name); + var request = PrepareFileRequest(WebRequestMethods.Ftp.DownloadFile, name); var response = await request.GetResponseAsync(); return response.GetResponseStream(); @@ -197,11 +125,15 @@ public async Task DownloadFileAsync(string name) public async Task UploadFileAsync(Stream stream, string name) { var request = PrepareFileRequest(WebRequestMethods.Ftp.UploadFile, name); - var targetStream = await request.GetRequestStreamAsync(); - await stream.CopyToAsync(targetStream); + using (var requestStream = await request.GetRequestStreamAsync()) + await stream.CopyToAsync(requestStream); + + using (var response = await request.GetResponseAsync()) + response.Close(); } + // --------------------------------------------------------------------------- /// /// Prepares the FTP request. After calling this the only thing that will be needed will be the FTP method. @@ -215,7 +147,7 @@ FtpWebRequest PrepareFileRequest( string method, string fileName) { - var fileUrl = Link; + var fileUrl = Configuration.Link; if (!string.IsNullOrWhiteSpace(fileName)) { @@ -236,25 +168,52 @@ FtpWebRequest PrepareFileRequest( Debug.Assert(request != null); request.Method = method; - request.UseBinary = UseBinary; - request.KeepAlive = KeepAlive; - request.EnableSsl = EnableSsl; + request.UseBinary = Configuration.UseBinary; + request.KeepAlive = Configuration.KeepAlive; + request.EnableSsl = Configuration.EnableSsl; - if (EnableSsl) + if (Configuration.EnableSsl) ServicePointManager.ServerCertificateValidationCallback = ServerCertificateValidation; - if (Credentials != null && - !string.IsNullOrWhiteSpace(Credentials.UserName)) + if (!string.IsNullOrWhiteSpace(Configuration.UserName)) request.Credentials = new NetworkCredential( - Credentials.UserName, - Credentials.Password); - - var clientCertificate = ClientCertificate?.Certificate; + Configuration.UserName, + Configuration.Password); - if (clientCertificate != null) - request.ClientCertificates.Add(clientCertificate); + if (ClientCertificate != null) + request.ClientCertificates.Add(ClientCertificate); return request; } + + /// + /// Callback to validate a server certificate. + /// + /// + /// The default implementation here allows for self-signed certificate within the intranet and + /// the local computer otherwise it fails if the server certificate is not kosher. + /// + bool ServerCertificateValidation( + object sender, + X509Certificate serverCertificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + if (sslPolicyErrors == SslPolicyErrors.None) + return true; + + if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || + sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) + { + // allow self-signed certificates + var z = Zone.CreateFromUrl(((WebRequest)sender).RequestUri.ToString()); + + if (z.SecurityZone == SecurityZone.Intranet || + z.SecurityZone == SecurityZone.MyComputer) + return true; + } + + return false; + } } } diff --git a/Aspects/FtpTransfer/TransferClientConfiguration.cs b/Aspects/FtpTransfer/TransferClientConfiguration.cs new file mode 100644 index 0000000..fa93854 --- /dev/null +++ b/Aspects/FtpTransfer/TransferClientConfiguration.cs @@ -0,0 +1,58 @@ +using System; + +namespace vm.Aspects.FtpTransfer +{ + /// + /// Class TransferClientConfiguration. + /// + /// + public class TransferClientConfiguration : ITransferClientConfiguration + { + /// + /// URL string of the target. + /// + /// This is the URL string of the resource to be downloaded (from) or uploaded (to) + public string Link { get; set; } + + /// + /// Gets or sets a value that specifies that an SSL connection should be used. + /// + public bool EnableSsl { get; set; } + + /// + /// Gets or sets a value which specifies the data type for file transfers. + /// + public bool UseBinary { get; set; } + + /// + /// Gets or sets a value indicating whether the control connection to the FTP server is closed after the request completes. + /// + public bool KeepAlive { get; set; } + + /// + /// The user name part of the credentials if required by the remote site. + /// + /// Needed in case the remote site requires user credentials. + public string UserName { get; set; } + + /// + /// Gets the password. + /// + public string Password { get; set; } + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// A new object that is a copy of this instance. + public object Clone() + => new TransferClientConfiguration + { + Link = Link, + EnableSsl = EnableSsl, + UseBinary = UseBinary, + KeepAlive = KeepAlive, + UserName = UserName, + Password = Password, + }; + } +} diff --git a/Aspects/vm.Aspects.FtpTransfer/vm.Aspects.FtpTransfer.csproj b/Aspects/FtpTransfer/vm.Aspects.FtpTransfer.csproj similarity index 96% rename from Aspects/vm.Aspects.FtpTransfer/vm.Aspects.FtpTransfer.csproj rename to Aspects/FtpTransfer/vm.Aspects.FtpTransfer.csproj index 03cb21a..3f17d75 100644 --- a/Aspects/vm.Aspects.FtpTransfer/vm.Aspects.FtpTransfer.csproj +++ b/Aspects/FtpTransfer/vm.Aspects.FtpTransfer.csproj @@ -119,6 +119,7 @@ ReleaseRequires %28none%29 0 + bin\Release\vm.Aspects.FtpTransfer.XML true @@ -140,16 +141,16 @@ Properties\AssemblyInfo.global.cs - - + - + + - - + + diff --git a/Aspects/Linq/Expressions/Serialization/NuGet/ExpressionSerialization.nuspec b/Aspects/Linq/Expressions/Serialization/NuGet/ExpressionSerialization.nuspec index 3ee64c0..c22cd0d 100644 --- a/Aspects/Linq/Expressions/Serialization/NuGet/ExpressionSerialization.nuspec +++ b/Aspects/Linq/Expressions/Serialization/NuGet/ExpressionSerialization.nuspec @@ -2,7 +2,7 @@ AspectExpressionSerialization - 1.0.67 + 1.0.68 Val Melamed Val Melamed diff --git a/Aspects/Linq/Expressions/Serialization/Properties/AssemblyInfo.cs b/Aspects/Linq/Expressions/Serialization/Properties/AssemblyInfo.cs index e4a7f59..2a8a267 100644 --- a/Aspects/Linq/Expressions/Serialization/Properties/AssemblyInfo.cs +++ b/Aspects/Linq/Expressions/Serialization/Properties/AssemblyInfo.cs @@ -4,9 +4,9 @@ [assembly: AssemblyTitle("vm.Aspects.Linq.Expressions.Serialization")] [assembly: AssemblyDescription("Serializes and deserializes LINQ expression trees to and from XML documents.")] -[assembly: AssemblyVersion("1.0.67")] -[assembly: AssemblyFileVersion("1.0.67")] -[assembly: AssemblyInformationalVersion("1.0.67")] +[assembly: AssemblyVersion("1.0.68")] +[assembly: AssemblyFileVersion("1.0.68")] +[assembly: AssemblyInformationalVersion("1.0.68")] [assembly: InternalsVisibleTo( "vm.Aspects.Linq.Expressions.Serialization.Test, " + diff --git a/Aspects/Model/Properties/AssemblyInfo.cs b/Aspects/Model/Properties/AssemblyInfo.cs index e2ef9ed..1a9f0bf 100644 --- a/Aspects/Model/Properties/AssemblyInfo.cs +++ b/Aspects/Model/Properties/AssemblyInfo.cs @@ -3,9 +3,9 @@ [assembly: AssemblyTitle("vm.Aspect.Model")] [assembly: AssemblyDescription("Defines the IRepository and related base classes and utilities - a framework of building domain object model.")] -[assembly: AssemblyVersion("1.0.67")] -[assembly: AssemblyFileVersion("1.0.67")] -[assembly: AssemblyInformationalVersion("1.0.67")] +[assembly: AssemblyVersion("1.0.68")] +[assembly: AssemblyFileVersion("1.0.68")] +[assembly: AssemblyInformationalVersion("1.0.68")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo( "vm.Aspects.Model.Tests, " + diff --git a/Aspects/NuGet/PublishAspects.cmd b/Aspects/NuGet/PublishAspects.cmd index d2943b8..ff2dbf5 100644 --- a/Aspects/NuGet/PublishAspects.cmd +++ b/Aspects/NuGet/PublishAspects.cmd @@ -1,6 +1,6 @@ if "%VSINSTALLDIR%"=="" call "%VS140COMNTOOLS%vsvars32.bat" set Configuration=Release -set vmAspectsVersion=1.0.67-beta +set vmAspectsVersion=1.0.68-beta pushd cd %~dp0.. @@ -21,6 +21,9 @@ if errorlevel 1 goto exit cd ..\Parsers msbuild vm.Aspects.Parsers.csproj %commonBuildOptions% if errorlevel 1 goto exit +cd ..\FtpTransfer +msbuild vm.Aspects.FtpTransfer.csproj %commonBuildOptions% +if errorlevel 1 goto exit cd ..\Wcf msbuild vm.Aspects.Wcf.csproj %commonBuildOptions% if errorlevel 1 goto exit diff --git a/Aspects/NuGet/vm.Aspects.nuspec b/Aspects/NuGet/vm.Aspects.nuspec index e3de352..2c704f4 100644 --- a/Aspects/NuGet/vm.Aspects.nuspec +++ b/Aspects/NuGet/vm.Aspects.nuspec @@ -2,7 +2,7 @@ vm.Aspects - 1.0.67-beta + 1.0.68-beta Val Melamed Val Melamed @@ -12,7 +12,7 @@ A set of classes, utilities, etc. addressing various common cross-cutting concerns or extending existing similar libraries like Enterprise Library, Unity, etc. - * Targeting the .NET 4.5.2 + * Added a project with a few classes for FTP transfer. https://github.com/vmelamed/vm/blob/master/LICENSE https://github.com/vmelamed/vm/tree/master/Aspects @@ -68,6 +68,10 @@ target="lib\net452"/> + + public virtual IHasherAsync ReleaseCertificate() { - Contract.Ensures(Contract.Result() != null); + Contract.Ensures(Contract.Result() != null); if (_publicKey == null) return this; @@ -593,7 +593,7 @@ public virtual IHasherAsync ReleaseCertificate() [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The caller owns it.")] public virtual IHasherAsync CloneLightHasher() { - Contract.Ensures(Contract.Result() != null); + Contract.Ensures(Contract.Result() != null); InitializeHashKey(); diff --git a/Aspects/Test/TestFtpClient/App.config b/Aspects/Test/TestFtpClient/App.config index 731f6de..8227adb 100644 --- a/Aspects/Test/TestFtpClient/App.config +++ b/Aspects/Test/TestFtpClient/App.config @@ -1,6 +1,6 @@ - + - + - \ No newline at end of file + diff --git a/Aspects/Test/TestFtpClient/Program.cs b/Aspects/Test/TestFtpClient/Program.cs index fb1c317..63b9878 100644 --- a/Aspects/Test/TestFtpClient/Program.cs +++ b/Aspects/Test/TestFtpClient/Program.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; using System.IO; -using System.Security.Cryptography.X509Certificates; +using System.Linq; using vm.Aspects; using vm.Aspects.FtpTransfer; @@ -13,28 +13,61 @@ static void Main(string[] args) { try { - var site = new TransferSite + var config = new TransferClientConfiguration { - Link = "ftp://ftp.testalmond.net", - EnableSsl = true, - UseBinary = true, - TransmissionDirection = TransmissionDirection.Inbound, - ClientCertificate = new CertificateData - { - StoreLocation = StoreLocation.LocalMachine, - StoreName = StoreName.My, - FindBy = X509FindType.FindByThumbprint, - FindByValue = "8114d495c16247dac51c586af91eb0d42c2d1ccf", - }, - Credentials = new FtpUserCredentials - { - UserName = "val", - Password = "Almond123" - }, + EnableSsl = true, + UseBinary = true, + UserName = "user", + Password = "password", }; - using (var rdr = new StreamReader(site.ListFiles())) - Console.WriteLine(rdr.ReadToEnd()); + config.Link = "ftp://localhost/outgoing"; + + var site1 = new TransferClient(config); + + config.Link = "ftp://localhost/incoming"; + + var site2 = new TransferClient(config); + + var files = new FtpParseMSDosListStreams() + .Parse(site1.ListFiles()) + .Take(4) + .ToList() + ; + var file1 = files[0]; + var file2 = files[1]; + var file3 = files[2]; + var file4 = files[3]; + + // download to file from site1 to a local file and then upload it to site2 + using (var fileStream = new FileStream(file1.Name, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var outStream = site1.DownloadFile(file1.Name)) + outStream.CopyTo(fileStream); + + using (var fileStream = new FileStream(file1.Name, FileMode.Open, FileAccess.Read, FileShare.None)) + site2.UploadFile(fileStream, file1.Name); + + // ---------------------------------------------------------- + + // download from site1 and then upload it to site2 w/o intermediate local file + using (var outStream = site1.DownloadFile(file2.Name)) + site2.UploadFile(outStream, file2.Name); + + // ---------------------------------------------------------- + + // async download to file from site1 to a local file and then async upload it to site2 + using (var fileStream = new FileStream(file3.Name, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var outStream = site1.DownloadFileAsync(file3.Name).Result) + outStream.CopyTo(fileStream); + + using (var fileStream = new FileStream(file3.Name, FileMode.Open, FileAccess.Read, FileShare.None)) + site2.UploadFileAsync(fileStream, file3.Name).GetAwaiter().GetResult(); + + // ---------------------------------------------------------- + + // download from site1 and then upload it to site2 w/o intermediate local file + using (var outStream = site1.DownloadFileAsync(file4.Name).Result) + site2.UploadFileAsync(outStream, file4.Name).GetAwaiter().GetResult(); } catch (Exception x) { diff --git a/Aspects/Test/TestFtpClient/TestFtpClient.csproj b/Aspects/Test/TestFtpClient/TestFtpClient.csproj index 937410e..a06a429 100644 --- a/Aspects/Test/TestFtpClient/TestFtpClient.csproj +++ b/Aspects/Test/TestFtpClient/TestFtpClient.csproj @@ -9,9 +9,10 @@ Properties TestFtpClient TestFtpClient - v4.6.1 + v4.5.2 512 true + AnyCPU @@ -54,7 +55,7 @@ {372A2FEF-C004-498C-B57F-CF0ACA66C37B} vm.Aspects.Diagnostics.ObjectDumper - + {113ce397-4859-4b92-8f10-e5ba2d6f1a35} vm.Aspects.FtpTransfer diff --git a/Aspects/Wcf/Properties/AssemblyInfo.cs b/Aspects/Wcf/Properties/AssemblyInfo.cs index 8c4a8c5..49bb978 100644 --- a/Aspects/Wcf/Properties/AssemblyInfo.cs +++ b/Aspects/Wcf/Properties/AssemblyInfo.cs @@ -3,9 +3,9 @@ [assembly: AssemblyTitle("Wcf")] [assembly: AssemblyDescription("A set of classes and generics simplifying the initial configuration work of creating WCF services.")] -[assembly: AssemblyVersion("1.0.67")] -[assembly: AssemblyFileVersion("1.0.67")] -[assembly: AssemblyInformationalVersion("1.0.67")] +[assembly: AssemblyVersion("1.0.68")] +[assembly: AssemblyFileVersion("1.0.68")] +[assembly: AssemblyInformationalVersion("1.0.68")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo( "vm.Aspects.Wcf.Test, " + diff --git a/Aspects/vm.Aspects.FtpTransfer/CertificateData.cs b/Aspects/vm.Aspects.FtpTransfer/CertificateData.cs deleted file mode 100644 index 5afbdfb..0000000 --- a/Aspects/vm.Aspects.FtpTransfer/CertificateData.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Security.Cryptography.X509Certificates; -using vm.Aspects.Security; - -namespace vm.Aspects.FtpTransfer -{ - /// - /// Encapsulates the data by which to find a certificate in the Windows certificate stores. - /// - public class CertificateData - { - /// - /// In case the remote site uses client certificates this property identifies the store location where the client certificate is. - /// - /// The store location where the store with the client certificate should be found. - public StoreLocation StoreLocation { get; set; } - - /// - /// In case the remote site uses client certificates this property identifies the store where the client certificate is. - /// - /// The store where the client certificate should be found. - public StoreName StoreName { get; set; } - - /// - /// Gets or sets the part by which the certificate can be found in the store. - /// - /// - /// The field by which the certificate can be found in the store. - /// - /// - /// When extracting the certificate the property always combines this criterion with - /// and the current time, so specifying this value as well as the other time related criteria - /// does not make sense. - /// - public X509FindType FindBy { get; set; } - - /// - /// Gets or sets the certificate field value by which it can be found in the store. - /// - /// - /// The certificate field value by which it can be found in the store. - /// - public string FindByValue { get; set; } - - /// - /// Extracts the first 509 certificate from the Windows certificate stores which matches the and combined with - /// and the current date and time. - /// - /// - /// The 509 certificate or null if the instance does not specify certificate, e.g. all fields are null. - /// - public X509Certificate2 Certificate - => CertificateFactory.GetCertificate(StoreLocation, StoreName, FindBy, FindByValue); - } -} diff --git a/Aspects/vm.Aspects.FtpTransfer/TransmissionDirection.cs b/Aspects/vm.Aspects.FtpTransfer/TransmissionDirection.cs deleted file mode 100644 index 0527a2c..0000000 --- a/Aspects/vm.Aspects.FtpTransfer/TransmissionDirection.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace vm.Aspects.FtpTransfer -{ - /// - /// Specifies the file transfer direction. - /// - public enum TransmissionDirection - { - /// - /// Unknown, uninitialized direction - /// - None, - - /// - /// The file transfer is from the remote target to the intranet consumers. - /// - Inbound, - - /// - /// The file transfer is from the intranet producers to the remote target. - /// - Outbound, - } -} diff --git a/vm.Aspects.sln b/vm.Aspects.sln index 5f21635..3573645 100644 --- a/vm.Aspects.sln +++ b/vm.Aspects.sln @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject Common Items\AssemblyInfo.global.cs = Common Items\AssemblyInfo.global.cs Common Items\Dictionary.xml = Common Items\Dictionary.xml + Aspects\FtpTransfer\vm.Aspects.FtpTransfer.csproj = Aspects\FtpTransfer\vm.Aspects.FtpTransfer.csproj Common Items\vm.snk = Common Items\vm.snk Common Items\vm.Test.snk = Common Items\vm.Test.snk Common Items\vmAllRules.ruleset = Common Items\vmAllRules.ruleset @@ -66,10 +67,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileCrypt", "Aspects\Securi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vm.Aspects.Wcf.Tests", "Aspects\Wcf\Tests\vm.Aspects.Wcf.Tests.csproj", "{9F87B835-8270-42F9-9567-26B2C7489C2B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vm.Aspects.FtpTransfer", "Aspects\vm.Aspects.FtpTransfer\vm.Aspects.FtpTransfer.csproj", "{113CE397-4859-4B92-8F10-E5BA2D6F1A35}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestFtpClient", "Aspects\Test\TestFtpClient\TestFtpClient.csproj", "{73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vm.Aspects.FtpTransfer", "Aspects\FtpTransfer\vm.Aspects.FtpTransfer.csproj", "{113CE397-4859-4B92-8F10-E5BA2D6F1A35}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -191,18 +192,18 @@ Global {9F87B835-8270-42F9-9567-26B2C7489C2B}.DebugUnitTest|Any CPU.Build.0 = Debug|Any CPU {9F87B835-8270-42F9-9567-26B2C7489C2B}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F87B835-8270-42F9-9567-26B2C7489C2B}.Release|Any CPU.Build.0 = Release|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.DebugUnitTest|Any CPU.ActiveCfg = Debug|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.DebugUnitTest|Any CPU.Build.0 = Debug|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Release|Any CPU.Build.0 = Release|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.Debug|Any CPU.Build.0 = Debug|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.DebugUnitTest|Any CPU.ActiveCfg = Debug|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.DebugUnitTest|Any CPU.Build.0 = Debug|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.Release|Any CPU.ActiveCfg = Release|Any CPU {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D}.Release|Any CPU.Build.0 = Release|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.DebugUnitTest|Any CPU.ActiveCfg = Debug|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.DebugUnitTest|Any CPU.Build.0 = Debug|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {113CE397-4859-4B92-8F10-E5BA2D6F1A35}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -223,7 +224,7 @@ Global {73B4C51D-31EE-4DBC-B7E9-FA13FF817F1D} = {B2E493FE-EFCF-4D0B-84C5-5C75B0D361B8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - EnterpriseLibraryConfigurationToolBinariesPathV6 = packages\EnterpriseLibrary.Common.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Logging.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Validation.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.Logging.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.PolicyInjection.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.WCF.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Validation.Integration.WCF.6.0.1304.0\lib\NET45 EnterpriseLibraryConfigurationToolBinariesPath = packages\EnterpriseLibrary.Common.5.0.505.0\lib\NET35;packages\EnterpriseLibrary.ExceptionHandling.5.0.505.0\lib\NET35;packages\EnterpriseLibrary.Logging.5.0.505.1\lib\NET35;packages\EnterpriseLibrary.ExceptionHandling.Logging.5.0.505.0\lib\NET35;packages\EnterpriseLibrary.PolicyInjection.5.0.505.0\lib\NET35;packages\EnterpriseLibrary.Validation.5.0.505.0\lib\NET35 + EnterpriseLibraryConfigurationToolBinariesPathV6 = packages\EnterpriseLibrary.Common.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Logging.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Validation.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.Logging.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.PolicyInjection.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.ExceptionHandling.WCF.6.0.1304.0\lib\NET45;packages\EnterpriseLibrary.Validation.Integration.WCF.6.0.1304.0\lib\NET45 EndGlobalSection EndGlobal