diff --git a/apps/ios/SimpleXChat/CryptoFile.swift b/apps/ios/SimpleXChat/CryptoFile.swift index 0e539ba97c..91f8997ad6 100644 --- a/apps/ios/SimpleXChat/CryptoFile.swift +++ b/apps/ios/SimpleXChat/CryptoFile.swift @@ -25,6 +25,18 @@ public func writeCryptoFile(path: String, data: Data) throws -> CryptoFileArgs { } } +public func writeCryptoImage(maxSize: Int, path: String, data: Data, encrypted: Bool) throws -> CryptoFileArgs { + let ptr: UnsafeMutableRawPointer = malloc(data.count) + memcpy(ptr, (data as NSData).bytes, data.count) + var cPath = path.cString(using: .utf8)! + let cjson = chat_write_image(getChatCtrl(), maxSize, &cPath, ptr, Int32(data.count), encrypted)! + let d = fromCString(cjson).data(using: .utf8)! + switch try jsonDecoder.decode(WriteFileResult.self, from: d) { + case let .result(cfArgs): return cfArgs + case let .error(err): throw RuntimeError(err) + } +} + public func readCryptoFile(path: String, cryptoArgs: CryptoFileArgs) throws -> Data { var cPath = path.cString(using: .utf8)! var cKey = cryptoArgs.fileKey.cString(using: .utf8)! diff --git a/apps/ios/SimpleXChat/FileUtils.swift b/apps/ios/SimpleXChat/FileUtils.swift index 8b0d082aed..f246bd12cc 100644 --- a/apps/ios/SimpleXChat/FileUtils.swift +++ b/apps/ios/SimpleXChat/FileUtils.swift @@ -223,6 +223,17 @@ public func saveFile(_ data: Data, _ fileName: String, encrypted: Bool) -> Crypt } } +public func saveImage(_ data: Data, _ fileName: String, maxSize: Long, encrypted: Bool) -> CryptoFile? { + let filePath = getAppFilePath(fileName) + do { + let cfArgs = try writeCryptoImage(maxSize: maxSize, path: filePath.path, data: data, encrypted: encrypted) + return CryptoFile(filePath: fileName, cryptoArgs: cfArgs) + } catch { + logger.error("FileUtils.saveImage error: \(error.localizedDescription)") + return nil + } +} + public func removeFile(_ url: URL) { do { try FileManager.default.removeItem(atPath: url.path) diff --git a/apps/ios/SimpleXChat/SimpleX.h b/apps/ios/SimpleXChat/SimpleX.h index 37759e7b56..af11f21e5b 100644 --- a/apps/ios/SimpleXChat/SimpleX.h +++ b/apps/ios/SimpleXChat/SimpleX.h @@ -35,7 +35,7 @@ extern char *chat_resize_image_to_str_size(const char *path, long maxSize); extern char *chat_write_file(chat_ctrl ctl, char *path, char *data, int len); // chat_write_image returns null-terminated string with JSON of WriteFileResult -extern char *chat_write_image(chat_ctrl ctl, long maxSize, char *path, char *data, int len); +extern char *chat_write_image(chat_ctrl ctl, long maxSize, char *path, char *data, int len, bool encrypted); // chat_read_file returns a buffer with: // result status (1 byte), then if diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt index e09aa42982..03c61f8268 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt @@ -72,11 +72,11 @@ fun Bitmap.clipToCircle(): Bitmap { return circle } -actual fun compressImageStr(bitmap: ImageBitmap): String { - val usePng = bitmap.hasAlpha() - val ext = if (usePng) "png" else "jpg" - return "data:image/$ext;base64," + Base64.encodeToString(compressImageData(bitmap, usePng).toByteArray(), Base64.NO_WRAP) -} +// actual fun compressImageStr(bitmap: ImageBitmap): String { +// val usePng = bitmap.hasAlpha() +// val ext = if (usePng) "png" else "jpg" +// return "data:image/$ext;base64," + Base64.encodeToString(compressImageData(bitmap, usePng).toByteArray(), Base64.NO_WRAP) +// } actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream { val stream = ByteArrayOutputStream() @@ -84,19 +84,19 @@ actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOut return stream } -actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream { - var img = image - var stream = compressImageData(img, usePng) - while (stream.size() > maxDataSize) { - val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble()) - val clippedRatio = min(ratio, 2.0) - val width = (img.width.toDouble() / clippedRatio).toInt() - val height = img.height * width / img.width - img = Bitmap.createScaledBitmap(img.asAndroidBitmap(), width, height, true).asImageBitmap() - stream = compressImageData(img, usePng) - } - return stream -} +// actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream { +// var img = image +// var stream = compressImageData(img, usePng) +// while (stream.size() > maxDataSize) { +// val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble()) +// val clippedRatio = min(ratio, 2.0) +// val width = (img.width.toDouble() / clippedRatio).toInt() +// val height = img.height * width / img.width +// img = Bitmap.createScaledBitmap(img.asAndroidBitmap(), width, height, true).asImageBitmap() +// stream = compressImageData(img, usePng) +// } +// return stream +// } actual fun GrayU8.toImageBitmap(): ImageBitmap = ConvertBitmap.grayToBitmap(this, Bitmap.Config.RGB_565).asImageBitmap() diff --git a/apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c b/apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c index ebda8c81a1..57a81b81b4 100644 --- a/apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c +++ b/apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c @@ -1,5 +1,6 @@ #include #include +#include #include //#include //#include @@ -68,7 +69,7 @@ extern char *chat_password_hash(const char *pwd, const char *salt); extern char *chat_valid_name(const char *name); extern int chat_json_length(const char *str); extern char *chat_write_file(chat_ctrl ctrl, const char *path, char *ptr, int length); -extern char *chat_write_image(chat_ctrl ctl, long maxSize, char *path, char *data, int len); +extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length, bool encrypt); extern char *chat_read_file(const char *path, const char *key, const char *nonce); extern char *chat_encrypt_file(chat_ctrl ctrl, const char *from_path, const char *to_path); extern char *chat_decrypt_file(const char *from_path, const char *key, const char *nonce, const char *to_path); @@ -185,11 +186,11 @@ Java_chat_simplex_common_platform_CoreKt_chatWriteFile(JNIEnv *env, jclass clazz } JNIEXPORT jstring JNICALL -Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer) { +Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer, jboolean encrypt) { const char *_path = encode_to_utf8_chars(env, path); jbyte *buff = (jbyte *) (*env)->GetDirectBufferAddress(env, buffer); jlong capacity = (*env)->GetDirectBufferCapacity(env, buffer); - jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity)); + jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity, encrypt)); (*env)->ReleaseStringUTFChars(env, path, _path); return res; } diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c b/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c index c2bfb60e99..76092e4079 100644 --- a/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -41,7 +42,7 @@ extern char *chat_password_hash(const char *pwd, const char *salt); extern char *chat_valid_name(const char *name); extern int chat_json_length(const char *str); extern char *chat_write_file(chat_ctrl ctrl, const char *path, char *ptr, int length); -extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length); +extern char *chat_write_image(chat_ctrl ctrl, long max_size, const char *path, char *ptr, int length, bool encrypt); extern char *chat_read_file(const char *path, const char *key, const char *nonce); extern char *chat_encrypt_file(chat_ctrl ctrl, const char *from_path, const char *to_path); extern char *chat_decrypt_file(const char *from_path, const char *key, const char *nonce, const char *to_path); @@ -195,11 +196,11 @@ Java_chat_simplex_common_platform_CoreKt_chatWriteFile(JNIEnv *env, jclass clazz } JNIEXPORT jstring JNICALL -Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer) { +Java_chat_simplex_common_platform_CoreKt_chatWriteImage(JNIEnv *env, jclass clazz, jlong controller, jlong maxSize, jstring path, jobject buffer, jboolean encrypt) { const char *_path = encode_to_utf8_chars(env, path); jbyte *buff = (jbyte *) (*env)->GetDirectBufferAddress(env, buffer); jlong capacity = (*env)->GetDirectBufferCapacity(env, buffer); - jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity)); + jstring res = decode_to_utf8_string(env, chat_write_image((void*)controller, maxSize, _path, buff, capacity, encrypt)); (*env)->ReleaseStringUTFChars(env, path, _path); return res; } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt index 28b46f592d..661479d6ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt @@ -1,5 +1,6 @@ package chat.simplex.common.model +import androidx.compose.ui.graphics.ImageBitmap import chat.simplex.common.platform.* import kotlinx.serialization.* import java.nio.ByteBuffer @@ -32,6 +33,19 @@ fun writeCryptoFile(path: String, data: ByteArray): CryptoFileArgs { } } +fun writeCryptoImage(maxSize: Long, image: ImageBitmap, path: String, encrypt: Boolean): CryptoFileArgs { + val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized") + val data = compressImageData(image, image.hasAlpha).toByteArray() + val buffer = ByteBuffer.allocateDirect(data.size) + buffer.put(data) + buffer.rewind() + val str = chatWriteImage(ctrl, maxSize, path, buffer, encrypt) + return when (val d = json.decodeFromString(WriteFileResult.serializer(), str)) { + is WriteFileResult.Result -> d.cryptoArgs + is WriteFileResult.Error -> throw Exception(d.writeError) + } +} + fun readCryptoFile(path: String, cryptoArgs: CryptoFileArgs): ByteArray { val res: Array = chatReadFile(path, cryptoArgs.fileKey, cryptoArgs.fileNonce) val status = (res[0] as Integer).toInt() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt index dc2dccf3ea..b3d14fa1e4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt @@ -32,6 +32,7 @@ external fun chatPasswordHash(pwd: String, salt: String): String external fun chatValidName(name: String): String external fun chatJsonLength(str: String): Int external fun chatWriteFile(ctrl: ChatCtrl, path: String, buffer: ByteBuffer): String +external fun chatWriteImage(ctrl: ChatCtrl, maxSize: Long, path: String, buffer: ByteBuffer, encrypt: Boolean): String external fun chatReadFile(path: String, key: String, nonce: String): Array external fun chatEncryptFile(ctrl: ChatCtrl, fromPath: String, toPath: String): String external fun chatDecryptFile(fromPath: String, key: String, nonce: String, toPath: String): String diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt index 449fcb4396..cfb3457599 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt @@ -9,9 +9,9 @@ import java.net.URI expect fun base64ToBitmap(base64ImageString: String): ImageBitmap // XXX: Not a part of platform services anymore? expect fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String -expect fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream +// expect fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream expect fun cropToSquare(image: ImageBitmap): ImageBitmap -expect fun compressImageStr(bitmap: ImageBitmap): String +// expect fun compressImageStr(bitmap: ImageBitmap): String expect fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream expect fun GrayU8.toImageBitmap(): ImageBitmap 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 76a06ed4a3..232b1723a0 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,24 +169,19 @@ fun saveImage(image: ImageBitmap): CryptoFile? { return try { val encrypted = chatController.appPrefs.privacyEncryptLocalFiles.get() val ext = if (image.hasAlpha()) "png" else "jpg" - val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE) val destFileName = generateNewFileName("IMG", ext, File(getAppFilePath(""))) val destFile = File(getAppFilePath(destFileName)) - if (encrypted) { - try { - val args = writeCryptoFile(destFile.absolutePath, dataResized.toByteArray()) + try { + val args = writeCryptoImage(MAX_IMAGE_SIZE, image, destFile.absolutePath, encrypted) + if (encrypted) { 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 { + CryptoFile.plain(destFileName) } - } else { - val output = FileOutputStream(destFile) - dataResized.writeTo(output) - output.flush() - output.close() - CryptoFile.plain(destFileName) + } 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 } } catch (e: Exception) { Log.e(TAG, "Util.kt saveImage error: ${e.stackTraceToString()}") @@ -198,14 +193,10 @@ fun desktopSaveImageInTmp(uri: URI): CryptoFile? { val image = getBitmapFromUri(uri) ?: return null return try { val ext = if (image.hasAlpha()) "png" else "jpg" - val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE) val destFileName = generateNewFileName("IMG", ext, tmpDir) val destFile = File(tmpDir, destFileName) - val output = FileOutputStream(destFile) - dataResized.writeTo(output) - output.flush() - output.close() - CryptoFile.plain(destFile.absolutePath) + val args = writeCryptoImage(MAX_IMAGE_SIZE, image, destFile.absolutePath, false) + CryptoFile(destFileName, args) } catch (e: Exception) { Log.e(TAG, "Util.kt desktopSaveImageInTmp error: ${e.stackTraceToString()}") null @@ -301,11 +292,7 @@ fun saveWallpaperFile(uri: URI): String? { fun saveWallpaperFile(image: ImageBitmap): String { val destFileName = generateNewFileName("wallpaper", "jpg", File(getWallpaperFilePath(""))) val destFile = File(getWallpaperFilePath(destFileName)) - val dataResized = resizeImageToDataSize(image, false, maxDataSize = 5_000_000) - val output = FileOutputStream(destFile) - dataResized.use { - it.writeTo(output) - } + writeCryptoImage(5_000_000, image, destFile.absolutePath, false) return destFile.name } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt index ca43ee3c85..9a188d9bd8 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt @@ -46,19 +46,19 @@ actual fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String { return str } -actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream { - var img = image - var stream = compressImageData(img, usePng) - while (stream.size() > maxDataSize) { - val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble()) - val clippedRatio = kotlin.math.min(ratio, 2.0) - val width = (img.width.toDouble() / clippedRatio).toInt() - val height = img.height * width / img.width - img = img.scale(width, height) - stream = compressImageData(img, usePng) - } - return stream -} +// actual fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream { +// var img = image +// var stream = compressImageData(img, usePng) +// while (stream.size() > maxDataSize) { +// val ratio = sqrt(stream.size().toDouble() / maxDataSize.toDouble()) +// val clippedRatio = kotlin.math.min(ratio, 2.0) +// val width = (img.width.toDouble() / clippedRatio).toInt() +// val height = img.height * width / img.width +// img = img.scale(width, height) +// stream = compressImageData(img, usePng) +// } +// return stream +// } actual fun cropToSquare(image: ImageBitmap): ImageBitmap { var xOffset = 0 @@ -73,17 +73,17 @@ actual fun cropToSquare(image: ImageBitmap): ImageBitmap { return image } -actual fun compressImageStr(bitmap: ImageBitmap): String { - val usePng = bitmap.hasAlpha() - val ext = if (usePng) "png" else "jpg" - return try { - val encoded = Base64.getEncoder().encodeToString(compressImageData(bitmap, usePng).toByteArray()) - "data:image/$ext;base64,$encoded" - } catch (e: Exception) { - Log.e(TAG, "resizeImageToStrSize error: $e") - throw e - } -} +// actual fun compressImageStr(bitmap: ImageBitmap): String { +// val usePng = bitmap.hasAlpha() +// val ext = if (usePng) "png" else "jpg" +// return try { +// val encoded = Base64.getEncoder().encodeToString(compressImageData(bitmap, usePng).toByteArray()) +// "data:image/$ext;base64,$encoded" +// } catch (e: Exception) { +// Log.e(TAG, "resizeImageToStrSize error: $e") +// throw e +// } +// } actual fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOutputStream { val writer = ImageIO.getImageWritersByFormatName(if (usePng) "png" else "jpg").next() diff --git a/src/Simplex/Chat/Mobile.hs b/src/Simplex/Chat/Mobile.hs index 55d08b9e75..57b28e1850 100644 --- a/src/Simplex/Chat/Mobile.hs +++ b/src/Simplex/Chat/Mobile.hs @@ -30,7 +30,7 @@ import Data.Word (Word8) import Database.SQLite.Simple (SQLError (..)) import qualified Database.SQLite.Simple as DB import Foreign.C.String -import Foreign.C.Types (CInt (..), CLong (..)) +import Foreign.C.Types (CBool (..), CInt (..), CLong (..)) import Foreign.Ptr import Foreign.StablePtr import Foreign.Storable (poke) @@ -105,7 +105,7 @@ foreign export ccall "chat_decrypt_media" cChatDecryptMedia :: CString -> Ptr Wo foreign export ccall "chat_write_file" cChatWriteFile :: StablePtr ChatController -> CString -> Ptr Word8 -> CInt -> IO CJSONString -foreign export ccall "chat_write_image" cChatWriteImage :: StablePtr ChatController -> CLong -> CString -> Ptr Word8 -> CInt -> IO CJSONString +foreign export ccall "chat_write_image" cChatWriteImage :: StablePtr ChatController -> CLong -> CString -> Ptr Word8 -> CInt -> CBool -> IO CJSONString foreign export ccall "chat_read_file" cChatReadFile :: CString -> CString -> CString -> IO (Ptr Word8) diff --git a/src/Simplex/Chat/Mobile/File.hs b/src/Simplex/Chat/Mobile/File.hs index e429e9b9c7..a304a79c9c 100644 --- a/src/Simplex/Chat/Mobile/File.hs +++ b/src/Simplex/Chat/Mobile/File.hs @@ -48,7 +48,7 @@ import Simplex.Messaging.Util (catchAll) import UnliftIO (Handle, IOMode (..), atomically, withFile) data WriteFileResult - = WFResult {cryptoArgs :: CryptoFileArgs} + = WFResult {cryptoArgs :: Maybe CryptoFileArgs} | WFError {writeError :: String} $(JQ.deriveToJSON (sumTypeJSON $ dropPrefix "WF") ''WriteFileResult) @@ -61,28 +61,33 @@ cChatWriteFile cc cPath ptr len = do r <- chatWriteFile c path s newCStringFromLazyBS $ J.encode r -cChatWriteImage :: StablePtr ChatController -> CLong -> CString -> Ptr Word8 -> CInt -> IO CJSONString -cChatWriteImage cc maxSize cPath ptr len = do +chatWriteFile :: ChatController -> FilePath -> ByteString -> IO WriteFileResult +chatWriteFile ChatController {random} path s = do + cfArgs <- atomically $ CF.randomArgs random + chatWriteFile_ (Just cfArgs) path s + +chatWriteFile_ :: Maybe CryptoFileArgs -> FilePath -> ByteString -> IO WriteFileResult +chatWriteFile_ cfArgs_ path s = do + let file = CryptoFile path cfArgs_ + either WFError (\_ -> WFResult cfArgs_) + <$> runCatchExceptT (withExceptT show $ CF.writeFile file $ LB.fromStrict s) + +cChatWriteImage :: StablePtr ChatController -> CLong -> CString -> Ptr Word8 -> CInt -> CBool -> IO CJSONString +cChatWriteImage cc maxSize cPath ptr len encrypt = do c <- deRefStablePtr cc path <- peekCString cPath src <- getByteString ptr len + cfArgs_ <- if encrypt /= 0 then Just <$> atomically (CF.randomArgs $ random c) else pure Nothing r <- case Picture.decodeResizeable src of Left e -> pure $ WFError e Right (ri, _metadata) -> do - let resized = resizeImageToSize True (fromIntegral maxSize) ri + let resized = resizeImageToSize False (fromIntegral maxSize) ri if LB.length resized > fromIntegral maxSize then pure $ WFError "unable to fit" - else chatWriteFile c path (LB.toStrict resized) + else chatWriteFile_ cfArgs_ path (LB.toStrict resized) newCStringFromLazyBS $ J.encode r -chatWriteFile :: ChatController -> FilePath -> ByteString -> IO WriteFileResult -chatWriteFile ChatController {random} path s = do - cfArgs <- atomically $ CF.randomArgs random - let file = CryptoFile path $ Just cfArgs - either WFError (\_ -> WFResult cfArgs) - <$> runCatchExceptT (withExceptT show $ CF.writeFile file $ LB.fromStrict s) - data ReadFileResult = RFResult {fileSize :: Int} | RFError {readError :: String} @@ -124,7 +129,7 @@ chatEncryptFile ChatController {random} fromPath toPath = encrypt = do cfArgs <- atomically $ CF.randomArgs random encryptFile fromPath toPath cfArgs - pure cfArgs + pure $ Just cfArgs cChatDecryptFile :: CString -> CString -> CString -> CString -> IO CString cChatDecryptFile cFromPath cKey cNonce cToPath = do