From fd42e70ddfdf9b54e9792c539df50b477100dd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:53:15 -0300 Subject: [PATCH 01/23] Compression EO initial implementation for NET --- dotnet/DotNetStandardClasses.sln | 7 + dotnet/GxCompress/Compression.cs | 93 +++++++ dotnet/GxCompress/GXCompressor.cs | 408 ++++++++++++++++++++++++++++ dotnet/GxCompress/GxCompress.csproj | 24 ++ dotnet/GxCompress/IGXCompressor.cs | 11 + 5 files changed, 543 insertions(+) create mode 100644 dotnet/GxCompress/Compression.cs create mode 100644 dotnet/GxCompress/GXCompressor.cs create mode 100644 dotnet/GxCompress/GxCompress.csproj create mode 100644 dotnet/GxCompress/IGXCompressor.cs diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 01e391f0b..978ba522c 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -263,6 +263,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GxCompress", "GxCompress\GxCompress.csproj", "{02C726EC-5923-4852-999D-AF9406A1ACBB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -641,6 +643,10 @@ Global {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -767,6 +773,7 @@ Global {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {02C726EC-5923-4852-999D-AF9406A1ACBB} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs new file mode 100644 index 000000000..dc55f48f6 --- /dev/null +++ b/dotnet/GxCompress/Compression.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.IO; + +namespace Genexus.Compression +{ + public class Compression + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(Compression).FullName); + + private string path; + private string format; + private List filesToCompress; + + public Compression() + { + filesToCompress = new List(); + } + + public Compression(string path, string format) + { + this.path = path; + this.format = format; + this.filesToCompress = new List(); + } + + public void SetPath(string path) + { + this.path = path; + } + + public void SetFormat(string format) + { + this.format = format; + } + + public void AddFile(string filePath) + { + FileInfo file = new FileInfo(filePath); + if (file.Exists) + { + filesToCompress.Add(file); + } + else + { + GXLogging.Error(log, $"File does not exist: {0}", file.FullName); + } + } + + public void AddFolder(string folderPath) + { + DirectoryInfo folder = new DirectoryInfo(folderPath); + if (folder.Exists && folder.Attributes.HasFlag(FileAttributes.Directory)) + { + FileInfo[] files = folder.GetFiles(); + DirectoryInfo[] directories = folder.GetDirectories(); + foreach (DirectoryInfo dir in directories) + { + AddFolder(dir.FullName); + } + foreach (FileInfo file in files) + { + AddFile(file.FullName); + } + } + else + { + GXLogging.Error("Folder does not exist or is not a directory: {0}", folder.FullName); + } + } + + public int Save() + { + if (filesToCompress.Count == 0) + { + GXLogging.Error("No files have been added for compression."); + return -4; + } + List paths = new List(); + foreach (FileInfo file in filesToCompress) + { + paths.Add(file.FullName); + } + return GXCompressor.CompressFiles(paths, path, format); + } + + public void Clear() + { + this.path = string.Empty; + this.format = string.Empty; + this.filesToCompress = new List(); + } + } +} diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs new file mode 100644 index 000000000..55814436e --- /dev/null +++ b/dotnet/GxCompress/GXCompressor.cs @@ -0,0 +1,408 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Security; +using ICSharpCode.SharpZipLib.Tar; + +namespace Genexus.Compression +{ + public enum CompressionFormat + { + GZIP, + TAR, + ZIP + } + + public class GXCompressor : IGXCompressor + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); + + public static int CompressFiles(List files, string path, string format) + { + if (files.Count == 0) + { + GXLogging.Error(log, "No files have been added for compression."); + return -4; + } + + FileInfo[] toCompress = new FileInfo[files.Count]; + int index = 0; + foreach (string filePath in files) + { + toCompress[index++] = new FileInfo(filePath); + } + + try + { + CompressionFormat compressionFormat = GetCompressionFormat(format); + switch (compressionFormat) + { + case CompressionFormat.ZIP: + CompressToZip(toCompress, path); + return 0; + case CompressionFormat.TAR: + CompressToTar(toCompress, path); + return 0; + case CompressionFormat.GZIP: + CompressToGzip(toCompress, path); + return 0; + } + } + catch (ArgumentException) + { + GXLogging.Error(log, "Unsupported compression format for compression: {0}", format); + return -3; + } + catch (Exception e) + { + GXLogging.Error(log, "An error occurred during the compression process: ", e); + return -1; + } + return -1; + } + + public static int CompressFolder(string folder, string path, string format) + { + DirectoryInfo toCompress = new DirectoryInfo(folder); + if (!toCompress.Exists) + { + GXLogging.Error(log, "The specified folder does not exist: {0}", toCompress.FullName); + return -2; + } + List list = new List { folder }; + return CompressFiles(list, path, format); + } + + public static Compression NewCompression(string path, string format) + { + return new Compression(path, format); + } + + public static int Decompress(string file, string path) + { + FileInfo toCompress = new FileInfo(file); + if (!toCompress.Exists) + { + GXLogging.Error(log, "The specified archive does not exist: {0}", toCompress.FullName); + return -2; + } + if (toCompress.Length == 0) + { + GXLogging.Error(log, "The archive located at {0} is empty", path); + return -4; + } + string extension = Path.GetExtension(toCompress.Name).Substring(1); + try + { + switch (extension.ToLower()) + { + case "zip": + DecompressZip(toCompress, path); + return 0; + case "tar": + DecompressTar(toCompress, path); + return 0; + case "gz": + DecompressGzip(toCompress, path); + return 0; + default: + GXLogging.Error(log, "Unsupported compression format for decompression: {0}", extension); + return -3; + } + } + catch (Exception e) + { + GXLogging.Error(log, "Decompression failed: {0}", e.Message); + return -1; + } + } + + private static void CompressToZip(FileInfo[] files, string outputPath) + { + try + { + using (FileStream fos = new FileStream(outputPath, FileMode.Create)) + using (ZipArchive zos = new ZipArchive(fos, ZipArchiveMode.Create)) + { + foreach (FileInfo file in files) + { + if (file.Exists) + { + AddFileToZip(zos, file, ""); + } + } + } + } + catch (IOException e) + { + GXLogging.Error(log, "Error while compressing to zip", e); + throw new Exception("Failed to compress files", e); + } + } + + private static void AddFileToZip(ZipArchive zos, FileInfo file, string baseDir) + { + string entryName = baseDir + file.Name; + if (file.Attributes.HasFlag(FileAttributes.Directory)) + { + DirectoryInfo dir = new DirectoryInfo(file.FullName); + FileInfo[] children = dir.GetFiles(); + DirectoryInfo[] directories = dir.GetDirectories(); + foreach (DirectoryInfo childDir in directories) + { + AddFileToZip(zos, new FileInfo(childDir.FullName), entryName + "/"); + } + foreach (FileInfo childFile in children) + { + AddFileToZip(zos, childFile, entryName + "/"); + } + } + else + { + ZipArchiveEntry zipEntry = zos.CreateEntryFromFile(file.FullName, entryName); + using (Stream entryStream = zipEntry.Open()) + using (FileStream fis = File.OpenRead(file.FullName)) + { + fis.CopyTo(entryStream); + } + } + } + + private static void CompressToTar(FileInfo[] files, string outputPath) + { + FileInfo outputTarFile = new FileInfo(outputPath); + try + { + using (FileStream fos = outputTarFile.Create()) + using (TarOutputStream taos = new TarOutputStream(fos)) + { + taos.IsStreamOwner = false; + + foreach (FileInfo file in files) + { + AddFileToTar(taos, file, ""); + } + taos.Close(); + } + } + catch (Exception e) + { + GXLogging.Error(log, "Error while compressing to tar", e); + throw new Exception("Error creating TAR archive", e); + } + } + + private static void AddFileToTar(TarOutputStream taos, FileInfo file, string baseDir) + { + if ((file.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + DirectoryInfo dir = new DirectoryInfo(file.FullName); + FileInfo[] children = dir.GetFiles(); + DirectoryInfo[] directories = dir.GetDirectories(); + foreach (DirectoryInfo childDir in directories) + { + AddFileToTar(taos, new FileInfo(childDir.FullName), baseDir + file.Name + "/"); + } + foreach (FileInfo childFile in children) + { + AddFileToTar(taos, childFile, baseDir + file.Name + "/"); + } + } + else + { + string entryName = baseDir + file.Name; + TarEntry entry = TarEntry.CreateEntryFromFile(file.FullName); + entry.Name = entryName; + entry.Size = file.Length; + taos.PutNextEntry(entry); + + using (FileStream fis = File.OpenRead(file.FullName)) + { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = fis.Read(buffer, 0, buffer.Length)) > 0) + { + taos.Write(buffer, 0, bytesRead); + } + } + taos.CloseEntry(); + } + } + + private static void CompressToGzip(FileInfo[] files, string outputPath) + { + if (files.Length > 1) + { + throw new ArgumentException("GZIP does not support multiple files. Consider archiving the files first."); + } + + FileInfo inputFile = files[0]; + FileInfo outputFile = new FileInfo(outputPath); + + try + { + using (FileStream inStream = inputFile.OpenRead()) + using (FileStream fout = outputFile.Create()) + using (BufferedStream outStream = new BufferedStream(fout)) + using (GZipStream gzOut = new GZipStream(outStream, CompressionMode.Compress)) + { + byte[] buffer = new byte[4096]; + int n; + while ((n = inStream.Read(buffer, 0, buffer.Length)) != -1) + { + gzOut.Write(buffer, 0, n); + } + } + } + catch (IOException e) + { + GXLogging.Error(log, "Error while compressing to gzip", e); + throw new Exception("Error compressing file with GZIP", e); + } + } + + private static void DecompressZip(FileInfo zipFile, string directory) + { + string zipFilePath = zipFile.FullName; + string targetDir = Path.GetFullPath(directory); + + try + { + using (FileStream fis = File.OpenRead(zipFilePath)) + using (ZipArchive zipIn = new ZipArchive(fis, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in zipIn.Entries) + { + string resolvedPath = Path.Combine(targetDir, entry.FullName); + + if (!resolvedPath.StartsWith(targetDir)) + { + throw new SecurityException("Zip entry is outside of the target dir: " + entry.FullName); + } + + string fullResolvedPath = Path.GetFullPath(resolvedPath); + + if (entry.FullName.EndsWith("/")) + { + if (!Directory.Exists(fullResolvedPath)) + { + Directory.CreateDirectory(fullResolvedPath); + } + } + else + { + string parentDir = Path.GetDirectoryName(fullResolvedPath); + if (!Directory.Exists(parentDir)) + { + Directory.CreateDirectory(parentDir); + } + using (FileStream outFile = new FileStream(fullResolvedPath, FileMode.Create, FileAccess.Write)) + { + entry.Open().CopyTo(outFile); + } + } + } + } + } + catch (IOException e) + { + GXLogging.Error(log, "Error while decompressing to zip", e); + throw new Exception("Failed to decompress ZIP file: " + zipFilePath, e); + } + + } + + private static void DecompressTar(FileInfo file, string outputPath) + { + string targetDir = Path.GetFullPath(outputPath); + try + { + using (FileStream fi = File.OpenRead(file.FullName)) + using (TarInputStream ti = new TarInputStream(fi)) + { + TarEntry entry; + while ((entry = ti.GetNextEntry()) != null) + { + string entryPath = Path.Combine(targetDir, entry.Name); + FileInfo outputFile = new FileInfo(Path.GetFullPath(entryPath)); + + if (!outputFile.FullName.StartsWith(targetDir)) + { + throw new IOException("Entry is outside of the target directory: " + entry.Name); + } + + if (entry.IsDirectory) + { + if (!outputFile.Exists) + { + Directory.CreateDirectory(outputFile.FullName); + } + } + else + { + DirectoryInfo parent = outputFile.Directory; + if (!parent.Exists) + { + Directory.CreateDirectory(parent.FullName); + } + + using (FileStream outStream = File.Create(outputFile.FullName)) + { + ti.CopyEntryContents(outStream); + } + } + } + } + } + catch (IOException e) + { + GXLogging.Error(log, "Error while decompressing to zip", e); + throw new Exception("Failed to decompress TAR file: " + file.Name, e); + } + } + + private static void DecompressGzip(FileInfo inputFile, string outputPath) + { + DirectoryInfo outputDir = new DirectoryInfo(outputPath); + if (!outputDir.Exists) + { + outputDir.Create(); + } + + string outputFileName = inputFile.Name; + if (outputFileName.EndsWith(".gz")) + outputFileName = outputFileName.Substring(0, outputFileName.Length - 3); + else + throw new ArgumentException("The input file does not have a .gz extension."); + + FileInfo outputFile = new FileInfo(Path.Combine(outputDir.FullName, outputFileName)); + using (FileStream fis = inputFile.OpenRead()) + using (GZipStream gzis = new GZipStream(fis, CompressionMode.Decompress)) + using (FileStream fos = outputFile.Create()) + { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = gzis.Read(buffer, 0, buffer.Length)) != -1) + { + fos.Write(buffer, 0, bytesRead); + } + } + } + + private static CompressionFormat GetCompressionFormat(string format) + { + try + { + return (CompressionFormat)Enum.Parse(typeof(CompressionFormat), format.ToUpper()); + } + catch (ArgumentException) + { + throw new ArgumentException("Invalid compression format: " + format); + } + } + + } + +} + diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/GxCompress/GxCompress.csproj new file mode 100644 index 000000000..593dff590 --- /dev/null +++ b/dotnet/GxCompress/GxCompress.csproj @@ -0,0 +1,24 @@ + + + + net6.0;net8.0 + Library + NETCORE;NODATIME + GeneXus.Compression + true + 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 + $(TargetsForTfmSpecificContentInPackage);CustomContentTarget + + + + + + + + + + + + + + diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs new file mode 100644 index 000000000..15dceb4b2 --- /dev/null +++ b/dotnet/GxCompress/IGXCompressor.cs @@ -0,0 +1,11 @@ +namespace Genexus.Compression +{ + public interface IGXCompressor + { + static int CompressFiles(List files, string path, string format) => 0; + static int CompressFolder(string folder, string path, string format) => 0; + static Compression NewCompression(string path, string format, int dictionarySize) => new Compression(); + static int Decompress(string file, string path) => 0; + } +} + From 26c1d47bcc50a84e7aa41743240a54a69f69a7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:15:48 -0300 Subject: [PATCH 02/23] Add GXClasses and SharpZipLib as a dependency --- dotnet/GxCompress/Compression.cs | 5 +++-- dotnet/GxCompress/GXCompressor.cs | 1 + dotnet/GxCompress/GxCompress.csproj | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs index dc55f48f6..178d345bf 100644 --- a/dotnet/GxCompress/Compression.cs +++ b/dotnet/GxCompress/Compression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using GeneXus; namespace Genexus.Compression { @@ -64,7 +65,7 @@ public void AddFolder(string folderPath) } else { - GXLogging.Error("Folder does not exist or is not a directory: {0}", folder.FullName); + GXLogging.Error(log, "Folder does not exist or is not a directory: {0}", folder.FullName); } } @@ -72,7 +73,7 @@ public int Save() { if (filesToCompress.Count == 0) { - GXLogging.Error("No files have been added for compression."); + GXLogging.Error(log, "No files have been added for compression."); return -4; } List paths = new List(); diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 55814436e..1ca3ec0fe 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -3,6 +3,7 @@ using System.IO; using System.IO.Compression; using System.Security; +using GeneXus; using ICSharpCode.SharpZipLib.Tar; namespace Genexus.Compression diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/GxCompress/GxCompress.csproj index 593dff590..86a6df857 100644 --- a/dotnet/GxCompress/GxCompress.csproj +++ b/dotnet/GxCompress/GxCompress.csproj @@ -10,15 +10,16 @@ $(TargetsForTfmSpecificContentInPackage);CustomContentTarget - - - - + + + + + From 5b296d3d8a3924212f741accb07e4c8e8afa9975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:23:19 -0300 Subject: [PATCH 03/23] Follow best coding practices --- dotnet/GxCompress/GXCompressor.cs | 8 ++++---- dotnet/GxCompress/IGXCompressor.cs | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 1ca3ec0fe..4bb6e6867 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -130,7 +130,7 @@ private static void CompressToZip(FileInfo[] files, string outputPath) { if (file.Exists) { - AddFileToZip(zos, file, ""); + AddFileToZip(zos, file, string.Empty); } } } @@ -182,7 +182,7 @@ private static void CompressToTar(FileInfo[] files, string outputPath) foreach (FileInfo file in files) { - AddFileToTar(taos, file, ""); + AddFileToTar(taos, file, string.Empty); } taos.Close(); } @@ -203,11 +203,11 @@ private static void AddFileToTar(TarOutputStream taos, FileInfo file, string bas DirectoryInfo[] directories = dir.GetDirectories(); foreach (DirectoryInfo childDir in directories) { - AddFileToTar(taos, new FileInfo(childDir.FullName), baseDir + file.Name + "/"); + AddFileToTar(taos, new FileInfo(childDir.FullName), Path.Combine(baseDir, file.Name, Path.DirectorySeparatorChar.ToString())); } foreach (FileInfo childFile in children) { - AddFileToTar(taos, childFile, baseDir + file.Name + "/"); + AddFileToTar(taos, childFile, Path.Combine(baseDir, file.Name, Path.DirectorySeparatorChar.ToString())); } } else diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs index 15dceb4b2..5ca34ce3a 100644 --- a/dotnet/GxCompress/IGXCompressor.cs +++ b/dotnet/GxCompress/IGXCompressor.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Genexus.Compression { public interface IGXCompressor From ccae7e2e66bad4c7cb13c5c1dc1e097170ce9bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:39:48 -0300 Subject: [PATCH 04/23] Switch to Path.DirectorySeparatorChar --- dotnet/GxCompress/GXCompressor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 4bb6e6867..91e661cd8 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -152,11 +152,11 @@ private static void AddFileToZip(ZipArchive zos, FileInfo file, string baseDir) DirectoryInfo[] directories = dir.GetDirectories(); foreach (DirectoryInfo childDir in directories) { - AddFileToZip(zos, new FileInfo(childDir.FullName), entryName + "/"); + AddFileToZip(zos, new FileInfo(childDir.FullName), entryName + Path.DirectorySeparatorChar.ToString()); } foreach (FileInfo childFile in children) { - AddFileToZip(zos, childFile, entryName + "/"); + AddFileToZip(zos, childFile, entryName + Path.DirectorySeparatorChar.ToString()); } } else @@ -284,7 +284,7 @@ private static void DecompressZip(FileInfo zipFile, string directory) string fullResolvedPath = Path.GetFullPath(resolvedPath); - if (entry.FullName.EndsWith("/")) + if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) { if (!Directory.Exists(fullResolvedPath)) { From f6624e52915acef1c2162066ed194166e832ba82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:04:20 -0300 Subject: [PATCH 05/23] Add support for rar, jar and 7z --- dotnet/GxCompress/GXCompressor.cs | 137 +++++++++++++++++++++++++++- dotnet/GxCompress/GxCompress.csproj | 1 + 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 91e661cd8..b2d29281f 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -5,6 +5,10 @@ using System.Security; using GeneXus; using ICSharpCode.SharpZipLib.Tar; +using SharpCompress.Archives; +using SharpCompress.Archives.Rar; +using SharpCompress.Archives.SevenZip; +using SharpCompress.Common; namespace Genexus.Compression { @@ -12,7 +16,8 @@ public enum CompressionFormat { GZIP, TAR, - ZIP + ZIP, + JAR } public class GXCompressor : IGXCompressor @@ -48,6 +53,9 @@ public static int CompressFiles(List files, string path, string format) case CompressionFormat.GZIP: CompressToGzip(toCompress, path); return 0; + case CompressionFormat.JAR: + CompressToJar(toCompress, path); + return 0; } } catch (ArgumentException) @@ -107,6 +115,15 @@ public static int Decompress(string file, string path) case "gz": DecompressGzip(toCompress, path); return 0; + case "jar": + DecompressJar(toCompress, path); + return 0; + case "7z": + Decompress7z(toCompress, path); + return 0; + case "rar": + DecompressRar(toCompress, path); + return 0; default: GXLogging.Error(log, "Unsupported compression format for decompression: {0}", extension); return -3; @@ -391,6 +408,124 @@ private static void DecompressGzip(FileInfo inputFile, string outputPath) } } + private static void Decompress7z(FileInfo file, string outputPath) + { + string targetDir = Path.GetFullPath(outputPath); + using (var sevenZFile = SevenZipArchive.Open(file.FullName)) + { + foreach (var entry in sevenZFile.Entries) + { + if (!entry.IsDirectory) + { + string resolvedPath = Path.Combine(targetDir, entry.Key); + FileInfo outputFile = new FileInfo(resolvedPath); + + if (!outputFile.FullName.StartsWith(targetDir, StringComparison.OrdinalIgnoreCase)) + { + throw new IOException("Entry is outside of the target dir: " + entry.Key); + } + + Directory.CreateDirectory(outputFile.DirectoryName); + + using (var outStream = outputFile.Open(FileMode.Create, FileAccess.Write)) + { + entry.WriteTo(outStream); + } + } + else + { + string dirPath = Path.Combine(targetDir, entry.Key); + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + } + } + } + } + + private static void CompressToJar(FileInfo[] files, string outputPath) + { + using (FileStream outputStream = new FileStream(outputPath, FileMode.Create)) + using (ZipArchive jos = new ZipArchive(outputStream, ZipArchiveMode.Create)) + { + byte[] buffer = new byte[1024]; + foreach (FileInfo file in files) + { + using (FileStream fis = file.OpenRead()) + { + ZipArchiveEntry entry = jos.CreateEntry(file.Name); + using (Stream entryStream = entry.Open()) + { + int length; + while ((length = fis.Read(buffer, 0, buffer.Length)) > 0) + { + entryStream.Write(buffer, 0, length); + } + } + } + } + } + } + + public static void DecompressJar(FileInfo jarFile, string outputPath) + { + if (!jarFile.Exists) + { + throw new IOException("The jar file does not exist."); + } + + DirectoryInfo outputDir = new DirectoryInfo(outputPath); + if (!outputDir.Exists) + { + outputDir.Create(); + } + + using (FileStream jarFileStream = jarFile.OpenRead()) + using (ZipArchive jis = new ZipArchive(jarFileStream, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in jis.Entries) + { + string entryPath = Path.Combine(outputPath, entry.FullName); + FileInfo outputFile = new FileInfo(entryPath); + + if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + if (!Directory.Exists(outputFile.FullName)) + { + Directory.CreateDirectory(outputFile.FullName); + } + } + else + { + Directory.CreateDirectory(outputFile.DirectoryName); + using (FileStream fos = new FileStream(outputFile.FullName, FileMode.Create)) + { + using (Stream entryStream = entry.Open()) + { + entryStream.CopyTo(fos); + } + } + } + } + } + } + + + public static void DecompressRar(FileInfo rarFile, string destinationPath) + { + using (var archive = RarArchive.Open(rarFile.FullName)) + { + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + entry.WriteToDirectory(destinationPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); + } + } + } + } + private static CompressionFormat GetCompressionFormat(string format) { try diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/GxCompress/GxCompress.csproj index 86a6df857..6cca9353e 100644 --- a/dotnet/GxCompress/GxCompress.csproj +++ b/dotnet/GxCompress/GxCompress.csproj @@ -15,6 +15,7 @@ + From 221e7e4a806291e1da4179eabc3a26d3716137a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:16:14 -0300 Subject: [PATCH 06/23] Add compression tests --- dotnet/GxCompress/GXCompressor.cs | 64 +++++------------ .../DotNetCoreUnitTest/CompressionTests.cs | 72 +++++++++++++++++++ .../DotNetCoreUnitTest.csproj | 1 + 3 files changed, 92 insertions(+), 45 deletions(-) create mode 100644 dotnet/test/DotNetCoreUnitTest/CompressionTests.cs diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index b2d29281f..659801082 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -118,9 +118,6 @@ public static int Decompress(string file, string path) case "jar": DecompressJar(toCompress, path); return 0; - case "7z": - Decompress7z(toCompress, path); - return 0; case "rar": DecompressRar(toCompress, path); return 0; @@ -138,52 +135,37 @@ public static int Decompress(string file, string path) private static void CompressToZip(FileInfo[] files, string outputPath) { - try + using (FileStream fos = new FileStream(outputPath, FileMode.Create)) + using (ZipArchive zos = new ZipArchive(fos, ZipArchiveMode.Create)) { - using (FileStream fos = new FileStream(outputPath, FileMode.Create)) - using (ZipArchive zos = new ZipArchive(fos, ZipArchiveMode.Create)) + foreach (FileInfo file in files) { - foreach (FileInfo file in files) + if (file.Exists) { - if (file.Exists) - { - AddFileToZip(zos, file, string.Empty); - } + AddFileToZip(zos, file, string.Empty); } } } - catch (IOException e) - { - GXLogging.Error(log, "Error while compressing to zip", e); - throw new Exception("Failed to compress files", e); - } } private static void AddFileToZip(ZipArchive zos, FileInfo file, string baseDir) { string entryName = baseDir + file.Name; - if (file.Attributes.HasFlag(FileAttributes.Directory)) + if ((file.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { - DirectoryInfo dir = new DirectoryInfo(file.FullName); - FileInfo[] children = dir.GetFiles(); - DirectoryInfo[] directories = dir.GetDirectories(); - foreach (DirectoryInfo childDir in directories) + DirectoryInfo dirInfo = file.Directory; + foreach (DirectoryInfo dir in dirInfo.GetDirectories()) { - AddFileToZip(zos, new FileInfo(childDir.FullName), entryName + Path.DirectorySeparatorChar.ToString()); + AddFileToZip(zos, new FileInfo(dir.FullName), entryName + Path.DirectorySeparatorChar); } - foreach (FileInfo childFile in children) + foreach (FileInfo childFile in dirInfo.GetFiles()) { - AddFileToZip(zos, childFile, entryName + Path.DirectorySeparatorChar.ToString()); + AddFileToZip(zos, childFile, entryName + Path.DirectorySeparatorChar); } } else { ZipArchiveEntry zipEntry = zos.CreateEntryFromFile(file.FullName, entryName); - using (Stream entryStream = zipEntry.Open()) - using (FileStream fis = File.OpenRead(file.FullName)) - { - fis.CopyTo(entryStream); - } } } @@ -258,26 +240,18 @@ private static void CompressToGzip(FileInfo[] files, string outputPath) FileInfo inputFile = files[0]; FileInfo outputFile = new FileInfo(outputPath); - try + using (FileStream inStream = new FileStream(inputFile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (FileStream fout = new FileStream(outputFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None)) + using (BufferedStream outStream = new BufferedStream(fout)) + using (GZipStream gzOut = new GZipStream(outStream, CompressionMode.Compress)) { - using (FileStream inStream = inputFile.OpenRead()) - using (FileStream fout = outputFile.Create()) - using (BufferedStream outStream = new BufferedStream(fout)) - using (GZipStream gzOut = new GZipStream(outStream, CompressionMode.Compress)) + byte[] buffer = new byte[4096]; + int n; + while ((n = inStream.Read(buffer, 0, buffer.Length)) != -1) { - byte[] buffer = new byte[4096]; - int n; - while ((n = inStream.Read(buffer, 0, buffer.Length)) != -1) - { - gzOut.Write(buffer, 0, n); - } + gzOut.Write(buffer, 0, n); } } - catch (IOException e) - { - GXLogging.Error(log, "Error while compressing to gzip", e); - throw new Exception("Error compressing file with GZIP", e); - } } private static void DecompressZip(FileInfo zipFile, string directory) diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs new file mode 100644 index 000000000..c3f23debc --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Genexus.Compression; +using Xunit; + +namespace xUnitTesting +{ + public class TestCompression : IDisposable + { + private List files; + private DirectoryInfo testDirectory; + + public TestCompression() + { + testDirectory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "testCompressor")); + testDirectory.Create(); + files = new List(); + string content = "This is a sample text to test the compression functionality."; + for (int i = 0; i < 3; i++) + { + string filePath = Path.Combine(testDirectory.FullName, $"testFile{i}.txt"); + File.WriteAllText(filePath, content); + files.Add(filePath); + } + } + + public void Dispose() + { + foreach (string filePath in files) + { + File.Delete(filePath); + } + testDirectory.Delete(true); + } + + [Fact] + public void TestCompressToZip() + { + string outputPath = Path.Combine(testDirectory.FullName, "output.zip"); + int result = GXCompressor.CompressFiles(files, outputPath, "ZIP"); + Assert.Equal(0, result); + Assert.True(File.Exists(outputPath)); + } + + [Fact] + public void TestCompressToTar() + { + string outputPath = Path.Combine(testDirectory.FullName, "output.tar"); + int result = GXCompressor.CompressFiles(files, outputPath, "TAR"); + Assert.Equal(0, result); + Assert.True(File.Exists(outputPath)); + } + + [Fact] + public void TestCompressToJar() + { + string outputPath = Path.Combine(testDirectory.FullName, "output.jar"); + int result = GXCompressor.CompressFiles(files, outputPath, "JAR"); + Assert.Equal(0, result); + Assert.True(File.Exists(outputPath)); + } + + [Fact] + public void TestUnsupportedFormat() + { + string outputPath = Path.Combine(testDirectory.FullName, "output.unknown"); + int result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN"); + Assert.Equal(-3, result); + } + } +} diff --git a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj index 942399345..7e3c2c5f2 100644 --- a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj +++ b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj @@ -78,6 +78,7 @@ + From 0dceab5f7dad6d5654212571f42a86f856633ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:08:52 -0300 Subject: [PATCH 07/23] Add gzip test and fix infinite loop in gzip compression --- dotnet/GxCompress/GXCompressor.cs | 14 ++++---------- dotnet/test/DotNetCoreUnitTest/CompressionTests.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 659801082..065847966 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -240,17 +240,11 @@ private static void CompressToGzip(FileInfo[] files, string outputPath) FileInfo inputFile = files[0]; FileInfo outputFile = new FileInfo(outputPath); - using (FileStream inStream = new FileStream(inputFile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - using (FileStream fout = new FileStream(outputFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None)) - using (BufferedStream outStream = new BufferedStream(fout)) - using (GZipStream gzOut = new GZipStream(outStream, CompressionMode.Compress)) + using (FileStream inStream = inputFile.OpenRead()) + using (FileStream fout = outputFile.Create()) + using (GZipStream gzOut = new GZipStream(fout, CompressionMode.Compress)) { - byte[] buffer = new byte[4096]; - int n; - while ((n = inStream.Read(buffer, 0, buffer.Length)) != -1) - { - gzOut.Write(buffer, 0, n); - } + inStream.CopyTo(gzOut); } } diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index c3f23debc..600d78f12 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -52,6 +52,18 @@ public void TestCompressToTar() Assert.True(File.Exists(outputPath)); } + [Fact] + public void TestCompressToGzip() + { + string outputPath = Path.Combine(testDirectory.FullName, "output.gz"); + string inputFilePath = Path.Combine(testDirectory.FullName, "test.txt"); + File.WriteAllText(inputFilePath, "This is a sample text to test the compression functionality."); + List singleFileCollection = new List { inputFilePath }; + int result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP"); + Assert.Equal(0, result); + Assert.True(File.Exists(outputPath)); + } + [Fact] public void TestCompressToJar() { From 6e094fd1a0ffd2030a7d671a829731243f534652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:54:22 -0300 Subject: [PATCH 08/23] Update compression methods to return a CompressionMessage object and add new tests --- dotnet/GxCompress/Compression.cs | 4 +- dotnet/GxCompress/CompressionMessage.cs | 26 +++++++ dotnet/GxCompress/GXCompressor.cs | 66 ++++++++++-------- dotnet/GxCompress/IGXCompressor.cs | 6 +- .../DotNetCoreUnitTest/CompressionTests.cs | 69 ++++++++++++++++--- 5 files changed, 126 insertions(+), 45 deletions(-) create mode 100644 dotnet/GxCompress/CompressionMessage.cs diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs index 178d345bf..0a8671625 100644 --- a/dotnet/GxCompress/Compression.cs +++ b/dotnet/GxCompress/Compression.cs @@ -69,12 +69,12 @@ public void AddFolder(string folderPath) } } - public int Save() + public CompressionMessage Save() { if (filesToCompress.Count == 0) { GXLogging.Error(log, "No files have been added for compression."); - return -4; + return new CompressionMessage(false, "No files have been added for compression."); } List paths = new List(); foreach (FileInfo file in filesToCompress) diff --git a/dotnet/GxCompress/CompressionMessage.cs b/dotnet/GxCompress/CompressionMessage.cs new file mode 100644 index 000000000..2772ce027 --- /dev/null +++ b/dotnet/GxCompress/CompressionMessage.cs @@ -0,0 +1,26 @@ +namespace Genexus.Compression +{ + public class CompressionMessage + { + private bool successfulOperation; + private string message; + + public CompressionMessage(bool successfulOperation, string message) + { + this.successfulOperation = successfulOperation; + this.message = message; + } + + public bool IsSuccessfulOperation + { + get { return successfulOperation; } + set { successfulOperation = value; } + } + + public string Message + { + get { return message; } + set { message = value; } + } + } +} diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 065847966..76aafc3a9 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -24,12 +24,12 @@ public class GXCompressor : IGXCompressor { static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); - public static int CompressFiles(List files, string path, string format) + public static CompressionMessage CompressFiles(List files, string path, string format) { if (files.Count == 0) { GXLogging.Error(log, "No files have been added for compression."); - return -4; + return new CompressionMessage(false, "No files have been added for compression."); } FileInfo[] toCompress = new FileInfo[files.Count]; @@ -46,38 +46,38 @@ public static int CompressFiles(List files, string path, string format) { case CompressionFormat.ZIP: CompressToZip(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case CompressionFormat.TAR: CompressToTar(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case CompressionFormat.GZIP: CompressToGzip(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case CompressionFormat.JAR: CompressToJar(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); } } - catch (ArgumentException) + catch (ArgumentException ae) { - GXLogging.Error(log, "Unsupported compression format for compression: {0}", format); - return -3; + GXLogging.Error(log, "Unsupported compression format for compression: " + format, ae); + return new CompressionMessage(false, "Unsupported compression format for compression: " + format); } catch (Exception e) { GXLogging.Error(log, "An error occurred during the compression process: ", e); - return -1; + return new CompressionMessage(false, "An error occurred during the compression process: " + e.Message); } - return -1; + return new CompressionMessage(false, "An error occurred during the compression process"); } - public static int CompressFolder(string folder, string path, string format) + public static CompressionMessage CompressFolder(string folder, string path, string format) { DirectoryInfo toCompress = new DirectoryInfo(folder); if (!toCompress.Exists) { GXLogging.Error(log, "The specified folder does not exist: {0}", toCompress.FullName); - return -2; + return new CompressionMessage(false, "The specified folder does not exist: " + toCompress.FullName); } List list = new List { folder }; return CompressFiles(list, path, format); @@ -88,18 +88,18 @@ public static Compression NewCompression(string path, string format) return new Compression(path, format); } - public static int Decompress(string file, string path) + public static CompressionMessage Decompress(string file, string path) { FileInfo toCompress = new FileInfo(file); if (!toCompress.Exists) { - GXLogging.Error(log, "The specified archive does not exist: {0}", toCompress.FullName); - return -2; + GXLogging.Error(log, "The specified archive does not exist: " + toCompress.FullName); + return new CompressionMessage(false, "The specified archive does not exist: " + toCompress.FullName); } if (toCompress.Length == 0) { - GXLogging.Error(log, "The archive located at {0} is empty", path); - return -4; + GXLogging.Error(log, "The archive located at " + toCompress.FullName + " is empty"); + return new CompressionMessage(false, "The archive located at " + toCompress.FullName + " is empty"); } string extension = Path.GetExtension(toCompress.Name).Substring(1); try @@ -108,31 +108,39 @@ public static int Decompress(string file, string path) { case "zip": DecompressZip(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case "tar": DecompressTar(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case "gz": DecompressGzip(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case "jar": DecompressJar(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); case "rar": DecompressRar(toCompress, path); - return 0; + return GetSuccessfulCompressionMessage(); + case "7z": + Decompress7z(toCompress, path); + return GetSuccessfulCompressionMessage(); default: - GXLogging.Error(log, "Unsupported compression format for decompression: {0}", extension); - return -3; + GXLogging.Error(log, "Unsupported compression format for decompression: " + extension); + return new CompressionMessage(false, "Unsupported compression format for decompression: " + extension); } } catch (Exception e) { - GXLogging.Error(log, "Decompression failed: {0}", e.Message); - return -1; + GXLogging.Error(log, "Decompression failed: ", e); + return new CompressionMessage(false, "An error occurred during the decompression process: " + e.Message); } } + private static CompressionMessage GetSuccessfulCompressionMessage() + { + return new CompressionMessage(true, "The operation was successful"); + } + private static void CompressToZip(FileInfo[] files, string outputPath) { using (FileStream fos = new FileStream(outputPath, FileMode.Create)) @@ -369,7 +377,7 @@ private static void DecompressGzip(FileInfo inputFile, string outputPath) { byte[] buffer = new byte[4096]; int bytesRead; - while ((bytesRead = gzis.Read(buffer, 0, buffer.Length)) != -1) + while ((bytesRead = gzis.Read(buffer, 0, buffer.Length)) > 0) { fos.Write(buffer, 0, bytesRead); } @@ -478,8 +486,6 @@ public static void DecompressJar(FileInfo jarFile, string outputPath) } } } - - public static void DecompressRar(FileInfo rarFile, string destinationPath) { using (var archive = RarArchive.Open(rarFile.FullName)) diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs index 5ca34ce3a..c70533b53 100644 --- a/dotnet/GxCompress/IGXCompressor.cs +++ b/dotnet/GxCompress/IGXCompressor.cs @@ -4,10 +4,10 @@ namespace Genexus.Compression { public interface IGXCompressor { - static int CompressFiles(List files, string path, string format) => 0; - static int CompressFolder(string folder, string path, string format) => 0; + static CompressionMessage CompressFiles(List files, string path, string format) => null; + static CompressionMessage CompressFolder(string folder, string path, string format) => null; static Compression NewCompression(string path, string format, int dictionarySize) => new Compression(); - static int Decompress(string file, string path) => 0; + static CompressionMessage Decompress(string file, string path) => null; } } diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index 600d78f12..dd40fa03c 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -38,8 +38,8 @@ public void Dispose() public void TestCompressToZip() { string outputPath = Path.Combine(testDirectory.FullName, "output.zip"); - int result = GXCompressor.CompressFiles(files, outputPath, "ZIP"); - Assert.Equal(0, result); + CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "ZIP"); + Assert.True(result.IsSuccessfulOperation); Assert.True(File.Exists(outputPath)); } @@ -47,8 +47,8 @@ public void TestCompressToZip() public void TestCompressToTar() { string outputPath = Path.Combine(testDirectory.FullName, "output.tar"); - int result = GXCompressor.CompressFiles(files, outputPath, "TAR"); - Assert.Equal(0, result); + CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "TAR"); + Assert.True(result.IsSuccessfulOperation); Assert.True(File.Exists(outputPath)); } @@ -59,8 +59,8 @@ public void TestCompressToGzip() string inputFilePath = Path.Combine(testDirectory.FullName, "test.txt"); File.WriteAllText(inputFilePath, "This is a sample text to test the compression functionality."); List singleFileCollection = new List { inputFilePath }; - int result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP"); - Assert.Equal(0, result); + CompressionMessage result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP"); + Assert.True(result.IsSuccessfulOperation); Assert.True(File.Exists(outputPath)); } @@ -68,8 +68,8 @@ public void TestCompressToGzip() public void TestCompressToJar() { string outputPath = Path.Combine(testDirectory.FullName, "output.jar"); - int result = GXCompressor.CompressFiles(files, outputPath, "JAR"); - Assert.Equal(0, result); + CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "JAR"); + Assert.True(result.IsSuccessfulOperation); Assert.True(File.Exists(outputPath)); } @@ -77,8 +77,57 @@ public void TestCompressToJar() public void TestUnsupportedFormat() { string outputPath = Path.Combine(testDirectory.FullName, "output.unknown"); - int result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN"); - Assert.Equal(-3, result); + CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN"); + Assert.False(result.IsSuccessfulOperation); + } + + [Fact] + public void TestDecompressZip() + { + string compressedPath = Path.Combine(testDirectory.FullName, "output.zip"); + GXCompressor.CompressFiles(files, compressedPath, "ZIP"); + string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressZip"); + Directory.CreateDirectory(decompressDirectory); + CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); + Assert.True(result.IsSuccessfulOperation); + Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); + } + + [Fact] + public void TestDecompressTar() + { + string compressedPath = Path.Combine(testDirectory.FullName, "output.tar"); + GXCompressor.CompressFiles(files, compressedPath, "TAR"); + string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressTar"); + Directory.CreateDirectory(decompressDirectory); + CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); + Assert.True(result.IsSuccessfulOperation); + Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); + } + + [Fact] + public void TestDecompressGzip() + { + string compressedPath = Path.Combine(testDirectory.FullName, "output.gz"); + List singleFileCollection = new List { files[0] }; + GXCompressor.CompressFiles(singleFileCollection, compressedPath, "GZIP"); + string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressGzip"); + Directory.CreateDirectory(decompressDirectory); + CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); + Assert.True(result.IsSuccessfulOperation); + Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); + } + + [Fact] + public void TestDecompressJar() + { + string compressedPath = Path.Combine(testDirectory.FullName, "output.jar"); + GXCompressor.CompressFiles(files, compressedPath, "JAR"); + string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressJar"); + Directory.CreateDirectory(decompressDirectory); + CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); + Assert.True(result.IsSuccessfulOperation); + Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } } } From 16fb080ae4da3800b3560ce9e1e504e92f28a8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:05:47 -0300 Subject: [PATCH 09/23] Make attributes readonly --- dotnet/GxCompress/CompressionMessage.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dotnet/GxCompress/CompressionMessage.cs b/dotnet/GxCompress/CompressionMessage.cs index 2772ce027..519417708 100644 --- a/dotnet/GxCompress/CompressionMessage.cs +++ b/dotnet/GxCompress/CompressionMessage.cs @@ -2,8 +2,8 @@ namespace Genexus.Compression { public class CompressionMessage { - private bool successfulOperation; - private string message; + private readonly bool successfulOperation; + private readonly string message; public CompressionMessage(bool successfulOperation, string message) { @@ -14,13 +14,11 @@ public CompressionMessage(bool successfulOperation, string message) public bool IsSuccessfulOperation { get { return successfulOperation; } - set { successfulOperation = value; } } public string Message { get { return message; } - set { message = value; } } } } From d8590f19cd9e15dbe8ae40783e0fd492ce2c9e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:47:37 -0300 Subject: [PATCH 10/23] Update compression methods to return a boolean and messages --- dotnet/GxCompress/Compression.cs | 31 +++++-- dotnet/GxCompress/CompressionMessage.cs | 24 ----- dotnet/GxCompress/GXCompressor.cs | 91 +++++++++++++------ dotnet/GxCompress/IGXCompressor.cs | 9 +- .../DotNetCoreUnitTest/CompressionTests.cs | 46 +++++----- 5 files changed, 113 insertions(+), 88 deletions(-) delete mode 100644 dotnet/GxCompress/CompressionMessage.cs diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs index 0a8671625..a0043e39f 100644 --- a/dotnet/GxCompress/Compression.cs +++ b/dotnet/GxCompress/Compression.cs @@ -1,6 +1,9 @@ +using System; using System.Collections.Generic; using System.IO; +using System.Text; using GeneXus; +using GeneXus.Utils; namespace Genexus.Compression { @@ -10,6 +13,7 @@ public class Compression private string path; private string format; + private GXBaseCollection messages; private List filesToCompress; public Compression() @@ -17,10 +21,11 @@ public Compression() filesToCompress = new List(); } - public Compression(string path, string format) + public Compression(string path, string format, ref GXBaseCollection messages) { this.path = path; this.format = format; + this.messages = messages; this.filesToCompress = new List(); } @@ -43,6 +48,7 @@ public void AddFile(string filePath) } else { + StorageMessages("File does not exist: " + file.FullName, messages); GXLogging.Error(log, $"File does not exist: {0}", file.FullName); } } @@ -65,23 +71,19 @@ public void AddFolder(string folderPath) } else { + StorageMessages("Folder does not exist or is not a directory: " + folder.FullName, messages); GXLogging.Error(log, "Folder does not exist or is not a directory: {0}", folder.FullName); } } - public CompressionMessage Save() + public bool Save() { - if (filesToCompress.Count == 0) - { - GXLogging.Error(log, "No files have been added for compression."); - return new CompressionMessage(false, "No files have been added for compression."); - } List paths = new List(); foreach (FileInfo file in filesToCompress) { paths.Add(file.FullName); } - return GXCompressor.CompressFiles(paths, path, format); + return GXCompressor.CompressFiles(paths, path, format, ref this.messages); } public void Clear() @@ -90,5 +92,18 @@ public void Clear() this.format = string.Empty; this.filesToCompress = new List(); } + + private void StorageMessages(string error, GXBaseCollection messages) + { + if (messages != null && messages.Count > 0) + { + SdtMessages_Message msg = new() + { + gxTpr_Type = 1, + gxTpr_Description = error + }; + messages.Add(msg); + } + } } } diff --git a/dotnet/GxCompress/CompressionMessage.cs b/dotnet/GxCompress/CompressionMessage.cs deleted file mode 100644 index 519417708..000000000 --- a/dotnet/GxCompress/CompressionMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Genexus.Compression -{ - public class CompressionMessage - { - private readonly bool successfulOperation; - private readonly string message; - - public CompressionMessage(bool successfulOperation, string message) - { - this.successfulOperation = successfulOperation; - this.message = message; - } - - public bool IsSuccessfulOperation - { - get { return successfulOperation; } - } - - public string Message - { - get { return message; } - } - } -} diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 76aafc3a9..f92ede46c 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -3,7 +3,9 @@ using System.IO; using System.IO.Compression; using System.Security; +using System.Text; using GeneXus; +using GeneXus.Utils; using ICSharpCode.SharpZipLib.Tar; using SharpCompress.Archives; using SharpCompress.Archives.Rar; @@ -23,13 +25,40 @@ public enum CompressionFormat public class GXCompressor : IGXCompressor { static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); + private static void StorageMessages(Exception ex, string error, GXBaseCollection messages) + { + if (messages != null && messages.Count > 0) + { + SdtMessages_Message msg = new() + { + gxTpr_Type = 1 + }; + if (ex != null) + { + StringBuilder str = new StringBuilder(); + str.Append(ex.Message); + while (ex.InnerException != null) + { + str.Append(ex.InnerException.Message); + ex = ex.InnerException; + } + msg.gxTpr_Description = str.ToString(); + } + else + { + msg.gxTpr_Description = error; + } + messages.Add(msg); + } + } - public static CompressionMessage CompressFiles(List files, string path, string format) + public static bool CompressFiles(List files, string path, string format, ref GXBaseCollection messages) { if (files.Count == 0) { GXLogging.Error(log, "No files have been added for compression."); - return new CompressionMessage(false, "No files have been added for compression."); + StorageMessages(null, "No files have been added for compression.", messages); + return false; } FileInfo[] toCompress = new FileInfo[files.Count]; @@ -46,60 +75,65 @@ public static CompressionMessage CompressFiles(List files, string path, { case CompressionFormat.ZIP: CompressToZip(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case CompressionFormat.TAR: CompressToTar(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case CompressionFormat.GZIP: CompressToGzip(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case CompressionFormat.JAR: CompressToJar(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; } } catch (ArgumentException ae) { GXLogging.Error(log, "Unsupported compression format for compression: " + format, ae); - return new CompressionMessage(false, "Unsupported compression format for compression: " + format); + StorageMessages(ae, "", messages); + return false; } catch (Exception e) { GXLogging.Error(log, "An error occurred during the compression process: ", e); - return new CompressionMessage(false, "An error occurred during the compression process: " + e.Message); + StorageMessages(e, "", messages); + return false; } - return new CompressionMessage(false, "An error occurred during the compression process"); + return false; } - public static CompressionMessage CompressFolder(string folder, string path, string format) + public static bool CompressFolder(string folder, string path, string format, ref GXBaseCollection messages) { DirectoryInfo toCompress = new DirectoryInfo(folder); if (!toCompress.Exists) { GXLogging.Error(log, "The specified folder does not exist: {0}", toCompress.FullName); - return new CompressionMessage(false, "The specified folder does not exist: " + toCompress.FullName); + StorageMessages(null, "The specified folder does not exist: " + toCompress.FullName, messages); + return false; } List list = new List { folder }; - return CompressFiles(list, path, format); + return CompressFiles(list, path, format, ref messages); } - public static Compression NewCompression(string path, string format) + public static Compression NewCompression(string path, string format, ref GXBaseCollection messages) { - return new Compression(path, format); + return new Compression(path, format, ref messages); } - public static CompressionMessage Decompress(string file, string path) + public static bool Decompress(string file, string path, ref GXBaseCollection messages) { FileInfo toCompress = new FileInfo(file); if (!toCompress.Exists) { GXLogging.Error(log, "The specified archive does not exist: " + toCompress.FullName); - return new CompressionMessage(false, "The specified archive does not exist: " + toCompress.FullName); + StorageMessages(null, "The specified folder does not exist: " + toCompress.FullName, messages); + return false; } if (toCompress.Length == 0) { GXLogging.Error(log, "The archive located at " + toCompress.FullName + " is empty"); - return new CompressionMessage(false, "The archive located at " + toCompress.FullName + " is empty"); + StorageMessages(null, "The archive located at " + toCompress.FullName + " is empty", messages); + return false; } string extension = Path.GetExtension(toCompress.Name).Substring(1); try @@ -108,39 +142,36 @@ public static CompressionMessage Decompress(string file, string path) { case "zip": DecompressZip(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case "tar": DecompressTar(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case "gz": DecompressGzip(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case "jar": DecompressJar(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case "rar": DecompressRar(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; case "7z": Decompress7z(toCompress, path); - return GetSuccessfulCompressionMessage(); + return true; default: GXLogging.Error(log, "Unsupported compression format for decompression: " + extension); - return new CompressionMessage(false, "Unsupported compression format for decompression: " + extension); + StorageMessages(null, "Unsupported compression format for decompression: " + extension, messages); + return false; } } catch (Exception e) { GXLogging.Error(log, "Decompression failed: ", e); - return new CompressionMessage(false, "An error occurred during the decompression process: " + e.Message); + StorageMessages(e, "", messages); + return false; } } - private static CompressionMessage GetSuccessfulCompressionMessage() - { - return new CompressionMessage(true, "The operation was successful"); - } - private static void CompressToZip(FileInfo[] files, string outputPath) { using (FileStream fos = new FileStream(outputPath, FileMode.Create)) diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs index c70533b53..ec92479af 100644 --- a/dotnet/GxCompress/IGXCompressor.cs +++ b/dotnet/GxCompress/IGXCompressor.cs @@ -1,13 +1,14 @@ +using GeneXus.Utils; using System.Collections.Generic; namespace Genexus.Compression { public interface IGXCompressor { - static CompressionMessage CompressFiles(List files, string path, string format) => null; - static CompressionMessage CompressFolder(string folder, string path, string format) => null; - static Compression NewCompression(string path, string format, int dictionarySize) => new Compression(); - static CompressionMessage Decompress(string file, string path) => null; + static bool CompressFiles(List files, string path, string format, ref GXBaseCollection messages) => false; + static bool CompressFolder(string folder, string path, string format, ref GXBaseCollection messages) => false; + static Compression NewCompression(string path, string format, int dictionarySize, ref GXBaseCollection messages) => new Compression(); + static bool Decompress(string file, string path, ref GXBaseCollection messages) => false; } } diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index dd40fa03c..3a3918a53 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using Genexus.Compression; +using GeneXus.Utils; using Xunit; namespace xUnitTesting @@ -10,6 +11,7 @@ public class TestCompression : IDisposable { private List files; private DirectoryInfo testDirectory; + private GXBaseCollection messages = null; public TestCompression() { @@ -38,8 +40,8 @@ public void Dispose() public void TestCompressToZip() { string outputPath = Path.Combine(testDirectory.FullName, "output.zip"); - CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "ZIP"); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.CompressFiles(files, outputPath, "ZIP", ref messages); + Assert.True(result); Assert.True(File.Exists(outputPath)); } @@ -47,8 +49,8 @@ public void TestCompressToZip() public void TestCompressToTar() { string outputPath = Path.Combine(testDirectory.FullName, "output.tar"); - CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "TAR"); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.CompressFiles(files, outputPath, "TAR", ref messages); + Assert.True(result); Assert.True(File.Exists(outputPath)); } @@ -59,8 +61,8 @@ public void TestCompressToGzip() string inputFilePath = Path.Combine(testDirectory.FullName, "test.txt"); File.WriteAllText(inputFilePath, "This is a sample text to test the compression functionality."); List singleFileCollection = new List { inputFilePath }; - CompressionMessage result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP"); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP", ref messages); + Assert.True(result); Assert.True(File.Exists(outputPath)); } @@ -68,8 +70,8 @@ public void TestCompressToGzip() public void TestCompressToJar() { string outputPath = Path.Combine(testDirectory.FullName, "output.jar"); - CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "JAR"); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.CompressFiles(files, outputPath, "JAR", ref messages); + Assert.True(result); Assert.True(File.Exists(outputPath)); } @@ -77,19 +79,19 @@ public void TestCompressToJar() public void TestUnsupportedFormat() { string outputPath = Path.Combine(testDirectory.FullName, "output.unknown"); - CompressionMessage result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN"); - Assert.False(result.IsSuccessfulOperation); + bool result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN", ref messages); + Assert.False(result); } [Fact] public void TestDecompressZip() { string compressedPath = Path.Combine(testDirectory.FullName, "output.zip"); - GXCompressor.CompressFiles(files, compressedPath, "ZIP"); + GXCompressor.CompressFiles(files, compressedPath, "ZIP", ref messages); string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressZip"); Directory.CreateDirectory(decompressDirectory); - CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); + Assert.True(result); Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } @@ -97,11 +99,11 @@ public void TestDecompressZip() public void TestDecompressTar() { string compressedPath = Path.Combine(testDirectory.FullName, "output.tar"); - GXCompressor.CompressFiles(files, compressedPath, "TAR"); + GXCompressor.CompressFiles(files, compressedPath, "TAR", ref messages); string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressTar"); Directory.CreateDirectory(decompressDirectory); - CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); + Assert.True(result); Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } @@ -110,11 +112,11 @@ public void TestDecompressGzip() { string compressedPath = Path.Combine(testDirectory.FullName, "output.gz"); List singleFileCollection = new List { files[0] }; - GXCompressor.CompressFiles(singleFileCollection, compressedPath, "GZIP"); + GXCompressor.CompressFiles(singleFileCollection, compressedPath, "GZIP", ref messages); string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressGzip"); Directory.CreateDirectory(decompressDirectory); - CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); + Assert.True(result); Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } @@ -122,11 +124,11 @@ public void TestDecompressGzip() public void TestDecompressJar() { string compressedPath = Path.Combine(testDirectory.FullName, "output.jar"); - GXCompressor.CompressFiles(files, compressedPath, "JAR"); + GXCompressor.CompressFiles(files, compressedPath, "JAR", ref messages); string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressJar"); Directory.CreateDirectory(decompressDirectory); - CompressionMessage result = GXCompressor.Decompress(compressedPath, decompressDirectory); - Assert.True(result.IsSuccessfulOperation); + bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); + Assert.True(result); Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } } From c5da1d2d04dfb6a62266a14993d51f9ee44a7919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:44:29 -0300 Subject: [PATCH 11/23] Refactor for better code quality --- dotnet/GxCompress/Compression.cs | 38 +++-------- dotnet/GxCompress/GXCompressor.cs | 106 +++++++++++++++++------------- 2 files changed, 68 insertions(+), 76 deletions(-) diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs index a0043e39f..5c46acdb4 100644 --- a/dotnet/GxCompress/Compression.cs +++ b/dotnet/GxCompress/Compression.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using GeneXus; using GeneXus.Utils; @@ -26,7 +25,7 @@ public Compression(string path, string format, ref GXBaseCollection(); + filesToCompress = new List(); } public void SetPath(string path) @@ -48,31 +47,23 @@ public void AddFile(string filePath) } else { - StorageMessages("File does not exist: " + file.FullName, messages); GXLogging.Error(log, $"File does not exist: {0}", file.FullName); } } public void AddFolder(string folderPath) { - DirectoryInfo folder = new DirectoryInfo(folderPath); - if (folder.Exists && folder.Attributes.HasFlag(FileAttributes.Directory)) + try { - FileInfo[] files = folder.GetFiles(); - DirectoryInfo[] directories = folder.GetDirectories(); - foreach (DirectoryInfo dir in directories) - { - AddFolder(dir.FullName); - } - foreach (FileInfo file in files) + var files = Directory.EnumerateFiles(folderPath, "*", SearchOption.AllDirectories); + foreach (string file in files) { - AddFile(file.FullName); + AddFile(Path.GetFullPath(file)); } } - else + catch (Exception e) { - StorageMessages("Folder does not exist or is not a directory: " + folder.FullName, messages); - GXLogging.Error(log, "Folder does not exist or is not a directory: {0}", folder.FullName); + GXLogging.Error(log, $"Failed to add foler", e); } } @@ -90,20 +81,7 @@ public void Clear() { this.path = string.Empty; this.format = string.Empty; - this.filesToCompress = new List(); - } - - private void StorageMessages(string error, GXBaseCollection messages) - { - if (messages != null && messages.Count > 0) - { - SdtMessages_Message msg = new() - { - gxTpr_Type = 1, - gxTpr_Description = error - }; - messages.Add(msg); - } + filesToCompress = new List(); } } } diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index f92ede46c..8ad34075a 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -25,7 +25,29 @@ public enum CompressionFormat public class GXCompressor : IGXCompressor { static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); - private static void StorageMessages(Exception ex, string error, GXBaseCollection messages) + + private static readonly Dictionary errorMessages = new Dictionary(); + + static GXCompressor() + { + errorMessages.Add("GENERIC_ERROR", "An error occurred during the compression/decompression process: "); + errorMessages.Add("NO_FILES_ADDED", "No files have been added for compression."); + errorMessages.Add("FILE_NOT_EXISTS", "File does not exist: "); + errorMessages.Add("FOLDER_NOT_EXISTS", "The specified folder does not exist: "); + errorMessages.Add("UNSUPPORTED_FORMAT", "Unsupported compression/decompression format: "); + errorMessages.Add("EMPTY_FILE", "The selected file is empty: "); + } + + public static string GetMessage(string key) + { + if (errorMessages.TryGetValue(key, out string value)) + { + return value; + } + return "An error ocurred in the compression/decompression process: "; + } + + private static void StorageMessages(string error, GXBaseCollection messages) { if (messages != null && messages.Count > 0) { @@ -33,21 +55,7 @@ private static void StorageMessages(Exception ex, string error, GXBaseCollection { gxTpr_Type = 1 }; - if (ex != null) - { - StringBuilder str = new StringBuilder(); - str.Append(ex.Message); - while (ex.InnerException != null) - { - str.Append(ex.InnerException.Message); - ex = ex.InnerException; - } - msg.gxTpr_Description = str.ToString(); - } - else - { - msg.gxTpr_Description = error; - } + msg.gxTpr_Description = error; messages.Add(msg); } } @@ -56,18 +64,23 @@ public static bool CompressFiles(List files, string path, string format, { if (files.Count == 0) { - GXLogging.Error(log, "No files have been added for compression."); - StorageMessages(null, "No files have been added for compression.", messages); + GXLogging.Error(log, $"{GetMessage("NO_FILES_ADDED")}"); + StorageMessages($"{GetMessage("NO_FILES_ADDED")}", messages); return false; } - FileInfo[] toCompress = new FileInfo[files.Count]; int index = 0; foreach (string filePath in files) { - toCompress[index++] = new FileInfo(filePath); + FileInfo file = new FileInfo(filePath); + if (!file.Exists) + { + GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}{filePath}"); + StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + filePath, messages); + continue; + } + toCompress[index++] = file; } - try { CompressionFormat compressionFormat = GetCompressionFormat(format); @@ -75,31 +88,31 @@ public static bool CompressFiles(List files, string path, string format, { case CompressionFormat.ZIP: CompressToZip(toCompress, path); - return true; + break; case CompressionFormat.TAR: CompressToTar(toCompress, path); - return true; + break; case CompressionFormat.GZIP: CompressToGzip(toCompress, path); - return true; + break; case CompressionFormat.JAR: CompressToJar(toCompress, path); - return true; + break; } + return true; } catch (ArgumentException ae) { - GXLogging.Error(log, "Unsupported compression format for compression: " + format, ae); - StorageMessages(ae, "", messages); + GXLogging.Error(log, $"{GetMessage("UNSUPPORTED_FORMAT")}" + format, ae); + StorageMessages(ae.Message, messages); return false; } catch (Exception e) { - GXLogging.Error(log, "An error occurred during the compression process: ", e); - StorageMessages(e, "", messages); + GXLogging.Error(log, $"{GetMessage("GENERIC_ERROR")}", e); + StorageMessages(e.Message, messages); return false; } - return false; } public static bool CompressFolder(string folder, string path, string format, ref GXBaseCollection messages) @@ -107,8 +120,8 @@ public static bool CompressFolder(string folder, string path, string format, ref DirectoryInfo toCompress = new DirectoryInfo(folder); if (!toCompress.Exists) { - GXLogging.Error(log, "The specified folder does not exist: {0}", toCompress.FullName); - StorageMessages(null, "The specified folder does not exist: " + toCompress.FullName, messages); + GXLogging.Error(log, $"{GetMessage("FOLDER_NOT_EXISTS")}", toCompress.FullName); + StorageMessages($"{GetMessage("FOLDER_NOT_EXISTS")}" + toCompress.FullName, messages); return false; } List list = new List { folder }; @@ -125,14 +138,14 @@ public static bool Decompress(string file, string path, ref GXBaseCollection Date: Thu, 25 Jul 2024 12:21:02 -0300 Subject: [PATCH 12/23] Fix zip slip security issue --- dotnet/GxCompress/GXCompressor.cs | 37 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs index 8ad34075a..d2711147f 100644 --- a/dotnet/GxCompress/GXCompressor.cs +++ b/dotnet/GxCompress/GXCompressor.cs @@ -3,7 +3,6 @@ using System.IO; using System.IO.Compression; using System.Security; -using System.Text; using GeneXus; using GeneXus.Utils; using ICSharpCode.SharpZipLib.Tar; @@ -304,7 +303,7 @@ private static void CompressToGzip(FileInfo[] files, string outputPath) private static void DecompressZip(FileInfo zipFile, string directory) { string zipFilePath = zipFile.FullName; - string targetDir = Path.GetFullPath(directory); + string targetDir = Path.GetFullPath(directory) + Path.DirectorySeparatorChar; try { @@ -313,30 +312,28 @@ private static void DecompressZip(FileInfo zipFile, string directory) { foreach (ZipArchiveEntry entry in zipIn.Entries) { - string resolvedPath = Path.Combine(targetDir, entry.FullName); - - if (!resolvedPath.StartsWith(targetDir)) + string rawOutputPath = Path.Combine(targetDir, entry.FullName); + string resolvedOutputPath = Path.GetFullPath(rawOutputPath); + if (!resolvedOutputPath.StartsWith(targetDir, StringComparison.Ordinal)) { throw new SecurityException("Zip entry is outside of the target dir: " + entry.FullName); } - string fullResolvedPath = Path.GetFullPath(resolvedPath); - - if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) + if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { - if (!Directory.Exists(fullResolvedPath)) + if (!Directory.Exists(resolvedOutputPath)) { - Directory.CreateDirectory(fullResolvedPath); + Directory.CreateDirectory(resolvedOutputPath); } } else { - string parentDir = Path.GetDirectoryName(fullResolvedPath); + string parentDir = Path.GetDirectoryName(resolvedOutputPath); if (!Directory.Exists(parentDir)) { Directory.CreateDirectory(parentDir); } - using (FileStream outFile = new FileStream(fullResolvedPath, FileMode.Create, FileAccess.Write)) + using (FileStream outFile = new FileStream(resolvedOutputPath, FileMode.Create, FileAccess.Write)) { entry.Open().CopyTo(outFile); } @@ -346,10 +343,9 @@ private static void DecompressZip(FileInfo zipFile, string directory) } catch (IOException e) { - GXLogging.Error(log, "Error while decompressing to zip", e); + GXLogging.Error(log, "Error while decompressing zip", e); throw new Exception("Failed to decompress ZIP file: " + zipFilePath, e); } - } private static void DecompressTar(FileInfo file, string outputPath) @@ -489,7 +485,7 @@ private static void CompressToJar(FileInfo[] files, string outputPath) } } - public static void DecompressJar(FileInfo jarFile, string outputPath) + private static void DecompressJar(FileInfo jarFile, string outputPath) { if (!jarFile.Exists) { @@ -502,14 +498,20 @@ public static void DecompressJar(FileInfo jarFile, string outputPath) outputDir.Create(); } + string resolvedOutputPath = Path.GetFullPath(outputPath) + Path.DirectorySeparatorChar; + using (FileStream jarFileStream = jarFile.OpenRead()) using (ZipArchive jis = new ZipArchive(jarFileStream, ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in jis.Entries) { string entryPath = Path.Combine(outputPath, entry.FullName); - FileInfo outputFile = new FileInfo(entryPath); - + string resolvedEntryPath = Path.GetFullPath(entryPath); + if (!resolvedEntryPath.StartsWith(resolvedOutputPath, StringComparison.Ordinal)) + { + throw new IOException("Entry is outside the target directory: " + entry.FullName); + } + FileInfo outputFile = new FileInfo(resolvedEntryPath); if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) { if (!Directory.Exists(outputFile.FullName)) @@ -531,6 +533,7 @@ public static void DecompressJar(FileInfo jarFile, string outputPath) } } } + public static void DecompressRar(FileInfo rarFile, string destinationPath) { using (var archive = RarArchive.Open(rarFile.FullName)) From 53db81dcf894e9f86a91a475d0c04cc5605542c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:47:22 -0300 Subject: [PATCH 13/23] Move GxCompress.csproj to src/dotnetframework directory --- dotnet/DotNetStandardClasses.sln | 12 ++++++------ .../{ => src/dotnetcore}/GxCompress/Compression.cs | 0 .../{ => src/dotnetcore}/GxCompress/GXCompressor.cs | 0 .../dotnetcore}/GxCompress/GxCompress.csproj | 3 +-- .../{ => src/dotnetcore}/GxCompress/IGXCompressor.cs | 0 .../DotNetCoreUnitTest/DotNetCoreUnitTest.csproj | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) rename dotnet/{ => src/dotnetcore}/GxCompress/Compression.cs (100%) rename dotnet/{ => src/dotnetcore}/GxCompress/GXCompressor.cs (100%) rename dotnet/{ => src/dotnetcore}/GxCompress/GxCompress.csproj (81%) rename dotnet/{ => src/dotnetcore}/GxCompress/IGXCompressor.cs (100%) diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 978ba522c..30219b2e8 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -263,7 +263,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GxCompress", "GxCompress\GxCompress.csproj", "{02C726EC-5923-4852-999D-AF9406A1ACBB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxCompress", "src\dotnetcore\GxCompress\GxCompress.csproj", "{7BBB4E94-8F79-42C0-9573-A4EB35E68503}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -643,10 +643,10 @@ Global {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.Build.0 = Release|Any CPU + {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -773,7 +773,7 @@ Global {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} - {02C726EC-5923-4852-999D-AF9406A1ACBB} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} + {7BBB4E94-8F79-42C0-9573-A4EB35E68503} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/src/dotnetcore/GxCompress/Compression.cs similarity index 100% rename from dotnet/GxCompress/Compression.cs rename to dotnet/src/dotnetcore/GxCompress/Compression.cs diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/src/dotnetcore/GxCompress/GXCompressor.cs similarity index 100% rename from dotnet/GxCompress/GXCompressor.cs rename to dotnet/src/dotnetcore/GxCompress/GXCompressor.cs diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj similarity index 81% rename from dotnet/GxCompress/GxCompress.csproj rename to dotnet/src/dotnetcore/GxCompress/GxCompress.csproj index 6cca9353e..7f601439e 100644 --- a/dotnet/GxCompress/GxCompress.csproj +++ b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj @@ -7,7 +7,6 @@ GeneXus.Compression true 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 - $(TargetsForTfmSpecificContentInPackage);CustomContentTarget @@ -20,7 +19,7 @@ - + diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/src/dotnetcore/GxCompress/IGXCompressor.cs similarity index 100% rename from dotnet/GxCompress/IGXCompressor.cs rename to dotnet/src/dotnetcore/GxCompress/IGXCompressor.cs diff --git a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj index 7e3c2c5f2..e8b518d58 100644 --- a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj +++ b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj @@ -78,10 +78,10 @@ - + From 527061a90ac9b2e616e4c40b585beccd25a67ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:52:44 -0300 Subject: [PATCH 14/23] Move GxCompress.csproj to src/dotnetframework directory --- dotnet/DotNetStandardClasses.sln | 12 ++++++------ .../{src/dotnetcore => }/GxCompress/Compression.cs | 0 .../{src/dotnetcore => }/GxCompress/GXCompressor.cs | 0 .../dotnetcore => }/GxCompress/GxCompress.csproj | 3 ++- .../{src/dotnetcore => }/GxCompress/IGXCompressor.cs | 0 .../DotNetCoreUnitTest/DotNetCoreUnitTest.csproj | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) rename dotnet/{src/dotnetcore => }/GxCompress/Compression.cs (100%) rename dotnet/{src/dotnetcore => }/GxCompress/GXCompressor.cs (100%) rename dotnet/{src/dotnetcore => }/GxCompress/GxCompress.csproj (81%) rename dotnet/{src/dotnetcore => }/GxCompress/IGXCompressor.cs (100%) diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 30219b2e8..978ba522c 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -263,7 +263,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxCompress", "src\dotnetcore\GxCompress\GxCompress.csproj", "{7BBB4E94-8F79-42C0-9573-A4EB35E68503}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GxCompress", "GxCompress\GxCompress.csproj", "{02C726EC-5923-4852-999D-AF9406A1ACBB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -643,10 +643,10 @@ Global {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU - {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7BBB4E94-8F79-42C0-9573-A4EB35E68503}.Release|Any CPU.Build.0 = Release|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -773,7 +773,7 @@ Global {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} - {7BBB4E94-8F79-42C0-9573-A4EB35E68503} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} + {02C726EC-5923-4852-999D-AF9406A1ACBB} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcore/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs similarity index 100% rename from dotnet/src/dotnetcore/GxCompress/Compression.cs rename to dotnet/GxCompress/Compression.cs diff --git a/dotnet/src/dotnetcore/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs similarity index 100% rename from dotnet/src/dotnetcore/GxCompress/GXCompressor.cs rename to dotnet/GxCompress/GXCompressor.cs diff --git a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj b/dotnet/GxCompress/GxCompress.csproj similarity index 81% rename from dotnet/src/dotnetcore/GxCompress/GxCompress.csproj rename to dotnet/GxCompress/GxCompress.csproj index 7f601439e..6cca9353e 100644 --- a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj +++ b/dotnet/GxCompress/GxCompress.csproj @@ -7,6 +7,7 @@ GeneXus.Compression true 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 + $(TargetsForTfmSpecificContentInPackage);CustomContentTarget @@ -19,7 +20,7 @@ - + diff --git a/dotnet/src/dotnetcore/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs similarity index 100% rename from dotnet/src/dotnetcore/GxCompress/IGXCompressor.cs rename to dotnet/GxCompress/IGXCompressor.cs diff --git a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj index e8b518d58..7e3c2c5f2 100644 --- a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj +++ b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj @@ -78,10 +78,10 @@ + - From 3dfbd7d7fef61e82fddc6ac139d7e790da6820b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:45:16 -0300 Subject: [PATCH 15/23] Update GxCompress.csproj --- dotnet/GxCompress/GxCompress.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/GxCompress/GxCompress.csproj index 6cca9353e..5eadf8761 100644 --- a/dotnet/GxCompress/GxCompress.csproj +++ b/dotnet/GxCompress/GxCompress.csproj @@ -7,7 +7,6 @@ GeneXus.Compression true 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 - $(TargetsForTfmSpecificContentInPackage);CustomContentTarget From 02e50a3c535800e0b63ec70628aa255d78377beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:57:25 +0900 Subject: [PATCH 16/23] Rework --- dotnet/DotNetStandardClasses.sln | 22 +- dotnet/GxCompress/Compression.cs | 87 --- dotnet/GxCompress/GXCompressor.cs | 566 ------------------ dotnet/GxCompress/IGXCompressor.cs | 14 - .../dotnetcore/GxCompress/GxCompress.csproj | 31 + .../dotnetframework/GxCompress/Compression.cs | 41 ++ .../GxCompress/GXCompressor.cs | 523 ++++++++++++++++ .../GxCompress/GxCompress.csproj | 9 +- .../GxCompress/IGXCompressor.cs | 13 + .../DotNetCoreUnitTest/CompressionTests.cs | 76 +-- 10 files changed, 629 insertions(+), 753 deletions(-) delete mode 100644 dotnet/GxCompress/Compression.cs delete mode 100644 dotnet/GxCompress/GXCompressor.cs delete mode 100644 dotnet/GxCompress/IGXCompressor.cs create mode 100644 dotnet/src/dotnetcore/GxCompress/GxCompress.csproj create mode 100644 dotnet/src/dotnetframework/GxCompress/Compression.cs create mode 100644 dotnet/src/dotnetframework/GxCompress/GXCompressor.cs rename dotnet/{ => src/dotnetframework}/GxCompress/GxCompress.csproj (71%) create mode 100644 dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 978ba522c..0d80dbea3 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -263,7 +263,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GxCompress", "GxCompress\GxCompress.csproj", "{02C726EC-5923-4852-999D-AF9406A1ACBB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxCompress", "src\dotnetframework\GxCompress\GxCompress.csproj", "{CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D}" + ProjectSection(ProjectDependencies) = postProject + {52ADD4DB-FBFE-4686-9E5F-830CDF859830} = {52ADD4DB-FBFE-4686-9E5F-830CDF859830} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxCompress", "src\dotnetcore\GxCompress\GxCompress.csproj", "{AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -643,10 +648,14 @@ Global {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {02C726EC-5923-4852-999D-AF9406A1ACBB}.Release|Any CPU.Build.0 = Release|Any CPU + {CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D}.Release|Any CPU.Build.0 = Release|Any CPU + {AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -773,7 +782,8 @@ Global {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} - {02C726EC-5923-4852-999D-AF9406A1ACBB} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} + {CF7E0F66-1B8F-4A08-81C5-B87E70EAB53D} = {F900A4AD-7249-41B4-B918-CB9E8C73747C} + {AADC2CE2-606B-4CC0-81E6-3E3CCC4F1BDA} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/GxCompress/Compression.cs b/dotnet/GxCompress/Compression.cs deleted file mode 100644 index 5c46acdb4..000000000 --- a/dotnet/GxCompress/Compression.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using GeneXus; -using GeneXus.Utils; - -namespace Genexus.Compression -{ - public class Compression - { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(Compression).FullName); - - private string path; - private string format; - private GXBaseCollection messages; - private List filesToCompress; - - public Compression() - { - filesToCompress = new List(); - } - - public Compression(string path, string format, ref GXBaseCollection messages) - { - this.path = path; - this.format = format; - this.messages = messages; - filesToCompress = new List(); - } - - public void SetPath(string path) - { - this.path = path; - } - - public void SetFormat(string format) - { - this.format = format; - } - - public void AddFile(string filePath) - { - FileInfo file = new FileInfo(filePath); - if (file.Exists) - { - filesToCompress.Add(file); - } - else - { - GXLogging.Error(log, $"File does not exist: {0}", file.FullName); - } - } - - public void AddFolder(string folderPath) - { - try - { - var files = Directory.EnumerateFiles(folderPath, "*", SearchOption.AllDirectories); - foreach (string file in files) - { - AddFile(Path.GetFullPath(file)); - } - } - catch (Exception e) - { - GXLogging.Error(log, $"Failed to add foler", e); - } - } - - public bool Save() - { - List paths = new List(); - foreach (FileInfo file in filesToCompress) - { - paths.Add(file.FullName); - } - return GXCompressor.CompressFiles(paths, path, format, ref this.messages); - } - - public void Clear() - { - this.path = string.Empty; - this.format = string.Empty; - filesToCompress = new List(); - } - } -} diff --git a/dotnet/GxCompress/GXCompressor.cs b/dotnet/GxCompress/GXCompressor.cs deleted file mode 100644 index d2711147f..000000000 --- a/dotnet/GxCompress/GXCompressor.cs +++ /dev/null @@ -1,566 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Security; -using GeneXus; -using GeneXus.Utils; -using ICSharpCode.SharpZipLib.Tar; -using SharpCompress.Archives; -using SharpCompress.Archives.Rar; -using SharpCompress.Archives.SevenZip; -using SharpCompress.Common; - -namespace Genexus.Compression -{ - public enum CompressionFormat - { - GZIP, - TAR, - ZIP, - JAR - } - - public class GXCompressor : IGXCompressor - { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); - - private static readonly Dictionary errorMessages = new Dictionary(); - - static GXCompressor() - { - errorMessages.Add("GENERIC_ERROR", "An error occurred during the compression/decompression process: "); - errorMessages.Add("NO_FILES_ADDED", "No files have been added for compression."); - errorMessages.Add("FILE_NOT_EXISTS", "File does not exist: "); - errorMessages.Add("FOLDER_NOT_EXISTS", "The specified folder does not exist: "); - errorMessages.Add("UNSUPPORTED_FORMAT", "Unsupported compression/decompression format: "); - errorMessages.Add("EMPTY_FILE", "The selected file is empty: "); - } - - public static string GetMessage(string key) - { - if (errorMessages.TryGetValue(key, out string value)) - { - return value; - } - return "An error ocurred in the compression/decompression process: "; - } - - private static void StorageMessages(string error, GXBaseCollection messages) - { - if (messages != null && messages.Count > 0) - { - SdtMessages_Message msg = new() - { - gxTpr_Type = 1 - }; - msg.gxTpr_Description = error; - messages.Add(msg); - } - } - - public static bool CompressFiles(List files, string path, string format, ref GXBaseCollection messages) - { - if (files.Count == 0) - { - GXLogging.Error(log, $"{GetMessage("NO_FILES_ADDED")}"); - StorageMessages($"{GetMessage("NO_FILES_ADDED")}", messages); - return false; - } - FileInfo[] toCompress = new FileInfo[files.Count]; - int index = 0; - foreach (string filePath in files) - { - FileInfo file = new FileInfo(filePath); - if (!file.Exists) - { - GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}{filePath}"); - StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + filePath, messages); - continue; - } - toCompress[index++] = file; - } - try - { - CompressionFormat compressionFormat = GetCompressionFormat(format); - switch (compressionFormat) - { - case CompressionFormat.ZIP: - CompressToZip(toCompress, path); - break; - case CompressionFormat.TAR: - CompressToTar(toCompress, path); - break; - case CompressionFormat.GZIP: - CompressToGzip(toCompress, path); - break; - case CompressionFormat.JAR: - CompressToJar(toCompress, path); - break; - } - return true; - } - catch (ArgumentException ae) - { - GXLogging.Error(log, $"{GetMessage("UNSUPPORTED_FORMAT")}" + format, ae); - StorageMessages(ae.Message, messages); - return false; - } - catch (Exception e) - { - GXLogging.Error(log, $"{GetMessage("GENERIC_ERROR")}", e); - StorageMessages(e.Message, messages); - return false; - } - } - - public static bool CompressFolder(string folder, string path, string format, ref GXBaseCollection messages) - { - DirectoryInfo toCompress = new DirectoryInfo(folder); - if (!toCompress.Exists) - { - GXLogging.Error(log, $"{GetMessage("FOLDER_NOT_EXISTS")}", toCompress.FullName); - StorageMessages($"{GetMessage("FOLDER_NOT_EXISTS")}" + toCompress.FullName, messages); - return false; - } - List list = new List { folder }; - return CompressFiles(list, path, format, ref messages); - } - - public static Compression NewCompression(string path, string format, ref GXBaseCollection messages) - { - return new Compression(path, format, ref messages); - } - - public static bool Decompress(string file, string path, ref GXBaseCollection messages) - { - FileInfo toCompress = new FileInfo(file); - if (!toCompress.Exists) - { - GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}" + toCompress.FullName); - StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + toCompress.FullName, messages); - return false; - } - if (toCompress.Length == 0) - { - GXLogging.Error(log, $"{GetMessage("EMPTY_FILE")}"); - StorageMessages($"{GetMessage("EMPTY_FILE")}", messages); - return false; - } - string extension = Path.GetExtension(toCompress.Name).Substring(1); - try - { - switch (extension.ToLower()) - { - case "zip": - DecompressZip(toCompress, path); - break; - case "tar": - DecompressTar(toCompress, path); - break; - case "gz": - DecompressGzip(toCompress, path); - break; - case "jar": - DecompressJar(toCompress, path); - break; - case "rar": - DecompressRar(toCompress, path); - break; - case "7z": - Decompress7z(toCompress, path); - break; - default: - GXLogging.Error(log, $"{GetMessage("UNSUPPORTED_FORMAT")}" + extension); - StorageMessages($"{GetMessage("UNSUPPORTED_FORMAT")}" + extension, messages); - return false; - } - return true; - } - catch (Exception e) - { - GXLogging.Error(log, $"{GetMessage("GENERIC_ERROR")}", e); - StorageMessages(e.Message, messages); - return false; - } - } - - private static void CompressToZip(FileInfo[] files, string outputPath) - { - using (FileStream fos = new FileStream(outputPath, FileMode.Create)) - using (ZipArchive zos = new ZipArchive(fos, ZipArchiveMode.Create)) - { - foreach (FileInfo file in files) - { - if (file.Exists) - { - AddFileToZip(zos, file, string.Empty); - } - } - } - } - - private static void AddFileToZip(ZipArchive zos, FileInfo file, string baseDir) - { - string entryName = baseDir + file.Name; - if ((file.Attributes & FileAttributes.Directory) == FileAttributes.Directory) - { - DirectoryInfo dirInfo = file.Directory; - foreach (DirectoryInfo dir in dirInfo.GetDirectories()) - { - AddFileToZip(zos, new FileInfo(dir.FullName), entryName + Path.DirectorySeparatorChar); - } - foreach (FileInfo childFile in dirInfo.GetFiles()) - { - AddFileToZip(zos, childFile, entryName + Path.DirectorySeparatorChar); - } - } - else - { - ZipArchiveEntry zipEntry = zos.CreateEntryFromFile(file.FullName, entryName); - } - } - - private static void CompressToTar(FileInfo[] files, string outputPath) - { - FileInfo outputTarFile = new FileInfo(outputPath); - try - { - using (FileStream fos = outputTarFile.Create()) - using (TarOutputStream taos = new TarOutputStream(fos)) - { - taos.IsStreamOwner = false; - - foreach (FileInfo file in files) - { - AddFileToTar(taos, file, string.Empty); - } - taos.Close(); - } - } - catch (Exception e) - { - GXLogging.Error(log, "Error while compressing to tar", e); - throw new Exception("Error creating TAR archive", e); - } - } - - private static void AddFileToTar(TarOutputStream taos, FileInfo file, string baseDir) - { - if ((file.Attributes & FileAttributes.Directory) == FileAttributes.Directory) - { - DirectoryInfo dir = new DirectoryInfo(file.FullName); - FileInfo[] children = dir.GetFiles(); - DirectoryInfo[] directories = dir.GetDirectories(); - foreach (DirectoryInfo childDir in directories) - { - AddFileToTar(taos, new FileInfo(childDir.FullName), Path.Combine(baseDir, file.Name, Path.DirectorySeparatorChar.ToString())); - } - foreach (FileInfo childFile in children) - { - AddFileToTar(taos, childFile, Path.Combine(baseDir, file.Name, Path.DirectorySeparatorChar.ToString())); - } - } - else - { - string entryName = baseDir + file.Name; - TarEntry entry = TarEntry.CreateEntryFromFile(file.FullName); - entry.Name = entryName; - entry.Size = file.Length; - taos.PutNextEntry(entry); - - using (FileStream fis = File.OpenRead(file.FullName)) - { - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = fis.Read(buffer, 0, buffer.Length)) > 0) - { - taos.Write(buffer, 0, bytesRead); - } - } - taos.CloseEntry(); - } - } - - private static void CompressToGzip(FileInfo[] files, string outputPath) - { - if (files.Length > 1) - { - throw new ArgumentException("GZIP does not support multiple files. Consider archiving the files first."); - } - - FileInfo inputFile = files[0]; - FileInfo outputFile = new FileInfo(outputPath); - - using (FileStream inStream = inputFile.OpenRead()) - using (FileStream fout = outputFile.Create()) - using (GZipStream gzOut = new GZipStream(fout, CompressionMode.Compress)) - { - inStream.CopyTo(gzOut); - } - } - - private static void DecompressZip(FileInfo zipFile, string directory) - { - string zipFilePath = zipFile.FullName; - string targetDir = Path.GetFullPath(directory) + Path.DirectorySeparatorChar; - - try - { - using (FileStream fis = File.OpenRead(zipFilePath)) - using (ZipArchive zipIn = new ZipArchive(fis, ZipArchiveMode.Read)) - { - foreach (ZipArchiveEntry entry in zipIn.Entries) - { - string rawOutputPath = Path.Combine(targetDir, entry.FullName); - string resolvedOutputPath = Path.GetFullPath(rawOutputPath); - if (!resolvedOutputPath.StartsWith(targetDir, StringComparison.Ordinal)) - { - throw new SecurityException("Zip entry is outside of the target dir: " + entry.FullName); - } - - if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) - { - if (!Directory.Exists(resolvedOutputPath)) - { - Directory.CreateDirectory(resolvedOutputPath); - } - } - else - { - string parentDir = Path.GetDirectoryName(resolvedOutputPath); - if (!Directory.Exists(parentDir)) - { - Directory.CreateDirectory(parentDir); - } - using (FileStream outFile = new FileStream(resolvedOutputPath, FileMode.Create, FileAccess.Write)) - { - entry.Open().CopyTo(outFile); - } - } - } - } - } - catch (IOException e) - { - GXLogging.Error(log, "Error while decompressing zip", e); - throw new Exception("Failed to decompress ZIP file: " + zipFilePath, e); - } - } - - private static void DecompressTar(FileInfo file, string outputPath) - { - string targetDir = Path.GetFullPath(outputPath); - try - { - using (FileStream fi = File.OpenRead(file.FullName)) - using (TarInputStream ti = new TarInputStream(fi)) - { - TarEntry entry; - while ((entry = ti.GetNextEntry()) != null) - { - string entryPath = Path.Combine(targetDir, entry.Name); - FileInfo outputFile = new FileInfo(Path.GetFullPath(entryPath)); - - if (!outputFile.FullName.StartsWith(targetDir)) - { - throw new IOException("Entry is outside of the target directory: " + entry.Name); - } - - if (entry.IsDirectory) - { - if (!outputFile.Exists) - { - Directory.CreateDirectory(outputFile.FullName); - } - } - else - { - DirectoryInfo parent = outputFile.Directory; - if (!parent.Exists) - { - Directory.CreateDirectory(parent.FullName); - } - - using (FileStream outStream = File.Create(outputFile.FullName)) - { - ti.CopyEntryContents(outStream); - } - } - } - } - } - catch (IOException e) - { - GXLogging.Error(log, "Error while decompressing to zip", e); - throw new Exception("Failed to decompress TAR file: " + file.Name, e); - } - } - - private static void DecompressGzip(FileInfo inputFile, string outputPath) - { - DirectoryInfo outputDir = new DirectoryInfo(outputPath); - if (!outputDir.Exists) - { - outputDir.Create(); - } - - string outputFileName = inputFile.Name; - if (outputFileName.EndsWith(".gz")) - outputFileName = outputFileName.Substring(0, outputFileName.Length - 3); - else - throw new ArgumentException("The input file does not have a .gz extension."); - - FileInfo outputFile = new FileInfo(Path.Combine(outputDir.FullName, outputFileName)); - using (FileStream fis = inputFile.OpenRead()) - using (GZipStream gzis = new GZipStream(fis, CompressionMode.Decompress)) - using (FileStream fos = outputFile.Create()) - { - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = gzis.Read(buffer, 0, buffer.Length)) > 0) - { - fos.Write(buffer, 0, bytesRead); - } - } - } - - private static void Decompress7z(FileInfo file, string outputPath) - { - string targetDir = Path.GetFullPath(outputPath); - using (var sevenZFile = SevenZipArchive.Open(file.FullName)) - { - foreach (var entry in sevenZFile.Entries) - { - if (!entry.IsDirectory) - { - string resolvedPath = Path.Combine(targetDir, entry.Key); - FileInfo outputFile = new FileInfo(resolvedPath); - - if (!outputFile.FullName.StartsWith(targetDir, StringComparison.OrdinalIgnoreCase)) - { - throw new IOException("Entry is outside of the target dir: " + entry.Key); - } - - Directory.CreateDirectory(outputFile.DirectoryName); - - using (var outStream = outputFile.Open(FileMode.Create, FileAccess.Write)) - { - entry.WriteTo(outStream); - } - } - else - { - string dirPath = Path.Combine(targetDir, entry.Key); - if (!Directory.Exists(dirPath)) - { - Directory.CreateDirectory(dirPath); - } - } - } - } - } - - private static void CompressToJar(FileInfo[] files, string outputPath) - { - using (FileStream outputStream = new FileStream(outputPath, FileMode.Create)) - using (ZipArchive jos = new ZipArchive(outputStream, ZipArchiveMode.Create)) - { - byte[] buffer = new byte[1024]; - foreach (FileInfo file in files) - { - using (FileStream fis = file.OpenRead()) - { - ZipArchiveEntry entry = jos.CreateEntry(file.Name); - using (Stream entryStream = entry.Open()) - { - int length; - while ((length = fis.Read(buffer, 0, buffer.Length)) > 0) - { - entryStream.Write(buffer, 0, length); - } - } - } - } - } - } - - private static void DecompressJar(FileInfo jarFile, string outputPath) - { - if (!jarFile.Exists) - { - throw new IOException("The jar file does not exist."); - } - - DirectoryInfo outputDir = new DirectoryInfo(outputPath); - if (!outputDir.Exists) - { - outputDir.Create(); - } - - string resolvedOutputPath = Path.GetFullPath(outputPath) + Path.DirectorySeparatorChar; - - using (FileStream jarFileStream = jarFile.OpenRead()) - using (ZipArchive jis = new ZipArchive(jarFileStream, ZipArchiveMode.Read)) - { - foreach (ZipArchiveEntry entry in jis.Entries) - { - string entryPath = Path.Combine(outputPath, entry.FullName); - string resolvedEntryPath = Path.GetFullPath(entryPath); - if (!resolvedEntryPath.StartsWith(resolvedOutputPath, StringComparison.Ordinal)) - { - throw new IOException("Entry is outside the target directory: " + entry.FullName); - } - FileInfo outputFile = new FileInfo(resolvedEntryPath); - if (entry.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - if (!Directory.Exists(outputFile.FullName)) - { - Directory.CreateDirectory(outputFile.FullName); - } - } - else - { - Directory.CreateDirectory(outputFile.DirectoryName); - using (FileStream fos = new FileStream(outputFile.FullName, FileMode.Create)) - { - using (Stream entryStream = entry.Open()) - { - entryStream.CopyTo(fos); - } - } - } - } - } - } - - public static void DecompressRar(FileInfo rarFile, string destinationPath) - { - using (var archive = RarArchive.Open(rarFile.FullName)) - { - foreach (var entry in archive.Entries) - { - if (!entry.IsDirectory) - { - entry.WriteToDirectory(destinationPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); - } - } - } - } - - private static CompressionFormat GetCompressionFormat(string format) - { - try - { - return (CompressionFormat)Enum.Parse(typeof(CompressionFormat), format.ToUpper()); - } - catch (ArgumentException) - { - throw new ArgumentException("Invalid compression format: " + format); - } - } - - } - -} - diff --git a/dotnet/GxCompress/IGXCompressor.cs b/dotnet/GxCompress/IGXCompressor.cs deleted file mode 100644 index ec92479af..000000000 --- a/dotnet/GxCompress/IGXCompressor.cs +++ /dev/null @@ -1,14 +0,0 @@ -using GeneXus.Utils; -using System.Collections.Generic; - -namespace Genexus.Compression -{ - public interface IGXCompressor - { - static bool CompressFiles(List files, string path, string format, ref GXBaseCollection messages) => false; - static bool CompressFolder(string folder, string path, string format, ref GXBaseCollection messages) => false; - static Compression NewCompression(string path, string format, int dictionarySize, ref GXBaseCollection messages) => new Compression(); - static bool Decompress(string file, string path, ref GXBaseCollection messages) => false; - } -} - diff --git a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj new file mode 100644 index 000000000..854e58980 --- /dev/null +++ b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj @@ -0,0 +1,31 @@ + + + + net6.0;net8.0 + Library + NETCORE + GeneXus.Compression.Core + true + 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetframework/GxCompress/Compression.cs b/dotnet/src/dotnetframework/GxCompress/Compression.cs new file mode 100644 index 000000000..b54ca012b --- /dev/null +++ b/dotnet/src/dotnetframework/GxCompress/Compression.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using GeneXus.Utils; + +namespace Genexus.Compression +{ + public class Compression + { + + private string path; + private List filesToCompress; + private GXBaseCollection messages; + + public Compression() + { + filesToCompress = new List(); + } + + public Compression(string path, ref GXBaseCollection messages) + { + this.path = path; + this.messages = messages; + filesToCompress = new List(); + } + + public void AddElement(string filePath) + { + filesToCompress.Add(filePath); + } + + public bool Save() + { + return GXCompressor.Compress(filesToCompress, path, ref this.messages); + } + + public void Clear() + { + this.path = string.Empty; + filesToCompress = new List(); + } + } +} diff --git a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs new file mode 100644 index 000000000..d52164383 --- /dev/null +++ b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs @@ -0,0 +1,523 @@ +using System; +using System.Collections.Generic; +#if !NETCORE +using System.Formats.Tar; +#endif +using System.IO; +using System.IO.Compression; +using GeneXus; +using GeneXus.Utils; +using SharpCompress.Archives; +using SharpCompress.Archives.Rar; +using SharpCompress.Common; +using SharpCompress.Writers.Tar; + +namespace Genexus.Compression +{ + public class GXCompressor : IGXCompressor + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); + + private static readonly Dictionary errorMessages = new Dictionary(); + + static GXCompressor() + { + errorMessages.Add("GENERIC_ERROR", "An error occurred during the compression/decompression process: "); + errorMessages.Add("NO_FILES_ADDED", "No files have been added for compression."); + errorMessages.Add("FILE_NOT_EXISTS", "File does not exist: "); + errorMessages.Add("FOLDER_NOT_EXISTS", "The specified folder does not exist: "); + errorMessages.Add("UNSUPPORTED_FORMAT", "Unsupported compression/decompression format: "); + errorMessages.Add("EMPTY_FILE", "The selected file is empty: "); + } + + public static string GetMessage(string key) + { + if (errorMessages.TryGetValue(key, out string value)) + { + return value; + } + return "An error occurred in the compression/decompression process: "; + } + + private static void StorageMessages(string error, GXBaseCollection messages) + { + if (messages != null) + { + SdtMessages_Message msg = new SdtMessages_Message() + { + gxTpr_Type = 1, + gxTpr_Description = error + }; + messages.Add(msg); + } + } + + public static bool Compress(List files, string path, ref GXBaseCollection messages) + { + if (files.Count == 0) + { + GXLogging.Error(log, $"{GetMessage("NO_FILES_ADDED")}"); + StorageMessages($"{GetMessage("NO_FILES_ADDED")}", messages); + return false; + } + + List toCompress = new List(); + foreach (string filePath in files) + { + FileInfo file = new FileInfo(filePath); + if (!file.Exists) + { + GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}{filePath}"); + StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + filePath, messages); + continue; + } + toCompress.Add(file); + } + + if (toCompress.Count == 0) + { + GXLogging.Error(log, $"{GetMessage("NO_FILES_ADDED")}"); + StorageMessages($"{GetMessage("NO_FILES_ADDED")}", messages); + return false; + } + + try + { + string compressionFormat = FileUtil.GetFileType(path).ToLower(); + + switch (compressionFormat) + { + case "zip": + CompressToZip(toCompress.ToArray(), path); + break; + case "tar": + CompressToTar(toCompress.ToArray(), path); + break; + case "gz": + CompressToGzip(toCompress.ToArray(), path); + break; + case "jar": + CompressToJar(toCompress.ToArray(), path); + break; + default: + GXLogging.Error(log, $"{GetMessage("UNSUPPORTED_FORMAT")}" + compressionFormat); + StorageMessages($"{GetMessage("UNSUPPORTED_FORMAT")}" + compressionFormat, messages); + return false; + } + return true; + } + catch (Exception e) + { + GXLogging.Error(log, $"{GetMessage("GENERIC_ERROR")}", e); + StorageMessages(e.Message, messages); + return false; + } + } + + public static Compression NewCompression(string path, string format, ref GXBaseCollection messages) + { + return new Compression(path, ref messages); + } + + public static bool Decompress(string file, string path, ref GXBaseCollection messages) + { + FileInfo fileInfo = new FileInfo(file); + if (!fileInfo.Exists) + { + GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}" + fileInfo.FullName); + StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + fileInfo.FullName, messages); + return false; + } + if (fileInfo.Length == 0) + { + GXLogging.Error(log, $"{GetMessage("EMPTY_FILE")}"); + StorageMessages($"{GetMessage("EMPTY_FILE")}", messages); + return false; + } + string extension = FileUtil.GetFileType(fileInfo.Name).ToLower(); + try + { + switch (extension) + { + case "zip": + DecompressZip(fileInfo, path); + break; + case "tar": + DecompressTar(fileInfo, path); + break; + case "gz": + DecompressGzip(fileInfo, path); + break; + case "jar": + DecompressJar(fileInfo, path); + break; + case "rar": + DecompressRar(fileInfo, path); + break; + case "7z": + Decompress7z(fileInfo, path); + break; + default: + GXLogging.Error(log, $"{GetMessage("UNSUPPORTED_FORMAT")}" + extension); + StorageMessages($"{GetMessage("UNSUPPORTED_FORMAT")}" + extension, messages); + return false; + } + return true; + } + catch (Exception e) + { + GXLogging.Error(log, $"{GetMessage("GENERIC_ERROR")}", e); + StorageMessages(e.Message, messages); + return false; + } + } + + private static void CompressToZip(FileSystemInfo[] files, string outputPath) + { + using (FileStream zipToOpen = new FileStream(outputPath, FileMode.Create)) + { + using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update)) + { + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + string entryName = fileInfo.Name; + AddFileToArchive(fileInfo.FullName, entryName, archive); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryToArchive(dirInfo, archive, dirInfo.FullName); + } + } + } + } + } + + private static void AddFileToArchive(string filePath, string entryName, ZipArchive archive) + { + ZipArchiveEntry entry = archive.CreateEntry(entryName); + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + using (Stream entryStream = entry.Open()) + { + fs.CopyTo(entryStream); + } + } + + private static void AddDirectoryToArchive(DirectoryInfo directory, ZipArchive archive, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = file.FullName.Substring(basePath.Length + 1).Replace('\\', '/'); + AddFileToArchive(file.FullName, entryName, archive); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryToArchive(subDirectory, archive, basePath); + } + } + + private static void CompressToTar(FileSystemInfo[] files, string outputPath) + { +#if !NETCORE + using (FileStream tarStream = new FileStream(outputPath, FileMode.Create)) + using (TarWriter tarWriter = new TarWriter(tarStream, TarEntryFormat.Pax, leaveOpen: false)) + { + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + tarWriter.WriteEntry(fileInfo.FullName, fileInfo.Name); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryToTar(tarWriter, dirInfo, dirInfo.FullName); + } + } + } + + void AddDirectoryToTar(TarWriter tarWriter, DirectoryInfo directory, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = file.FullName.Substring(basePath.Length + 1).Replace('\\', '/'); + tarWriter.WriteEntry(file.FullName, entryName); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryToTar(tarWriter, subDirectory, basePath); + } + } +#else + using (FileStream tarStream = File.Create(outputPath)) + using (var tarOutputStream = new ICSharpCode.SharpZipLib.Tar.TarOutputStream(tarStream)) + { + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + AddFileToTar(tarOutputStream, fileInfo.FullName, fileInfo.Name); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryFilesToTar(tarOutputStream, dirInfo, dirInfo.FullName); + } + } + } + + void AddFileToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, string filePath, string entryName) + { + var entry = ICSharpCode.SharpZipLib.Tar.TarEntry.CreateEntryFromFile(filePath); + entry.Name = entryName.Replace('\\', '/'); + tarOutputStream.PutNextEntry(entry); + + using (FileStream fs = File.OpenRead(filePath)) + { + fs.CopyTo(tarOutputStream); + } + tarOutputStream.CloseEntry(); + } + + void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, DirectoryInfo directory, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = file.FullName.Substring(basePath.Length + 1).Replace('\\', '/'); + AddFileToTar(tarOutputStream, file.FullName, entryName); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryFilesToTar(tarOutputStream, subDirectory, basePath); + } + } +#endif + } + + private static void CompressToGzip(FileSystemInfo[] files, string outputPath) + { +#if !NETCORE + string tempTarPath = Path.GetTempFileName(); + + using (FileStream tarStream = new FileStream(tempTarPath, FileMode.Create)) + using (TarWriter tarWriter = new TarWriter(tarStream, TarEntryFormat.Pax, leaveOpen: false)) + { + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + tarWriter.WriteEntry(fileInfo.FullName, fileInfo.Name); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryToTar(tarWriter, dirInfo, dirInfo.FullName); + } + } + } + + using (FileStream tarStream = new FileStream(tempTarPath, FileMode.Open)) + using (FileStream gzipStream = new FileStream(outputPath, FileMode.Create)) + using (GZipStream compressionStream = new GZipStream(gzipStream, CompressionLevel.Optimal)) + { + tarStream.CopyTo(compressionStream); + } + + File.Delete(tempTarPath); + + void AddDirectoryToTar(TarWriter tarWriter, DirectoryInfo directory, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = Path.GetRelativePath(basePath, file.FullName).Replace('\\', '/'); + tarWriter.WriteEntry(file.FullName, entryName); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryToTar(tarWriter, subDirectory, basePath); + } + } +#else + // Code for .NET Framework 4.6.2 using SharpZipLib + string tempTarPath = Path.GetTempFileName(); + + using (FileStream tarStream = File.Create(tempTarPath)) + using (var tarOutputStream = new ICSharpCode.SharpZipLib.Tar.TarOutputStream(tarStream)) + { + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + AddFileToTar(tarOutputStream, fileInfo.FullName, fileInfo.Name); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryFilesToTar(tarOutputStream, dirInfo, dirInfo.FullName); + } + } + } + + using (FileStream tarStream = File.OpenRead(tempTarPath)) + using (FileStream gzipStream = File.Create(outputPath)) + using (var gzipOutputStream = new ICSharpCode.SharpZipLib.GZip.GZipOutputStream(gzipStream)) + { + tarStream.CopyTo(gzipOutputStream); + } + + File.Delete(tempTarPath); + + void AddFileToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, string filePath, string entryName) + { + var entry = ICSharpCode.SharpZipLib.Tar.TarEntry.CreateEntryFromFile(filePath); + entry.Name = entryName.Replace('\\', '/'); + tarOutputStream.PutNextEntry(entry); + + using (FileStream fs = File.OpenRead(filePath)) + { + fs.CopyTo(tarOutputStream); + } + tarOutputStream.CloseEntry(); + } + + void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, DirectoryInfo directory, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = Path.GetRelativePath(basePath, file.FullName).Replace('\\', '/'); + AddFileToTar(tarOutputStream, file.FullName, entryName); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryFilesToTar(tarOutputStream, subDirectory, basePath); + } + } +#endif + } + + private static void CompressToJar(FileSystemInfo[] files, string outputPath) + { + using (FileStream jarStream = new FileStream(outputPath, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(jarStream, ZipArchiveMode.Create)) + { + // Add the manifest file + ZipArchiveEntry manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF"); + using (StreamWriter writer = new StreamWriter(manifestEntry.Open())) + { + writer.WriteLine("Manifest-Version: 1.0"); + } + + foreach (FileSystemInfo fsi in files) + { + if (fsi is FileInfo fileInfo) + { + string entryName = fileInfo.Name; + AddFileToArchive(fileInfo.FullName, entryName, archive); + } + else if (fsi is DirectoryInfo dirInfo) + { + AddDirectoryToArchive(dirInfo, archive, dirInfo.FullName); + } + } + } + + void AddFileToArchive(string filePath, string entryName, ZipArchive archive) + { + ZipArchiveEntry entry = archive.CreateEntry(entryName); + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + using (Stream entryStream = entry.Open()) + { + fs.CopyTo(entryStream); + } + } + + void AddDirectoryToArchive(DirectoryInfo directory, ZipArchive archive, string basePath) + { + foreach (var file in directory.GetFiles()) + { + string entryName = Path.GetRelativePath(basePath, file.FullName).Replace('\\', '/'); + AddFileToArchive(file.FullName, entryName, archive); + } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryToArchive(subDirectory, archive, basePath); + } + } + } + + private static void DecompressZip(FileInfo file, string directory) + { + ZipFile.ExtractToDirectory(file.FullName, directory); + } + + private static void DecompressTar(FileInfo file, string directory) + { +#if !NETCORE + TarFile.ExtractToDirectory(file.FullName, directory, overwriteFiles: true); +#else + using (Stream inStream = File.OpenRead(file.FullName)) + using (var tarInputStream = new ICSharpCode.SharpZipLib.Tar.TarInputStream(inStream)) + { + ICSharpCode.SharpZipLib.Tar.TarEntry tarEntry; + while ((tarEntry = tarInputStream.GetNextEntry()) != null) + { + string outPath = Path.Combine(directory, tarEntry.Name); + if (tarEntry.IsDirectory) + { + Directory.CreateDirectory(outPath); + } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + using (FileStream outStream = File.Create(outPath)) + { + tarInputStream.CopyEntryContents(outStream); + } + } + } + } +#endif + } + + private static void DecompressGzip(FileInfo file, string directory) + { + string decompressedFileName = Path.GetFileNameWithoutExtension(file.Name); + string outputPath = Path.Combine(directory, decompressedFileName); + + using (FileStream originalFileStream = file.OpenRead()) + using (FileStream decompressedFileStream = File.Create(outputPath)) + using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress)) + { + decompressionStream.CopyTo(decompressedFileStream); + } + } + + private static void Decompress7z(FileInfo file, string directory) + { + using (ArchiveFile archiveFile = new ArchiveFile(file.FullName)) + { + archiveFile.Extract(directory); + } + } + + private static void DecompressJar(FileInfo file, string directory) + { + ZipFile.ExtractToDirectory(file.FullName, directory); + } + + private static void DecompressRar(FileInfo file, string directory) + { + using (var archive = RarArchive.Open(file.FullName)) + { + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + entry.WriteToDirectory(directory, new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + } + } + } + } +} \ No newline at end of file diff --git a/dotnet/GxCompress/GxCompress.csproj b/dotnet/src/dotnetframework/GxCompress/GxCompress.csproj similarity index 71% rename from dotnet/GxCompress/GxCompress.csproj rename to dotnet/src/dotnetframework/GxCompress/GxCompress.csproj index 5eadf8761..4d39e8419 100644 --- a/dotnet/GxCompress/GxCompress.csproj +++ b/dotnet/src/dotnetframework/GxCompress/GxCompress.csproj @@ -1,10 +1,9 @@ - net6.0;net8.0 + net462 Library - NETCORE;NODATIME - GeneXus.Compression + GeneXus.Compression.Fmk true 618;1607;1698;SYSLIB0021;SYSLIB0027;SYSLIB0028;SYSLIB0023 @@ -14,12 +13,12 @@ - + - + diff --git a/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs new file mode 100644 index 000000000..69c6f0b8b --- /dev/null +++ b/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs @@ -0,0 +1,13 @@ +using GeneXus.Utils; +using System.Collections.Generic; + +namespace Genexus.Compression +{ + public interface IGXCompressor + { + static bool Compress(List files, string path, ref GXBaseCollection messages) => false; + static Compression NewCompression(string path, int dictionarySize, ref GXBaseCollection messages) => new Compression(); + static bool Decompress(string file, string path, ref GXBaseCollection messages) => false; + } +} + diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index 3a3918a53..b0f5d458f 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -1,135 +1,61 @@ using System; using System.Collections.Generic; using System.IO; -using Genexus.Compression; using GeneXus.Utils; using Xunit; namespace xUnitTesting { - public class TestCompression : IDisposable + public class TestCompression { - private List files; - private DirectoryInfo testDirectory; - private GXBaseCollection messages = null; public TestCompression() { - testDirectory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "testCompressor")); - testDirectory.Create(); - files = new List(); - string content = "This is a sample text to test the compression functionality."; - for (int i = 0; i < 3; i++) - { - string filePath = Path.Combine(testDirectory.FullName, $"testFile{i}.txt"); - File.WriteAllText(filePath, content); - files.Add(filePath); - } - } - - public void Dispose() - { - foreach (string filePath in files) - { - File.Delete(filePath); - } - testDirectory.Delete(true); } [Fact] public void TestCompressToZip() { - string outputPath = Path.Combine(testDirectory.FullName, "output.zip"); - bool result = GXCompressor.CompressFiles(files, outputPath, "ZIP", ref messages); - Assert.True(result); - Assert.True(File.Exists(outputPath)); } [Fact] public void TestCompressToTar() { - string outputPath = Path.Combine(testDirectory.FullName, "output.tar"); - bool result = GXCompressor.CompressFiles(files, outputPath, "TAR", ref messages); - Assert.True(result); - Assert.True(File.Exists(outputPath)); } [Fact] public void TestCompressToGzip() { - string outputPath = Path.Combine(testDirectory.FullName, "output.gz"); - string inputFilePath = Path.Combine(testDirectory.FullName, "test.txt"); - File.WriteAllText(inputFilePath, "This is a sample text to test the compression functionality."); - List singleFileCollection = new List { inputFilePath }; - bool result = GXCompressor.CompressFiles(singleFileCollection, outputPath, "GZIP", ref messages); - Assert.True(result); - Assert.True(File.Exists(outputPath)); } [Fact] public void TestCompressToJar() { - string outputPath = Path.Combine(testDirectory.FullName, "output.jar"); - bool result = GXCompressor.CompressFiles(files, outputPath, "JAR", ref messages); - Assert.True(result); - Assert.True(File.Exists(outputPath)); } [Fact] public void TestUnsupportedFormat() { - string outputPath = Path.Combine(testDirectory.FullName, "output.unknown"); - bool result = GXCompressor.CompressFiles(files, outputPath, "UNKNOWN", ref messages); - Assert.False(result); } [Fact] public void TestDecompressZip() { - string compressedPath = Path.Combine(testDirectory.FullName, "output.zip"); - GXCompressor.CompressFiles(files, compressedPath, "ZIP", ref messages); - string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressZip"); - Directory.CreateDirectory(decompressDirectory); - bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); - Assert.True(result); - Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } [Fact] public void TestDecompressTar() { - string compressedPath = Path.Combine(testDirectory.FullName, "output.tar"); - GXCompressor.CompressFiles(files, compressedPath, "TAR", ref messages); - string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressTar"); - Directory.CreateDirectory(decompressDirectory); - bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); - Assert.True(result); - Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } [Fact] public void TestDecompressGzip() { - string compressedPath = Path.Combine(testDirectory.FullName, "output.gz"); - List singleFileCollection = new List { files[0] }; - GXCompressor.CompressFiles(singleFileCollection, compressedPath, "GZIP", ref messages); - string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressGzip"); - Directory.CreateDirectory(decompressDirectory); - bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); - Assert.True(result); - Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } [Fact] public void TestDecompressJar() { - string compressedPath = Path.Combine(testDirectory.FullName, "output.jar"); - GXCompressor.CompressFiles(files, compressedPath, "JAR", ref messages); - string decompressDirectory = Path.Combine(testDirectory.FullName, "decompressJar"); - Directory.CreateDirectory(decompressDirectory); - bool result = GXCompressor.Decompress(compressedPath, decompressDirectory, ref messages); - Assert.True(result); - Assert.True(Directory.GetFiles(decompressDirectory).Length > 0); } } } From 3d06b73fe68747028c83fe245d10625c23ff7501 Mon Sep 17 00:00:00 2001 From: Claudia Murialdo Date: Fri, 8 Nov 2024 00:01:52 -0300 Subject: [PATCH 17/23] Try to fix some build errors --- .../dotnetcore/GxCompress/GxCompress.csproj | 2 +- .../GxCompress/GXCompressor.cs | 82 +++++++++++++------ .../GxCompress/IGXCompressor.cs | 7 ++ .../DotNetCoreUnitTest.csproj | 4 +- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj index 854e58980..9f580aa6a 100644 --- a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj +++ b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj @@ -1,7 +1,7 @@ - net6.0;net8.0 + net8.0 Library NETCORE GeneXus.Compression.Core diff --git a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs index d52164383..1fc63a234 100644 --- a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs +++ b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs @@ -1,20 +1,24 @@ using System; using System.Collections.Generic; -#if !NETCORE +#if NETCORE using System.Formats.Tar; #endif using System.IO; using System.IO.Compression; using GeneXus; using GeneXus.Utils; +using SharpCompress.Writers; using SharpCompress.Archives; -using SharpCompress.Archives.Rar; using SharpCompress.Common; +#if !NETCORE using SharpCompress.Writers.Tar; +#endif + + namespace Genexus.Compression { - public class GXCompressor : IGXCompressor + public class GXCompressor// : IGXCompressor { static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); @@ -217,17 +221,22 @@ private static void AddDirectoryToArchive(DirectoryInfo directory, ZipArchive ar } } +#if !NETCORE private static void CompressToTar(FileSystemInfo[] files, string outputPath) { -#if !NETCORE + TarWriterOptions tarOptions = new TarWriterOptions(CompressionType.LZip, false);//TarEntryFormat.Pax using (FileStream tarStream = new FileStream(outputPath, FileMode.Create)) - using (TarWriter tarWriter = new TarWriter(tarStream, TarEntryFormat.Pax, leaveOpen: false)) + + using (TarWriter tarWriter = new TarWriter(tarStream, tarOptions)) { foreach (FileSystemInfo fsi in files) { if (fsi is FileInfo fileInfo) { - tarWriter.WriteEntry(fileInfo.FullName, fileInfo.Name); + using (FileStream source = File.OpenRead(fsi.FullName)) + { + tarWriter.Write(fileInfo.FullName, source); + } } else if (fsi is DirectoryInfo dirInfo) { @@ -235,20 +244,45 @@ private static void CompressToTar(FileSystemInfo[] files, string outputPath) } } } + } - void AddDirectoryToTar(TarWriter tarWriter, DirectoryInfo directory, string basePath) + static void AddDirectoryToTar(TarWriter tarWriter, DirectoryInfo directory, string basePath) + { + foreach (var file in directory.GetFiles()) { - foreach (var file in directory.GetFiles()) - { - string entryName = file.FullName.Substring(basePath.Length + 1).Replace('\\', '/'); - tarWriter.WriteEntry(file.FullName, entryName); - } - foreach (var subDirectory in directory.GetDirectories()) + string entryName = file.FullName.Substring(basePath.Length + 1).Replace('\\', '/'); + using (FileStream source = File.OpenRead(file.FullName)) { - AddDirectoryToTar(tarWriter, subDirectory, basePath); + tarWriter.Write(file.FullName, source); } } + foreach (var subDirectory in directory.GetDirectories()) + { + AddDirectoryToTar(tarWriter, subDirectory, basePath); + } + } + static string GetRelativePath(string basePath, string fullPath) + { + Uri baseUri = new Uri(basePath); + Uri fullUri = new Uri(fullPath); + + if (baseUri.Scheme != fullUri.Scheme) + { + throw new InvalidOperationException("Paths must have the same scheme"); + } + + Uri relativeUri = baseUri.MakeRelativeUri(fullUri); + return Uri.UnescapeDataString(relativeUri.ToString()); + } + #else + static string GetRelativePath(string basePath, string fullPath) + { + return Path.GetRelativePath(basePath, fullPath).Replace('\\', '/'); + } + + private static void CompressToTar(FileSystemInfo[] files, string outputPath) + { using (FileStream tarStream = File.Create(outputPath)) using (var tarOutputStream = new ICSharpCode.SharpZipLib.Tar.TarOutputStream(tarStream)) { @@ -264,8 +298,9 @@ void AddDirectoryToTar(TarWriter tarWriter, DirectoryInfo directory, string base } } } + } - void AddFileToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, string filePath, string entryName) + static void AddFileToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, string filePath, string entryName) { var entry = ICSharpCode.SharpZipLib.Tar.TarEntry.CreateEntryFromFile(filePath); entry.Name = entryName.Replace('\\', '/'); @@ -278,7 +313,7 @@ void AddFileToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, s tarOutputStream.CloseEntry(); } - void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, DirectoryInfo directory, string basePath) + static void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutputStream, DirectoryInfo directory, string basePath) { foreach (var file in directory.GetFiles()) { @@ -291,11 +326,10 @@ void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutpu } } #endif - } private static void CompressToGzip(FileSystemInfo[] files, string outputPath) { -#if !NETCORE +#if NETCORE string tempTarPath = Path.GetTempFileName(); using (FileStream tarStream = new FileStream(tempTarPath, FileMode.Create)) @@ -381,7 +415,7 @@ void AddDirectoryFilesToTar(ICSharpCode.SharpZipLib.Tar.TarOutputStream tarOutpu { foreach (var file in directory.GetFiles()) { - string entryName = Path.GetRelativePath(basePath, file.FullName).Replace('\\', '/'); + string entryName = GetRelativePath(basePath, file.FullName); AddFileToTar(tarOutputStream, file.FullName, entryName); } foreach (var subDirectory in directory.GetDirectories()) @@ -432,7 +466,7 @@ void AddDirectoryToArchive(DirectoryInfo directory, ZipArchive archive, string b { foreach (var file in directory.GetFiles()) { - string entryName = Path.GetRelativePath(basePath, file.FullName).Replace('\\', '/'); + string entryName = GetRelativePath(basePath, file.FullName); AddFileToArchive(file.FullName, entryName, archive); } foreach (var subDirectory in directory.GetDirectories()) @@ -449,7 +483,7 @@ private static void DecompressZip(FileInfo file, string directory) private static void DecompressTar(FileInfo file, string directory) { -#if !NETCORE +#if NETCORE TarFile.ExtractToDirectory(file.FullName, directory, overwriteFiles: true); #else using (Stream inStream = File.OpenRead(file.FullName)) @@ -491,10 +525,10 @@ private static void DecompressGzip(FileInfo file, string directory) private static void Decompress7z(FileInfo file, string directory) { - using (ArchiveFile archiveFile = new ArchiveFile(file.FullName)) + /*using (ArchiveFile archiveFile = new ArchiveFile(file.FullName)) { archiveFile.Extract(directory); - } + }*/ } private static void DecompressJar(FileInfo file, string directory) @@ -504,7 +538,7 @@ private static void DecompressJar(FileInfo file, string directory) private static void DecompressRar(FileInfo file, string directory) { - using (var archive = RarArchive.Open(file.FullName)) + using (var archive = SharpCompress.Archives.Rar.RarArchive.Open(file.FullName)) { foreach (var entry in archive.Entries) { diff --git a/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs index 69c6f0b8b..6315c2556 100644 --- a/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs +++ b/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs @@ -5,9 +5,16 @@ namespace Genexus.Compression { public interface IGXCompressor { +#if NETCORE static bool Compress(List files, string path, ref GXBaseCollection messages) => false; static Compression NewCompression(string path, int dictionarySize, ref GXBaseCollection messages) => new Compression(); static bool Decompress(string file, string path, ref GXBaseCollection messages) => false; +#else + bool Compress(List files, string path, ref GXBaseCollection messages); + Compression NewCompression(string path, int dictionarySize, ref GXBaseCollection messages); + bool Decompress(string file, string path, ref GXBaseCollection messages); + +#endif } } diff --git a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj index 7e3c2c5f2..136611958 100644 --- a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj +++ b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj @@ -1,6 +1,6 @@ - net6.0;net8.0 + net8.0 CS8032;1701;1702;NU1701 Major @@ -78,7 +78,7 @@ - + From cac4fb35665e48d7856191a529c3fea6e92633bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:08:11 +0900 Subject: [PATCH 18/23] Add checks for DoS and directory traversal attacks --- .../GxCompress/GXCompressor.cs | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs index 1fc63a234..de2855154 100644 --- a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs +++ b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs @@ -56,7 +56,7 @@ private static void StorageMessages(string error, GXBaseCollection files, string path, ref GXBaseCollection messages) + public static bool Compress(List files, string path, long maxCombinedFileSize, ref GXBaseCollection messages) { if (files.Count == 0) { @@ -65,17 +65,42 @@ public static bool Compress(List files, string path, ref GXBaseCollectio return false; } + long totalSize = 0; List toCompress = new List(); foreach (string filePath in files) { - FileInfo file = new FileInfo(filePath); - if (!file.Exists) + var file = new FileInfo(filePath); + try { - GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}{filePath}"); - StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + filePath, messages); - continue; + string normalizedPath = Path.GetFullPath(filePath); + if (!file.Exists) + { + GXLogging.Error(log, $"{GetMessage("FILE_NOT_EXISTS")}{filePath}"); + StorageMessages($"{GetMessage("FILE_NOT_EXISTS")}" + filePath, messages); + continue; + } + if (normalizedPath.Contains(Path.DirectorySeparatorChar + ".." + Path.DirectorySeparatorChar) || + normalizedPath.EndsWith(Path.DirectorySeparatorChar + "..") || + normalizedPath.StartsWith(".." + Path.DirectorySeparatorChar)) + { + GXLogging.Error(log, "Potential directory traversal attack detected: " + normalizedPath); + StorageMessages("Potential directory traversal attack detected: " + normalizedPath, messages); + return false; + } + long fileSize = file.Length; + totalSize += fileSize; + if (totalSize > maxCombinedFileSize) + { + GXLogging.Error(log, "The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize); + StorageMessages("The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize, messages); + break; + } + toCompress.Add(file); + } + catch (IOException e) + { + GXLogging.Error(log, "Error normalizing path for file " + filePath, e); } - toCompress.Add(file); } if (toCompress.Count == 0) From 552d1b241f34a4162600b22b092c40cb847e13a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:33:02 +0900 Subject: [PATCH 19/23] Add unit tests --- .../DotNetCoreUnitTest/CompressionTests.cs | 283 ++++++++++++++++-- 1 file changed, 264 insertions(+), 19 deletions(-) diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index b0f5d458f..807114fb0 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -1,61 +1,306 @@ +using Genexus.Compression; +using GeneXus.Utils; using System; using System.Collections.Generic; using System.IO; -using GeneXus.Utils; using Xunit; namespace xUnitTesting { public class TestCompression { + private GXBaseCollection messages = null; public TestCompression() { } - [Fact] - public void TestCompressToZip() + private void CreateTestFiles(string rootPath, out List filePaths, out List dirPaths) { - } + filePaths = new List(); + dirPaths = new List(); - [Fact] - public void TestCompressToTar() - { - } + // Create files in the root directory + for (int i = 0; i < 3; i++) + { + string filePath = Path.Combine(rootPath, $"testfile_{i}.txt"); + File.WriteAllText(filePath, $"This is test file {i}"); + filePaths.Add(filePath); + } - [Fact] - public void TestCompressToGzip() - { + // Create subdirectories with files + for (int i = 0; i < 2; i++) + { + string dirPath = Path.Combine(rootPath, $"testdir_{i}"); + Directory.CreateDirectory(dirPath); + dirPaths.Add(dirPath); + + for (int j = 0; j < 2; j++) + { + string filePath = Path.Combine(dirPath, $"testfile_{i}_{j}.txt"); + File.WriteAllText(filePath, $"This is test file {i}_{j}"); + filePaths.Add(filePath); + } + } } - [Fact] - public void TestCompressToJar() + private void CleanupTestFiles(List filePaths, List dirPaths, string rootPath) { + foreach (string filePath in filePaths) + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + + foreach (string dirPath in dirPaths) + { + if (Directory.Exists(dirPath)) + { + Directory.Delete(dirPath, true); + } + } + + if (Directory.Exists(rootPath)) + { + Directory.Delete(rootPath, true); + } } [Fact] - public void TestUnsupportedFormat() + public void TestZip() { + string rootPath = Path.Combine(Path.GetTempPath(), "TestZip_" + Guid.NewGuid().ToString()); + Directory.CreateDirectory(rootPath); + + List filePaths; + List dirPaths; + + CreateTestFiles(rootPath, out filePaths, out dirPaths); + + try + { + List itemsToCompress = new List(); + itemsToCompress.AddRange(filePaths); + itemsToCompress.AddRange(dirPaths); + + string zipFilePath = Path.Combine(Path.GetTempPath(), "test.zip"); + + bool compressResult = GXCompressor.Compress(itemsToCompress, zipFilePath, 0, ref messages); + Assert.True(compressResult); + + CleanupTestFiles(filePaths, dirPaths, rootPath); + Directory.CreateDirectory(rootPath); + + bool decompressResult = GXCompressor.Decompress(zipFilePath, rootPath, ref messages); + Assert.True(decompressResult); + + foreach (string filePath in filePaths) + { + Assert.True(File.Exists(filePath)); + } + + foreach (string dirPath in dirPaths) + { + Assert.True(Directory.Exists(dirPath)); + } + + CleanupTestFiles(filePaths, dirPaths, rootPath); + + if (File.Exists(zipFilePath)) + { + File.Delete(zipFilePath); + } + } + finally + { + if (Directory.Exists(rootPath)) + { + Directory.Delete(rootPath, true); + } + } } [Fact] - public void TestDecompressZip() + public void TestTar() { + string rootPath = Path.Combine(Path.GetTempPath(), "TestTar_" + Guid.NewGuid().ToString()); + Directory.CreateDirectory(rootPath); + + List filePaths; + List dirPaths; + + CreateTestFiles(rootPath, out filePaths, out dirPaths); + + try + { + List itemsToCompress = new List(); + itemsToCompress.AddRange(filePaths); + itemsToCompress.AddRange(dirPaths); + + string tarFilePath = Path.Combine(Path.GetTempPath(), "test.tar"); + + bool compressResult = GXCompressor.Compress(itemsToCompress, tarFilePath, 0, ref messages); + Assert.True(compressResult); + + CleanupTestFiles(filePaths, dirPaths, rootPath); + Directory.CreateDirectory(rootPath); + + bool decompressResult = GXCompressor.Decompress(tarFilePath, rootPath, ref messages); + Assert.True(decompressResult); + + foreach (string filePath in filePaths) + { + Assert.True(File.Exists(filePath)); + } + + foreach (string dirPath in dirPaths) + { + Assert.True(Directory.Exists(dirPath)); + } + + CleanupTestFiles(filePaths, dirPaths, rootPath); + + if (File.Exists(tarFilePath)) + { + File.Delete(tarFilePath); + } + } + finally + { + if (Directory.Exists(rootPath)) + { + Directory.Delete(rootPath, true); + } + } } [Fact] - public void TestDecompressTar() + public void TestGZip() { + string rootPath = Path.Combine(Path.GetTempPath(), "TestGZip_" + Guid.NewGuid().ToString()); + Directory.CreateDirectory(rootPath); + + List filePaths; + List dirPaths; + + string testFilePath = Path.Combine(rootPath, "testfile.txt"); + File.WriteAllText(testFilePath, "This is a test file for GZip compression."); + filePaths = new List { testFilePath }; + dirPaths = new List(); + + try + { + string gzipFilePath = Path.Combine(Path.GetTempPath(), "test.gz"); + + bool compressResult = GXCompressor.Compress(new List { testFilePath }, gzipFilePath, 0, ref messages); + Assert.True(compressResult); + + CleanupTestFiles(filePaths, dirPaths, rootPath); + Directory.CreateDirectory(rootPath); + + bool decompressResult = GXCompressor.Decompress(gzipFilePath, rootPath, ref messages); + Assert.True(decompressResult); + + Assert.True(File.Exists(testFilePath)); + + CleanupTestFiles(filePaths, dirPaths, rootPath); + + if (File.Exists(gzipFilePath)) + { + File.Delete(gzipFilePath); + } + } + finally + { + if (Directory.Exists(rootPath)) + { + Directory.Delete(rootPath, true); + } + } } [Fact] - public void TestDecompressGzip() + public void TestJar() { + string rootPath = Path.Combine(Path.GetTempPath(), "TestJar_" + Guid.NewGuid().ToString()); + Directory.CreateDirectory(rootPath); + + List filePaths; + List dirPaths; + + CreateTestFiles(rootPath, out filePaths, out dirPaths); + + try + { + List itemsToCompress = new List(); + itemsToCompress.AddRange(filePaths); + itemsToCompress.AddRange(dirPaths); + + string jarFilePath = Path.Combine(Path.GetTempPath(), "test.jar"); + + bool compressResult = GXCompressor.Compress(itemsToCompress, jarFilePath, 0, ref messages); + Assert.True(compressResult); + + CleanupTestFiles(filePaths, dirPaths, rootPath); + Directory.CreateDirectory(rootPath); + + bool decompressResult = GXCompressor.Decompress(jarFilePath, rootPath, ref messages); + Assert.True(decompressResult); + + foreach (string filePath in filePaths) + { + Assert.True(File.Exists(filePath)); + } + + foreach (string dirPath in dirPaths) + { + Assert.True(Directory.Exists(dirPath)); + } + + CleanupTestFiles(filePaths, dirPaths, rootPath); + + if (File.Exists(jarFilePath)) + { + File.Delete(jarFilePath); + } + } + finally + { + if (Directory.Exists(rootPath)) + { + Directory.Delete(rootPath, true); + } + } } [Fact] - public void TestDecompressJar() + public void TestUnsupportedFormat() { + string rootPath = Path.Combine(Path.GetTempPath(), "TestUnsupportedFormat_" + Guid.NewGuid().ToString()); + Directory.CreateDirectory(rootPath); + + List filePaths; + List dirPaths; + + CreateTestFiles(rootPath, out filePaths, out dirPaths); + + try + { + List itemsToCompress = new List(); + itemsToCompress.AddRange(filePaths); + itemsToCompress.AddRange(dirPaths); + + string unsupportedFilePath = Path.Combine(Path.GetTempPath(), "test.unsupported"); + + bool compressResult = GXCompressor.Compress(itemsToCompress, unsupportedFilePath, 0, ref messages); + Assert.False(compressResult); + } + finally + { + CleanupTestFiles(filePaths, dirPaths, rootPath); + } } } -} +} \ No newline at end of file From f11370c1b7c388c5b19d373fcf9c65da206ddbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:42:39 +0900 Subject: [PATCH 20/23] Fix compilation error and add option for not checking filesize --- dotnet/src/dotnetframework/GxCompress/Compression.cs | 2 +- dotnet/src/dotnetframework/GxCompress/GXCompressor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/dotnetframework/GxCompress/Compression.cs b/dotnet/src/dotnetframework/GxCompress/Compression.cs index b54ca012b..7c2a95b22 100644 --- a/dotnet/src/dotnetframework/GxCompress/Compression.cs +++ b/dotnet/src/dotnetframework/GxCompress/Compression.cs @@ -29,7 +29,7 @@ public void AddElement(string filePath) public bool Save() { - return GXCompressor.Compress(filesToCompress, path, ref this.messages); + return GXCompressor.Compress(filesToCompress, path, -1, ref this.messages); } public void Clear() diff --git a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs index de2855154..d56ebc64f 100644 --- a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs +++ b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs @@ -89,7 +89,7 @@ public static bool Compress(List files, string path, long maxCombinedFil } long fileSize = file.Length; totalSize += fileSize; - if (totalSize > maxCombinedFileSize) + if (totalSize > maxCombinedFileSize && maxCombinedFileSize > -1) { GXLogging.Error(log, "The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize); StorageMessages("The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize, messages); From 10941f6050b0e24d8ad5023a6013410602ad4ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:52:07 +0900 Subject: [PATCH 21/23] Fix compilation error --- .../dotnetframework/GxCompress/Compression.cs | 6 ++++-- .../GxCompress/GXCompressor.cs | 14 ++++++------- .../GxCompress/IGXCompressor.cs | 20 ------------------- .../DotNetCoreUnitTest/CompressionTests.cs | 8 ++++---- 4 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs diff --git a/dotnet/src/dotnetframework/GxCompress/Compression.cs b/dotnet/src/dotnetframework/GxCompress/Compression.cs index 7c2a95b22..df68f252c 100644 --- a/dotnet/src/dotnetframework/GxCompress/Compression.cs +++ b/dotnet/src/dotnetframework/GxCompress/Compression.cs @@ -7,6 +7,7 @@ public class Compression { private string path; + private long MaxCombinedFileSize; private List filesToCompress; private GXBaseCollection messages; @@ -15,9 +16,10 @@ public Compression() filesToCompress = new List(); } - public Compression(string path, ref GXBaseCollection messages) + public Compression(string path, long MaxCombinedFileSize, ref GXBaseCollection messages) { this.path = path; + this.MaxCombinedFileSize = MaxCombinedFileSize; this.messages = messages; filesToCompress = new List(); } @@ -29,7 +31,7 @@ public void AddElement(string filePath) public bool Save() { - return GXCompressor.Compress(filesToCompress, path, -1, ref this.messages); + return GXCompressor.Compress(filesToCompress, path, MaxCombinedFileSize, ref this.messages); } public void Clear() diff --git a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs index d56ebc64f..efc78e1b3 100644 --- a/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs +++ b/dotnet/src/dotnetframework/GxCompress/GXCompressor.cs @@ -18,7 +18,7 @@ namespace Genexus.Compression { - public class GXCompressor// : IGXCompressor + public class GXCompressor { static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXCompressor).FullName); @@ -56,7 +56,7 @@ private static void StorageMessages(string error, GXBaseCollection files, string path, long maxCombinedFileSize, ref GXBaseCollection messages) + public static bool Compress(List files, string path, long MaxCombinedFileSize, ref GXBaseCollection messages) { if (files.Count == 0) { @@ -89,10 +89,10 @@ public static bool Compress(List files, string path, long maxCombinedFil } long fileSize = file.Length; totalSize += fileSize; - if (totalSize > maxCombinedFileSize && maxCombinedFileSize > -1) + if (totalSize > MaxCombinedFileSize && MaxCombinedFileSize > -1) { - GXLogging.Error(log, "The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize); - StorageMessages("The files selected for compression exceed the maximum permitted file size of " + maxCombinedFileSize, messages); + GXLogging.Error(log, "The files selected for compression exceed the maximum permitted file size of " + MaxCombinedFileSize); + StorageMessages("The files selected for compression exceed the maximum permitted file size of " + MaxCombinedFileSize, messages); break; } toCompress.Add(file); @@ -143,9 +143,9 @@ public static bool Compress(List files, string path, long maxCombinedFil } } - public static Compression NewCompression(string path, string format, ref GXBaseCollection messages) + public static Compression NewCompression(string path, long MaxCombinedFileSize, ref GXBaseCollection messages) { - return new Compression(path, ref messages); + return new Compression(path, MaxCombinedFileSize, ref messages); } public static bool Decompress(string file, string path, ref GXBaseCollection messages) diff --git a/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs b/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs deleted file mode 100644 index 6315c2556..000000000 --- a/dotnet/src/dotnetframework/GxCompress/IGXCompressor.cs +++ /dev/null @@ -1,20 +0,0 @@ -using GeneXus.Utils; -using System.Collections.Generic; - -namespace Genexus.Compression -{ - public interface IGXCompressor - { -#if NETCORE - static bool Compress(List files, string path, ref GXBaseCollection messages) => false; - static Compression NewCompression(string path, int dictionarySize, ref GXBaseCollection messages) => new Compression(); - static bool Decompress(string file, string path, ref GXBaseCollection messages) => false; -#else - bool Compress(List files, string path, ref GXBaseCollection messages); - Compression NewCompression(string path, int dictionarySize, ref GXBaseCollection messages); - bool Decompress(string file, string path, ref GXBaseCollection messages); - -#endif - } -} - diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index 807114fb0..134949009 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -87,7 +87,7 @@ public void TestZip() string zipFilePath = Path.Combine(Path.GetTempPath(), "test.zip"); - bool compressResult = GXCompressor.Compress(itemsToCompress, zipFilePath, 0, ref messages); + bool compressResult = GXCompressor.Compress(itemsToCompress, zipFilePath, -1, ref messages); Assert.True(compressResult); CleanupTestFiles(filePaths, dirPaths, rootPath); @@ -141,7 +141,7 @@ public void TestTar() string tarFilePath = Path.Combine(Path.GetTempPath(), "test.tar"); - bool compressResult = GXCompressor.Compress(itemsToCompress, tarFilePath, 0, ref messages); + bool compressResult = GXCompressor.Compress(itemsToCompress, tarFilePath, -1, ref messages); Assert.True(compressResult); CleanupTestFiles(filePaths, dirPaths, rootPath); @@ -240,7 +240,7 @@ public void TestJar() string jarFilePath = Path.Combine(Path.GetTempPath(), "test.jar"); - bool compressResult = GXCompressor.Compress(itemsToCompress, jarFilePath, 0, ref messages); + bool compressResult = GXCompressor.Compress(itemsToCompress, jarFilePath, -1, ref messages); Assert.True(compressResult); CleanupTestFiles(filePaths, dirPaths, rootPath); @@ -294,7 +294,7 @@ public void TestUnsupportedFormat() string unsupportedFilePath = Path.Combine(Path.GetTempPath(), "test.unsupported"); - bool compressResult = GXCompressor.Compress(itemsToCompress, unsupportedFilePath, 0, ref messages); + bool compressResult = GXCompressor.Compress(itemsToCompress, unsupportedFilePath, -1, ref messages); Assert.False(compressResult); } finally From 19bb3d098f57483bd3a98527ce001674921519fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Tue, 19 Nov 2024 19:57:33 +0900 Subject: [PATCH 22/23] Remove interface dependency --- dotnet/src/dotnetcore/GxCompress/GxCompress.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj index 9f580aa6a..716facc0c 100644 --- a/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj +++ b/dotnet/src/dotnetcore/GxCompress/GxCompress.csproj @@ -12,7 +12,6 @@ - From 72bc65ca79a159aaf6889524ec1505d71618b20d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Mon, 25 Nov 2024 03:18:27 +0900 Subject: [PATCH 23/23] Try make tests --- .../DotNetCoreUnitTest/CompressionTests.cs | 308 +++++------------- 1 file changed, 74 insertions(+), 234 deletions(-) diff --git a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs index 134949009..4d9048a10 100644 --- a/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs +++ b/dotnet/test/DotNetCoreUnitTest/CompressionTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Xunit; namespace xUnitTesting @@ -15,292 +16,131 @@ public TestCompression() { } - private void CreateTestFiles(string rootPath, out List filePaths, out List dirPaths) - { - filePaths = new List(); - dirPaths = new List(); - - // Create files in the root directory - for (int i = 0; i < 3; i++) - { - string filePath = Path.Combine(rootPath, $"testfile_{i}.txt"); - File.WriteAllText(filePath, $"This is test file {i}"); - filePaths.Add(filePath); - } - - // Create subdirectories with files - for (int i = 0; i < 2; i++) - { - string dirPath = Path.Combine(rootPath, $"testdir_{i}"); - Directory.CreateDirectory(dirPath); - dirPaths.Add(dirPath); - - for (int j = 0; j < 2; j++) - { - string filePath = Path.Combine(dirPath, $"testfile_{i}_{j}.txt"); - File.WriteAllText(filePath, $"This is test file {i}_{j}"); - filePaths.Add(filePath); - } - } - } - - private void CleanupTestFiles(List filePaths, List dirPaths, string rootPath) - { - foreach (string filePath in filePaths) - { - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - } - - foreach (string dirPath in dirPaths) - { - if (Directory.Exists(dirPath)) - { - Directory.Delete(dirPath, true); - } - } - - if (Directory.Exists(rootPath)) - { - Directory.Delete(rootPath, true); - } - } - [Fact] public void TestZip() { - string rootPath = Path.Combine(Path.GetTempPath(), "TestZip_" + Guid.NewGuid().ToString()); - Directory.CreateDirectory(rootPath); - - List filePaths; - List dirPaths; - - CreateTestFiles(rootPath, out filePaths, out dirPaths); + string tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string decompressedFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string compressedFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".zip"); try { - List itemsToCompress = new List(); - itemsToCompress.AddRange(filePaths); - itemsToCompress.AddRange(dirPaths); - - string zipFilePath = Path.Combine(Path.GetTempPath(), "test.zip"); + CreateTestFilesAndFolders(tempFolder); - bool compressResult = GXCompressor.Compress(itemsToCompress, zipFilePath, -1, ref messages); - Assert.True(compressResult); + var filesToCompress = new List { tempFolder }; - CleanupTestFiles(filePaths, dirPaths, rootPath); - Directory.CreateDirectory(rootPath); + bool compressionResult = GXCompressor.Compress(filesToCompress, compressedFile, -1, ref messages); + Assert.True(compressionResult, "Compression failed"); + Assert.True(File.Exists(compressedFile), "Compressed file does not exist"); - bool decompressResult = GXCompressor.Decompress(zipFilePath, rootPath, ref messages); - Assert.True(decompressResult); + bool decompressionResult = GXCompressor.Decompress(compressedFile, decompressedFolder, ref messages); + Assert.True(decompressionResult, "Decompression failed"); - foreach (string filePath in filePaths) - { - Assert.True(File.Exists(filePath)); - } - - foreach (string dirPath in dirPaths) - { - Assert.True(Directory.Exists(dirPath)); - } - - CleanupTestFiles(filePaths, dirPaths, rootPath); - - if (File.Exists(zipFilePath)) - { - File.Delete(zipFilePath); - } + bool directoriesAreEqual = CompareDirectories(tempFolder, decompressedFolder); + Assert.True(directoriesAreEqual, "Decompressed content does not match the original"); } finally { - if (Directory.Exists(rootPath)) + DeleteTestFilesAndFolders(tempFolder); + DeleteTestFilesAndFolders(decompressedFolder); + if (File.Exists(compressedFile)) { - Directory.Delete(rootPath, true); + File.Delete(compressedFile); } } - } - - [Fact] - public void TestTar() - { - string rootPath = Path.Combine(Path.GetTempPath(), "TestTar_" + Guid.NewGuid().ToString()); - Directory.CreateDirectory(rootPath); - - List filePaths; - List dirPaths; - - CreateTestFiles(rootPath, out filePaths, out dirPaths); - try + bool CompareDirectories(string dir1, string dir2) { - List itemsToCompress = new List(); - itemsToCompress.AddRange(filePaths); - itemsToCompress.AddRange(dirPaths); - - string tarFilePath = Path.Combine(Path.GetTempPath(), "test.tar"); - - bool compressResult = GXCompressor.Compress(itemsToCompress, tarFilePath, -1, ref messages); - Assert.True(compressResult); - - CleanupTestFiles(filePaths, dirPaths, rootPath); - Directory.CreateDirectory(rootPath); + string[] dir1Files = Directory.GetFiles(dir1, "*", SearchOption.AllDirectories); + string[] dir2Files = Directory.GetFiles(dir2, "*", SearchOption.AllDirectories); - bool decompressResult = GXCompressor.Decompress(tarFilePath, rootPath, ref messages); - Assert.True(decompressResult); + if (dir1Files.Length != dir2Files.Length) + return false; - foreach (string filePath in filePaths) - { - Assert.True(File.Exists(filePath)); - } + Array.Sort(dir1Files); + Array.Sort(dir2Files); - foreach (string dirPath in dirPaths) + for (int i = 0; i < dir1Files.Length; i++) { - Assert.True(Directory.Exists(dirPath)); - } + string relativePath1 = dir1Files[i].Substring(dir1.Length).TrimStart(Path.DirectorySeparatorChar); + string relativePath2 = dir2Files[i].Substring(dir2.Length).TrimStart(Path.DirectorySeparatorChar); - CleanupTestFiles(filePaths, dirPaths, rootPath); + if (!string.Equals(relativePath1, relativePath2, StringComparison.OrdinalIgnoreCase)) + return false; - if (File.Exists(tarFilePath)) - { - File.Delete(tarFilePath); + if (!FileContentsAreEqual(dir1Files[i], dir2Files[i])) + return false; } + + return true; } - finally + + bool FileContentsAreEqual(string filePath1, string filePath2) { - if (Directory.Exists(rootPath)) + byte[] file1Bytes = File.ReadAllBytes(filePath1); + byte[] file2Bytes = File.ReadAllBytes(filePath2); + + if (file1Bytes.Length != file2Bytes.Length) + return false; + + for (int i = 0; i < file1Bytes.Length; i++) { - Directory.Delete(rootPath, true); + if (file1Bytes[i] != file2Bytes[i]) + return false; } + + return true; } } - [Fact] - public void TestGZip() + private void CreateTestFilesAndFolders(string basePath) { - string rootPath = Path.Combine(Path.GetTempPath(), "TestGZip_" + Guid.NewGuid().ToString()); - Directory.CreateDirectory(rootPath); - - List filePaths; - List dirPaths; - - string testFilePath = Path.Combine(rootPath, "testfile.txt"); - File.WriteAllText(testFilePath, "This is a test file for GZip compression."); - filePaths = new List { testFilePath }; - dirPaths = new List(); - - try - { - string gzipFilePath = Path.Combine(Path.GetTempPath(), "test.gz"); + Directory.CreateDirectory(basePath); - bool compressResult = GXCompressor.Compress(new List { testFilePath }, gzipFilePath, 0, ref messages); - Assert.True(compressResult); + string subDir1 = Path.Combine(basePath, "SubDir1"); + string subDir2 = Path.Combine(basePath, "SubDir2"); + Directory.CreateDirectory(subDir1); + Directory.CreateDirectory(subDir2); - CleanupTestFiles(filePaths, dirPaths, rootPath); - Directory.CreateDirectory(rootPath); + File.WriteAllText(Path.Combine(basePath, "file1.txt"), "Content of file1."); + File.WriteAllText(Path.Combine(basePath, "file2.txt"), "Content of file2."); - bool decompressResult = GXCompressor.Decompress(gzipFilePath, rootPath, ref messages); - Assert.True(decompressResult); + File.WriteAllText(Path.Combine(subDir1, "file3.txt"), "Content of file3 in SubDir1."); + File.WriteAllText(Path.Combine(subDir1, "file4.txt"), "Content of file4 in SubDir1."); - Assert.True(File.Exists(testFilePath)); - - CleanupTestFiles(filePaths, dirPaths, rootPath); + File.WriteAllText(Path.Combine(subDir2, "file5.txt"), "Content of file5 in SubDir2."); + File.WriteAllText(Path.Combine(subDir2, "file6.txt"), "Content of file6 in SubDir2."); + } - if (File.Exists(gzipFilePath)) - { - File.Delete(gzipFilePath); - } - } - finally + private void DeleteTestFilesAndFolders(string basePath) + { + if (Directory.Exists(basePath)) { - if (Directory.Exists(rootPath)) - { - Directory.Delete(rootPath, true); - } + Directory.Delete(basePath, true); } } [Fact] - public void TestJar() + public void TestTar() { - string rootPath = Path.Combine(Path.GetTempPath(), "TestJar_" + Guid.NewGuid().ToString()); - Directory.CreateDirectory(rootPath); - - List filePaths; - List dirPaths; - - CreateTestFiles(rootPath, out filePaths, out dirPaths); - - try - { - List itemsToCompress = new List(); - itemsToCompress.AddRange(filePaths); - itemsToCompress.AddRange(dirPaths); - - string jarFilePath = Path.Combine(Path.GetTempPath(), "test.jar"); - - bool compressResult = GXCompressor.Compress(itemsToCompress, jarFilePath, -1, ref messages); - Assert.True(compressResult); - - CleanupTestFiles(filePaths, dirPaths, rootPath); - Directory.CreateDirectory(rootPath); - - bool decompressResult = GXCompressor.Decompress(jarFilePath, rootPath, ref messages); - Assert.True(decompressResult); - - foreach (string filePath in filePaths) - { - Assert.True(File.Exists(filePath)); - } + } - foreach (string dirPath in dirPaths) - { - Assert.True(Directory.Exists(dirPath)); - } + [Fact] + public void TestGZip() + { + } - CleanupTestFiles(filePaths, dirPaths, rootPath); - if (File.Exists(jarFilePath)) - { - File.Delete(jarFilePath); - } - } - finally - { - if (Directory.Exists(rootPath)) - { - Directory.Delete(rootPath, true); - } - } + [Fact] + public void TestJar() + { } [Fact] public void TestUnsupportedFormat() { - string rootPath = Path.Combine(Path.GetTempPath(), "TestUnsupportedFormat_" + Guid.NewGuid().ToString()); - Directory.CreateDirectory(rootPath); - - List filePaths; - List dirPaths; - - CreateTestFiles(rootPath, out filePaths, out dirPaths); - - try - { - List itemsToCompress = new List(); - itemsToCompress.AddRange(filePaths); - itemsToCompress.AddRange(dirPaths); - - string unsupportedFilePath = Path.Combine(Path.GetTempPath(), "test.unsupported"); - - bool compressResult = GXCompressor.Compress(itemsToCompress, unsupportedFilePath, -1, ref messages); - Assert.False(compressResult); - } - finally - { - CleanupTestFiles(filePaths, dirPaths, rootPath); - } } + } + } \ No newline at end of file