Rework how the app handles the removal of plain copy of encrypted files when VFS is enabled
This commit is contained in:
parent
31e30e6214
commit
09d5868820
10 changed files with 42 additions and 48 deletions
|
@ -24,6 +24,9 @@ Group changes to describe their impact on the project, as follows:
|
||||||
- Replaced voice recordings file name by localized placeholder text, like for video conferences invitations
|
- Replaced voice recordings file name by localized placeholder text, like for video conferences invitations
|
||||||
- Removed jetifier as it is not needed
|
- Removed jetifier as it is not needed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Plain copy of encrypted files (when VFS is enabled) not cleaned
|
||||||
|
|
||||||
## [5.0.8] - 2023-03-20
|
## [5.0.8] - 2023-03-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -209,7 +209,7 @@ class ChatMessageContentData(
|
||||||
private fun deletePlainFilePath() {
|
private fun deletePlainFilePath() {
|
||||||
val path = filePath.value.orEmpty()
|
val path = filePath.value.orEmpty()
|
||||||
if (path.isNotEmpty() && isFileEncrypted) {
|
if (path.isNotEmpty() && isFileEncrypted) {
|
||||||
Log.i("[Content] Deleting file used for preview: $path")
|
Log.i("[Content] [VFS] Deleting file used for preview: $path")
|
||||||
FileUtils.deleteFile(path)
|
FileUtils.deleteFile(path)
|
||||||
filePath.value = ""
|
filePath.value = ""
|
||||||
}
|
}
|
||||||
|
@ -247,7 +247,7 @@ class ChatMessageContentData(
|
||||||
|
|
||||||
if (content.isFile || (content.isFileTransfer && chatMessage.isOutgoing)) {
|
if (content.isFile || (content.isFileTransfer && chatMessage.isOutgoing)) {
|
||||||
val path = if (isFileEncrypted) {
|
val path = if (isFileEncrypted) {
|
||||||
Log.i("[Content] Content is encrypted, requesting plain file path")
|
Log.i("[Content] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
|
||||||
content.exportPlainFile()
|
content.exportPlainFile()
|
||||||
} else {
|
} else {
|
||||||
content.filePath ?: ""
|
content.filePath ?: ""
|
||||||
|
|
|
@ -1204,16 +1204,10 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
{
|
{
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
Log.w("[Chat Room] Content is encrypted, requesting plain file path")
|
Log.i("[Chat Room] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
|
||||||
val plainFilePath = content.exportPlainFile()
|
val plainFilePath = content.exportPlainFile()
|
||||||
Log.i("[Chat Room] Making a copy of [$plainFilePath] to the cache directory before exporting it")
|
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), plainFilePath)) {
|
||||||
val cacheCopyPath = FileUtils.copyFileToCache(plainFilePath)
|
showDialogToSuggestOpeningFileAsText()
|
||||||
if (cacheCopyPath != null) {
|
|
||||||
Log.i("[Chat Room] Cache copy has been made: $cacheCopyPath")
|
|
||||||
FileUtils.deleteFile(plainFilePath)
|
|
||||||
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), cacheCopyPath)) {
|
|
||||||
showDialogToSuggestOpeningFileAsText()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,7 +33,7 @@ open class FileViewerViewModel(val content: Content) : ViewModel() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
filePath = if (deleteAfterUse) {
|
filePath = if (deleteAfterUse) {
|
||||||
Log.i("[File Viewer] Content is encrypted, requesting plain file path")
|
Log.i("[File Viewer] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
|
||||||
content.exportPlainFile()
|
content.exportPlainFile()
|
||||||
} else {
|
} else {
|
||||||
content.filePath.orEmpty()
|
content.filePath.orEmpty()
|
||||||
|
@ -42,7 +42,7 @@ open class FileViewerViewModel(val content: Content) : ViewModel() {
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
if (deleteAfterUse) {
|
if (deleteAfterUse) {
|
||||||
Log.i("[File Viewer] Deleting temporary plain file: $filePath")
|
Log.i("[File Viewer] [VFS] Deleting temporary plain file [$filePath]")
|
||||||
FileUtils.deleteFile(filePath)
|
FileUtils.deleteFile(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ class Api23Compatibility {
|
||||||
val isContentEncrypted = content.isFileEncrypted
|
val isContentEncrypted = content.isFileEncrypted
|
||||||
val filePath = if (content.isFileEncrypted) {
|
val filePath = if (content.isFileEncrypted) {
|
||||||
val plainFilePath = content.exportPlainFile().orEmpty()
|
val plainFilePath = content.exportPlainFile().orEmpty()
|
||||||
Log.w("[Media Store] Content is encrypted, plain file path is: $plainFilePath")
|
Log.i("[Media Store] [VFS] Content is encrypted, plain file path is: $plainFilePath")
|
||||||
plainFilePath
|
plainFilePath
|
||||||
} else content.filePath
|
} else content.filePath
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ class Api29Compatibility {
|
||||||
val isContentEncrypted = content.isFileEncrypted
|
val isContentEncrypted = content.isFileEncrypted
|
||||||
val filePath = if (content.isFileEncrypted) {
|
val filePath = if (content.isFileEncrypted) {
|
||||||
val plainFilePath = content.exportPlainFile().orEmpty()
|
val plainFilePath = content.exportPlainFile().orEmpty()
|
||||||
Log.w("[Media Store] Content is encrypted, plain file path is: $plainFilePath")
|
Log.i("[Media Store] [VFS] Content is encrypted, plain file path is: $plainFilePath")
|
||||||
plainFilePath
|
plainFilePath
|
||||||
} else content.filePath
|
} else content.filePath
|
||||||
|
|
||||||
|
|
|
@ -358,6 +358,10 @@ class CoreContext(
|
||||||
collator.strength = Collator.NO_DECOMPOSITION
|
collator.strength = Collator.NO_DECOMPOSITION
|
||||||
|
|
||||||
if (corePreferences.vfsEnabled) {
|
if (corePreferences.vfsEnabled) {
|
||||||
|
val notClearedCount = FileUtils.countFilesInDirectory(corePreferences.vfsCachePath)
|
||||||
|
if (notClearedCount != 0) {
|
||||||
|
Log.w("[Context] [VFS] There are [$notClearedCount] plain files not cleared from previous app lifetime, removing them now")
|
||||||
|
}
|
||||||
FileUtils.clearExistingPlainFiles()
|
FileUtils.clearExistingPlainFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -661,6 +661,9 @@ class CorePreferences constructor(private val context: Context) {
|
||||||
val staticPicturePath: String
|
val staticPicturePath: String
|
||||||
get() = context.filesDir.absolutePath + "/share/images/nowebcamcif.jpg"
|
get() = context.filesDir.absolutePath + "/share/images/nowebcamcif.jpg"
|
||||||
|
|
||||||
|
val vfsCachePath: String
|
||||||
|
get() = context.cacheDir.absolutePath + "/evfs/"
|
||||||
|
|
||||||
fun copyAssetsFromPackage() {
|
fun copyAssetsFromPackage() {
|
||||||
copy("linphonerc_default", configPath)
|
copy("linphonerc_default", configPath)
|
||||||
copy("linphonerc_factory", factoryConfigPath, true)
|
copy("linphonerc_factory", factoryConfigPath, true)
|
||||||
|
|
|
@ -324,12 +324,12 @@ fun loadRoundImageWithCoil(imageView: ImageView, path: String?) {
|
||||||
@BindingAdapter("coil")
|
@BindingAdapter("coil")
|
||||||
fun loadImageWithCoil(imageView: ImageView, path: String?) {
|
fun loadImageWithCoil(imageView: ImageView, path: String?) {
|
||||||
if (path != null && path.isNotEmpty() && FileUtils.isExtensionImage(path)) {
|
if (path != null && path.isNotEmpty() && FileUtils.isExtensionImage(path)) {
|
||||||
if (corePreferences.vfsEnabled && path.endsWith(FileUtils.VFS_PLAIN_FILE_EXTENSION)) {
|
if (corePreferences.vfsEnabled && path.startsWith(corePreferences.vfsCachePath)) {
|
||||||
imageView.load(path) {
|
imageView.load(path) {
|
||||||
diskCachePolicy(CachePolicy.DISABLED)
|
diskCachePolicy(CachePolicy.DISABLED)
|
||||||
listener(
|
listener(
|
||||||
onError = { _, result ->
|
onError = { _, result ->
|
||||||
Log.e("[Data Binding] [Coil] Error loading [$path]: ${result.throwable}")
|
Log.e("[Data Binding] [VFS] [Coil] Error loading [$path]: ${result.throwable}")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,12 @@ import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
|
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
|
|
||||||
class FileUtils {
|
class FileUtils {
|
||||||
companion object {
|
companion object {
|
||||||
const val VFS_PLAIN_FILE_EXTENSION = ".bctbx_evfs_plain"
|
|
||||||
|
|
||||||
fun getNameFromFilePath(filePath: String): String {
|
fun getNameFromFilePath(filePath: String): String {
|
||||||
var name = filePath
|
var name = filePath
|
||||||
val i = filePath.lastIndexOf('/')
|
val i = filePath.lastIndexOf('/')
|
||||||
|
@ -54,15 +53,11 @@ class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExtensionFromFileName(fileName: String): String {
|
fun getExtensionFromFileName(fileName: String): String {
|
||||||
val realFileName = if (fileName.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
|
var extension = MimeTypeMap.getFileExtensionFromUrl(fileName)
|
||||||
fileName.substring(0, fileName.length - VFS_PLAIN_FILE_EXTENSION.length)
|
|
||||||
} else fileName
|
|
||||||
|
|
||||||
var extension = MimeTypeMap.getFileExtensionFromUrl(realFileName)
|
|
||||||
if (extension.isNullOrEmpty()) {
|
if (extension.isNullOrEmpty()) {
|
||||||
val i = realFileName.lastIndexOf('.')
|
val i = fileName.lastIndexOf('.')
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
extension = realFileName.substring(i + 1)
|
extension = fileName.substring(i + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,21 +97,10 @@ class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearExistingPlainFiles() {
|
fun clearExistingPlainFiles() {
|
||||||
for (file in coreContext.context.filesDir.listFiles().orEmpty()) {
|
val dir = File(corePreferences.vfsCachePath)
|
||||||
if (file.path.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
|
if (dir.exists()) {
|
||||||
Log.w("[File Utils] Found forgotten plain file: ${file.path}, deleting it")
|
for (file in dir.listFiles().orEmpty()) {
|
||||||
deleteFile(file.path)
|
Log.w("[File Utils] [VFS] Found forgotten plain file [${file.path}], deleting it")
|
||||||
}
|
|
||||||
}
|
|
||||||
for (file in coreContext.context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.listFiles().orEmpty()) {
|
|
||||||
if (file.path.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
|
|
||||||
Log.w("[File Utils] Found forgotten plain file: ${file.path}, deleting it")
|
|
||||||
deleteFile(file.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (file in coreContext.context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.listFiles().orEmpty()) {
|
|
||||||
if (file.path.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
|
|
||||||
Log.w("[File Utils] Found forgotten plain file: ${file.path}, deleting it")
|
|
||||||
deleteFile(file.path)
|
deleteFile(file.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,14 +128,10 @@ class FileUtils {
|
||||||
val path = coreContext.context.cacheDir
|
val path = coreContext.context.cacheDir
|
||||||
Log.i("[File Utils] Cache directory is: $path")
|
Log.i("[File Utils] Cache directory is: $path")
|
||||||
|
|
||||||
val realFileName = if (fileName.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
|
var file = File(path, fileName)
|
||||||
fileName.substring(0, fileName.length - VFS_PLAIN_FILE_EXTENSION.length)
|
|
||||||
} else fileName
|
|
||||||
var file = File(path, realFileName)
|
|
||||||
|
|
||||||
var prefix = 1
|
var prefix = 1
|
||||||
while (file.exists()) {
|
while (file.exists()) {
|
||||||
file = File(path, prefix.toString() + "_" + realFileName)
|
file = File(path, prefix.toString() + "_" + fileName)
|
||||||
Log.w("[File Utils] File with that name already exists, renamed to ${file.name}")
|
Log.w("[File Utils] File with that name already exists, renamed to ${file.name}")
|
||||||
prefix += 1
|
prefix += 1
|
||||||
}
|
}
|
||||||
|
@ -299,7 +279,9 @@ class FileUtils {
|
||||||
} else {
|
} else {
|
||||||
Log.e("[File Utils] Copy failed")
|
Log.e("[File Utils] Copy failed")
|
||||||
}
|
}
|
||||||
remoteFile?.close()
|
withContext(Dispatchers.IO) {
|
||||||
|
remoteFile?.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e("[File Utils] getFilePath exception: ", e)
|
Log.e("[File Utils] getFilePath exception: ", e)
|
||||||
|
@ -535,5 +517,13 @@ class FileUtils {
|
||||||
outStream.flush()
|
outStream.flush()
|
||||||
outStream.close()
|
outStream.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun countFilesInDirectory(path: String): Int {
|
||||||
|
val dir = File(path)
|
||||||
|
if (dir.exists()) {
|
||||||
|
return dir.listFiles().orEmpty().size
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue