Try Apache implementation of ZipFile instead

Docs: https://commons.apache.org/proper/commons-compress/
Related StackOverflow post: https://stackoverflow.com/a/54236244/4421500

Related to #9438

(cherry picked from commit c623258e8c289ecebb2a673fd118686ab56bd0bd)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt
This commit is contained in:
arkon 2023-05-03 14:00:33 -04:00 committed by Jobobby04
parent fe0f9f053b
commit 782b6dec02
6 changed files with 33 additions and 55 deletions

View File

@ -210,6 +210,7 @@ dependencies {
// Disk // Disk
implementation(libs.disklrucache) implementation(libs.disklrucache)
implementation(libs.unifile) implementation(libs.unifile)
implementation(libs.compress)
implementation(libs.junrar) implementation(libs.junrar)
// SY --> // SY -->
implementation(libs.zip4j) implementation(libs.zip4j)

View File

@ -101,13 +101,7 @@ class ChapterLoader(
source is LocalSource -> source.getFormat(chapter.chapter).let { format -> source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
when (format) { when (format) {
is Format.Directory -> DirectoryPageLoader(format.file) is Format.Directory -> DirectoryPageLoader(format.file)
// SY --> is Format.Zip -> ZipPageLoader(format.file)
is Format.Zip -> try {
ZipPageLoader(format.file, context)
} catch (e: Throwable) {
error(context.getString(R.string.wrong_cbz_archive_password))
}
// SY <--
is Format.Rar -> try { is Format.Rar -> try {
RarPageLoader(format.file) RarPageLoader(format.file)
} catch (e: UnsupportedRarV5Exception) { } catch (e: UnsupportedRarV5Exception) {
@ -124,13 +118,7 @@ class ChapterLoader(
source is LocalSource -> source.getFormat(chapter.chapter).let { format -> source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
when (format) { when (format) {
is Format.Directory -> DirectoryPageLoader(format.file) is Format.Directory -> DirectoryPageLoader(format.file)
// SY --> is Format.Zip -> ZipPageLoader(format.file)
is Format.Zip -> try {
ZipPageLoader(format.file, context)
} catch (e: Throwable) {
error(context.getString(R.string.wrong_cbz_archive_password))
}
// SY <--
is Format.Rar -> try { is Format.Rar -> try {
RarPageLoader(format.file) RarPageLoader(format.file)
} catch (e: UnsupportedRarV5Exception) { } catch (e: UnsupportedRarV5Exception) {

View File

@ -47,9 +47,7 @@ internal class DownloadPageLoader(
} }
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> { private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
// SY --> val loader = ZipPageLoader(File(chapterPath.filePath!!)).also { zipPageLoader = it }
val loader = ZipPageLoader(File(chapterPath.filePath!!), context).also { zipPageLoader = it }
// SY <--
return loader.getPages() return loader.getPages()
} }

View File

@ -1,27 +1,23 @@
package eu.kanade.tachiyomi.ui.reader.loader package eu.kanade.tachiyomi.ui.reader.loader
import android.app.Application import android.app.Application
import android.content.Context
import android.os.Build import android.os.Build
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.util.storage.CbzCrypto import eu.kanade.tachiyomi.util.storage.CbzCrypto
import net.lingala.zip4j.ZipFile import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.zip.ZipInputStream import net.lingala.zip4j.ZipFile as Zip4JFile
/** /**
* Loader used to load a chapter from a .zip or .cbz file. * Loader used to load a chapter from a .zip or .cbz file.
*/ */
internal class ZipPageLoader( internal class ZipPageLoader(file: File) : PageLoader() {
file: File,
// SY -->
context: Context,
// SY <--
) : PageLoader() {
private val context: Application by injectLazy() private val context: Application by injectLazy()
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also { private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
@ -31,11 +27,11 @@ internal class ZipPageLoader(
// SY --> // SY -->
init { init {
val zip = ZipFile(file) val zip = Zip4JFile(file)
if (zip.isEncrypted) { if (zip.isEncrypted) {
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) { if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
this.recycle() this.recycle()
throw Exception(context.getString(R.string.wrong_cbz_archive_password)) throw IllegalStateException(context.getString(R.string.wrong_cbz_archive_password))
} }
unzipEncrypted(zip) unzipEncrypted(zip)
} else { } else {
@ -44,34 +40,26 @@ internal class ZipPageLoader(
} }
private fun unzip(file: File) { private fun unzip(file: File) {
// SY <-- // SY <--
ZipInputStream(FileInputStream(file)).use { zipInputStream -> ByteArrayOutputStream().use { byteArrayOutputStream ->
generateSequence { zipInputStream.nextEntry } FileInputStream(file).use { it.copyTo(byteArrayOutputStream) }
.filterNot { it.isDirectory }
.forEach { entry -> ZipFile(SeekableInMemoryByteChannel(byteArrayOutputStream.toByteArray())).use { zip ->
File(tmpDir, entry.name.substringAfterLast("/")) zip.entries.asSequence()
.also { it.createNewFile() } .filterNot { it.isDirectory }
.outputStream().use { pageOutputStream -> .forEach { entry ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { File(tmpDir, entry.name.substringAfterLast("/"))
pageOutputStream.write(zipInputStream.readNBytes(entry.size.toInt())) .also { it.createNewFile() }
} else { .outputStream().use { pageOutputStream ->
val buffer = ByteArray(2048) zip.getInputStream(entry).copyTo(pageOutputStream)
var len: Int pageOutputStream.flush()
while (
zipInputStream.read(buffer, 0, buffer.size)
.also { len = it } >= 0
) {
pageOutputStream.write(buffer, 0, len)
}
} }
pageOutputStream.flush() }
} }
zipInputStream.closeEntry()
}
} }
} }
// SY --> // SY -->
private fun unzipEncrypted(zip: ZipFile) { private fun unzipEncrypted(zip: Zip4JFile) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
zip.charset = StandardCharsets.ISO_8859_1 zip.charset = StandardCharsets.ISO_8859_1
} }

View File

@ -146,10 +146,12 @@ object CbzCrypto {
return decrypt(securityPreferences.sqlPassword().get(), ALIAS_SQL).toByteArray() return decrypt(securityPreferences.sqlPassword().get(), ALIAS_SQL).toByteArray()
} }
/** Function that returns true when the supplied password /**
* can Successfully decrypt the supplied zip archive */ * Function that returns true when the supplied password
// not very elegant but this is the solution recommended by the maintainer for checking passwords * can Successfully decrypt the supplied zip archive
// a real password check will likely be implemented in the future though * not very elegant but this is the solution recommended by the maintainer for checking passwords
* a real password check will likely be implemented in the future though
*/
fun checkCbzPassword(zip4j: ZipFile, password: CharArray): Boolean { fun checkCbzPassword(zip4j: ZipFile, password: CharArray): Boolean {
try { try {
zip4j.setPassword(password) zip4j.setPassword(password)

View File

@ -32,6 +32,7 @@ jsoup = "org.jsoup:jsoup:1.15.4"
disklrucache = "com.jakewharton:disklrucache:2.0.2" disklrucache = "com.jakewharton:disklrucache:2.0.2"
unifile = "com.github.tachiyomiorg:unifile:17bec43" unifile = "com.github.tachiyomiorg:unifile:17bec43"
compress = "org.apache.commons:commons-compress:1.23.0"
junrar = "com.github.junrar:junrar:7.5.4" junrar = "com.github.junrar:junrar:7.5.4"
zip4j = "net.lingala.zip4j:zip4j:2.11.5" zip4j = "net.lingala.zip4j:zip4j:2.11.5"