1+ package com.groupdocs.ui.manager
2+
3+ import com.groupdocs.ui.Defaults
4+ import com.groupdocs.ui.status.InternalServerException
5+ import org.koin.core.component.KoinComponent
6+ import java.io.File
7+ import java.nio.file.Path
8+ import java.nio.file.Paths
9+ import java.util.*
10+ import java.util.regex.Pattern
11+ import kotlin.math.min
12+
13+ /* *
14+ * Manages path to temp, cache, files or result directory according to config
15+ * Does not create or check is any directory exist
16+ */
17+ class PathManagerImpl (
18+ private val applicationConfig : com.groupdocs.ui.config.ApplicationConfig
19+ ) : PathManager, KoinComponent {
20+ private val isLocalProvider: Boolean by lazy {
21+ applicationConfig.comparison.filesProviderTypeOrDefault == Defaults .Comparison .FilesProviderType .LOCAL
22+ }
23+
24+ override val tempDirectory: Path by lazy {
25+ val absoluteOrRelativeTempDirectory = Paths .get(applicationConfig.comparison.tempDirectoryOrDefault)
26+ if (absoluteOrRelativeTempDirectory.isAbsolute) {
27+ absoluteOrRelativeTempDirectory
28+ } else if (isLocalProvider) {
29+ filesDirectory.resolve(absoluteOrRelativeTempDirectory)
30+ } else {
31+ absoluteOrRelativeTempDirectory.toAbsolutePath()
32+ }.normalize()
33+ }
34+
35+ override val resultDirectory: Path by lazy {
36+ if (! isLocalProvider) {
37+ throw InternalServerException (" Access to result directory is impossible, because local provider is not enabled" )
38+ }
39+ val absoluteOrRelativeResultDirectory = applicationConfig.local.resultDirectoryOrDefault
40+ if (absoluteOrRelativeResultDirectory.isAbsolute) {
41+ absoluteOrRelativeResultDirectory
42+ } else {
43+ filesDirectory.resolve(absoluteOrRelativeResultDirectory).toAbsolutePath()
44+ }.normalize()
45+ }
46+
47+ override val filesDirectory: Path by lazy {
48+ if (! isLocalProvider) {
49+ throw InternalServerException (" Access to files directory is impossible, because local provider is not enabled" )
50+ }
51+ val absoluteOrRelativeFilesDirectory = applicationConfig.local.filesDirectoryOrDefault
52+ absoluteOrRelativeFilesDirectory.toAbsolutePath().normalize()
53+ }
54+
55+ override fun createPathForTempFile (): Path = tempDirectory.resolve(" gd_${UUID .randomUUID()} .tmp" )
56+
57+ override fun createPathForResultFile (sourceName : String , targetName : String , extension : String ): Path {
58+ sourceName.matches(FILENAME_PATTERN ) || throw InternalServerException (" Source file name contains forbidden characters" )
59+ targetName.matches(FILENAME_PATTERN ) || throw InternalServerException (" Target file name contains forbidden characters" )
60+
61+ val sourceNameWithoutExtension = File (sourceName).nameWithoutExtension
62+ val targetNameWithoutExtension = File (targetName).nameWithoutExtension
63+
64+ val resultFileName = if (sourceNameWithoutExtension.length + targetNameWithoutExtension.length > MAX_FILENAME_LENGTH ) {
65+ val halfOfFileNameLength = MAX_FILENAME_LENGTH / 2
66+ val trimmedSourceFileName = sourceNameWithoutExtension
67+ .substring(min(halfOfFileNameLength, sourceNameWithoutExtension.length))
68+ val trimmedTargetFileName = targetNameWithoutExtension
69+ .substring(min(halfOfFileNameLength, targetNameWithoutExtension.length))
70+ " result-${trimmedSourceFileName} -${trimmedTargetFileName} _${System .currentTimeMillis()} .$extension "
71+ } else " result-${sourceNameWithoutExtension} -${targetNameWithoutExtension} .$extension "
72+
73+ return resultDirectory.resolve(resultFileName)
74+ }
75+
76+ override fun assertPathIsInsideFilesDirectory (pathToCheck : String ): Path {
77+ val checkPath = Paths .get(pathToCheck)
78+ val path = if (! checkPath.isAbsolute) {
79+ filesDirectory.resolve(pathToCheck).toAbsolutePath().normalize()
80+ } else checkPath.normalize()
81+
82+ if (! path.startsWith(filesDirectory)) {
83+ // Avoid accessing to any directory outside filesDirectory
84+ throw AccessDeniedException ()
85+ }
86+ return path
87+ }
88+
89+ private companion object {
90+ val FILENAME_PATTERN = Pattern .compile(" ^[^</*?\"\\\\ >:|]+\$ " ).toRegex()
91+ const val MAX_FILENAME_LENGTH = 255 - 55 // 55 for extension, prefix and so on
92+ }
93+ }
94+
95+ interface PathManager {
96+ val tempDirectory: Path
97+ val resultDirectory: Path
98+ val filesDirectory: Path
99+ fun createPathForTempFile (): Path
100+ fun assertPathIsInsideFilesDirectory (pathToCheck : String ): Path
101+ fun createPathForResultFile (sourceName : String , targetName : String , extension : String ): Path
102+ }
103+
104+ class AccessDeniedException (message : String = " Access denied!" ) : InternalServerException(message)
0 commit comments