Skip to content

Commit

Permalink
fix: add s3 escaping and use correct operator when filtering names
Browse files Browse the repository at this point in the history
  • Loading branch information
lost-illusi0n committed Jan 21, 2024
1 parent 0e823e7 commit cd08d6c
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 14 deletions.
2 changes: 2 additions & 0 deletions mailserver/runner/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("aws.sdk.kotlin:s3:0.32.0-beta")

implementation("net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0")

implementation(project(":smtp-agent"))
implementation(project(":smtp"))
implementation(project(":imap-agent"))
Expand Down
43 changes: 29 additions & 14 deletions mailserver/runner/src/main/kotlin/storage/filesystems/s3.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import dev.sitar.kmail.runner.KmailConfig
import dev.sitar.kmail.runner.storage.Attributable
import dev.sitar.kmail.runner.storage.Attributes
import mu.KotlinLogging
import net.thauvin.erik.urlencoder.UrlEncoderUtil
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.Charset

private interface S3Context {
val config: KmailConfig.Mailbox.Filesystem.S3
Expand Down Expand Up @@ -93,6 +97,17 @@ class S3Attributes(val folder: S3Folder) : Attributes, S3Context by folder {
}
}

private const val ALLOWED_S3_CHARACTERS = "/"

private fun String.escape(): String {
return UrlEncoderUtil.encode(this, ALLOWED_S3_CHARACTERS)
}

private fun String.unescape(): String {
return UrlEncoderUtil.decode(this)

}

class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable, FsFolder, S3Context by fileSystem {
override val name: String = folderKey.folders.last()

Expand All @@ -105,7 +120,7 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,
override suspend fun createFolder(name: String): S3Folder {
client.putObject {
bucket = config.bucket.name
this.key = "$folderKey$name/"
this.key = "$folderKey$name/".escape()
}

return S3Folder(fileSystem, folderKey.append(name))
Expand All @@ -114,7 +129,7 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,
override suspend fun getFile(name: String): FsFile {
val obj = client.headObject {
this.bucket = config.bucket.name
this.key = "$folderKey$name"
this.key = "$folderKey$name".escape()
}

return FsFile(name, obj.contentLength)
Expand All @@ -125,10 +140,10 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,
return client.listObjectsV2 {
bucket = config.bucket.name
this.delimiter = "/"
this.prefix = folderKey.toString()
this.prefix = folderKey.toString().escape()
}.contents.orEmpty()
.filter { it.isFolder() }
.map { S3Folder(fileSystem, folderKey.append(it.key!!.split('/').last())) }
.map { S3Folder(fileSystem, folderKey.append(it.key!!.split('/').last().unescape())) }
}

private fun Object.isFolder(): Boolean {
Expand All @@ -139,18 +154,18 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,
return client.listObjectsV2 {
bucket = config.bucket.name
this.delimiter = "/"
this.prefix = folderKey.toString()
this.prefix = folderKey.toString().escape()
}.contents.orEmpty().filterNot { it.isNotAcceptableFile() }.map { FsFile(it.key!!.split('/').last(), it.size) }
}

private fun Object.isNotAcceptableFile(): Boolean {
return key!!.split('/').last().startsWith("kmail_") && key == folderKey.toString()
return key!!.split('/').last().startsWith("kmail_") || key == folderKey.toString()
}

override suspend fun readFile(name: String): ByteArray? {
val request = GetObjectRequest {
this.bucket = config.bucket.name
this.key = "$folderKey$name"
this.key = "$folderKey$name".escape()
}

return try {
Expand All @@ -163,7 +178,7 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,
override suspend fun writeFile(name: String, contents: ByteArray): FsFile {
client.putObject {
bucket = config.bucket.name
key = "$folderKey$name"
key = "$folderKey$name".escape()
body = ByteStream.fromBytes(contents)
}

Expand All @@ -175,26 +190,26 @@ class S3Folder(val fileSystem: S3FileSystem, val folderKey: Key): Attributable,

client.copyObject {
bucket = config.bucket.name
key = "${folder.folderKey}$file"
copySource = "/${config.bucket.name}/$folderKey$file"
key = "${folder.folderKey}$file".escape()
copySource = "/${config.bucket.name}/$folderKey$file".escape()
}

client.deleteObject {
bucket = config.bucket.name
this.key = "$folderKey$file"
this.key = "$folderKey$file".escape()
}
}

override suspend fun rename(from: String, to: String) {
client.copyObject {
bucket = config.bucket.name
key = "$folderKey$to"
copySource = "/${config.bucket.name}/$folderKey$from"
key = "$folderKey$to".escape()
copySource = "/${config.bucket.name}/$folderKey$from".escape()
}

client.deleteObject {
bucket = config.bucket.name
this.key = "$folderKey$from"
this.key = "$folderKey$from".escape()
}
}
}
Expand Down

0 comments on commit cd08d6c

Please sign in to comment.