From 07a9c7b3c6ab4fa41415ee2d779576f4d79a6155 Mon Sep 17 00:00:00 2001 From: TranceLove Date: Mon, 16 May 2022 14:30:33 +0800 Subject: [PATCH] ISO9660 and ZStandard archive support Fixes #2988 Fixes #3318 --- app/build.gradle | 10 +- .../AbstractCommonsArchiveHelperCallable.kt | 9 +- .../compress/CompressedHelperCallable.kt | 4 + .../compress/Iso9660HelperCallable.kt | 94 +++++++++++++ .../compress/SevenZipHelperCallable.kt | 3 +- .../compress/TarZstHelperCallable.kt | 37 +++++ .../compressed/CompressedHelper.java | 47 ++++++- .../compressed/extractcontents/Extractor.java | 4 + .../AbstractCommonsArchiveExtractor.kt | 19 ++- .../AbstractCompressedTarArchiveExtractor.kt | 14 +- .../helpers/AbstractIsoExtractor.kt | 131 ++++++++++++++++++ .../helpers/Iso9660Extractor.kt | 43 ++++++ .../helpers/TarZstExtractor.kt | 44 ++++++ .../extractcontents/helpers/ZstdExtractor.kt | 44 ++++++ .../helpers/Iso9660Decompressor.kt | 31 +++++ .../helpers/TarZstDecompressor.kt | 33 +++++ .../com/amaze/filemanager/ui/icons/Icons.java | 3 +- .../amaze/filemanager/ui/icons/MimeTypes.java | 1 + ...ractCompressedHelperCallableArchiveTest.kt | 6 +- .../CompressedHelperForBadArchiveTest.kt | 24 +++- .../compress/Iso9660HelperCallableTest.kt | 36 +++++ .../compress/TarZstHelperCallableTest.kt | 37 +++++ .../UnknownCompressedHelperCallableTest.kt | 2 +- .../services/ExtractServiceTest.kt | 6 - .../extractcontents/AbstractExtractorTest.kt | 9 +- .../extractcontents/IsoExtractorTest.kt | 30 ++++ .../extractcontents/TarZstExtractorTest.kt | 30 ++++ .../extractcontents/ZstdExtractorTest.kt | 30 ++++ app/src/test/resources/test-archive.iso | Bin 0 -> 417792 bytes app/src/test/resources/test-archive.tar.zst | Bin 0 -> 833 bytes app/src/test/resources/test-archive.txt.zst | Bin 0 -> 50 bytes app/src/test/resources/test-archive.udf | Bin 0 -> 3258368 bytes app/src/test/resources/test.txt.zst | Bin 0 -> 50 bytes .../CompressedHelperCallableTestSuite.kt | 1 + .../extractcontents/ExtractorTestSuite.kt | 2 + build.gradle | 1 + 36 files changed, 747 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallable.kt create mode 100644 app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallable.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractIsoExtractor.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/Iso9660Extractor.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/TarZstExtractor.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/ZstdExtractor.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/Iso9660Decompressor.kt create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/TarZstDecompressor.kt create mode 100644 app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallableTest.kt create mode 100644 app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallableTest.kt create mode 100644 app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/IsoExtractorTest.kt create mode 100644 app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/TarZstExtractorTest.kt create mode 100644 app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ZstdExtractorTest.kt create mode 100644 app/src/test/resources/test-archive.iso create mode 100644 app/src/test/resources/test-archive.tar.zst create mode 100644 app/src/test/resources/test-archive.txt.zst create mode 100644 app/src/test/resources/test-archive.udf create mode 100644 app/src/test/resources/test.txt.zst diff --git a/app/build.gradle b/app/build.gradle index a1984d0db8..d9ec0fffe2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -151,6 +151,7 @@ dependencies { testImplementation "org.jsoup:jsoup:$jsoupVersion" testImplementation "androidx.room:room-migration:$roomVersion" testImplementation "io.mockk:mockk:$mockkVersion" + testImplementation "com.github.luben:zstd-jni:${zstdJniVersion}" kaptTest "com.google.auto.service:auto-service:1.0-rc4" androidTestImplementation "junit:junit:$junitVersion"//tests the app logic @@ -170,6 +171,14 @@ dependencies { playImplementation "com.github.junrar:junrar:$junrarVersion" + implementation "com.github.luben:zstd-jni:${zstdJniVersion}@aar" + implementation 'com.github.stephenc.java-iso-tools:loopy-core:1.2.2' + + implementation "com.github.luben:zstd-jni:${zstdJniVersion}@aar" + implementation ("com.github.stephenc.java-iso-tools:loopy-core:1.2.2") { + transitive = false + } + implementation "com.afollestad.material-dialogs:core:$materialDialogsVersion" implementation "com.afollestad.material-dialogs:commons:$materialDialogsVersion" @@ -255,7 +264,6 @@ dependencies { configurations.all { resolutionStrategy { dependencySubstitution { - substitute module("commons-logging:commons-logging-api:1.1") with module("commons-logging:commons-logging:1.1.1") substitute module("com.android.support:support-annotations:27.1.1") with module("com.android.support:support-annotations:27.0.2") // These two lines are added to prevent possible class clashes between awaitility (which uses hamcrest 2.1) and junit (which uses hamcrest 1.3). substitute module('org.hamcrest:hamcrest-core:1.3') with module("org.hamcrest:hamcrest:2.1") diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCommonsArchiveHelperCallable.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCommonsArchiveHelperCallable.kt index cd82d3eace..d7a4db02a9 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCommonsArchiveHelperCallable.kt +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCommonsArchiveHelperCallable.kt @@ -29,10 +29,8 @@ import org.apache.commons.compress.archivers.ArchiveEntry import org.apache.commons.compress.archivers.ArchiveException import org.apache.commons.compress.archivers.ArchiveInputStream import java.io.FileInputStream -import java.io.IOException import java.io.InputStream import java.lang.ref.WeakReference -import java.util.* abstract class AbstractCommonsArchiveHelperCallable( context: Context, @@ -54,7 +52,7 @@ abstract class AbstractCommonsArchiveHelperCallable( @Throws(ArchiveException::class) @Suppress("LabeledExpression") public override fun addElements(elements: ArrayList) { - try { + runCatching { createFrom(FileInputStream(filePath)).use { tarInputStream -> var entry: ArchiveEntry? while (tarInputStream.nextEntry.also { entry = it } != null) { @@ -90,8 +88,9 @@ abstract class AbstractCommonsArchiveHelperCallable( } } } - } catch (e: IOException) { - throw ArchiveException(String.format("Tarball archive %s is corrupt", filePath), e) + }.onFailure { + logger.error("Error enumerating archive entries", it) + throw ArchiveException(String.format("Tarball archive %s is corrupt", filePath)) } } } diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallable.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallable.kt index 88e72fd6fd..d23d50ccad 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallable.kt +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallable.kt @@ -23,6 +23,8 @@ package com.amaze.filemanager.asynchronous.asynctasks.compress import androidx.annotation.WorkerThread import com.amaze.filemanager.adapters.data.CompressedObjectParcelable import org.apache.commons.compress.archivers.ArchiveException +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.Callable @@ -31,6 +33,8 @@ abstract class CompressedHelperCallable internal constructor( ) : Callable> { + protected val logger: Logger = LoggerFactory.getLogger(javaClass) + @WorkerThread @Throws(ArchiveException::class) override fun call(): ArrayList { diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallable.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallable.kt new file mode 100644 index 0000000000..7001bbe448 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallable.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.asynchronous.asynctasks.compress + +import com.amaze.filemanager.adapters.data.CompressedObjectParcelable +import com.amaze.filemanager.filesystem.compressed.CompressedHelper +import com.amaze.filemanager.filesystem.compressed.extractcontents.Extractor +import net.didion.loopy.FileEntry +import net.didion.loopy.iso9660.ISO9660FileEntry +import net.didion.loopy.iso9660.ISO9660FileSystem +import java.io.File +import java.io.IOException +import java.lang.reflect.Field + +class Iso9660HelperCallable( + private val filePath: String, + private val relativePath: String, + createBackItem: Boolean +) : CompressedHelperCallable(createBackItem) { + + private val SLASH = Regex("/") + + // Hack. ISO9660FileEntry doesn't have getter for parentPath, we need to read it on our own + private val parentPathField: Field = + ISO9660FileEntry::class.java.getDeclaredField("parentPath").also { + it.isAccessible = true + } + + override fun addElements(elements: ArrayList) { + val isoFile = ISO9660FileSystem(File(filePath), true) + + val fileEntries: List = runCatching { + isoFile.entries?.let { isoFileEntries -> + isoFileEntries.runCatching { + isoFileEntries.toList().partition { entry -> + CompressedHelper.isEntryPathValid((entry as FileEntry).path) + }.let { pair -> + pair.first as List + } + }.onFailure { + return + }.getOrThrow() + } ?: throw IOException("Empty archive or file is corrupt") + }.onFailure { + throw Extractor.BadArchiveNotice(it) + }.getOrThrow().filter { + it.name != "." + } + + val slashCount = if (relativePath == "") { + 0 + } else { + SLASH.findAll("$relativePath/").count() + } + + fileEntries.filter { + val parentPath = parentPathField.get(it)?.toString() ?: "" + ( + if (slashCount == 0) { + parentPath == "" + } else { + parentPath == "$relativePath/" + } + ) + }.forEach { entry -> + elements.add( + CompressedObjectParcelable( + entry.name, + entry.lastModifiedTime, + entry.size.toLong(), + entry.isDirectory + ) + ) + } + } +} diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/SevenZipHelperCallable.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/SevenZipHelperCallable.kt index 4b5fc19f41..a84b502700 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/SevenZipHelperCallable.kt +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/SevenZipHelperCallable.kt @@ -29,7 +29,6 @@ import org.apache.commons.compress.PasswordRequiredException import org.apache.commons.compress.archivers.ArchiveException import java.io.File import java.io.IOException -import java.lang.UnsupportedOperationException class SevenZipHelperCallable( private val filePath: String, @@ -58,7 +57,7 @@ class SevenZipHelperCallable( ) val isInRelativeDir = ( name.contains(CompressedHelper.SEPARATOR) && - name.substring(0, name.lastIndexOf(CompressedHelper.SEPARATOR)) + name.substringBeforeLast(CompressedHelper.SEPARATOR) == relativePath ) if (isInBaseDir || isInRelativeDir) { diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallable.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallable.kt new file mode 100644 index 0000000000..b4058d4263 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallable.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.asynchronous.asynctasks.compress + +import android.content.Context +import org.apache.commons.compress.compressors.CompressorInputStream +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream + +class TarZstHelperCallable( + context: Context, + filePath: String, + relativePath: String, + goBack: Boolean +) : + AbstractCompressedTarArchiveHelperCallable(context, filePath, relativePath, goBack) { + + override fun getCompressorInputStreamClass(): Class = + ZstdCompressorInputStream::class.java +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/CompressedHelper.java b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/CompressedHelper.java index 8dc2a80cf7..2443c64b42 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/CompressedHelper.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/CompressedHelper.java @@ -30,6 +30,7 @@ import com.amaze.filemanager.filesystem.compressed.extractcontents.Extractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.Bzip2Extractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.GzipExtractor; +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.Iso9660Extractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.LzmaExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.RarExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.SevenZipExtractor; @@ -38,9 +39,12 @@ import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.TarGzExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.TarLzmaExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.TarXzExtractor; +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.TarZstExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.XzExtractor; import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.ZipExtractor; +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.ZstdExtractor; import com.amaze.filemanager.filesystem.compressed.showcontents.Decompressor; +import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.Iso9660Decompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.RarDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.SevenZipDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarBzip2Decompressor; @@ -48,6 +52,7 @@ import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarGzDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarLzmaDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarXzDecompressor; +import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarZstDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.UnknownCompressedFileDecompressor; import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.ZipDecompressor; import com.amaze.filemanager.utils.Utils; @@ -80,10 +85,14 @@ public abstract class CompressedHelper { public static final String fileExtension7zip = "7z"; public static final String fileExtensionTarLzma = "tar.lzma"; public static final String fileExtensionTarXz = "tar.xz"; + public static final String fileExtensionTarZst = "tar.zst"; public static final String fileExtensionXz = "xz"; public static final String fileExtensionLzma = "lzma"; public static final String fileExtensionGz = "gz"; public static final String fileExtensionBzip2 = "bz2"; + public static final String fileExtensionZst = "zst"; + + public static final String fileExtensionIso = "iso"; private static final String TAG = CompressedHelper.class.getSimpleName(); @@ -114,6 +123,9 @@ public static Extractor getExtractorInstance( } else if (isLzippedTar(type)) { extractor = new TarLzmaExtractor(context, file.getPath(), outputPath, listener, updatePosition); + } else if (isZstdTar(type)) { + extractor = + new TarZstExtractor(context, file.getPath(), outputPath, listener, updatePosition); } else if (is7zip(type)) { extractor = new SevenZipExtractor(context, file.getPath(), outputPath, listener, updatePosition); @@ -125,6 +137,11 @@ public static Extractor getExtractorInstance( extractor = new GzipExtractor(context, file.getPath(), outputPath, listener, updatePosition); } else if (isBzip2(type)) { extractor = new Bzip2Extractor(context, file.getPath(), outputPath, listener, updatePosition); + } else if (isZst(type)) { + extractor = new ZstdExtractor(context, file.getPath(), outputPath, listener, updatePosition); + } else if (isIso(type)) { + extractor = + new Iso9660Extractor(context, file.getPath(), outputPath, listener, updatePosition); } else { if (BuildConfig.DEBUG) { throw new IllegalArgumentException("The compressed file has no way of opening it: " + file); @@ -156,9 +173,13 @@ public static Decompressor getCompressorInstance(@NonNull Context context, @NonN decompressor = new TarXzDecompressor(context); } else if (isLzippedTar(type)) { decompressor = new TarLzmaDecompressor(context); + } else if (isZstdTar(type)) { + decompressor = new TarZstDecompressor(context); } else if (is7zip(type)) { decompressor = new SevenZipDecompressor(context); - } else if (isXz(type) || isLzma(type) || isGzip(type) || isBzip2(type)) { + } else if (isIso(type)) { + decompressor = new Iso9660Decompressor(context); + } else if (isXz(type) || isLzma(type) || isGzip(type) || isBzip2(type) || isZst(type)) { // These 4 types are only compressing one single file. // Hence invoking this UnknownCompressedFileDecompressor which only returns the filename // without the compression extension @@ -190,10 +211,13 @@ public static boolean isFileExtractable(String path) { || isBzippedTar(type) || isXzippedTar(type) || isLzippedTar(type) + || isZstdTar(type) || isBzip2(type) || isGzip(type) || isLzma(type) - || isXz(type); + || isXz(type) + || isZst(type) + || isIso(type); } /** @@ -214,12 +238,15 @@ public static String getFileName(String compressedName) { || isGzip(compressedName) || isBzip2(compressedName) || isLzma(compressedName) - || isXz(compressedName)) { + || isXz(compressedName) + || isZst(compressedName) + || isIso(compressedName)) { return compressedName.substring(0, compressedName.lastIndexOf(".")); } else if (isGzippedTar(compressedName) || isXzippedTar(compressedName) || isLzippedTar(compressedName) - || isBzippedTar(compressedName)) { + || isBzippedTar(compressedName) + || isZstdTar(compressedName)) { return compressedName.substring(0, Utils.nthToLastCharIndex(2, compressedName, '.')); } else { return compressedName; @@ -265,6 +292,10 @@ private static boolean isLzippedTar(String type) { return type.endsWith(fileExtensionTarLzma); } + private static boolean isZstdTar(String type) { + return type.endsWith(fileExtensionTarZst); + } + private static boolean isXz(String type) { return type.endsWith(fileExtensionXz) && !isXzippedTar(type); } @@ -281,6 +312,14 @@ private static boolean isBzip2(String type) { return type.endsWith(fileExtensionBzip2) && !isBzippedTar(type); } + private static boolean isZst(String type) { + return type.endsWith(fileExtensionZst) && !isZstdTar(type); + } + + private static boolean isIso(String type) { + return type.endsWith(fileExtensionIso); + } + private static String getExtension(String path) { return path.substring(path.indexOf('.') + 1).toLowerCase(); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/Extractor.java b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/Extractor.java index 4b9b8506b2..d0122a5302 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/Extractor.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/Extractor.java @@ -119,5 +119,9 @@ public static class BadArchiveNotice extends IOException { public BadArchiveNotice(@NonNull Throwable reason) { super(reason); } + + public BadArchiveNotice(@NonNull String reason) { + super(reason); + } } } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCommonsArchiveExtractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCommonsArchiveExtractor.kt index d2e0ee964d..04b07970d0 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCommonsArchiveExtractor.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCommonsArchiveExtractor.kt @@ -30,8 +30,11 @@ import com.amaze.filemanager.filesystem.compressed.extractcontents.Extractor import com.amaze.filemanager.filesystem.files.GenericCopyUtil import org.apache.commons.compress.archivers.ArchiveEntry import org.apache.commons.compress.archivers.ArchiveInputStream -import java.io.* -import java.util.* +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileInputStream +import java.io.IOException +import java.io.InputStream abstract class AbstractCommonsArchiveExtractor( context: Context, @@ -49,6 +52,14 @@ abstract class AbstractCommonsArchiveExtractor( */ abstract fun createFrom(inputStream: InputStream): ArchiveInputStream + protected open val onErrorHandler: (Throwable) -> Unit = { e -> + if (e !is EmptyArchiveNotice) { + throw BadArchiveNotice(e) + } else { + throw e + } + } + @Throws(IOException::class) @Suppress("EmptyWhileBlock") override fun extractWithFilter(filter: Filter) { @@ -56,7 +67,7 @@ abstract class AbstractCommonsArchiveExtractor( val archiveEntries = ArrayList() var inputStream = createFrom(FileInputStream(filePath)) var archiveEntry: ArchiveEntry? - try { + runCatching { while (inputStream.nextEntry.also { archiveEntry = it } != null) { archiveEntry?.run { if (filter.shouldExtract(name, isDirectory)) { @@ -82,7 +93,7 @@ abstract class AbstractCommonsArchiveExtractor( } else { throw EmptyArchiveNotice() } - } finally { + }.onFailure(onErrorHandler).also { inputStream.close() } } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCompressedTarArchiveExtractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCompressedTarArchiveExtractor.kt index 160869b244..0ce9fb89a2 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCompressedTarArchiveExtractor.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractCompressedTarArchiveExtractor.kt @@ -54,10 +54,16 @@ abstract class AbstractCompressedTarArchiveExtractor( abstract fun getCompressorInputStreamClass(): Class override fun createFrom(inputStream: InputStream): TarArchiveInputStream { - return runCatching { - TarArchiveInputStream(compressorInputStreamConstructor.newInstance(inputStream)) - }.getOrElse { - throw BadArchiveNotice(it) + if (inputStream.available() < 1) { + throw BadArchiveNotice( + "Empty input stream - no valid compressed data can be found" + ) + } else { + return runCatching { + TarArchiveInputStream(compressorInputStreamConstructor.newInstance(inputStream)) + }.getOrElse { + throw BadArchiveNotice(it) + } } } } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractIsoExtractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractIsoExtractor.kt new file mode 100644 index 0000000000..2650a44d17 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/AbstractIsoExtractor.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents.helpers + +import android.content.Context +import com.amaze.filemanager.R +import com.amaze.filemanager.application.AppConfig +import com.amaze.filemanager.fileoperations.utils.UpdatePosition +import com.amaze.filemanager.filesystem.FileUtil +import com.amaze.filemanager.filesystem.MakeDirectoryOperation +import com.amaze.filemanager.filesystem.compressed.CompressedHelper +import com.amaze.filemanager.filesystem.compressed.extractcontents.Extractor +import com.amaze.filemanager.filesystem.files.GenericCopyUtil +import net.didion.loopy.AbstractBlockFileSystem +import net.didion.loopy.FileEntry +import java.io.BufferedOutputStream +import java.io.File +import java.io.IOException + +abstract class AbstractIsoExtractor( + context: Context, + filePath: String, + outputPath: String, + listener: OnUpdate, + updatePosition: UpdatePosition +) : Extractor(context, filePath, outputPath, listener, updatePosition) { + + protected abstract val fileSystemImplementation: Class + + override fun extractWithFilter(filter: Filter) { + val isoFile = fileSystemImplementation.getDeclaredConstructor( + File::class.java, + Boolean::class.java + ).newInstance(File(filePath), true) + var totalBytes = 0L + // Quirk. AbstractBlockFileSystem.getEntries().hasNextElement() would return true even if + // it's empty or nothing, by then it's already too late but had to catch NPE on our own + val fileEntries: List = runCatching { + isoFile.entries?.let { isoFileEntries -> + isoFileEntries.toList().partition { entry -> + CompressedHelper.isEntryPathValid((entry as FileEntry).path) + }.let { pair -> + pair.first as List + } + } ?: throw IOException("Empty archive or file is corrupt") + }.onFailure { + throw BadArchiveNotice(it) + }.getOrThrow() + + if (fileEntries.isNotEmpty()) { + totalBytes = fileEntries.sumOf { it.size.toLong() } + listener.onStart(totalBytes, fileEntries.first().name) + fileEntries.forEach { entry -> + if (!listener.isCancelled) { + listener.onUpdate(entry.name) + extractEntry(context, isoFile, entry, outputPath) + } + } + listener.onFinish() + } else { + throw EmptyArchiveNotice() + } + } + + private fun extractEntry( + context: Context, + archive: AbstractBlockFileSystem, + entry: FileEntry, + outputDir: String + ) { + val name = fixEntryName(entry.path).replace( + "\\\\".toRegex(), + CompressedHelper.SEPARATOR + ) + val outputFile = File(outputDir, name) + if (!outputFile.canonicalPath.startsWith(outputDir)) { + throw IOException("Incorrect RAR FileHeader path!") + } + if (entry.isDirectory) { + MakeDirectoryOperation.mkdir(outputFile, context) + outputFile.setLastModified(entry.lastModifiedTime) + return + } + if (!outputFile.parentFile.exists()) { + MakeDirectoryOperation.mkdir(outputFile.parentFile, context) + outputFile.parentFile.setLastModified(entry.lastModifiedTime) + } + + archive.getInputStream(entry).use { inputStream -> + FileUtil.getOutputStream(outputFile, context)?.let { fileOutputStream -> + BufferedOutputStream(fileOutputStream).run { + var len: Int + val buf = ByteArray(GenericCopyUtil.DEFAULT_BUFFER_SIZE) + while (inputStream.read(buf).also { len = it } != -1) { + if (!listener.isCancelled) { + write(buf, 0, len) + updatePosition.updatePosition(len.toLong()) + } else break + } + close() + outputFile.setLastModified(entry.lastModifiedTime) + } + } ?: AppConfig.toast( + context, + context.getString( + R.string.error_archive_cannot_extract, + entry.path, + outputDir + ) + ) + } + } +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/Iso9660Extractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/Iso9660Extractor.kt new file mode 100644 index 0000000000..6278d3b59c --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/Iso9660Extractor.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents.helpers + +import android.content.Context +import com.amaze.filemanager.fileoperations.utils.UpdatePosition +import net.didion.loopy.AbstractBlockFileSystem +import net.didion.loopy.iso9660.ISO9660FileSystem + +class Iso9660Extractor( + context: Context, + filePath: String, + outputPath: String, + listener: OnUpdate, + updatePosition: UpdatePosition +) : AbstractIsoExtractor( + context, + filePath, + outputPath, + listener, + updatePosition +) { + override val fileSystemImplementation: Class + get() = ISO9660FileSystem::class.java +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/TarZstExtractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/TarZstExtractor.kt new file mode 100644 index 0000000000..552ca3f704 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/TarZstExtractor.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents.helpers + +import android.content.Context +import com.amaze.filemanager.fileoperations.utils.UpdatePosition +import org.apache.commons.compress.compressors.CompressorInputStream +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream + +class TarZstExtractor( + context: Context, + filePath: String, + outputPath: String, + listener: OnUpdate, + updatePosition: UpdatePosition +) : AbstractCompressedTarArchiveExtractor( + context, + filePath, + outputPath, + listener, + updatePosition +) { + + override fun getCompressorInputStreamClass(): Class = + ZstdCompressorInputStream::class.java +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/ZstdExtractor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/ZstdExtractor.kt new file mode 100644 index 0000000000..19b392e58c --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/extractcontents/helpers/ZstdExtractor.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents.helpers + +import android.content.Context +import com.amaze.filemanager.fileoperations.utils.UpdatePosition +import org.apache.commons.compress.compressors.CompressorInputStream +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream + +class ZstdExtractor( + context: Context, + filePath: String, + outputPath: String, + listener: OnUpdate, + updatePosition: UpdatePosition +) : AbstractCommonsCompressedFileExtractor( + context, + filePath, + outputPath, + listener, + updatePosition +) { + override fun getCompressorInputStreamClass(): Class { + return ZstdCompressorInputStream::class.java + } +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/Iso9660Decompressor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/Iso9660Decompressor.kt new file mode 100644 index 0000000000..ac7128733f --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/Iso9660Decompressor.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.showcontents.helpers + +import android.content.Context +import com.amaze.filemanager.asynchronous.asynctasks.compress.CompressedHelperCallable +import com.amaze.filemanager.asynchronous.asynctasks.compress.Iso9660HelperCallable +import com.amaze.filemanager.filesystem.compressed.showcontents.Decompressor + +open class Iso9660Decompressor(context: Context) : Decompressor(context) { + override fun changePath(path: String, addGoBackItem: Boolean): CompressedHelperCallable = + Iso9660HelperCallable(filePath, path, addGoBackItem) +} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/TarZstDecompressor.kt b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/TarZstDecompressor.kt new file mode 100644 index 0000000000..55499d3d20 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/compressed/showcontents/helpers/TarZstDecompressor.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.showcontents.helpers + +import android.content.Context +import com.amaze.filemanager.asynchronous.asynctasks.compress.TarZstHelperCallable +import com.amaze.filemanager.filesystem.compressed.showcontents.Decompressor + +class TarZstDecompressor(context: Context) : Decompressor(context) { + override fun changePath( + path: String, + addGoBackItem: Boolean + ) = + TarZstHelperCallable(context, filePath, path, addGoBackItem) +} diff --git a/app/src/main/java/com/amaze/filemanager/ui/icons/Icons.java b/app/src/main/java/com/amaze/filemanager/ui/icons/Icons.java index 60587ab12c..09814c56a0 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/icons/Icons.java +++ b/app/src/main/java/com/amaze/filemanager/ui/icons/Icons.java @@ -131,7 +131,8 @@ private static void putKeys(int resId, String... mimeTypes) { "application/x-rar-compressed", "application/x-lzma", "application/x-xz", - "application/x-bzip2"); + "application/x-bzip2", + "application/zst"); putKeys(CONTACT, "text/x-vcard", "text/vcard"); putKeys(EVENTS, "text/calendar", "text/x-vcalendar"); putKeys( diff --git a/app/src/main/java/com/amaze/filemanager/ui/icons/MimeTypes.java b/app/src/main/java/com/amaze/filemanager/ui/icons/MimeTypes.java index 49b982e333..429ee6fdd9 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/icons/MimeTypes.java +++ b/app/src/main/java/com/amaze/filemanager/ui/icons/MimeTypes.java @@ -75,6 +75,7 @@ public final class MimeTypes { MIME_TYPES.put("xz", "application/x-xz"); MIME_TYPES.put("lzma", "application/x-lzma"); MIME_TYPES.put("Z", "application/x-compress"); + MIME_TYPES.put("zst", "application/zst"); MIME_TYPES.put("bat", "application/x-msdownload"); MIME_TYPES.put("ksh", "text/plain"); diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCompressedHelperCallableArchiveTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCompressedHelperCallableArchiveTest.kt index 13e5952269..880b086953 100644 --- a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCompressedHelperCallableArchiveTest.kt +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/AbstractCompressedHelperCallableArchiveTest.kt @@ -73,7 +73,11 @@ abstract class AbstractCompressedHelperCallableArchiveTest : open fun testSublevels() { var task = createCallable("test-archive") var result = task.call() - assertEquals("Thrown from $javaClass.name", 5, result.size.toLong()) + assertEquals( + "Thrown from $javaClass.name; Entries saw ${result.joinToString { "${it.path}\n" }}", + 5, + result.size.toLong() + ) assertEquals("1", result[0].name) assertEntryTimestampCorrect(result[0]) assertEquals("2", result[1].name) diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperForBadArchiveTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperForBadArchiveTest.kt index 908f1cc79f..bec64c67ff 100644 --- a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperForBadArchiveTest.kt +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperForBadArchiveTest.kt @@ -20,7 +20,8 @@ package com.amaze.filemanager.asynchronous.asynctasks.compress -import android.os.Build.VERSION_CODES.* +import android.os.Build.VERSION_CODES.KITKAT +import android.os.Build.VERSION_CODES.P import android.os.Environment import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -29,7 +30,10 @@ import com.amaze.filemanager.shadows.ShadowMultiDex import com.amaze.filemanager.test.randomBytes import com.amaze.filemanager.test.supportedArchiveExtensions import org.apache.commons.compress.archivers.ArchiveException -import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config @@ -73,14 +77,20 @@ class CompressedHelperForBadArchiveTest { ApplicationProvider.getApplicationContext(), badArchive ) - Assert.assertNotNull(task) + assertNotNull(task) task!! try { val result = task.changePath("", false).call() - Assert.assertNull("Thrown from ${task.javaClass}", result) + // FIXME: Quirk. ZStdCompressorInputStream behaves differently that it cannot throw + // if encountering corrupt/zero byte tar.zst archives + if (archiveType in arrayOf("tar.zst", "iso")) { + assertEquals(0, result.size) + } else { + assertNull("Thrown from ${task.javaClass}", result) + } } catch (exception: ArchiveException) { - Assert.assertNotNull(exception) - Assert.assertTrue( + assertNotNull(exception) + assertTrue( "Thrown from ${task.javaClass}: ${exception.javaClass} was thrown", ArchiveException::class.java.isAssignableFrom(exception.javaClass) ) @@ -96,6 +106,6 @@ class CompressedHelperForBadArchiveTest { * = filename without compressed extension. They won't throw exceptions, so excluded from * list */ - private val excludedArchiveTypes = listOf("tar", "rar", "bz2", "lzma", "gz", "xz") + private val excludedArchiveTypes = listOf("tar", "rar", "bz2", "lzma", "gz", "xz", "zst") } } diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallableTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallableTest.kt new file mode 100644 index 0000000000..b3da8b309a --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/Iso9660HelperCallableTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.asynchronous.asynctasks.compress + +import java.io.File + +class Iso9660HelperCallableTest : AbstractCompressedHelperCallableArchiveTest() { + + override val archiveFileName: String + get() = "test-archive.iso" + + override fun doCreateCallable(archive: File, relativePath: String): CompressedHelperCallable = + Iso9660HelperCallable( + archive.absolutePath, + relativePath, + false + ) +} diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallableTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallableTest.kt new file mode 100644 index 0000000000..f506f89e0e --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/TarZstHelperCallableTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.asynchronous.asynctasks.compress + +import androidx.test.core.app.ApplicationProvider +import java.io.File + +class TarZstHelperCallableTest : AbstractCompressedHelperCallableArchiveTest() { + override val archiveFileName: String + get() = "test-archive.tar.zst" + + override fun doCreateCallable(archive: File, relativePath: String): CompressedHelperCallable = + TarZstHelperCallable( + ApplicationProvider.getApplicationContext(), + archive.absolutePath, + relativePath, + false + ) +} diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/UnknownCompressedHelperCallableTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/UnknownCompressedHelperCallableTest.kt index 921c797aac..61f34e82e2 100644 --- a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/UnknownCompressedHelperCallableTest.kt +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/compress/UnknownCompressedHelperCallableTest.kt @@ -32,7 +32,7 @@ class UnknownCompressedHelperCallableTest : AbstractCompressedHelperCallableTest */ @Test fun testExtract() { - listOf("lzma", "gz", "xz", "bz2").forEach { ext -> + listOf("lzma", "gz", "xz", "bz2", "zst").forEach { ext -> doTestExtract( UnknownCompressedFileHelperCallable( File( diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/services/ExtractServiceTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/services/ExtractServiceTest.kt index 8765894503..d4bad171ef 100644 --- a/app/src/test/java/com/amaze/filemanager/asynchronous/services/ExtractServiceTest.kt +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/services/ExtractServiceTest.kt @@ -48,7 +48,6 @@ import org.awaitility.Awaitility.await import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull -import org.junit.Assert.fail import org.junit.Before import org.junit.Ignore import org.junit.Rule @@ -445,11 +444,6 @@ class ExtractServiceTest { performTest(badArchive) ShadowLooper.idleMainLooper() await() - .conditionEvaluationListener { condition -> - if (condition.remainingTimeInMS <= 0 && !condition.isSatisfied) { - fail("Extractor unable to handle bad archive for $archiveType") - } - } .atMost(10, TimeUnit.SECONDS) .until { ShadowToast.getLatestToast() != null diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/AbstractExtractorTest.kt b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/AbstractExtractorTest.kt index dbc2444af7..50fae807a8 100644 --- a/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/AbstractExtractorTest.kt +++ b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/AbstractExtractorTest.kt @@ -32,13 +32,18 @@ import com.amaze.filemanager.fileoperations.filesystem.compressed.ArchivePasswor import com.amaze.filemanager.fileoperations.utils.UpdatePosition import com.amaze.filemanager.shadows.ShadowMultiDex import com.amaze.filemanager.test.randomBytes -import org.junit.* +import org.junit.After import org.junit.Assert.assertTrue import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config import org.robolectric.shadows.ShadowEnvironment -import java.io.* +import java.io.ByteArrayInputStream +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/IsoExtractorTest.kt b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/IsoExtractorTest.kt new file mode 100644 index 0000000000..7f08614a70 --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/IsoExtractorTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents + +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.Iso9660Extractor + +class IsoExtractorTest : AbstractArchiveExtractorTest() { + + override val archiveType: String = "iso" + + override fun extractorClass(): Class = Iso9660Extractor::class.java +} diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/TarZstExtractorTest.kt b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/TarZstExtractorTest.kt new file mode 100644 index 0000000000..023097dddd --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/TarZstExtractorTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents + +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.TarZstExtractor + +class TarZstExtractorTest : AbstractArchiveExtractorTest() { + + override val archiveType: String = "tar.zst" + + override fun extractorClass(): Class = TarZstExtractor::class.java +} diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ZstdExtractorTest.kt b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ZstdExtractorTest.kt new file mode 100644 index 0000000000..f00396c217 --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ZstdExtractorTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.filesystem.compressed.extractcontents + +import com.amaze.filemanager.filesystem.compressed.extractcontents.helpers.ZstdExtractor + +class ZstdExtractorTest : AbstractCompressedFileExtractorTest() { + + override fun extractorClass(): Class = ZstdExtractor::class.java + + override val archiveType: String = "zst" +} diff --git a/app/src/test/resources/test-archive.iso b/app/src/test/resources/test-archive.iso new file mode 100644 index 0000000000000000000000000000000000000000..525ab6ca024360c42c73f1eca413ec2687f0a336 GIT binary patch literal 417792 zcmeI*4X{+zeE{&Yi-ad(VpM8r2K4fLd?`NO2cK#iANv+qd0*Mx=YUDW0QxG5Xu)Ws zF%?QiQ)9+fon(?U+SH^bwXKOVaVFE+Y3mFzHrh$8F_T&{e$-kmesrcWmfCZ7-}V8I zFNI+xGU2^xo2?!%0@c51rUXWTKz1j@v25*R$i2IdR?^x3DC%$6P)%$2EH69@-P@=!|94tsF{^j$EN(!Fzvq@=gTFC4> z{*TLMglszgkLk>iO~?N+HHB-5gT%=vhkv#nF8~0Kr zGw!9@yu9o~nGX!!sJ}96Oa@=&@>4S{)wKp^A-r0B{p!F-?Z_N~283;udmO|nr`7H# zeC_yYr_2mVJS-6pfn+A_eepx3%b)G(J~jOg#n zgx0;oc;!clc+O%ieAnjthw+nVSH8-e+>nHgmHQ#Zy3VXqSKClsZST6Srs_`8RHv@? zp}N}NbzP0sx*F@$HGinC`R}@}^sZ4conEJ|j-k5N?p@bGzk2TVwQH}>t6GsM0RjXF z)C2}9ckYX?>irEK9{&&HukVQ8GKP=t593?L#>%}1V_(jw6Hk^7#gld7No^M}CqRGz zfiWmBFjCAsLh*luk>2q5e;D5~Hio473C6n)rJ07X@6r8CZYch*7yqkPrUVEOATTBc z21bgRM=1WMPX&mn-pKq2#sBHmTBg^D|D8ke|B}7q|6v^euU2GAfB*pk1ddOEfy%Ra zV%zJ*%;E9>Frwv%-!hI!{J$war>K%z;TfE%cn%TIp9j z7vQ8!*tp^9EuXludEJIh>6e0swx;WDeJHMxrcCW*fZF^ZCk0g3FeN~M009D{USOc| z48PdSdgpr`q4=LZTQH_N=iO(9$N$6l{r~FFR;{INe04s4@c4gxmB#-Q7BmmV{df-G z0^z>uWJ-Vl0Rm%AU|^*4*pI9D-(LOv+G{5d>=*wR?Hm6WiT`76f7f9G1PBnQ3Jkkem-#1D=O^qJ{}=8X{}+n?RVPyd1PBlqa{>b+T{C}N#s3A>&u>BP z6ovib|Kfe)|6=if%)9bL*9vAIqdqAdYEvy)tX7@0$`JK!5;& zF)GkFLaa>V|E#J_ozZS$T9+(o?I^FvC&k{}%A{E6T%Jz~J>`5cx1ysb=KG4h z<=$LxSDLw~(3vkKrT)IY-eNgf)>};adP{{>$+ALMJ}Ip(mGj+6Dc{}EQ!eC6eO>vE zQa;J`_LMtv<)pJv$|ZgMOS=lC75QRiFO#0 z0t5&UIIzH#({_LN^W#7J!8>;EeD0av3AwLkzjbk6`Giki|La$rz4M~WAHVB|-UM-LP}Qjaz^5pEqV3e)Lz1 z2fBCt_>nbTzh9m{^)KFf?tcnz|L;$p`pDL%wO2iI-FbcIKlk>nD}Oqn^p$`6?ThC> ze8USLoAlVKORmW5crI_AZaw$$++81k<=zK>}Ti3q+sXsaG`zsgkYS_GI*Y|c!{KA)aJ^c9{Q|7MQcF(;3zW9o>{(kds zJvnvfjM8J|z9_@ggfb@%m6+wb3SfBTIe?axeE(elPaKOf)p z(u)uGU-rsJnttuh`<}gW`_nJK{k|0+f8aMe|7*hT#@FwE@|_FXTDJGk%KgENB`=*i zbKBErv`pWzYRYZ*?!39U{bx&OO*`*5#(i;VU*YYEUwdovoJk*^b^hGDZkT$(_($G+ ze#?E&eeRB1`fi-_!;^l|dF!5UFaF!p#_jyf+uP2)dE0B7KfLCT{^^|CuiO4?_pd(R z_03&7TOYmqwf0YypZ)U7|9|`63q}8ZK)fJ8fB*pk zV?ZE||9t@%1D~@l5+Fc;0D-*)7Kr|PbI21QK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ bfB*pk1PBlyK!5-N0t5&UAV7e?C>8iW*|645 literal 0 HcmV?d00001 diff --git a/app/src/test/resources/test-archive.tar.zst b/app/src/test/resources/test-archive.tar.zst new file mode 100644 index 0000000000000000000000000000000000000000..8dd59ecfaee2eac3014faa2fd7f23106658e8f8f GIT binary patch literal 833 zcmV-H1HSwywJ-f-08gD60CXyJWpi{bVRB<=X?A5V05C8xFgG_f05C8xF*h|Z05LN& zFf%eTHa0OZ05CB#Gczy%ATxaAc4YuFFElqYHa0diG&MCe03a|m zHa7}kH8n6{FJdn@H!)!^VlQJaG%_(WWG^%`Hf(8db9HSlVrgzMFfcGSG&3-59q+e} z1&I)o@5tlXPX|Mu0;*b2R0Mf?J!>n-R%*YAmQ_6(qw9o5J@H9`R zSqNcjQq!7mpL!&OtZA$>eOpxl7)UVkwD<)i=;pXpXzWoW8I-8rZG_kA`vyo}uw6v- z2k#>BtkC~JGBAWyCqt1XIp`B6g4Z7~A+=Z-jiktjQiS0`CmlOJ1DHZkN&5<-`4lM( zKPNmam3tgO1;6v;bExBtlZa4#Db5f2M2r2gIKmbK$c6iYI){Sraz9_4)Gd&Agx*aT zmH$T7vA@H2c=-ka*_l(^Q&5cojR<>Bu<@{s2JW1{NN)uk?kQh$|{*oCWipr}}Y zGQhG}T8sri9EcAJkkw)#&&w9`Rj87x%-XLZecmik^Ez9N(5+^4G&DCeG%qkPFf}tZ zH8nLhH#jjcF)uhWGBPnTFE=wYHVQW&fP)m2q&fYmJkVE>jLJMQ(}AlVf?`E1AHZsm zsn=sOb#PQcPzR5YParB8s}k#Iz<{{|v8xHgRmVVG0di?f&9I{Z3W1>ruR1P#+``g;OI6f* F9{|My5as{? literal 0 HcmV?d00001 diff --git a/app/src/test/resources/test-archive.udf b/app/src/test/resources/test-archive.udf new file mode 100644 index 0000000000000000000000000000000000000000..f799a99d9edb5f375ca1c9207cb845943f87fc98 GIT binary patch literal 3258368 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 E4@7zhO8@`> literal 0 HcmV?d00001 diff --git a/app/src/test/resources/test.txt.zst b/app/src/test/resources/test.txt.zst new file mode 100644 index 0000000000000000000000000000000000000000..7598a59c2371e190256b63fc8eec41294b1a8c14 GIT binary patch literal 50 zcmdPcs{dC-Rg;k+F)2AEH7z|OGb=kMH!r`Su&B7Cw5+_MvdYlN*u>P#+``g;OI6f* F9{|My5as{? literal 0 HcmV?d00001 diff --git a/app/src/testPlay/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallableTestSuite.kt b/app/src/testPlay/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallableTestSuite.kt index 229c7b3fc2..74525c949e 100644 --- a/app/src/testPlay/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallableTestSuite.kt +++ b/app/src/testPlay/java/com/amaze/filemanager/asynchronous/asynctasks/compress/CompressedHelperCallableTestSuite.kt @@ -34,6 +34,7 @@ import org.junit.runners.Suite.SuiteClasses TarLzmaHelperCallableTest::class, TarXzHelperCallableTest::class, TarXzHelperCallableTest2::class, + TarZstHelperCallableTest::class, SevenZipHelperCallableTest::class, SevenZipHelperCallableTest2::class, SevenZipHelperCallableTest3::class, diff --git a/app/src/testPlay/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ExtractorTestSuite.kt b/app/src/testPlay/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ExtractorTestSuite.kt index 78054e5bd9..cb85e3d86e 100644 --- a/app/src/testPlay/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ExtractorTestSuite.kt +++ b/app/src/testPlay/java/com/amaze/filemanager/filesystem/compressed/extractcontents/ExtractorTestSuite.kt @@ -30,6 +30,7 @@ import org.junit.runners.Suite.SuiteClasses Bzip2ExtractorTest::class, LzmaExtractorTest::class, XzExtractorTest::class, + ZstdExtractorTest::class, TarGzExtractorTest::class, TgzExtractorTest::class, ZipExtractorTest::class, @@ -39,6 +40,7 @@ import org.junit.runners.Suite.SuiteClasses TarBzip2ExtractorTest2::class, TarLzmaExtractorTest::class, TarXzExtractorTest::class, + TarZstExtractorTest::class, SevenZipExtractorTest::class, SevenZipWithoutTimestampTest::class, PasswordProtectedRarTest::class, diff --git a/build.gradle b/build.gradle index be887ac69a..00c6e915d0 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,7 @@ buildscript { rxAndroidVersion = "2.1.1" rxJavaVersion = "2.2.9" okHttpVersion = "4.9.0" + zstdJniVersion = "1.5.2-5" } repositories { google()