Fix closing ZipFile

master
Koitharu 2 years ago
parent 2cd67e7cf8
commit e2993d47b6
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -4,6 +4,7 @@ import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import okhttp3.internal.closeQuietly
import okio.Closeable import okio.Closeable
import org.json.JSONArray import org.json.JSONArray
import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException
@ -38,7 +39,7 @@ class BackupZipInput private constructor(val file: File) : Closeable {
fun cleanupAsync() { fun cleanupAsync() {
processLifecycleScope.launch(Dispatchers.IO, CoroutineStart.ATOMIC) { processLifecycleScope.launch(Dispatchers.IO, CoroutineStart.ATOMIC) {
runCatching { runCatching {
close() closeQuietly()
file.delete() file.delete()
} }
} }
@ -46,14 +47,22 @@ class BackupZipInput private constructor(val file: File) : Closeable {
companion object { companion object {
fun from(file: File): BackupZipInput = try { fun from(file: File): BackupZipInput {
val res = BackupZipInput(file) var res: BackupZipInput? = null
if (res.zipFile.getEntry("index") == null) { return try {
throw BadBackupFormatException(null) res = BackupZipInput(file)
if (res.zipFile.getEntry("index") == null) {
throw BadBackupFormatException(null)
}
res
} catch (exception: Exception) {
res?.closeQuietly()
throw if (exception is ZipException) {
BadBackupFormatException(exception)
} else {
exception
}
} }
res
} catch (e: ZipException) {
throw BadBackupFormatException(e)
} }
} }
} }

@ -10,10 +10,13 @@ import android.provider.OpenableColumns
import androidx.core.database.getStringOrNull import androidx.core.database.getStringOrNull
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import okhttp3.internal.closeQuietly
import org.jetbrains.annotations.Blocking
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.fs.FileSequence import org.koitharu.kotatsu.core.fs.FileSequence
import java.io.File import java.io.File
import java.io.FileFilter import java.io.FileFilter
import java.io.InputStream
import java.nio.file.attribute.BasicFileAttributes import java.nio.file.attribute.BasicFileAttributes
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile import java.util.zip.ZipFile
@ -32,10 +35,19 @@ fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() }
fun File.isNotEmpty() = length() != 0L fun File.isNotEmpty() = length() != 0L
@Blocking
fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use { fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use {
it.readText() it.readText()
} }
@Blocking
fun ZipFile.getInputStreamOrClose(entry: ZipEntry): InputStream = try {
getInputStream(entry)
} catch (e: Throwable) {
closeQuietly()
throw e
}
fun File.getStorageName(context: Context): String = runCatching { fun File.getStorageName(context: Context): String = runCatching {
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

@ -41,7 +41,7 @@ fun Uri.source(): Source = when (scheme) {
URI_SCHEME_ZIP -> { URI_SCHEME_ZIP -> {
val zip = ZipFile(schemeSpecificPart) val zip = ZipFile(schemeSpecificPart)
val entry = zip.getEntry(fragment) val entry = zip.getEntry(fragment)
zip.getInputStream(entry).source().withExtraCloseable(zip) zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip)
} }
else -> unsupportedUri(this) else -> unsupportedUri(this)

@ -22,6 +22,7 @@ import okio.source
import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose
import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.local.data.isFileUri import org.koitharu.kotatsu.local.data.isFileUri
import org.koitharu.kotatsu.local.data.isZipUri import org.koitharu.kotatsu.local.data.isZipUri
@ -68,7 +69,7 @@ class MangaPageFetcher(
val entry = zip.getEntry(uri.fragment) val entry = zip.getEntry(uri.fragment)
SourceResult( SourceResult(
source = ImageSource( source = ImageSource(
source = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer(), source = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer(),
context = context, context = context,
metadata = MangaPageMetadata(page), metadata = MangaPageMetadata(page),
), ),

@ -12,6 +12,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import okio.buffer import okio.buffer
import okio.source import okio.source
import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose
import org.koitharu.kotatsu.local.data.util.withExtraCloseable import org.koitharu.kotatsu.local.data.util.withExtraCloseable
import java.util.zip.ZipFile import java.util.zip.ZipFile
@ -24,7 +25,7 @@ class CbzFetcher(
val zip = ZipFile(uri.schemeSpecificPart) val zip = ZipFile(uri.schemeSpecificPart)
val entry = zip.getEntry(uri.fragment) val entry = zip.getEntry(uri.fragment)
val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name) val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name)
val bufferedSource = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer() val bufferedSource = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer()
SourceResult( SourceResult(
source = ImageSource( source = ImageSource(
source = bufferedSource, source = bufferedSource,

@ -112,32 +112,33 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) {
return runInterruptible(Dispatchers.IO) { return runInterruptible(Dispatchers.IO) {
val uri = Uri.parse(chapter.url) val uri = Uri.parse(chapter.url)
val file = uri.toFile() val file = uri.toFile()
val zip = ZipFile(file) ZipFile(file).use { zip ->
val index = zip.getEntry(LocalMangaOutput.ENTRY_NAME_INDEX)?.let(zip::readText)?.let(::MangaIndex) val index = zip.getEntry(LocalMangaOutput.ENTRY_NAME_INDEX)?.let(zip::readText)?.let(::MangaIndex)
var entries = zip.entries().asSequence() var entries = zip.entries().asSequence()
entries = if (index != null) { entries = if (index != null) {
val pattern = index.getChapterNamesPattern(chapter) val pattern = index.getChapterNamesPattern(chapter)
entries.filter { x -> !x.isDirectory && x.name.substringBefore('.').matches(pattern) } entries.filter { x -> !x.isDirectory && x.name.substringBefore('.').matches(pattern) }
} else { } else {
val parent = uri.fragment.orEmpty() val parent = uri.fragment.orEmpty()
entries.filter { x -> entries.filter { x ->
!x.isDirectory && x.name.substringBeforeLast( !x.isDirectory && x.name.substringBeforeLast(
File.separatorChar, File.separatorChar,
"", "",
) == parent ) == parent
}
} }
entries
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
.map { x ->
val entryUri = zipUri(file, x.name)
MangaPage(
id = entryUri.longHashCode(),
url = entryUri,
preview = null,
source = LocalMangaSource,
)
}
} }
entries
.toListSorted(compareBy(AlphanumComparator()) { x -> x.name })
.map { x ->
val entryUri = zipUri(file, x.name)
MangaPage(
id = entryUri.longHashCode(),
url = entryUri,
preview = null,
source = LocalMangaSource,
)
}
} }
} }

@ -67,10 +67,11 @@ class DetectReaderModeUseCase @Inject constructor(
val size = when { val size = when {
uri.isZipUri() -> runInterruptible(Dispatchers.IO) { uri.isZipUri() -> runInterruptible(Dispatchers.IO) {
val zip = ZipFile(uri.schemeSpecificPart) ZipFile(uri.schemeSpecificPart).use { zip ->
val entry = zip.getEntry(uri.fragment) val entry = zip.getEntry(uri.fragment)
zip.getInputStream(entry).use { zip.getInputStream(entry).use {
getBitmapSize(it) getBitmapSize(it)
}
} }
} }

Loading…
Cancel
Save