Skip to content

Commit 6d192d8

Browse files
authored
Merge pull request #6237 from nextcloud/improvePathHandling
improve path handling
2 parents 4d55572 + 74e1306 commit 6d192d8

10 files changed

Lines changed: 328 additions & 59 deletions

File tree

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,10 @@ class ChatActivity :
976976
return@launch
977977
}
978978

979-
val file = File(context.cacheDir, filename)
979+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, filename)
980+
if (file == null) {
981+
return@launch
982+
}
980983
if (file.exists()) {
981984
if (isCurrentlyPlaying) {
982985
chatViewModel.pauseMediaPlayer(true)
@@ -1932,7 +1935,10 @@ class ChatActivity :
19321935

19331936
private fun setUpWaveform(message: ChatMessage, thenPlay: Boolean = true, backgroundPlayAllowed: Boolean = false) {
19341937
val filename = message.fileParameters.name
1935-
val file = File(context.cacheDir, filename!!)
1938+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, filename)
1939+
if (file == null) {
1940+
return
1941+
}
19361942
if (file.exists() && message.voiceMessageFloatArray == null) {
19371943
message.isDownloadingVoiceMessage = true
19381944
chatViewModel.syncVoiceMessageUiState(message)
@@ -2540,7 +2546,12 @@ class ChatActivity :
25402546
if (cursor != null && cursor.moveToFirst()) {
25412547
val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
25422548
val fileName = ContactUtils.getDisplayNameFromDeviceContact(context, id) + ".vcf"
2543-
val file = File(context.cacheDir, fileName)
2549+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, fileName)
2550+
if (file == null) {
2551+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
2552+
cursor.close()
2553+
return
2554+
}
25442555
writeContactToVcfFile(cursor, file)
25452556

25462557
val shareUri = FileProvider.getUriForFile(
@@ -4042,12 +4053,16 @@ class ChatActivity :
40424053
}
40434054

40444055
fun share(message: ChatMessage) {
4045-
val filename = message.fileParameters.name
4046-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4056+
val sharedFile = FileUtils.resolveSharedAttachmentFile(applicationContext.cacheDir, message.fileParameters.name)
4057+
if (sharedFile == null) {
4058+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4059+
return
4060+
}
4061+
path = sharedFile.absolutePath
40474062
val shareUri = FileProvider.getUriForFile(
40484063
this,
40494064
BuildConfig.APPLICATION_ID,
4050-
File(path)
4065+
sharedFile
40514066
)
40524067

40534068
val shareIntent: Intent = Intent().apply {
@@ -4060,9 +4075,12 @@ class ChatActivity :
40604075
}
40614076

40624077
fun checkIfSharable(message: ChatMessage) {
4063-
val filename = message.fileParameters.name
4064-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4065-
val file = File(context.cacheDir, filename!!)
4078+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4079+
if (file == null) {
4080+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4081+
return
4082+
}
4083+
path = file.absolutePath
40664084
if (file.exists()) {
40674085
share(message)
40684086
} else {
@@ -4083,9 +4101,12 @@ class ChatActivity :
40834101
}
40844102

40854103
fun checkIfSaveable(message: ChatMessage) {
4086-
val filename = message.fileParameters.name
4087-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4088-
val file = File(context.cacheDir, filename!!)
4104+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4105+
if (file == null) {
4106+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4107+
return
4108+
}
4109+
path = file.absolutePath
40894110
if (file.exists()) {
40904111
showSaveToStorageWarning(message)
40914112
} else {
@@ -4115,12 +4136,16 @@ class ChatActivity :
41154136
var metaData = ""
41164137
var objectId = ""
41174138
if (message.hasFileAttachment) {
4118-
val filename = message.fileParameters.name
4119-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4139+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4140+
if (file == null) {
4141+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4142+
return@launch
4143+
}
4144+
path = file.absolutePath
41204145
shareUri = FileProvider.getUriForFile(
41214146
context,
41224147
BuildConfig.APPLICATION_ID,
4123-
File(path)
4148+
file
41244149
)
41254150

41264151
grantUriPermission(
@@ -4345,14 +4370,15 @@ class ChatActivity :
43454370
Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent ->
43464371
takeVideoIntent.resolveActivity(packageManager)?.also {
43474372
val videoFile: File? = try {
4348-
val outputDir = context.cacheDir
4373+
val outputDir = FileUtils.getSharedAttachmentsDirectory(context.cacheDir)
4374+
?: throw IOException("Could not create shared attachments directory")
43494375
val dateFormat = SimpleDateFormat(FILE_DATE_PATTERN, Locale.ROOT)
43504376
val date = dateFormat.format(Date())
43514377
val videoName = String.format(
43524378
context.resources.getString(R.string.nc_video_filename),
43534379
date
43544380
)
4355-
File("$outputDir/$videoName$VIDEO_SUFFIX")
4381+
File(outputDir, "$videoName$VIDEO_SUFFIX")
43564382
} catch (e: IOException) {
43574383
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
43584384
Log.e(TAG, "error while creating video file", e)

app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package com.nextcloud.talk.fullscreenfile
1212

1313
import android.content.Intent
1414
import android.os.Bundle
15+
import android.util.Log
1516
import android.widget.FrameLayout
1617
import androidx.appcompat.app.AppCompatActivity
1718
import androidx.compose.material3.MaterialTheme
@@ -35,6 +36,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
3536
import com.nextcloud.talk.ui.SwipeToCloseLayout
3637
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
3738
import com.nextcloud.talk.ui.theme.ViewThemeUtils
39+
import com.nextcloud.talk.utils.FileUtils
3840
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
3941
import java.io.File
4042
import javax.inject.Inject
@@ -48,6 +50,7 @@ class FullScreenImageActivity : AppCompatActivity() {
4850
private lateinit var windowInsetsController: WindowInsetsControllerCompat
4951
private lateinit var path: String
5052
private lateinit var fileName: String
53+
private lateinit var imageFile: File
5154
private lateinit var swipeToCloseLayout: SwipeToCloseLayout
5255
private var showFullscreen by mutableStateOf(false)
5356

@@ -57,7 +60,12 @@ class FullScreenImageActivity : AppCompatActivity() {
5760

5861
fileName = intent.getStringExtra("FILE_NAME").orEmpty()
5962
val isGif = intent.getBooleanExtra("IS_GIF", false)
60-
path = applicationContext.cacheDir.absolutePath + "/" + fileName
63+
imageFile = FileUtils.resolveSharedAttachmentFile(applicationContext.cacheDir, fileName) ?: run {
64+
Log.e(TAG, "Invalid image filename: $fileName")
65+
finish()
66+
return
67+
}
68+
path = imageFile.absolutePath
6169

6270
enableEdgeToEdge(
6371
statusBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT),
@@ -123,7 +131,7 @@ class FullScreenImageActivity : AppCompatActivity() {
123131
}
124132

125133
private fun shareFile() {
126-
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, File(path))
134+
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, imageFile)
127135
val shareIntent = Intent().apply {
128136
action = Intent.ACTION_SEND
129137
putExtra(Intent.EXTRA_STREAM, shareUri)
@@ -141,4 +149,8 @@ class FullScreenImageActivity : AppCompatActivity() {
141149
private fun showBitmapError() {
142150
Snackbar.make(swipeToCloseLayout, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
143151
}
152+
153+
companion object {
154+
private val TAG = FullScreenImageActivity::class.java.simpleName
155+
}
144156
}

app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package com.nextcloud.talk.fullscreenfile
1212

1313
import android.content.Intent
1414
import android.os.Bundle
15+
import android.util.Log
1516
import android.view.WindowManager
1617
import android.widget.FrameLayout
1718
import androidx.activity.SystemBarStyle
@@ -41,6 +42,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
4142
import com.nextcloud.talk.ui.SwipeToCloseLayout
4243
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
4344
import com.nextcloud.talk.ui.theme.ViewThemeUtils
45+
import com.nextcloud.talk.utils.FileUtils
4446
import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX_GENERIC
4547
import java.io.File
4648
import javax.inject.Inject
@@ -53,6 +55,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
5355

5456
private lateinit var path: String
5557
private lateinit var fileName: String
58+
private lateinit var mediaFile: File
5659
private var player: ExoPlayer? by mutableStateOf(null)
5760
private var playWhenReadyState: Boolean = true
5861
private var playBackPosition: Long = 0L
@@ -64,7 +67,12 @@ class FullScreenMediaActivity : AppCompatActivity() {
6467

6568
fileName = intent.getStringExtra("FILE_NAME").orEmpty()
6669
val isAudioOnly = intent.getBooleanExtra("AUDIO_ONLY", false)
67-
path = applicationContext.cacheDir.absolutePath + "/" + fileName
70+
mediaFile = FileUtils.resolveSharedAttachmentFile(applicationContext.cacheDir, fileName) ?: run {
71+
Log.e(TAG, "Invalid media filename: $fileName")
72+
finish()
73+
return
74+
}
75+
path = mediaFile.absolutePath
6876

6977
enableEdgeToEdge(
7078
statusBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT),
@@ -128,7 +136,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
128136
}
129137

130138
private fun preparePlayer() {
131-
val mediaItem: MediaItem = MediaItem.fromUri(File(path).toUri())
139+
val mediaItem: MediaItem = MediaItem.fromUri(mediaFile.toUri())
132140
player?.let { exoPlayer ->
133141
exoPlayer.setMediaItem(mediaItem)
134142
exoPlayer.playWhenReady = playWhenReadyState
@@ -160,7 +168,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
160168
}
161169

162170
private fun shareFile() {
163-
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, File(path))
171+
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, mediaFile)
164172
val shareIntent = Intent().apply {
165173
action = Intent.ACTION_SEND
166174
putExtra(Intent.EXTRA_STREAM, shareUri)
@@ -174,4 +182,8 @@ class FullScreenMediaActivity : AppCompatActivity() {
174182
val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance(fileName)
175183
saveFragment.show(supportFragmentManager, SaveToStorageDialogFragment.TAG)
176184
}
185+
186+
companion object {
187+
private val TAG = FullScreenMediaActivity::class.java.simpleName
188+
}
177189
}

app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package com.nextcloud.talk.fullscreenfile
1111
import android.content.ComponentName
1212
import android.content.Intent
1313
import android.os.Bundle
14+
import android.util.Log
1415
import androidx.activity.compose.setContent
1516
import androidx.appcompat.app.AppCompatActivity
1617
import androidx.compose.material3.MaterialTheme
@@ -25,6 +26,7 @@ import com.nextcloud.talk.components.ColoredStatusBar
2526
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
2627
import com.nextcloud.talk.ui.theme.ViewThemeUtils
2728
import com.nextcloud.talk.utils.AccountUtils.canWeOpenFilesApp
29+
import com.nextcloud.talk.utils.FileUtils
2830
import com.nextcloud.talk.utils.Mimetype.TEXT_PREFIX_GENERIC
2931
import com.nextcloud.talk.utils.adjustUIForAPILevel35
3032
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT
@@ -37,6 +39,7 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
3739

3840
@Inject
3941
lateinit var viewThemeUtils: ViewThemeUtils
42+
private lateinit var textFile: File
4043

4144
override fun onCreate(savedInstanceState: Bundle?) {
4245
super.onCreate(savedInstanceState)
@@ -48,8 +51,12 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
4851
val link = intent.getStringExtra("LINK")
4952
val username = intent.getStringExtra("USERNAME").orEmpty()
5053
val baseUrl = intent.getStringExtra("BASE_URL").orEmpty()
51-
val path = applicationContext.cacheDir.absolutePath + "/" + fileName
52-
val text = readFile(path)
54+
textFile = FileUtils.resolveSharedAttachmentFile(applicationContext.cacheDir, fileName) ?: run {
55+
Log.e(TAG, "Invalid text filename: $fileName")
56+
finish()
57+
return
58+
}
59+
val text = readFile(textFile)
5360

5461
adjustUIForAPILevel35()
5562

@@ -62,7 +69,7 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
6269
text = text,
6370
isMarkdown = isMarkdown,
6471
actions = FullScreenTextActions(
65-
onShare = { shareFile(path) },
72+
onShare = { shareFile() },
6673
onSave = { showSaveDialog(fileName) },
6774
onOpenInFilesApp = if (fileId.isNotEmpty()) {
6875
{ openInFilesApp(link, fileId, username, baseUrl) }
@@ -94,8 +101,8 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
94101
}
95102
}
96103

97-
private fun shareFile(path: String) {
98-
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, File(path))
104+
private fun shareFile() {
105+
val shareUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, textFile)
99106
val shareIntent = Intent().apply {
100107
action = Intent.ACTION_SEND
101108
putExtra(Intent.EXTRA_STREAM, shareUri)
@@ -110,5 +117,9 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
110117
saveFragment.show(supportFragmentManager, SaveToStorageDialogFragment.TAG)
111118
}
112119

113-
private fun readFile(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8)
120+
private fun readFile(file: File) = file.inputStream().readBytes().toString(Charsets.UTF_8)
121+
122+
companion object {
123+
private val TAG = FullScreenTextViewerActivity::class.java.simpleName
124+
}
114125
}

app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
1818
import com.nextcloud.talk.data.user.model.User
1919
import com.nextcloud.talk.users.UserManager
2020
import com.nextcloud.talk.utils.ApiUtils
21+
import com.nextcloud.talk.utils.FileUtils
2122
import com.nextcloud.talk.utils.database.user.CurrentUserProviderOld
2223
import com.nextcloud.talk.utils.preferences.AppPreferences
2324
import okhttp3.ResponseBody
@@ -88,15 +89,21 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
8889
}
8990

9091
private fun executeDownload(body: ResponseBody?, fileName: String): Result {
91-
if (body == null) {
92-
Log.e(TAG, "Response body when downloading $fileName is null!")
92+
val targetFile = FileUtils.resolveSharedAttachmentFile(context.cacheDir, fileName)
93+
if (body == null || targetFile == null) {
94+
if (body == null) {
95+
Log.e(TAG, "Response body when downloading $fileName is null!")
96+
}
97+
if (targetFile == null) {
98+
Log.e(TAG, "Refused to download file with unsafe name: $fileName")
99+
}
93100
return Result.failure()
94101
}
95102

96103
var count: Int
97104
val data = ByteArray(BYTE_UNIT_DIVIDER * DATA_BYTES)
98105
val bis: InputStream = BufferedInputStream(body.byteStream(), BYTE_UNIT_DIVIDER * DOWNLOAD_STREAM_SIZE)
99-
val outputFile = File(context.cacheDir, fileName + "_")
106+
val outputFile = File(targetFile.parentFile, targetFile.name + "_")
100107
val output: OutputStream = FileOutputStream(outputFile)
101108
var total: Long = 0
102109
val startTime = System.currentTimeMillis()
@@ -122,20 +129,16 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
122129
output.close()
123130
bis.close()
124131

125-
return onDownloadComplete(fileName)
132+
return onDownloadComplete(outputFile, targetFile)
126133
}
127134

128-
private fun onDownloadComplete(fileName: String): Result {
129-
val tempFile = File(context.cacheDir, fileName + "_")
130-
val targetFile = File(context.cacheDir, fileName)
131-
132-
return if (tempFile.renameTo(targetFile)) {
135+
private fun onDownloadComplete(tempFile: File, targetFile: File): Result =
136+
if (tempFile.renameTo(targetFile)) {
133137
setProgressAsync(Data.Builder().putBoolean(SUCCESS, true).build())
134138
Result.success()
135139
} else {
136140
Result.failure()
137141
}
138-
}
139142

140143
companion object {
141144
const val TAG = "DownloadFileToCache"

0 commit comments

Comments
 (0)