kunfei 3 лет назад
Родитель
Сommit
8df4cc10e4

+ 1 - 0
app/src/main/java/io/legado/app/help/AppWebDav.kt

@@ -21,6 +21,7 @@ import io.legado.app.lib.webdav.WebDavFile
 import io.legado.app.model.remote.RemoteBookWebDav
 import io.legado.app.ui.widget.dialog.WaitDialog
 import io.legado.app.utils.*
+import io.legado.app.utils.compress.ZipUtils
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.ensureActive

+ 1 - 0
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -13,6 +13,7 @@ import io.legado.app.help.DefaultData
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.ui.book.read.page.provider.ChapterProvider
 import io.legado.app.utils.*
+import io.legado.app.utils.compress.ZipUtils
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import splitties.init.appCtx

+ 1 - 0
app/src/main/java/io/legado/app/help/storage/Backup.kt

@@ -14,6 +14,7 @@ import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ThemeConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.utils.*
+import io.legado.app.utils.compress.ZipUtils
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.withContext

+ 1 - 0
app/src/main/java/io/legado/app/help/storage/Restore.kt

@@ -20,6 +20,7 @@ import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ThemeConfig
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.utils.*
+import io.legado.app.utils.compress.ZipUtils
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.delay

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt

@@ -34,6 +34,7 @@ import io.legado.app.ui.book.read.ReadBookActivity
 import io.legado.app.ui.document.HandleFileContract
 import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
 import io.legado.app.utils.*
+import io.legado.app.utils.compress.ZipUtils
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import splitties.init.appCtx
 

+ 4 - 1
app/src/main/java/io/legado/app/utils/ArchiveUtils.kt

@@ -2,6 +2,9 @@ package io.legado.app.utils
 
 import android.net.Uri
 import androidx.documentfile.provider.DocumentFile
+import io.legado.app.utils.compress.RarUtils
+import io.legado.app.utils.compress.SevenZipUtils
+import io.legado.app.utils.compress.ZipUtils
 import splitties.init.appCtx
 import java.io.File
 
@@ -52,7 +55,7 @@ object ArchiveUtils {
         val workPath = workPathFileDoc.toString()
         archiveFileDoc.uri.inputStream(appCtx).getOrThrow().use {
             when {
-                name.endsWith(".zip", ignoreCase = true) -> ZipUtils.unZipToPath(it, workPath) 
+                name.endsWith(".zip", ignoreCase = true) -> ZipUtils.unZipToPath(it, workPath)
                 name.endsWith(".rar", ignoreCase = true) -> RarUtils.unRarToPath(it, workPath)
                 name.endsWith(".7z", ignoreCase = true) -> SevenZipUtils.un7zToPath(it, workPath)
                 else -> throw IllegalArgumentException("Unexpected archive format")

+ 0 - 75
app/src/main/java/io/legado/app/utils/SevenZipUtils.kt

@@ -1,75 +0,0 @@
-package io.legado.app.utils
-
-import android.annotation.SuppressLint
-import android.os.ParcelFileDescriptor
-import androidx.annotation.Keep
-import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
-import org.apache.commons.compress.archivers.sevenz.SevenZFile
-import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
-import java.io.File
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.nio.channels.FileChannel
-
-@Keep
-@Suppress("unused","MemberVisibilityCanBePrivate")
-object SevenZipUtils {
-
-    fun un7zToPath(inputStream: InputStream, path:String){
-        un7zToPath(inputStream,File(path))
-    }
-    fun un7zToPath(byteArray: ByteArray, path:String){
-        un7zToPath(byteArray,File(path))
-    }
-    fun un7zToPath(pfd: ParcelFileDescriptor, path:String){
-        un7zToPath(pfd,File(path))
-    }
-    fun un7zToPath(fileChannel: FileChannel, path:String){
-        un7zToPath(fileChannel,File(path))
-    }
-
-    fun un7zToPath(inputStream: InputStream, destDir: File?){
-        un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())),destDir)
-    }
-    fun un7zToPath(byteArray: ByteArray, destDir: File?){
-        un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)),destDir)
-    }
-    fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?){
-        un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)),destDir)
-    }
-    @SuppressLint("NewApi")
-    fun un7zToPath(fileChannel: FileChannel, destDir: File?){
-        un7zToPath(SevenZFile(fileChannel),destDir)
-    }
-    fun un7zToPath(file: File, destDir: File?){
-        un7zToPath(SevenZFile(file),destDir)
-    }
-    fun un7zToPath(filePath: String, destDir: File?){
-        un7zToPath(SevenZFile(File(filePath)),destDir)
-    }
-
-
-    fun un7zToPath(sevenZFile:SevenZFile, destDir: File?){
-        var entry: SevenZArchiveEntry?
-        while (sevenZFile.nextEntry.also { entry=it }!=null) {
-            val entryFile = File(destDir, entry!!.name)
-            if (entry!!.isDirectory) {
-                if (!entryFile.exists()) {
-                    entryFile.mkdirs()
-                }
-                continue
-            }
-            if (entryFile.parentFile?.exists() != true) {
-                entryFile.parentFile?.mkdirs()
-            }
-            if (!entryFile.exists()) {
-                entryFile.createNewFile()
-                entryFile.setReadable(true)
-                entryFile.setExecutable(true)
-            }
-            FileOutputStream(entryFile).use {
-               sevenZFile.getInputStream(entry).copyTo(it)
-            }
-        }
-    }
-}

+ 25 - 7
app/src/main/java/io/legado/app/utils/RarUtils.kt → app/src/main/java/io/legado/app/utils/compress/RarUtils.kt

@@ -1,4 +1,4 @@
-package io.legado.app.utils
+package io.legado.app.utils.compress
 
 import androidx.annotation.Keep
 import com.github.junrar.Archive
@@ -9,20 +9,30 @@ import java.io.FileOutputStream
 import java.io.InputStream
 
 @Keep
-@Suppress("unused","MemberVisibilityCanBePrivate")
+@Suppress("unused", "MemberVisibilityCanBePrivate")
 object RarUtils {
-    fun unRarToPath(inputStream: InputStream,path:String){
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun unRarToPath(inputStream: InputStream, path: String) {
         unRarToPath(inputStream, File(path))
     }
-    fun unRarToPath(byteArray: ByteArray,path:String){
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun unRarToPath(byteArray: ByteArray, path: String) {
         unRarToPath(byteArray, File(path))
     }
-    fun unRarToPath(zipPath:String,path:String){
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun unRarToPath(zipPath: String, path: String) {
         unRarToPath(zipPath, File(path))
     }
-    fun unRarToPath(file: File,path:String){
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun unRarToPath(file: File, path: String) {
         unRarToPath(file, File(path))
     }
+
+    @Throws(NullPointerException::class, SecurityException::class)
     fun unRarToPath(inputStream: InputStream, destDir: File?) {
         Archive(inputStream).use {
             unRarToPath(it, destDir)
@@ -30,28 +40,36 @@ object RarUtils {
         inputStream.close()
     }
 
+    @Throws(NullPointerException::class, SecurityException::class)
     fun unRarToPath(byteArray: ByteArray, destDir: File?) {
         Archive(ByteArrayInputStream(byteArray)).use {
             unRarToPath(it, destDir)
         }
     }
 
-    fun unRarToPath(filePath:String, destDir: File?) {
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun unRarToPath(filePath: String, destDir: File?) {
         Archive(File(filePath)).use {
             unRarToPath(it, destDir)
         }
     }
 
+    @Throws(NullPointerException::class, SecurityException::class)
     fun unRarToPath(file: File, destDir: File?) {
         Archive(file).use {
             unRarToPath(it, destDir)
         }
     }
 
+    @Throws(NullPointerException::class, SecurityException::class)
     fun unRarToPath(archive: Archive, destDir: File?) {
+        destDir ?: throw NullPointerException("解决路径不能为空")
         var entry: FileHeader?
         while (archive.nextFileHeader().also { entry = it } != null) {
             val entryFile = File(destDir, entry!!.fileName)
+            if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
+                throw SecurityException("压缩文件只能解压到指定路径")
+            }
             if (entry!!.isDirectory) {
                 if (!entryFile.exists()) {
                     entryFile.mkdirs()

+ 98 - 0
app/src/main/java/io/legado/app/utils/compress/SevenZipUtils.kt

@@ -0,0 +1,98 @@
+package io.legado.app.utils.compress
+
+import android.annotation.SuppressLint
+import android.os.ParcelFileDescriptor
+import androidx.annotation.Keep
+import io.legado.app.utils.ParcelFileDescriptorChannel
+import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
+import org.apache.commons.compress.archivers.sevenz.SevenZFile
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.nio.channels.FileChannel
+
+@Keep
+@Suppress("unused", "MemberVisibilityCanBePrivate")
+object SevenZipUtils {
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(inputStream: InputStream, path: String) {
+        un7zToPath(inputStream, File(path))
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(byteArray: ByteArray, path: String) {
+        un7zToPath(byteArray, File(path))
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(pfd: ParcelFileDescriptor, path: String) {
+        un7zToPath(pfd, File(path))
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(fileChannel: FileChannel, path: String) {
+        un7zToPath(fileChannel, File(path))
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(inputStream: InputStream, destDir: File?) {
+        un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())), destDir)
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(byteArray: ByteArray, destDir: File?) {
+        un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)), destDir)
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?) {
+        un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)), destDir)
+    }
+
+    @SuppressLint("NewApi")
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(fileChannel: FileChannel, destDir: File?) {
+        un7zToPath(SevenZFile(fileChannel), destDir)
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(file: File, destDir: File?) {
+        un7zToPath(SevenZFile(file), destDir)
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(filePath: String, destDir: File?) {
+        un7zToPath(SevenZFile(File(filePath)), destDir)
+    }
+
+    @Throws(NullPointerException::class, SecurityException::class)
+    fun un7zToPath(sevenZFile: SevenZFile, destDir: File?) {
+        destDir ?: throw NullPointerException("解决路径不能为空")
+        var entry: SevenZArchiveEntry?
+        while (sevenZFile.nextEntry.also { entry = it } != null) {
+            val entryFile = File(destDir, entry!!.name)
+            if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
+                throw SecurityException("压缩文件只能解压到指定路径")
+            }
+            if (entry!!.isDirectory) {
+                if (!entryFile.exists()) {
+                    entryFile.mkdirs()
+                }
+                continue
+            }
+            if (entryFile.parentFile?.exists() != true) {
+                entryFile.parentFile?.mkdirs()
+            }
+            if (!entryFile.exists()) {
+                entryFile.createNewFile()
+                entryFile.setReadable(true)
+                entryFile.setExecutable(true)
+            }
+            FileOutputStream(entryFile).use {
+                sevenZFile.getInputStream(entry).copyTo(it)
+            }
+        }
+    }
+}

+ 18 - 8
app/src/main/java/io/legado/app/utils/ZipUtils.kt → app/src/main/java/io/legado/app/utils/compress/ZipUtils.kt

@@ -1,5 +1,7 @@
-package io.legado.app.utils
+package io.legado.app.utils.compress
 
+import io.legado.app.utils.DebugLog
+import io.legado.app.utils.printOnDebug
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import java.io.*
@@ -174,15 +176,21 @@ object ZipUtils {
         return true
     }
 
+    @Throws(SecurityException::class)
     fun unZipToPath(inputStream: InputStream, path: String) {
-        val zipInputStream = ZipInputStream(inputStream)
-        unZipToPath(zipInputStream, path)
+        ZipInputStream(inputStream).use {
+            unZipToPath(it, path)
+        }
     }
 
+    @Throws(SecurityException::class)
     fun unZipToPath(zipInputStream: ZipInputStream, path: String) {
         var entry: ZipEntry?
         while (zipInputStream.nextEntry.also { entry = it } != null) {
             val entryFile = File(path, entry!!.name)
+            if (!entryFile.canonicalPath.startsWith(path)) {
+                throw SecurityException("压缩文件只能解压到指定路径")
+            }
             if (entry!!.isDirectory) {
                 if (!entryFile.exists()) {
                     entryFile.mkdirs()
@@ -201,7 +209,6 @@ object ZipUtils {
                 zipInputStream.copyTo(it)
             }
         }
-        zipInputStream.close()
     }
 
     /**
@@ -302,7 +309,7 @@ object ZipUtils {
         return files
     }
 
-    @Throws(IOException::class)
+    @Throws(IOException::class, SecurityException::class)
     private fun unzipChildFile(
         destDir: File,
         files: MutableList<File>,
@@ -311,14 +318,17 @@ object ZipUtils {
         name: String
     ): Boolean {
         val file = File(destDir, name)
+        if (!file.canonicalPath.startsWith(destDir.canonicalPath)) {
+            throw SecurityException("压缩文件只能解压到指定路径")
+        }
         files.add(file)
         if (entry.isDirectory) {
             return createOrExistsDir(file)
         } else {
             if (!createOrExistsFile(file)) return false
-            BufferedInputStream(zip.getInputStream(entry)).use { `in` ->
-                BufferedOutputStream(FileOutputStream(file)).use { out ->
-                    out.write(`in`.readBytes())
+            zip.getInputStream(entry).use { input ->
+                FileOutputStream(file).use { out ->
+                    input.copyTo(out)
                 }
             }
         }