diff --git a/README.md b/README.md index aeb775e7..50a1e115 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ repositories { } ext { - rlibVersion = "10.0.alpha11" + rlibVersion = "10.0.alpha12" } dependencies { diff --git a/build.gradle b/build.gradle index dcd7d9b0..b05bd7a9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -rootProject.version = "10.0.alpha11" +rootProject.version = "10.0.alpha12" group = 'javasabr.rlib' allprojects { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 00927cef..72ad9316 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ jakarta-mail = "2.1.3" # https://mvnrepository.com/artifact/org.eclipse.angus/angus-mail angus-mail = "2.0.4" # https://mvnrepository.com/artifact/org.testcontainers/testcontainers -testcontainers = "1.21.3" +testcontainers = "2.0.3" # https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine junit-jupiter = "6.0.1" # https://mvnrepository.com/artifact/org.projectlombok/lombok @@ -39,4 +39,4 @@ mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version. assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj-core" } [bundles] -mail = ["jakarta-mail-api", "angus-mail"] \ No newline at end of file +mail = ["jakarta-mail-api", "angus-mail"] diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/SymmetryCrypt.java b/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/SymmetryCrypt.java deleted file mode 100644 index 02cc7274..00000000 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/SymmetryCrypt.java +++ /dev/null @@ -1,77 +0,0 @@ -package javasabr.rlib.common.util.crypt; - -import java.io.Serial; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.ShortBufferException; -import lombok.AccessLevel; -import lombok.experimental.FieldDefaults; - -/** - * @author JavaSaBr - */ -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class SymmetryCrypt { - - public static final String ALG_RC_4 = "RC4"; - - Cipher ecipher; - Cipher dcipher; - - public SymmetryCrypt(String key) - throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException { - this(key, ALG_RC_4); - } - - public SymmetryCrypt(String key, String algorithm) - throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException { - - Cipher ecipher = Cipher.getInstance(algorithm); - Cipher dcipher = Cipher.getInstance(algorithm); - - byte[] bytes = key.getBytes(StandardCharsets.UTF_8); - var secretKey = new SecretKey() { - - @Serial - private static final long serialVersionUID = -8907627571317506056L; - - @Override - public String getAlgorithm() { - return algorithm; - } - - @Override - public byte[] getEncoded() { - return bytes; - } - - @Override - public String getFormat() { - return "RAW"; - } - }; - - ecipher.init(Cipher.ENCRYPT_MODE, secretKey); - dcipher.init(Cipher.DECRYPT_MODE, secretKey); - - this.ecipher = ecipher; - this.dcipher = dcipher; - } - - public void decrypt(byte[] in, int offset, int length, byte[] out) - throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { - dcipher.doFinal(in, offset, length, out, offset); - } - - public void encrypt(byte[] in, int offset, int length, byte[] out) - throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { - ecipher.doFinal(in, offset, length, out, offset); - } -} diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/package-info.java b/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/package-info.java deleted file mode 100644 index c73e858e..00000000 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/crypt/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package javasabr.rlib.common.util.crypt; - -import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java index b0a7e422..b6f41c0f 100644 --- a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java +++ b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java @@ -400,36 +400,37 @@ public static String getFirstFreeName(Path directory, Path file) { * * @param destination the destination folder. * @param zipFile the zip file. + * + * @return the count of unpacked files */ - public static void unzip(Path destination, Path zipFile) { - + public static int unzip(Path destination, Path zipFile) { if (!Files.exists(destination)) { throw new IllegalArgumentException("The folder " + destination + " doesn't exist."); } - + Path normalizedDestination = destination.normalize(); + int count = 0; try (var zin = new ZipInputStream(Files.newInputStream(zipFile))) { for (var entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) { - String entryName = entry.getName(); Path targetFile = destination .resolve(entryName) - .toRealPath(LinkOption.NOFOLLOW_LINKS); - - if (!targetFile.startsWith(destination)) { + .normalize(); + if (!targetFile.startsWith(normalizedDestination)) { LOGGER.warning(entryName, "Unexpected entry name:[%s] which is outside"::formatted); continue; } - if (entry.isDirectory()) { Files.createDirectories(targetFile); } else { + Files.createDirectories(targetFile.getParent()); Files.copy(zin, targetFile, StandardCopyOption.REPLACE_EXISTING); + count++; } } - } catch (IOException e) { throw new UncheckedIOException(e); } + return count; } /** diff --git a/rlib-io/src/test/java/javasabr/rlib/io/FileUtilsTest.java b/rlib-io/src/test/java/javasabr/rlib/io/FileUtilsTest.java index 005c198a..a1c5c435 100644 --- a/rlib-io/src/test/java/javasabr/rlib/io/FileUtilsTest.java +++ b/rlib-io/src/test/java/javasabr/rlib/io/FileUtilsTest.java @@ -2,6 +2,13 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import javasabr.rlib.io.util.FileUtils; import org.junit.jupiter.api.Test; @@ -74,4 +81,60 @@ void shouldCheckExistingExtension() { assertThat(FileUtils.hasExtension(path6)).isFalse(); assertThat(FileUtils.hasExtension(path7)).isFalse(); } + + @Test + void shouldUnzipFileCorrectly() throws IOException { + // given: + Path zipFile = Files.createTempFile("test-archive", ".zip"); + + try (var zout = new ZipOutputStream(Files.newOutputStream(zipFile, StandardOpenOption.CREATE))) { + zout.putNextEntry(new ZipEntry("fileA.txt")); + zout.write("test text".getBytes(StandardCharsets.UTF_8)); + + zout.putNextEntry(new ZipEntry("../fileB.txt")); + zout.write("test text 2".getBytes(StandardCharsets.UTF_8)); + + ZipEntry dirAEntry = new ZipEntry("dir_a/"); + dirAEntry.setMethod(ZipEntry.STORED); + dirAEntry.setSize(0); + dirAEntry.setCrc(0); + zout.putNextEntry(dirAEntry); + + zout.putNextEntry(new ZipEntry("dir_a/fileC.txt")); + zout.write("test text 3".getBytes(StandardCharsets.UTF_8)); + + zout.putNextEntry(new ZipEntry("dir_a/../fileD.txt")); + zout.write("test text 4".getBytes(StandardCharsets.UTF_8)); + + zout.putNextEntry(new ZipEntry("dir_a/../../../fileE.txt")); + zout.write("test text 5".getBytes(StandardCharsets.UTF_8)); + } + + Path tempDirectory = Files.createTempDirectory("test-unzip"); + Path outputDir = tempDirectory + .resolve("output") + .resolve("folder"); + + Files.createDirectories(outputDir); + + // when: + int unpackedFiles = FileUtils.unzip(outputDir, zipFile); + + // then: + assertThat(unpackedFiles).isEqualTo(3); + assertThat(outputDir + .resolve("fileA.txt")) + .exists(); + assertThat(outputDir + .resolve("dir_a") + .resolve("fileC.txt")) + .exists(); + assertThat(tempDirectory + .resolve("output") + .resolve("fileB.txt")) + .doesNotExist(); + assertThat(tempDirectory + .resolve("fileE.txt")) + .doesNotExist(); + } }