diff --git a/mirai-core/src/commonMain/kotlin/message/ImageDecoder.kt b/mirai-core/src/commonMain/kotlin/message/ImageDecoder.kt index c179dafb3..513815153 100644 --- a/mirai-core/src/commonMain/kotlin/message/ImageDecoder.kt +++ b/mirai-core/src/commonMain/kotlin/message/ImageDecoder.kt @@ -18,20 +18,21 @@ import java.io.IOException //SOF0-SOF3 SOF5-SOF7 SOF9-SOF11 SOF13-SOF15 Segment // (0xC4, 0xC8 and 0xCC not included due to is not an SOF) private val JPG_SOF_RANGE = listOf( - 0xC0.toByte()..0xC3.toByte(), - 0xC5.toByte()..0xC7.toByte(), - 0xC9.toByte()..0xCB.toByte(), - 0xCD.toByte()..0xCF.toByte() + 0xC0..0xC3, + 0xC5..0xC7, + 0xC9..0xCB, + 0xCD..0xCF ) // https://docs.fileformat.com/image/jpeg/ +// http://www.vip.sugovica.hu/Sardi/kepnezo/JPEG%20File%20Layout%20and%20Format.htm private fun Input.getJPGImageInfo(): ImageInfo { require(readBytes(2).contentEquals(byteArrayOf(0xFF.toByte(), 0xD8.toByte()))) { "It's not a valid jpg file" } //0xFF Segment Start while (readByte() == 0xFF.toByte()) { - val type = readByte() + val type = readByte().toIntUnsigned() //Find SOF if (JPG_SOF_RANGE.any { it.contains(type) }) { //Length @@ -42,15 +43,16 @@ private fun Input.getJPGImageInfo(): ImageInfo { val width = readShort().toInt() return ImageInfo(width = width, height = height, imageType = ImageType.JPG) } else { - //SOS Segment, header is ended - if (type == 0xDA.toByte()) { - break + when (type) { + //SOS Segment, header is ended + 0xDA -> break + //0x00 (Byte alignment) and 0x01 (TEM) + in 0x00..0x01 -> continue + //RST[0-7] no length and content, skip + in 0xD0..0xD7 -> continue + //Normal segment, Skipped size=Segment Length - 2 (Length data itself) + else -> discardExact(readShort().toIntUnsigned() - 2) } - //Other segment, skip - discardExact( - //Skip size=segment length - 2 (length data itself) - readShort().toIntUnsigned() - 2 - ) } } throw IllegalArgumentException("It's not a valid jpg file, failed to find an SOF segment") diff --git a/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt b/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt index 5ab1751ac..b40f2a680 100644 --- a/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt @@ -15,6 +15,7 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.withUse import org.junit.jupiter.api.Test +import java.io.File import java.io.IOException import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -52,6 +53,23 @@ internal class ImageReadingTest : AbstractTest() { "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch( ImageType.JPG ) + //FF 01 + "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF 01 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch( + ImageType.JPG + ) + //FF 00 + "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF 00 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch( + ImageType.JPG + ) + //RST[0-7] + "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF D0 FF D1 FF D2 FF D3 FF D4 FF D5 FF D6 FF D7 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch( + ImageType.JPG + ) + println("Current path: "+File(".").absolutePath) + //Issue 1610 + File("./src/commonTest/resources/image/jpeg-header-issue-1610.bin").readBytes().testRead( + ImageType.JPG + ) //Failed to find assertFailsWith(IllegalArgumentException::class) { "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF DA".testMatch( @@ -83,6 +101,13 @@ internal class ImageReadingTest : AbstractTest() { } } + private fun ByteArray.testRead(type: ImageType) { + this.toExternalResource().withUse { + calculateImageInfo().run { + assertEquals(type, imageType, "imageType") + } + } + } private fun String.testMatch(type: ImageType) { this.hexToBytes().toExternalResource().withUse { calculateImageInfo().run { diff --git a/mirai-core/src/commonTest/resources/image/jpeg-header-issue-1610.bin b/mirai-core/src/commonTest/resources/image/jpeg-header-issue-1610.bin new file mode 100644 index 000000000..bebf5752e Binary files /dev/null and b/mirai-core/src/commonTest/resources/image/jpeg-header-issue-1610.bin differ