Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISO9660 and ZStandard archive support #3959

Draft
wants to merge 1 commit into
base: release/4.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"

Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -54,7 +52,7 @@ abstract class AbstractCommonsArchiveHelperCallable(
@Throws(ArchiveException::class)
@Suppress("LabeledExpression")
public override fun addElements(elements: ArrayList<CompressedObjectParcelable>) {
try {
runCatching {
createFrom(FileInputStream(filePath)).use { tarInputStream ->
var entry: ArchiveEntry?
while (tarInputStream.nextEntry.also { entry = it } != null) {
Expand Down Expand Up @@ -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))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -31,6 +33,8 @@ abstract class CompressedHelperCallable internal constructor(
) :
Callable<ArrayList<CompressedObjectParcelable>> {

protected val logger: Logger = LoggerFactory.getLogger(javaClass)

@WorkerThread
@Throws(ArchiveException::class)
override fun call(): ArrayList<CompressedObjectParcelable> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2014-2023 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
* Emmanuel Messulam<[email protected]>, Raymond Lai <airwave209gt at gmail.com> 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 <http://www.gnu.org/licenses/>.
*/

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<CompressedObjectParcelable>) {
val isoFile = ISO9660FileSystem(File(filePath), true)

val fileEntries: List<FileEntry> = runCatching {
isoFile.entries?.let { isoFileEntries ->
isoFileEntries.runCatching {
isoFileEntries.toList().partition { entry ->
CompressedHelper.isEntryPathValid((entry as FileEntry).path)
}.let { pair ->
pair.first as List<FileEntry>
}
}.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
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2014-2021 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
* Emmanuel Messulam<[email protected]>, Raymond Lai <airwave209gt at gmail.com> 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 <http://www.gnu.org/licenses/>.
*/

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<out CompressorInputStream> =
ZstdCompressorInputStream::class.java
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,16 +39,20 @@
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;
import com.amaze.filemanager.filesystem.compressed.showcontents.helpers.TarDecompressor;
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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,9 @@ public static class BadArchiveNotice extends IOException {
public BadArchiveNotice(@NonNull Throwable reason) {
super(reason);
}

public BadArchiveNotice(@NonNull String reason) {
super(reason);
}
}
}