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 4f47fda130..1a3703822d 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 @@ -21,12 +21,19 @@ import java.net.URI import kotlin.math.min import kotlin.math.sqrt +private const val MAX_IMAGE_DIMENSION = 4320 + actual fun base64ToBitmap(base64ImageString: String): ImageBitmap { val imageString = base64ImageString .removePrefix("data:image/png;base64,") .removePrefix("data:image/jpg;base64,") return try { val imageBytes = Base64.decode(imageString, Base64.NO_WRAP) + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) + if (options.outWidth <= 0 || options.outHeight <= 0 || options.outWidth > MAX_IMAGE_DIMENSION || options.outHeight > MAX_IMAGE_DIMENSION || options.outHeight > options.outWidth * 256) { + return errorBitmap.asImageBitmap() + } BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size).asImageBitmap() } catch (e: Exception) { Log.e(TAG, "base64ToBitmap error: $e") 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 76a9ab4c16..8c27db443b 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 @@ -3834,7 +3834,7 @@ object MsgReactionSerializer : KSerializer { when(val t = json["type"]?.jsonPrimitive?.content ?: "") { "emoji" -> { val msgReaction = try { - val emoji = Json.decodeFromString(json["emoji"].toString()) + val emoji = decoder.json.decodeFromString(json["emoji"].toString()) MsgReaction.Emoji(emoji) } catch (e: Throwable) { MsgReaction.Unknown(t, json) @@ -4276,7 +4276,7 @@ object MsgContentSerializer : KSerializer { when (t) { "text" -> MsgContent.MCText(text) "link" -> { - val preview = Json.decodeFromString(json["preview"].toString()) + val preview = decoder.json.decodeFromString(json["preview"].toString()) MsgContent.MCLink(text, preview) } "image" -> { @@ -4294,11 +4294,11 @@ object MsgContentSerializer : KSerializer { } "file" -> MsgContent.MCFile(text) "report" -> { - val reason = Json.decodeFromString(json["reason"].toString()) + val reason = decoder.json.decodeFromString(json["reason"].toString()) MsgContent.MCReport(text, reason) } "chat" -> { - val chatLink = Json.decodeFromString(json["chatLink"].toString()) + val chatLink = decoder.json.decodeFromString(json["chatLink"].toString()) MsgContent.MCChat(text, chatLink) } else -> MsgContent.MCUnknown(t, text, json) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt index f36da6c908..9a9626b1b8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt @@ -437,7 +437,10 @@ fun PriorityLayout( ) { measureable, constraints -> // Find important element which should tell what max width other elements can use // Expecting only one such element. Can be less than one but not more - val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(constraints) + // Constrain max image height to prevent crashes and scroll issues from images with extreme aspect ratios + val maxImageHeight = (constraints.maxWidth * 2.33f).toInt().coerceAtMost(constraints.maxHeight) + val imageConstraints = constraints.copy(maxHeight = maxImageHeight) + val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(imageConstraints) val placeables: List = measureable.map { if (it.layoutId == priorityLayoutId) imagePlaceable!! 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 d3b8cdcb58..ee00e1649f 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 @@ -21,12 +21,26 @@ import kotlin.math.sqrt private fun errorBitmap(): ImageBitmap = ImageIO.read(ByteArrayInputStream(Base64.getMimeDecoder().decode("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAKVJREFUeF7t1kENACEUQ0FQhnVQ9lfGO+xggITQdvbMzArPey+8fa3tAfwAEdABZQspQStgBssEcgAIkSAJkiAJljtEgiRIgmUCSZAESZAESZAEyx0iQRIkwTKBJEiCv5fgvTd1wDmn7QAP4AeIgA4oW0gJWgEzWCZwbQ7gAA7ggLKFOIADOKBMIAeAEAmSIAmSYLlDJEiCJFgmkARJkARJ8N8S/ADTZUewBvnTOQAAAABJRU5ErkJggg=="))).toComposeImageBitmap() +private const val MAX_IMAGE_DIMENSION = 4320 + actual fun base64ToBitmap(base64ImageString: String): ImageBitmap { val imageString = base64ImageString .removePrefix("data:image/png;base64,") .removePrefix("data:image/jpg;base64,") return try { - ImageIO.read(ByteArrayInputStream(Base64.getMimeDecoder().decode(imageString))).toComposeImageBitmap() + val bytes = Base64.getMimeDecoder().decode(imageString) + val stream = ImageIO.createImageInputStream(ByteArrayInputStream(bytes)) + val reader = ImageIO.getImageReaders(stream).next() + reader.setInput(stream) + val width = reader.getWidth(0) + val height = reader.getHeight(0) + if (width <= 0 || height <= 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION || height > width * 256) { + reader.dispose() + return errorBitmap() + } + val image = reader.read(0) + reader.dispose() + image.toComposeImageBitmap() } catch (e: Throwable) { Log.e(TAG, "base64ToBitmap error: $e") errorBitmap()