diff options
author | Marvin Borner | 2019-04-19 23:38:36 +0200 |
---|---|---|
committer | Marvin Borner | 2019-04-19 23:38:36 +0200 |
commit | 9fe9795c996a6e37e894f7e42ba8761320c47798 (patch) | |
tree | 2b3bc8b325ff0e3ea31a555e7bdbfba933194c5f | |
parent | 5523bd7a5e30b09baa6bb3ccd23252d0ebef6000 (diff) |
Increased stability and security
Co-authored-by: LarsVomMars <lars@kroenner.eu>
-rw-r--r-- | src/main/kotlin/App.kt | 13 | ||||
-rw-r--r-- | src/main/kotlin/DatabaseController.kt | 1 | ||||
-rw-r--r-- | src/main/kotlin/FileController.kt | 35 | ||||
-rw-r--r-- | src/main/resources/js/files.js | 18 | ||||
-rw-r--r-- | src/main/resources/views/files.rocker.html | 4 |
5 files changed, 50 insertions, 21 deletions
diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt index d3f0cd9..50c7c18 100644 --- a/src/main/kotlin/App.kt +++ b/src/main/kotlin/App.kt @@ -11,6 +11,7 @@ import io.javalin.security.* import io.javalin.security.SecurityUtil.roles import io.javalin.staticfiles.* import java.io.* +import java.net.* import java.util.logging.* const val fileHome = "files" @@ -36,6 +37,18 @@ fun main() { app.routes { /** + * Normalizes and cleans the requested url + */ + before("/*") { ctx -> + run { + if (URI(ctx.url()).normalize().toString() != ctx.url()) { + log.warning("Normalized url from ${ctx.url()} to ${URI(ctx.url()).normalize()}") + ctx.redirect(URI(ctx.url()).normalize().toString()) + } + } + } + + /** * Main page * TODO: Create landing page */ diff --git a/src/main/kotlin/DatabaseController.kt b/src/main/kotlin/DatabaseController.kt index d65e800..888171c 100644 --- a/src/main/kotlin/DatabaseController.kt +++ b/src/main/kotlin/DatabaseController.kt @@ -228,7 +228,6 @@ class DatabaseController(dbFileLocation: String = "main.db") { } } - /** * Removes the file from the database */ diff --git a/src/main/kotlin/FileController.kt b/src/main/kotlin/FileController.kt index 06c1402..15bf11f 100644 --- a/src/main/kotlin/FileController.kt +++ b/src/main/kotlin/FileController.kt @@ -19,13 +19,14 @@ class FileController { fun crawl(ctx: Context) { try { val usersFileHome = "$fileHome/${userHandler.getVerifiedUserId(ctx)}" + val firstParam = ctx.splat(0) ?: "" File(usersFileHome).mkdirs() when { - File("$usersFileHome/${ctx.splats()[0]}").isDirectory -> { + File("$usersFileHome/$firstParam").isDirectory -> { val files = ArrayList<Array<String>>() - Files.list(Paths.get("$usersFileHome/${ctx.splats()[0]}/")).forEach { + Files.list(Paths.get("$usersFileHome/$firstParam/")).forEach { val filename = it.toString() - .drop(usersFileHome.length + (if (ctx.splats()[0].isNotEmpty()) ctx.splats()[0].length + 2 else 1)) + .drop(usersFileHome.length + (if (firstParam.isNotEmpty()) firstParam.length + 2 else 1)) val filePath = "$usersFileHome${it.toString().drop(usersFileHome.length)}" val file = File(filePath) val fileSize = if (file.isDirectory) getDirectorySize(file) else file.length() @@ -45,28 +46,31 @@ class FileController { //files.sortWith(String.CASE_INSENSITIVE_ORDER) // TODO: Reimplement file array sorting in backend ctx.render( "files.rocker.html", TemplateUtil.model( - "files", files, - "path", ctx.splats()[0] + "files", + files, + "path", + (if (firstParam.firstOrNull() != '/' && firstParam.isNotEmpty()) "/$firstParam" else firstParam) ) ) } - isHumanReadable(File("$usersFileHome/${ctx.splats()[0]}")) -> + isHumanReadable(File("$usersFileHome/$firstParam")) -> ctx.render( "fileview.rocker.html", TemplateUtil.model( "content", Files.readAllLines( - Paths.get("$usersFileHome/${ctx.splats()[0]}"), + Paths.get("$usersFileHome/$firstParam"), Charsets.UTF_8 ).joinToString(separator = "\n"), - "filename", File("$usersFileHome/${ctx.splats()[0]}").name, - "extension", File("$usersFileHome/${ctx.splats()[0]}").extension + "filename", File("$usersFileHome/$firstParam").name, + "extension", File("$usersFileHome/$firstParam").extension ) ) else -> { - ctx.contentType(Files.probeContentType(Paths.get("$usersFileHome/${ctx.splats()[0]}"))) - ctx.result(FileInputStream(File("$usersFileHome/${ctx.splats()[0]}"))) + ctx.contentType(Files.probeContentType(Paths.get("$usersFileHome/$firstParam"))) + ctx.result(FileInputStream(File("$usersFileHome/$firstParam"))) } } - } catch (_: Exception) { + } catch (err: Exception) { + log.warning(err.toString()) throw NotFoundResponse("Error: File or directory does not exist.") } } @@ -115,7 +119,7 @@ class FileController { */ fun upload(ctx: Context) { ctx.uploadedFiles("file").forEach { (_, content, name, _) -> - val path = "${ctx.splats()[0]}/$name" + val path = "${ctx.splat(0)}/$name" val userId = userHandler.getVerifiedUserId(ctx) var addPath = "" path.split("/").forEach { @@ -145,7 +149,7 @@ class FileController { fun delete(ctx: Context) { // TODO: Fix deleting of directories val userId = userHandler.getVerifiedUserId(ctx) if (userId > 0) { - val path = ctx.splats()[0] + val path = ctx.splat(0) ?: "" File("$fileHome/$userId/$path").delete() // File.deleteRecursively() kind of "crashes" server but deletes folder :'( databaseController.deleteFile(path, userId) // kind of works for deleting directories } @@ -157,9 +161,10 @@ class FileController { fun share(ctx: Context) { val userId = userHandler.getVerifiedUserId(ctx) val shareType = ctx.queryParam("type").toString() + val firstParam = ctx.splat(0) ?: "" if (userId > 0) { val path = - "${(if (ctx.splats()[0].startsWith("/")) ctx.splats()[0] else "/${ctx.splats()[0]}")}${if (shareType == "dir") "/" else ""}" + "${(if (firstParam.startsWith("/")) firstParam else "/$firstParam")}${if (shareType == "dir") "/" else ""}" val accessId = databaseController.getAccessId(path, userId) ctx.result("${ctx.host()}/shared?id=$accessId") } diff --git a/src/main/resources/js/files.js b/src/main/resources/js/files.js index 69cdf32..3346a31 100644 --- a/src/main/resources/js/files.js +++ b/src/main/resources/js/files.js @@ -51,12 +51,16 @@ drop.addEventListener('drop', e => { } else { subItem.file(subFile => { // TODO: Add support for nested directory upload with more than 1 layer - via webkitRelativePath on firefox? - console.log(uploadedFiles); - console.log(`${path}/${file.name}/${subFile.name}`); if (!uploadedFiles.includes(`${path}/${file.name}/${subFile.name}`)) { const formData = new FormData(); const request = new XMLHttpRequest(); + request.upload.onprogress = e => { + if (e.lengthComputable) { + console.log(`${subFile.name}: ${e.loaded / e.total * 100}%`) + } + }; + uploadedFiles.push(`${path}/${file.name}/${subFile.name}`); formData.append("file", subFile); if (subFile.webkitRelativePath === "") request.open("POST", `/upload/${path}/${file.name}`); @@ -74,6 +78,12 @@ drop.addEventListener('drop', e => { const formData = new FormData(); const request = new XMLHttpRequest(); + request.upload.onprogress = e => { + if (e.lengthComputable) { + console.log(`${file.name}: ${e.loaded / e.total * 100}%`) + } + }; + formData.append("file", file); request.open("POST", `/upload/${path}`); request.send(formData); @@ -114,7 +124,7 @@ function setListeners() { element.addEventListener("click", () => { if (images.indexOf(extension) === -1 && videos.indexOf(extension) === -1 && audio.indexOf(extension) === -1) - window.location = filename; // download binary files + window.location = `/files/${path}/${filename}`; // download binary files }); }); @@ -151,7 +161,7 @@ function setListeners() { // normal files document.querySelectorAll("[data-href]").forEach(element => { element.addEventListener("click", () => { - window.location = element.getAttribute("data-href"); + window.location = `/files${path}/${element.getAttribute("data-href")}`; }) }); diff --git a/src/main/resources/views/files.rocker.html b/src/main/resources/views/files.rocker.html index a31f6bb..02a7660 100644 --- a/src/main/resources/views/files.rocker.html +++ b/src/main/resources/views/files.rocker.html @@ -19,7 +19,9 @@ @for (int i = 0; i < path.split("/").length - 1; i++) { @path.split("/")[i] <i class='icon ion-ios-arrow-forward'></i> } - @path.split("/")[path.split("/").length - 1] + @if (path.split("/").length > 0) { + @(path.split("/")[path.split("/").length - 1]) + } </h2> <table id="table"> |