From c01c629f73def5f58660161671b57d1920f2821d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:48:54 +0000 Subject: [PATCH] mobile: use GMT timezone in filenames to prevent leaking user location (#1837) * ios: use GMT timezone in filenames to prevent leaking user location * android: use GMT timestamp in generated file names --- .../simplex/app/views/helpers/RecAndPlay.kt | 4 ++-- .../chat/simplex/app/views/helpers/Util.kt | 24 ++++++++++++------- apps/ios/Shared/Model/ImageUtils.swift | 4 ++-- .../Chat/ComposeMessage/ComposeView.swift | 16 +++++++------ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt index 5b8a198983..f0d17d5803 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt @@ -50,8 +50,8 @@ class RecorderNative(private val recordedBytesLimit: Long): Recorder { rec.setAudioEncodingBitRate(16000) rec.setMaxDuration(MAX_VOICE_MILLIS_FOR_SENDING) rec.setMaxFileSize(recordedBytesLimit) - val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) - val path = getAppFilePath(SimplexApp.context, uniqueCombine(SimplexApp.context, getAppFilePath(SimplexApp.context, "voice_${timestamp}.m4a"))) + val fileToSave = generateNewFileName(SimplexApp.context, "voice", "m4a") + val path = getAppFilePath(SimplexApp.context, fileToSave) filePath = path rec.setOutputFile(path) rec.prepare() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt index a620b6725e..27e2f5220f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -331,8 +331,7 @@ fun saveImage(context: Context, image: Bitmap): String? { return try { val ext = if (image.hasAlpha()) "png" else "jpg" val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE) - val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) - val fileToSave = uniqueCombine(context, "IMG_${timestamp}.$ext") + val fileToSave = generateNewFileName(context, "IMG", ext) val file = File(getAppFilePath(context, fileToSave)) val output = FileOutputStream(file) dataResized.writeTo(output) @@ -355,8 +354,7 @@ fun saveAnimImage(context: Context, uri: Uri): String? { } // Just in case the image has a strange extension if (ext.length < 3 || ext.length > 4) ext = "gif" - val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) - val fileToSave = uniqueCombine(context, "IMG_${timestamp}.$ext") + val fileToSave = generateNewFileName(context, "IMG", ext) val file = File(getAppFilePath(context, fileToSave)) val output = FileOutputStream(file) context.contentResolver.openInputStream(uri)!!.use { input -> @@ -390,15 +388,23 @@ fun saveFileFromUri(context: Context, uri: Uri): String? { } } +fun generateNewFileName(context: Context, prefix: String, ext: String): String { + val sdf = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) + sdf.timeZone = TimeZone.getTimeZone("GMT") + val timestamp = sdf.format(Date()) + return uniqueCombine(context, "${prefix}_$timestamp.$ext") +} + fun uniqueCombine(context: Context, fileName: String): String { - fun tryCombine(fileName: String, n: Int): String { - val name = File(fileName).nameWithoutExtension - val ext = File(fileName).extension + val orig = File(fileName) + val name = orig.nameWithoutExtension + val ext = orig.extension + fun tryCombine(n: Int): String { val suffix = if (n == 0) "" else "_$n" val f = "$name$suffix.$ext" - return if (File(getAppFilePath(context, f)).exists()) tryCombine(fileName, n + 1) else f + return if (File(getAppFilePath(context, f)).exists()) tryCombine(n + 1) else f } - return tryCombine(fileName, 0) + return tryCombine(0) } fun formatBytes(bytes: Long): String { diff --git a/apps/ios/Shared/Model/ImageUtils.swift b/apps/ios/Shared/Model/ImageUtils.swift index 79382e4d6d..92be827dfc 100644 --- a/apps/ios/Shared/Model/ImageUtils.swift +++ b/apps/ios/Shared/Model/ImageUtils.swift @@ -165,8 +165,7 @@ func saveFileFromURL(_ url: URL) -> String? { } func generateNewFileName(_ prefix: String, _ ext: String) -> String { - let fileName = uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)") - return fileName + uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)") } private func uniqueCombine(_ fileName: String) -> String { @@ -191,6 +190,7 @@ private func getTimestamp() -> String { df = DateFormatter() df.dateFormat = "yyyyMMdd_HHmmss" df.locale = Locale(identifier: "US") + df.timeZone = TimeZone(secondsFromGMT: 0) tsFormatter = df } return df.string(from: Date()) diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 9d4b714164..0136159ed4 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -553,14 +553,16 @@ struct ComposeView: View { sent = await send(checkLinkPreview(), quoted: quoted, live: live) case let .imagePreviews(imagePreviews: images): let last = min(chosenImages.count, images.count) - 1 - for i in 0..= 0 { + for i in 0..