From 68966a15515827eb4f6a69689fcc88c5718230fc Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:39:06 +0700 Subject: [PATCH] android, desktop: alert for local encryption/decryption errors (#4081) * android, desktop: more alert in case of local encryption/decryption errors * sending a message without a file --- .../simplex/common/platform/Share.android.kt | 2 ++ .../common/views/helpers/Utils.android.kt | 9 +++++- .../chat/simplex/common/model/ChatModel.kt | 7 ++++- .../simplex/common/views/chat/ComposeView.kt | 21 +++++++++---- .../common/views/chat/item/CIFileView.kt | 1 + .../views/chat/item/ImageFullScreenView.kt | 1 + .../simplex/common/views/helpers/Utils.kt | 30 +++++++++++++++---- .../simplex/common/platform/Share.desktop.kt | 1 + .../views/chat/item/ChatItemView.desktop.kt | 1 + .../common/views/helpers/Utils.desktop.kt | 2 ++ 10 files changed, 62 insertions(+), 13 deletions(-) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt index dc8ead80ee..ad0d914ea8 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt @@ -46,6 +46,7 @@ actual fun shareFile(text: String, fileSource: CryptoFile) { decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs, tmpFile.absolutePath) } catch (e: Exception) { Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) return } getAppFileUri(tmpFile.absolutePath) @@ -112,6 +113,7 @@ fun saveImage(ciFile: CIFile?) { decryptCryptoFile(filePath, ciFile.fileSource.cryptoArgs, tmpFile.absolutePath) } catch (e: Exception) { Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) return@createTmpFileAndDelete } tmpFile.inputStream().use { it.copyTo(outputStream) } diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt index 904f9a555a..ed0c4c9532 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt @@ -27,6 +27,7 @@ import androidx.core.text.HtmlCompat import chat.simplex.common.helpers.* import chat.simplex.common.model.* import chat.simplex.common.platform.* +import chat.simplex.res.MR import dev.icerock.moko.resources.StringResource import java.io.* import java.net.URI @@ -187,7 +188,13 @@ actual suspend fun getLoadedImage(file: CIFile?): Pair? return if (filePath != null && file != null) { try { val data = if (file.fileSource?.cryptoArgs != null) { - readCryptoFile(getAppFilePath(file.fileSource.filePath), file.fileSource.cryptoArgs) + try { + readCryptoFile(getAppFilePath(file.fileSource.filePath), file.fileSource.cryptoArgs) + } catch (e: Exception) { + Log.e(TAG, "Unable to read crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + return null + } } else { File(getAppFilePath(file.fileName)).readBytes() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index ca9056a058..a21e1c8cac 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2696,7 +2696,12 @@ data class CryptoFile( private fun decryptToTmpFile(): URI? { val absoluteFilePath = if (isAbsolutePath) filePath else getAppFilePath(filePath) val tmpFile = createTmpFileIfNeeded() - decryptCryptoFile(absoluteFilePath, cryptoArgs ?: return null, tmpFile.absolutePath) + try { + decryptCryptoFile(absoluteFilePath, cryptoArgs ?: return null, tmpFile.absolutePath) + } catch (e: Exception) { + Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + } return tmpFile.toURI() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index 9ab2b47702..26ff8796d4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -534,23 +534,34 @@ fun ComposeView( AudioPlayer.stop(tmpFile.absolutePath) if (remoteHost == null) { val actualFile = File(getAppFilePath(tmpFile.name.replaceAfter(RecorderInterface.extension, ""))) - files.add(withContext(Dispatchers.IO) { + val file = withContext(Dispatchers.IO) { if (chatController.appPrefs.privacyEncryptLocalFiles.get()) { - val args = encryptCryptoFile(tmpFile.absolutePath, actualFile.absolutePath) - tmpFile.delete() + val args = try { + encryptCryptoFile(tmpFile.absolutePath, actualFile.absolutePath) + } catch (e: Exception) { + Log.e(TAG, "Unable to encrypt plain file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + return@withContext null + } finally { + tmpFile.delete() + } CryptoFile(actualFile.name, args) } else { Files.move(tmpFile.toPath(), actualFile.toPath()) CryptoFile.plain(actualFile.name) } - }) + } + if (file != null) { + files.add(file) + msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000)) + } deleteUnusedFiles() } else { files.add(CryptoFile.plain(tmpFile.absolutePath)) // It will be deleted on JVM shutdown or next start (if the app crashes unexpectedly) filesToDelete.remove(tmpFile) + msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000)) } - msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000)) } is ComposePreview.FilePreview -> { val file = if (remoteHost == null) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt index da766d920e..dbed57d5fc 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt @@ -222,6 +222,7 @@ fun rememberSaveFileLauncher(ciFile: CIFile?): FileChooserLauncher = decryptCryptoFile(filePath, ciFile.fileSource.cryptoArgs, tmpFile.absolutePath) } catch (e: Exception) { Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) tmpFile.delete() return@createTmpFileAndDelete } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt index 16d59f6aeb..316843a908 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt @@ -16,6 +16,7 @@ import chat.simplex.common.model.CryptoFile import chat.simplex.common.platform.* import chat.simplex.common.views.chat.ProviderMedia import chat.simplex.common.views.helpers.* +import chat.simplex.res.MR import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import java.net.URI diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt index 182861c0d6..9a6e3a0f9a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt @@ -169,8 +169,14 @@ fun saveImage(image: ImageBitmap): CryptoFile? { val destFileName = generateNewFileName("IMG", ext, File(getAppFilePath(""))) val destFile = File(getAppFilePath(destFileName)) if (encrypted) { - val args = writeCryptoFile(destFile.absolutePath, dataResized.toByteArray()) - CryptoFile(destFileName, args) + try { + val args = writeCryptoFile(destFile.absolutePath, dataResized.toByteArray()) + CryptoFile(destFileName, args) + } catch (e: Exception) { + Log.e(TAG, "Unable to write crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + null + } } else { val output = FileOutputStream(destFile) dataResized.writeTo(output) @@ -216,8 +222,14 @@ fun saveAnimImage(uri: URI): CryptoFile? { val destFileName = generateNewFileName("IMG", ext, File(getAppFilePath(""))) val destFile = File(getAppFilePath(destFileName)) if (encrypted) { - val args = writeCryptoFile(destFile.absolutePath, uri.inputStream()?.readBytes() ?: return null) - CryptoFile(destFileName, args) + try { + val args = writeCryptoFile(destFile.absolutePath, uri.inputStream()?.readBytes() ?: return null) + CryptoFile(destFileName, args) + } catch (e: Exception) { + Log.e(TAG, "Unable to read crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + null + } } else { Files.copy(uri.inputStream(), destFile.toPath()) CryptoFile.plain(destFileName) @@ -241,8 +253,14 @@ fun saveFileFromUri(uri: URI, withAlertOnException: Boolean = true): CryptoFile? if (encrypted) { createTmpFileAndDelete { tmpFile -> Files.copy(inputStream, tmpFile.toPath()) - val args = encryptCryptoFile(tmpFile.absolutePath, destFile.absolutePath) - CryptoFile(destFileName, args) + try { + val args = encryptCryptoFile(tmpFile.absolutePath, destFile.absolutePath) + CryptoFile(destFileName, args) + } catch (e: Exception) { + Log.e(TAG, "Unable to encrypt plain file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) + null + } } } else { Files.copy(inputStream, destFile.toPath()) diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt index d1442bc5e3..5817275a5f 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt @@ -32,6 +32,7 @@ actual fun shareFile(text: String, fileSource: CryptoFile) { showToast(generalGetString(MR.strings.file_saved)) } catch (e: Exception) { Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) } } else { copyFileToFile(File(absolutePath), to) {} diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt index 30e1ad0352..9b2911350f 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt @@ -66,6 +66,7 @@ actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = w decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs ?: return@withLongRunningApi, tmpFile.absolutePath) } catch (e: Exception) { Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) return@withLongRunningApi } tmpFile.absolutePath diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt index 9fa93cdfdf..8fcd1ac5c7 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt @@ -12,6 +12,7 @@ import chat.simplex.common.model.CIFile import chat.simplex.common.model.readCryptoFile import chat.simplex.common.platform.* import chat.simplex.common.simplexWindowState +import chat.simplex.res.MR import kotlinx.coroutines.delay import java.io.ByteArrayInputStream import java.io.File @@ -142,6 +143,7 @@ actual suspend fun getLoadedImage(file: CIFile?): Pair? if (bitmap != null) bitmap to data else null } catch (e: Exception) { Log.e(TAG, "Unable to read crypto file: " + e.stackTraceToString()) + AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error), text = e.stackTraceToString()) null } } else {