Skip to content

Commit 74e1306

Browse files
committed
improve path handling
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
1 parent 726b20b commit 74e1306

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
@@ -973,7 +973,10 @@ class ChatActivity :
973973
return@launch
974974
}
975975

976-
val file = File(context.cacheDir, filename)
976+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, filename)
977+
if (file == null) {
978+
return@launch
979+
}
977980
if (file.exists()) {
978981
if (isCurrentlyPlaying) {
979982
chatViewModel.pauseMediaPlayer(true)
@@ -1929,7 +1932,10 @@ class ChatActivity :
19291932

19301933
private fun setUpWaveform(message: ChatMessage, thenPlay: Boolean = true, backgroundPlayAllowed: Boolean = false) {
19311934
val filename = message.fileParameters.name
1932-
val file = File(context.cacheDir, filename!!)
1935+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, filename)
1936+
if (file == null) {
1937+
return
1938+
}
19331939
if (file.exists() && message.voiceMessageFloatArray == null) {
19341940
message.isDownloadingVoiceMessage = true
19351941
chatViewModel.syncVoiceMessageUiState(message)
@@ -2537,7 +2543,12 @@ class ChatActivity :
25372543
if (cursor != null && cursor.moveToFirst()) {
25382544
val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
25392545
val fileName = ContactUtils.getDisplayNameFromDeviceContact(context, id) + ".vcf"
2540-
val file = File(context.cacheDir, fileName)
2546+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, fileName)
2547+
if (file == null) {
2548+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
2549+
cursor.close()
2550+
return
2551+
}
25412552
writeContactToVcfFile(cursor, file)
25422553

25432554
val shareUri = FileProvider.getUriForFile(
@@ -4039,12 +4050,16 @@ class ChatActivity :
40394050
}
40404051

40414052
fun share(message: ChatMessage) {
4042-
val filename = message.fileParameters.name
4043-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4053+
val sharedFile = FileUtils.resolveSharedAttachmentFile(applicationContext.cacheDir, message.fileParameters.name)
4054+
if (sharedFile == null) {
4055+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4056+
return
4057+
}
4058+
path = sharedFile.absolutePath
40444059
val shareUri = FileProvider.getUriForFile(
40454060
this,
40464061
BuildConfig.APPLICATION_ID,
4047-
File(path)
4062+
sharedFile
40484063
)
40494064

40504065
val shareIntent: Intent = Intent().apply {
@@ -4057,9 +4072,12 @@ class ChatActivity :
40574072
}
40584073

40594074
fun checkIfSharable(message: ChatMessage) {
4060-
val filename = message.fileParameters.name
4061-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4062-
val file = File(context.cacheDir, filename!!)
4075+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4076+
if (file == null) {
4077+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4078+
return
4079+
}
4080+
path = file.absolutePath
40634081
if (file.exists()) {
40644082
share(message)
40654083
} else {
@@ -4080,9 +4098,12 @@ class ChatActivity :
40804098
}
40814099

40824100
fun checkIfSaveable(message: ChatMessage) {
4083-
val filename = message.fileParameters.name
4084-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4085-
val file = File(context.cacheDir, filename!!)
4101+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4102+
if (file == null) {
4103+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4104+
return
4105+
}
4106+
path = file.absolutePath
40864107
if (file.exists()) {
40874108
showSaveToStorageWarning(message)
40884109
} else {
@@ -4112,12 +4133,16 @@ class ChatActivity :
41124133
var metaData = ""
41134134
var objectId = ""
41144135
if (message.hasFileAttachment) {
4115-
val filename = message.fileParameters.name
4116-
path = applicationContext.cacheDir.absolutePath + "/" + filename
4136+
val file = FileUtils.resolveSharedAttachmentFile(context.cacheDir, message.fileParameters.name)
4137+
if (file == null) {
4138+
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
4139+
return@launch
4140+
}
4141+
path = file.absolutePath
41174142
shareUri = FileProvider.getUriForFile(
41184143
context,
41194144
BuildConfig.APPLICATION_ID,
4120-
File(path)
4145+
file
41214146
)
41224147

41234148
grantUriPermission(
@@ -4342,14 +4367,15 @@ class ChatActivity :
43424367
Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent ->
43434368
takeVideoIntent.resolveActivity(packageManager)?.also {
43444369
val videoFile: File? = try {
4345-
val outputDir = context.cacheDir
4370+
val outputDir = FileUtils.getSharedAttachmentsDirectory(context.cacheDir)
4371+
?: throw IOException("Could not create shared attachments directory")
43464372
val dateFormat = SimpleDateFormat(FILE_DATE_PATTERN, Locale.ROOT)
43474373
val date = dateFormat.format(Date())
43484374
val videoName = String.format(
43494375
context.resources.getString(R.string.nc_video_filename),
43504376
date
43514377
)
4352-
File("$outputDir/$videoName$VIDEO_SUFFIX")
4378+
File(outputDir, "$videoName$VIDEO_SUFFIX")
43534379
} catch (e: IOException) {
43544380
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
43554381
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)