Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.-KotlinExtensions

# Ignore R8 warnings for unused optional XZ and Zstandard support in Apache Commons Compress.
-dontwarn org.apache.commons.compress.compressors.xz.**
-dontwarn org.apache.commons.compress.compressors.zstandard.**

# OkHTTP
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ threegpp-telecom-charsets = "1.0.1"
commons-io = "2.20.0"
commons-text = "1.14.0"
commonsCodec = "1.19.0"
commons-compress = "1.28.0"
bouncy-castle = "1.82"
okhttp = "5.2.1"
retrofit = "3.0.0"
Expand Down Expand Up @@ -90,6 +91,7 @@ threegpp-telecom-charsets = { group = "com.github.brake.threegpp", name = "telec
commons-io = { group = "commons-io", name = "commons-io", version.ref = "commons-io" }
commons-text = { group = "org.apache.commons", name = "commons-text", version.ref = "commons-text" }
commons-codec = { module = "commons-codec:commons-codec", version.ref = "commonsCodec" }
commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "commons-compress" }
bouncy-castle = { group = "org.bouncycastle", name = "bcpkix-jdk18on", version.ref = "bouncy-castle" }
cdoc4j = { group = "org.open-eid.cdoc4j", name = "cdoc4j", version.ref = "cdoc4j" }
unboundid-ldapsdk = { group = "com.unboundid", name = "unboundid-ldapsdk", version.ref = "unboundid-ldapsdk" }
Expand Down
1 change: 1 addition & 0 deletions utils-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies {
api(libs.commons.io)
api(libs.commons.text)
api(libs.commons.codec)
api(libs.commons.compress)
implementation(libs.guava)
implementation(libs.gson)
implementation(libs.threegpp.telecom.charsets)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ class FileTest {
mimeType: String,
): File {
val tempFile = File.createTempFile(fileName, ".$fileExtension", context.cacheDir)

tempFile.deleteOnExit()
val file = mock(File::class.java)
`when`(file.name).thenReturn("$fileName.$fileExtension")
`when`(file.path).thenReturn(tempFile.path)

`when`(mimeTypeMap.getMimeTypeFromExtension(file.extension.lowercase())).thenReturn(mimeType)
`when`(
mimeTypeMap.getMimeTypeFromExtension(fileExtension.lowercase())
).thenReturn(mimeType)

return file
return tempFile
}

private fun createSignedPDF(): File? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil.parseXMLFile
import ee.ria.DigiDoc.utilsLib.file.FileUtil.readFileAsString
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.compress.archivers.zip.ZipFile
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.util.Locale
import java.util.zip.ZipException
import java.util.zip.ZipFile

private const val FILE_EXTENSIONS_LOG_TAG = "FileExtensions"

Expand All @@ -56,8 +56,7 @@ fun File.isXades(context: Context): Boolean {
val tempContainerFiles = File(context.filesDir, "tempContainerFiles")

try {
// Check if file is a zip file. If not, throw ZipException
ZipFile(this)
checkIsZipFile(this)

val signaturesXmlFile = getFileInContainerZip(this, "signatures.xml", tempContainerFiles)
val fileExists = signaturesXmlFile?.exists()
Expand All @@ -74,8 +73,7 @@ fun File.isCades(context: Context): Boolean {
val tempContainerFiles = File(context.filesDir, "tempContainerFiles")

try {
// Check if file is a zip file. If not, throw ZipException
ZipFile(this)
checkIsZipFile(this)

val signaturesXmlFile = getFileInContainerZip(this, "p7s", tempContainerFiles)
val fileExists = signaturesXmlFile?.exists()
Expand All @@ -93,8 +91,7 @@ fun File.mimeType(context: Context): String {

val tempContainerFiles = File(context.filesDir, "tempContainerFiles")
try {
// Check if file is a zip file. If not, throw ZipException
ZipFile(this)
checkIsZipFile(this)

val mimetypeFile = getFileInContainerZip(this, "mimetype", tempContainerFiles)
mimetypeFile?.let {
Expand Down Expand Up @@ -156,3 +153,12 @@ fun File.md5Hash(): String {
fun File.saveAs(destinationPath: String) {
this.copyTo(File(destinationPath), overwrite = true)
}

// Check if file is a zip file. If not, throw ZipException
@Throws(ZipException::class)
private fun checkIsZipFile(file: File) {
ZipFile
.Builder()
.setFile(file)
.get()
}
63 changes: 49 additions & 14 deletions utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/file/FileUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.infoLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils
import org.apache.commons.lang3.StringUtils
Expand All @@ -55,8 +56,6 @@ import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.util.Objects
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.SAXParserFactory

Expand Down Expand Up @@ -120,7 +119,9 @@ object FileUtil {
replacement: String,
): String {
var trimmed = input.trim { it <= ' ' }
if (trimmed.startsWith(".")) {
if (trimmed.startsWith("..")) {
trimmed = trimmed.removePrefix("..")
} else if (trimmed.startsWith(".")) {
trimmed = DEFAULT_FILENAME + trimmed
}
val sb = StringBuilder(trimmed.length)
Expand Down Expand Up @@ -359,22 +360,56 @@ object FileUtil {
): File? {
createDirectoryIfNotExist(outputFolder.path)

ZipInputStream(containerFile.inputStream()).use { zipInputStream ->
var zipEntry: ZipEntry?
val resolvedDestDir = outputFolder.canonicalFile

while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
val entryName = zipEntry?.name
if (entryName != null && File(entryName).name.contains(fileNameToFind)) {
val outputFile = File(outputFolder, File(entryName).name)
FileOutputStream(outputFile).use { outputStream ->
zipInputStream.copyTo(outputStream)
}
return outputFile
val matches = { entryName: String ->
val file = File(entryName)
val fileName = file.name
val nameWithoutExt = file.nameWithoutExtension
val extension = file.extension

when {
fileNameToFind.contains(".") -> {
fileName.equals(fileNameToFind, ignoreCase = true)
}

else -> {
extension.equals(fileNameToFind, ignoreCase = true)
|| (extension.isEmpty() &&
nameWithoutExt.equals(fileNameToFind, ignoreCase = true))
}
}
}

return null
val zip =
ZipFile
.Builder()
.setFile(containerFile)
.get()

zip.use {
val entry =
it.entries
.asSequence()
.filterNot { archiveEntry -> archiveEntry.isDirectory }
.firstOrNull { archiveEntry -> matches(archiveEntry.name) }
?: return null

val outputFile = File(outputFolder, File(entry.name).name)
val resolvedOutFile = outputFile.canonicalFile

if (!resolvedOutFile.path.startsWith(resolvedDestDir.path + File.separator)) {
return null
}

it.getInputStream(entry).use { input ->
resolvedOutFile.outputStream().use { output ->
input.copyTo(output)
}
}

return resolvedOutFile
}
}

fun readFileAsString(file: File): String = file.readText()
Expand Down
Loading