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:
parent
fe0f9f053b
commit
782b6dec02
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user