Normalize Paths (#1245)
* Normalize Paths * Formatting * Different format
This commit is contained in:
@@ -11,6 +11,7 @@ import kotlin.concurrent.thread
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.deleteRecursively
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.outputStream
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
@@ -43,5 +44,11 @@ object TemporaryFileStorage {
|
||||
}
|
||||
}
|
||||
|
||||
fun retrieveFile(name: String): Path = folder.resolve(name)
|
||||
fun retrieveFile(name: String): Path {
|
||||
val file = folder.resolve(name).normalize()
|
||||
check(file.startsWith(folder)) {
|
||||
"File $name is not in ${folder.name}"
|
||||
}
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ import java.io.InputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.outputStream
|
||||
import kotlin.io.path.relativeTo
|
||||
|
||||
object Extension {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
@@ -77,17 +81,20 @@ object Extension {
|
||||
apkName: String,
|
||||
): Int =
|
||||
installAPK(true) {
|
||||
val savePath = "${applicationDirs.extensionsRoot}/$apkName"
|
||||
val rootPath = Path(applicationDirs.extensionsRoot)
|
||||
val downloadedFile = rootPath.resolve(apkName).normalize()
|
||||
check(downloadedFile.startsWith(rootPath) && downloadedFile.parent == rootPath) {
|
||||
"File '$apkName' is not a valid extension file"
|
||||
}
|
||||
logger.debug { "Saving apk at $apkName" }
|
||||
// download apk file
|
||||
val downloadedFile = File(savePath)
|
||||
downloadedFile.sink().buffer().use { sink ->
|
||||
downloadedFile.outputStream().sink().buffer().use { sink ->
|
||||
inputStream.source().use { source ->
|
||||
sink.writeAll(source)
|
||||
sink.flush()
|
||||
}
|
||||
}
|
||||
savePath
|
||||
downloadedFile.absolutePathString()
|
||||
}
|
||||
|
||||
suspend fun installAPK(
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.relativeTo
|
||||
|
||||
object PackageTools {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
@@ -68,10 +69,11 @@ object PackageTools {
|
||||
.skipExceptions(false)
|
||||
.to(jarFilePath)
|
||||
if (handler.hasException()) {
|
||||
val errorFile: Path = File(applicationDirs.extensionsRoot).toPath().resolve("$fileNameWithoutType-error.txt")
|
||||
val rootPath = Path(applicationDirs.extensionsRoot)
|
||||
val errorFile: Path = rootPath.resolve("$fileNameWithoutType-error.txt")
|
||||
logger.error {
|
||||
"""
|
||||
Detail Error Information in File $errorFile
|
||||
Detail Error Information in File ${errorFile.relativeTo(rootPath)}
|
||||
Please report this file to one of following link if possible (any one).
|
||||
https://sourceforge.net/p/dex2jar/tickets/
|
||||
https://bitbucket.org/pxb1988/dex2jar/issues
|
||||
|
||||
@@ -147,7 +147,7 @@ fun applicationSetup() {
|
||||
.replace(Regex("(\"basicAuth(?:Username|Password)\"\\s:\\s)(?!\"\")\".*\""), "$1\"******\"")
|
||||
}
|
||||
|
||||
logger.debug("Data Root directory is set to: ${applicationDirs.dataRoot}")
|
||||
logger.debug { "Data Root directory is set to: ${applicationDirs.dataRoot}" }
|
||||
|
||||
// Migrate Directories from old versions
|
||||
File("$ApplicationRootDir/manga-thumbnails").renameTo(applicationDirs.tempThumbnailCacheRoot)
|
||||
|
||||
@@ -115,8 +115,7 @@ class TestExtensionCompatibility {
|
||||
semaphore.withPermit {
|
||||
logger.info { "${mangaCount.getAndIncrement()} - Now fetching manga from $source" }
|
||||
try {
|
||||
manga.copyFrom(repeat { source.getMangaDetails(manga) })
|
||||
manga.initialized = true
|
||||
repeat { source.getMangaDetails(manga) }
|
||||
} catch (e: Exception) {
|
||||
logger.warn {
|
||||
"Failed to fetch manga info from $source for ${manga.title} (${source.mangaDetailsRequest(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package suwayomi.tachidesk
|
||||
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
class PathTest {
|
||||
@Test
|
||||
fun testCanonicalPath() {
|
||||
val path = Path("/test/path/")
|
||||
assert(path.resolve("child/path").startsWith(path))
|
||||
assertFalse(path.resolve("../parent/child/path").normalize().startsWith(path))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParentPath() {
|
||||
val path = Path("/test/path/")
|
||||
assertEquals(path.resolve("child.txt").parent, path)
|
||||
assertEquals(path.resolve("child.txt").normalize().parent, path)
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,105 @@
|
||||
package suwayomi.tachidesk.graphql
|
||||
|
||||
import com.expediagroup.graphql.server.types.GraphQLRequest
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.UploadedFile
|
||||
import io.javalin.plugin.json.JSON_MAPPER_KEY
|
||||
import io.javalin.plugin.json.JavalinJackson
|
||||
import io.javalin.plugin.json.JsonMapper
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import suwayomi.tachidesk.graphql.server.JavalinGraphQLRequestParser
|
||||
import java.io.ByteArrayInputStream
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class RequestParserTest {
|
||||
private val ctx = mockk<Context>(relaxed = true)
|
||||
private val requestParser = JavalinGraphQLRequestParser()
|
||||
|
||||
@Test
|
||||
fun testZero() =
|
||||
runTest {
|
||||
every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
(JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
every {
|
||||
ctx.formParam("operations")
|
||||
} returns
|
||||
"""{ "query": "mutation (${'$'}file: Upload!) {
|
||||
|singleUpload(file: ${'$'}file) { id } }", "variables": { "file": null }
|
||||
|}
|
||||
""".trimMargin()
|
||||
every { ctx.formParam("map") } returns """{ "0": ["variables.file"] }"""
|
||||
every { ctx.uploadedFile("0") } returns
|
||||
UploadedFile(
|
||||
ByteArrayInputStream(byteArrayOf()),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
)
|
||||
val test = requestParser.parseRequest(ctx)
|
||||
assertIs<GraphQLRequest>(test)
|
||||
assertNotNull(test.variables?.get("file"))
|
||||
println("File: " + test.variables?.get("file"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTest() =
|
||||
runTest {
|
||||
every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
(JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
every {
|
||||
ctx.formParam("operations")
|
||||
} returns
|
||||
"""{ "query": "mutation (${'$'}file: Upload!) {
|
||||
|singleUpload(file: ${'$'}file) { id } }", "variables": { "file": null }
|
||||
|}
|
||||
""".trimMargin()
|
||||
every { ctx.formParam("map") } returns """{ "test": ["variables.file"] }"""
|
||||
every { ctx.uploadedFile("test") } returns
|
||||
UploadedFile(
|
||||
ByteArrayInputStream(byteArrayOf()),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
)
|
||||
val test = requestParser.parseRequest(ctx)
|
||||
assertIs<GraphQLRequest>(test)
|
||||
assertNotNull(test.variables?.get("file"))
|
||||
println("File: " + test.variables?.get("file"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testList() =
|
||||
runTest {
|
||||
every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
(JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
every {
|
||||
ctx.formParam("operations")
|
||||
} returns
|
||||
"""{ "query": "mutation (${'$'}files: [Upload!]!) {
|
||||
|singleUpload(files: ${'$'}files) { id } }", "variables": { "files": [null, null] }
|
||||
|}
|
||||
""".trimMargin()
|
||||
every { ctx.formParam("map") } returns
|
||||
"""
|
||||
{ "test": ["variables.files.0"], "test2": ["variables.files.1"] }
|
||||
""".trimIndent()
|
||||
every { ctx.uploadedFile("test") } returns
|
||||
UploadedFile(
|
||||
ByteArrayInputStream(byteArrayOf()),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
)
|
||||
every { ctx.uploadedFile("test2") } returns
|
||||
UploadedFile(
|
||||
ByteArrayInputStream(byteArrayOf()),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
)
|
||||
val test = requestParser.parseRequest(ctx)
|
||||
assertIs<GraphQLRequest>(test)
|
||||
val files = test.variables?.get("files")
|
||||
assertIs<List<*>>(files)
|
||||
assert(files.all { it is UploadedFile })
|
||||
println("Files: $files")
|
||||
}
|
||||
// @Test
|
||||
// fun testZero() =
|
||||
// runTest {
|
||||
// ctx.jsonMapper()
|
||||
// every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
// (JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
// every {
|
||||
// ctx.formParam("operations")
|
||||
// } returns
|
||||
// """{ "query": "mutation (${'$'}file: Upload!) {
|
||||
// |singleUpload(file: ${'$'}file) { id } }", "variables": { "file": null }
|
||||
// |}
|
||||
// """.trimMargin()
|
||||
// every { ctx.formParam("map") } returns """{ "0": ["variables.file"] }"""
|
||||
// every { ctx.uploadedFile("0") } returns
|
||||
// UploadedFile(
|
||||
// ByteArrayInputStream(byteArrayOf()),
|
||||
// "",
|
||||
// "",
|
||||
// "",
|
||||
// 0,
|
||||
// )
|
||||
// val test = requestParser.parseRequest(ctx)
|
||||
// assertIs<GraphQLRequest>(test)
|
||||
// assertNotNull(test.variables?.get("file"))
|
||||
// println("File: " + test.variables?.get("file"))
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testTest() =
|
||||
// runTest {
|
||||
// every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
// (JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
// every {
|
||||
// ctx.formParam("operations")
|
||||
// } returns
|
||||
// """{ "query": "mutation (${'$'}file: Upload!) {
|
||||
// |singleUpload(file: ${'$'}file) { id } }", "variables": { "file": null }
|
||||
// |}
|
||||
// """.trimMargin()
|
||||
// every { ctx.formParam("map") } returns """{ "test": ["variables.file"] }"""
|
||||
// every { ctx.uploadedFile("test") } returns
|
||||
// UploadedFile(
|
||||
// ByteArrayInputStream(byteArrayOf()),
|
||||
// "",
|
||||
// "",
|
||||
// "",
|
||||
// 0,
|
||||
// )
|
||||
// val test = requestParser.parseRequest(ctx)
|
||||
// assertIs<GraphQLRequest>(test)
|
||||
// assertNotNull(test.variables?.get("file"))
|
||||
// println("File: " + test.variables?.get("file"))
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testList() =
|
||||
// runTest {
|
||||
// every { ctx.appAttribute<JsonMapper>(JSON_MAPPER_KEY) } returns
|
||||
// (JavalinJackson(JavalinJackson.defaultMapper()))
|
||||
// every {
|
||||
// ctx.formParam("operations")
|
||||
// } returns
|
||||
// """{ "query": "mutation (${'$'}files: [Upload!]!) {
|
||||
// |singleUpload(files: ${'$'}files) { id } }", "variables": { "files": [null, null] }
|
||||
// |}
|
||||
// """.trimMargin()
|
||||
// every { ctx.formParam("map") } returns
|
||||
// """
|
||||
// { "test": ["variables.files.0"], "test2": ["variables.files.1"] }
|
||||
// """.trimIndent()
|
||||
// every { ctx.uploadedFile("test") } returns
|
||||
// UploadedFile(
|
||||
// ByteArrayInputStream(byteArrayOf()),
|
||||
// "",
|
||||
// "",
|
||||
// "",
|
||||
// 0,
|
||||
// )
|
||||
// every { ctx.uploadedFile("test2") } returns
|
||||
// UploadedFile(
|
||||
// ByteArrayInputStream(byteArrayOf()),
|
||||
// "",
|
||||
// "",
|
||||
// "",
|
||||
// 0,
|
||||
// )
|
||||
// val test = requestParser.parseRequest(ctx)
|
||||
// assertIs<GraphQLRequest>(test)
|
||||
// val files = test.variables?.get("files")
|
||||
// assertIs<List<*>>(files)
|
||||
// assert(files.all { it is UploadedFile })
|
||||
// println("Files: $files")
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import io.javalin.plugin.json.JavalinJackson
|
||||
import io.javalin.json.JavalinJackson
|
||||
import io.javalin.json.toJsonString
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
@@ -346,7 +347,7 @@ class FilterListTest : ApplicationTest() {
|
||||
private var sourceCount = 0L
|
||||
|
||||
private fun registerSource(sourceClass: KClass<*>): EmptyFilterListSource =
|
||||
synchronized(sourceCount) {
|
||||
synchronized(sourceClass) {
|
||||
val source = sourceClass.primaryConstructor!!.call(sourceCount) as EmptyFilterListSource
|
||||
registerCatalogueSource(sourceCount to source)
|
||||
sourceCount++
|
||||
|
||||
@@ -58,7 +58,7 @@ open class ApplicationTest {
|
||||
// Application dirs
|
||||
val applicationDirs = ApplicationDirs()
|
||||
|
||||
logger.debug("Data Root directory is set to: ${applicationDirs.dataRoot}")
|
||||
logger.debug { "Data Root directory is set to: ${applicationDirs.dataRoot}" }
|
||||
|
||||
// make dirs we need
|
||||
listOf(
|
||||
@@ -110,7 +110,7 @@ open class ApplicationTest {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("Exception while creating initial server.conf", e)
|
||||
logger.error(e) { "Exception while creating initial server.conf" }
|
||||
}
|
||||
|
||||
// copy local source icon
|
||||
@@ -124,7 +124,7 @@ open class ApplicationTest {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("Exception while copying Local source's icon", e)
|
||||
logger.error(e) { "Exception while copying Local source's icon" }
|
||||
}
|
||||
|
||||
// create system tray
|
||||
@@ -146,7 +146,7 @@ open class ApplicationTest {
|
||||
if (serverConfig.socksProxyEnabled.value) {
|
||||
System.getProperties()["socksProxyHost"] = serverConfig.socksProxyHost.value
|
||||
System.getProperties()["socksProxyPort"] = serverConfig.socksProxyPort.value
|
||||
logger.info("Socks Proxy is enabled to ${serverConfig.socksProxyHost.value}:${serverConfig.socksProxyPort.value}")
|
||||
logger.info { "Socks Proxy is enabled to ${serverConfig.socksProxyHost.value}:${serverConfig.socksProxyPort.value}" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ package suwayomi.tachidesk.test
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import io.github.oshai.kotlinlogging.DelegatingKLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import org.jetbrains.exposed.dao.id.IdTable
|
||||
import org.jetbrains.exposed.sql.batchInsert
|
||||
@@ -20,7 +21,7 @@ import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||
|
||||
fun setLoggingEnabled(enabled: Boolean = true) {
|
||||
val logger = (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger)
|
||||
val logger = ((KotlinLogging.logger(Logger.ROOT_LOGGER_NAME) as DelegatingKLogger<*>).underlyingLogger as ch.qos.logback.classic.Logger)
|
||||
logger.level =
|
||||
if (enabled) {
|
||||
Level.DEBUG
|
||||
|
||||
Reference in New Issue
Block a user