From 8ac5891d46f00d10af7010c220402ced878f805f Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:53:09 +0100 Subject: [PATCH 1/6] feat: Added CopyFilter --- Copy/Types/CopyFilter.cs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Copy/Types/CopyFilter.cs diff --git a/Copy/Types/CopyFilter.cs b/Copy/Types/CopyFilter.cs new file mode 100644 index 0000000..4dc25a1 --- /dev/null +++ b/Copy/Types/CopyFilter.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Copy.Types +{ + /// + /// Filter for files to copy. + /// + public class CopyFilter + { + /// + /// Pattern the name of the files must match. + /// + [JsonProperty(PropertyName = "Name", Required = Required.DisallowNull)] + public string Name { get; set; } = ".*"; + /// + /// Pattern the author of the files must match. + /// + [JsonProperty(PropertyName = "Author", Required = Required.DisallowNull)] + public string Author { get; set; } = ".*"; + /// + /// Date the files must be created after. + /// + [JsonProperty(PropertyName = "CreatedAfter", Required = Required.DisallowNull)] + public DateTime CreatedAfter { get; set; } = DateTime.MinValue; + /// + /// Size the files must not exceed. + /// + [JsonProperty(PropertyName = "MaxSize", Required = Required.DisallowNull)] + public ulong MaxSize { get; set; } = ulong.MaxValue; + /// + /// Size the files must exceed. + /// + [JsonProperty(PropertyName = "MinSize", Required = Required.DisallowNull)] + public ulong MinSize { get; set; } = 0; + } +} From 3c433e44e8f4265cb23635d75be005125441fb64 Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:59:33 +0100 Subject: [PATCH 2/6] feat(CopyTask.cs): Changed type of Filter property --- Copy/Types/CopyTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Copy/Types/CopyTask.cs b/Copy/Types/CopyTask.cs index 75cb2e9..b95ced9 100644 --- a/Copy/Types/CopyTask.cs +++ b/Copy/Types/CopyTask.cs @@ -31,6 +31,6 @@ public class CopyTask /// Filter for files to copy. /// [JsonProperty(PropertyName = "Filter", Required = Required.DisallowNull)] - public string Filter { get; set; } = ".*"; + public CopyFilter Filter { get; set; } = new(); } } \ No newline at end of file From 07815414929d0db4c76253107302b0403a2a46a9 Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:59:50 +0100 Subject: [PATCH 3/6] feat(IClient.cs): Added filter to the list files method --- Copy/Types/IClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Copy/Types/IClient.cs b/Copy/Types/IClient.cs index 8b5a3fd..bd0a0a7 100644 --- a/Copy/Types/IClient.cs +++ b/Copy/Types/IClient.cs @@ -13,8 +13,9 @@ internal interface IClient : IDisposable /// List files in a directory. /// /// Path to the directory. + /// Filter for files to list. /// Array of file names. - string[] ListFiles(string path); + string[] ListFiles(string path, CopyFilter filter); /// /// Check if a file exists. /// From 540e16178b7455694f0214fc526a0279932da754 Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:02:52 +0100 Subject: [PATCH 4/6] feat(Config.cs): Conversion to new Filter --- Copy/Config.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Copy/Config.cs b/Copy/Config.cs index 42c07de..093f2ba 100644 --- a/Copy/Config.cs +++ b/Copy/Config.cs @@ -111,7 +111,10 @@ public static string GetDefault() Source = "source", Destination = "destination", Client = "SFTP", - Filter = "*.txt" + Filter = new CopyFilter() + { + Name = ".*\\.txt" + } }, new CopyTask() { @@ -119,7 +122,10 @@ public static string GetDefault() Destination = "destination", Client = "Local", Delete = true, - Filter = "*.txt" + Filter = new CopyFilter() + { + Name = ".*\\.txt" + } }, new CopyTask() { From 594c48ef8f53f719c3bccdd133a15eaebbbeaa57 Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:38:59 +0100 Subject: [PATCH 5/6] feat(Clients): New implementation --- Copy/Clients/Exchange.cs | 2 +- Copy/Clients/FTP.cs | 10 ++++++++-- Copy/Clients/Local.cs | 18 ++++++++++++++++-- Copy/Clients/SFTP.cs | 11 +++++++++-- Copy/Types/Exceptions.cs | 8 ++++++++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Copy/Clients/Exchange.cs b/Copy/Clients/Exchange.cs index f47cf44..e2847bd 100644 --- a/Copy/Clients/Exchange.cs +++ b/Copy/Clients/Exchange.cs @@ -35,7 +35,7 @@ public bool DoFileExist(string path) throw new NotImplementedException(); } - public string[] ListFiles(string path) + public string[] ListFiles(string path, CopyFilter filter) { throw new NotImplementedException(); } diff --git a/Copy/Clients/FTP.cs b/Copy/Clients/FTP.cs index d5c53f9..151500b 100644 --- a/Copy/Clients/FTP.cs +++ b/Copy/Clients/FTP.cs @@ -1,6 +1,7 @@ using Copy.Types; using FluentFTP; using System.Net; +using System.Text.RegularExpressions; namespace Copy.Clients { @@ -37,7 +38,7 @@ public bool DoFileExist(string path) return FtpClient.FileExists(path); } - public string[] ListFiles(string path) + public string[] ListFiles(string path, CopyFilter filter) { if (!FtpClient.DirectoryExists(path)) { @@ -45,7 +46,12 @@ public string[] ListFiles(string path) throw new DirectoryNotFoundException($"Directory {path} does not exist"); } - FtpListItem[] files = FtpClient.GetListing(path); + Regex nameRegex = new(filter.Name); + Regex authorRegex = new(filter.Author); + + FtpListItem[] files = FtpClient.GetListing(path) + .Where(f => nameRegex.IsMatch(f.Name) && authorRegex.IsMatch(f.RawOwner) && f.Created >= filter.CreatedAfter && (ulong)f.Size <= filter.MaxSize && (ulong)f.Size >= filter.MinSize) + .ToArray(); return files.Select(f => Path.Combine(path, f.Name)).ToArray(); } diff --git a/Copy/Clients/Local.cs b/Copy/Clients/Local.cs index 1835dd6..64f6372 100644 --- a/Copy/Clients/Local.cs +++ b/Copy/Clients/Local.cs @@ -1,4 +1,7 @@ using Copy.Types; +using System.Runtime.Versioning; +using System.Security.Principal; +using System.Text.RegularExpressions; namespace Copy.Clients { @@ -28,7 +31,8 @@ public bool DoFileExist(string path) return File.Exists(path); } - public string[] ListFiles(string path) + [SupportedOSPlatform("windows")] + public string[] ListFiles(string path, CopyFilter filter) { if (!Directory.Exists(path)) { @@ -36,7 +40,17 @@ public string[] ListFiles(string path) throw new DirectoryNotFoundException($"Directory {path} does not exist"); } - return Directory.GetFiles(path); + Regex nameRegex = new(filter.Name); + Regex authorRegex = new(filter.Author); + + return Directory.GetFiles(path) + .Where(f => + { + FileInfo fileInfo = new(f); + bool authorMatch = authorRegex.IsMatch(fileInfo.GetAccessControl().GetOwner(typeof(NTAccount))?.Value ?? throw new FileOwnerNotFoundException($"Impossible to get owner of {f}")); + return nameRegex.IsMatch(Path.GetFileName(f)) && authorMatch && File.GetCreationTime(f) >= filter.CreatedAfter && (ulong)fileInfo.Length <= filter.MaxSize && (ulong)fileInfo.Length >= filter.MinSize; + }) + .ToArray(); } public Stream GetFile(string path) diff --git a/Copy/Clients/SFTP.cs b/Copy/Clients/SFTP.cs index 80c004e..d057a70 100644 --- a/Copy/Clients/SFTP.cs +++ b/Copy/Clients/SFTP.cs @@ -1,5 +1,6 @@ using Copy.Types; using Renci.SshNet; +using System.Text.RegularExpressions; namespace Copy.Clients { @@ -23,7 +24,7 @@ public SFTP(Client credentials) SftpClient.Connect(); } - public string[] ListFiles(string path) + public string[] ListFiles(string path, CopyFilter filter) { if (!SftpClient.Exists(path)) { @@ -31,7 +32,13 @@ public string[] ListFiles(string path) throw new DirectoryNotFoundException($"Directory {path} does not exist"); } - var files = SftpClient.ListDirectory(path); + if (filter.Author != ".*") Logger.Warn($"Filtering by author is not supported by SFTP"); + + Regex nameRegex = new(filter.Name); + Regex authorRegex = new(filter.Author); + + var files = SftpClient.ListDirectory(path) + .Where(f => nameRegex.IsMatch(f.Name) && f.LastWriteTime >= filter.CreatedAfter && (ulong)f.Length <= filter.MaxSize && (ulong)f.Length >= filter.MinSize); return files.Select(f => f.FullName.Remove(0, 1)).ToArray(); } diff --git a/Copy/Types/Exceptions.cs b/Copy/Types/Exceptions.cs index b40414f..fb70a2a 100644 --- a/Copy/Types/Exceptions.cs +++ b/Copy/Types/Exceptions.cs @@ -7,4 +7,12 @@ internal class ClientNotFoundException(string message) : Exception(message) { } + + /// + /// Exception thrown when a file owner is not found. + /// + /// The message to display. + internal class FileOwnerNotFoundException(string message) : Exception(message) + { + } } From 557e8c89f917cca14c17074ce7648df9e5c2b1b0 Mon Sep 17 00:00:00 2001 From: Flo <62435410+FRFlo@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:39:16 +0100 Subject: [PATCH 6/6] feat(Program.cs): New filter system --- Copy/Program.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Copy/Program.cs b/Copy/Program.cs index 05eae99..9fcc73b 100644 --- a/Copy/Program.cs +++ b/Copy/Program.cs @@ -1,6 +1,5 @@ using Copy.Clients; using Copy.Types; -using System.Text.RegularExpressions; namespace Copy { @@ -63,10 +62,9 @@ public static void Main(string[] args) throw new ClientNotFoundException($"Client {task.Source} not found"); } - Regex filter = new(task.Filter); - IEnumerable files = client.ListFiles(task.Source).Where(f => filter.IsMatch(f)); + string[] files = client.ListFiles(task.Source, task.Filter); - Logger.Info($"Treating {files.Count()} files"); + Logger.Info($"Treating {files.Length} files"); foreach (string file in files) { @@ -86,7 +84,7 @@ public static void Main(string[] args) Logger.Debug($"Copied {file} to {task.Destination}"); } - Logger.Info($"Copied {files.Count()} files from {task.Source} to {task.Destination} using {task.Client}"); + Logger.Info($"Copied {files.Length} files from {task.Source} to {task.Destination} using {task.Client}"); } Logger.Info("All tasks executed");