diff --git a/src/constants.rs b/src/constants.rs index ed4d5d7324..59b69d8450 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -223,6 +223,10 @@ pub(crate) const WORSE_AVATAR_BYTES: usize = 20_000; // this also fits to Outloo pub const BALANCED_IMAGE_SIZE: u32 = 1280; pub const WORSE_IMAGE_SIZE: u32 = 640; +/// Limit for received images size. Bigger images become `Viewtype::File` to avoid excessive memory +/// usage by UIs. +pub const MAX_RCVD_IMAGE_PIXELS: u32 = 50_000_000; + // Key for the folder configuration version (see below). pub(crate) const DC_FOLDERS_CONFIGURED_KEY: &str = "folders_configured"; // this value can be increased if the folder configuration is changed and must be redone on next program start diff --git a/src/mimeparser.rs b/src/mimeparser.rs index d5d01eae0c..bc6b38cd2f 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1393,6 +1393,20 @@ impl MimeMessage { } else { Viewtype::File } + } else if msg_type == Viewtype::Image + || msg_type == Viewtype::Gif + || msg_type == Viewtype::Sticker + { + match get_filemeta(decoded_data) { + // image size is known, not too big, keep msg_type: + Ok((width, height)) if width * height <= constants::MAX_RCVD_IMAGE_PIXELS => { + part.param.set_i64(Param::Width, width.into()); + part.param.set_i64(Param::Height, height.into()); + msg_type + } + // image is too big or size is unknown, display as file: + _ => Viewtype::File, + } } else { msg_type }; @@ -1413,13 +1427,6 @@ impl MimeMessage { }; info!(context, "added blobfile: {:?}", blob.as_name()); - if mime_type.type_() == mime::IMAGE { - if let Ok((width, height)) = get_filemeta(decoded_data) { - part.param.set_int(Param::Width, width as i32); - part.param.set_int(Param::Height, height as i32); - } - } - part.typ = msg_type; part.org_filename = Some(filename.to_string()); part.mimetype = Some(mime_type); diff --git a/src/mimeparser/mimeparser_tests.rs b/src/mimeparser/mimeparser_tests.rs index 211176aee4..4ca2d0c5c0 100644 --- a/src/mimeparser/mimeparser_tests.rs +++ b/src/mimeparser/mimeparser_tests.rs @@ -835,15 +835,7 @@ Content-Transfer-Encoding: base64 Content-Disposition: inline; filename="JPEG_filename.jpg" -ISVb1L3m7z15Wy5w97a2cJg6W8P8YKOYfWn3PJ/UCSFcvCPtvBhcXieiN3M3ljguzG4XK7BnGgxG -acAQdY8e0cWz1n+zKPNeNn4Iu3GXAXz4/IPksHk54inl1//0Lv8ggZjljfjnf0q1SPftYI7lpZWT -/4aTCkimRrAIcwrQJPnZJRb7BPSC6kfn1QJHMv77mRMz2+4WbdfpyPQQ0CWLJsgVXtBsSMf2Awal -n+zZzhGpXyCbWTEw1ccqZcK5KaiKNqWv51N4yVXw9dzJoCvxbYtCFGZZJdx7c+ObDotaF1/9KY4C -xJjgK9/NgTXCZP1jYm0XIBnJsFSNg0pnMRETttTuGbOVi1/s/F1RGv5RNZsCUt21d9FhkWQQXsd2 -rOzDgTdag6BQCN3hSU9eKW/GhNBuMibRN9eS7Sm1y2qFU1HgGJBQfPPRPLKxXaNi++Zt0tnon2IU -8pg5rP/IvStXYQNUQ9SiFdfAUkLU5b1j8ltnka8xl+oXsleSG44GPz6kM0RmwUrGkl4z/+NfHSsI -K+TuvC7qOah0WLFhcsXWn2+dDV1bXuAeC769TkqkpHhdXfUHnVgK3Pv7u3rVPT5AMeFUGxRB2dP4 -CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I= +iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFuG0UUhr/szuxkxtlduzVx00jGUAhcI56CF0DiHXgFHqDiMfoCvAJC4gIJEBdVaZoGOY5dJ45r7zozmRlvzIVDm3Iu//PpP7/+s/OC/0+UREkEGZGr5N6mAX78MwISQEYJ6j6QYqZf7fzsYyRGJDISuQ9g6uMjW00WRCSRCP4DwNSEg496v828fC++B4yBGro7h+PliiiJHtR9B1vrmbtg359cOk+UqA8cDJm+eHu8yDfii6sAxK0u3hlsnPFn1WvT4XxYUqqtKu8cTMNKT8+nz3/aaWft3svKecBD/O9ETu2O//G7fXt5mHX2xwGP32aIEUNNvX/uh3z2pAkcKD+9XOLVNoMESw7YC+aPP8nqqSluyiuVaZRXCKLEQK3PxsP27UHe1lXV9eczCu2V8ippJA2gz+YTNTK9ttZOoYr+aeXwHp+k245cnFRNt1tmMJg3XV0dXQd3F5LcGn5fDnVQgwINRyFvtddnNmwBSW1qXfzNSDwoM+2A+aTbShfDd89Ka9ybiiLvGa33gK8THrWfVNUSSGLTALMRo/xBXnLbACu3QtRXY+sgkWnawKxCcLjPtr29gVZdPTidBUcSaXJeTfUL+mW4631Fmse+772xIGjSza/KTW/MYE/UCXPbEWs//Xxvqf8qZgYhG0jt8cnTnJpEgfDQ6rvE0n9tq66ICafil5OnOYt0hY94FUPoxI136uPpw4VIYNd+M5utrqvGpg0hc50bRl6BvLanX4pb/3347uWi/WgNHtYyyrdwszsX5fjZtwghy7C5sK2WgUoErcjIoEWr4fAHHhoRP+XZ86L/uMAarLFgsMBaAJhM8Ief9Ey7yHSmyXTp0GRoF7KQEdD/ArmtONKkgCleAAAAAElFTkSuQmCC ----11019878869865180-- @@ -908,15 +900,7 @@ Content-ID: Content-Disposition: inline; filename="1.png" -ISVb1L3m7z15Wy5w97a2cJg6W8P8YKOYfWn3PJ/UCSFcvCPtvBhcXieiN3M3ljguzG4XK7BnGgxG -acAQdY8e0cWz1n+zKPNeNn4Iu3GXAXz4/IPksHk54inl1//0Lv8ggZjljfjnf0q1SPftYI7lpZWT -/4aTCkimRrAIcwrQJPnZJRb7BPSC6kfn1QJHMv77mRMz2+4WbdfpyPQQ0CWLJsgVXtBsSMf2Awal -n+zZzhGpXyCbWTEw1ccqZcK5KaiKNqWv51N4yVXw9dzJoCvxbYtCFGZZJdx7c+ObDotaF1/9KY4C -xJjgK9/NgTXCZP1jYm0XIBnJsFSNg0pnMRETttTuGbOVi1/s/F1RGv5RNZsCUt21d9FhkWQQXsd2 -rOzDgTdag6BQCN3hSU9eKW/GhNBuMibRN9eS7Sm1y2qFU1HgGJBQfPPRPLKxXaNi++Zt0tnon2IU -8pg5rP/IvStXYQNUQ9SiFdfAUkLU5b1j8ltnka8xl+oXsleSG44GPz6kM0RmwUrGkl4z/+NfHSsI -K+TuvC7qOah0WLFhcsXWn2+dDV1bXuAeC769TkqkpHhdXfUHnVgK3Pv7u3rVPT5AMeFUGxRB2dP4 -CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I= +iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFuG0UUhr/szuxkxtlduzVx00jGUAhcI56CF0DiHXgFHqDiMfoCvAJC4gIJEBdVaZoGOY5dJ45r7zozmRlvzIVDm3Iu//PpP7/+s/OC/0+UREkEGZGr5N6mAX78MwISQEYJ6j6QYqZf7fzsYyRGJDISuQ9g6uMjW00WRCSRCP4DwNSEg496v828fC++B4yBGro7h+PliiiJHtR9B1vrmbtg359cOk+UqA8cDJm+eHu8yDfii6sAxK0u3hlsnPFn1WvT4XxYUqqtKu8cTMNKT8+nz3/aaWft3svKecBD/O9ETu2O//G7fXt5mHX2xwGP32aIEUNNvX/uh3z2pAkcKD+9XOLVNoMESw7YC+aPP8nqqSluyiuVaZRXCKLEQK3PxsP27UHe1lXV9eczCu2V8ippJA2gz+YTNTK9ttZOoYr+aeXwHp+k245cnFRNt1tmMJg3XV0dXQd3F5LcGn5fDnVQgwINRyFvtddnNmwBSW1qXfzNSDwoM+2A+aTbShfDd89Ka9ybiiLvGa33gK8THrWfVNUSSGLTALMRo/xBXnLbACu3QtRXY+sgkWnawKxCcLjPtr29gVZdPTidBUcSaXJeTfUL+mW4631Fmse+772xIGjSza/KTW/MYE/UCXPbEWs//Xxvqf8qZgYhG0jt8cnTnJpEgfDQ6rvE0n9tq66ICafil5OnOYt0hY94FUPoxI136uPpw4VIYNd+M5utrqvGpg0hc50bRl6BvLanX4pb/3347uWi/WgNHtYyyrdwszsX5fjZtwghy7C5sK2WgUoErcjIoEWr4fAHHhoRP+XZ86L/uMAarLFgsMBaAJhM8Ief9Ey7yHSmyXTp0GRoF7KQEdD/ArmtONKkgCleAAAAAElFTkSuQmCC --------------10CC6C2609EB38DA782C5CA9-- --------------779C1631600DF3DB8C02E53A--"#; @@ -979,15 +963,7 @@ Content-Type: image/jpeg; Content-Transfer-Encoding: base64 Content-ID: -ISVb1L3m7z15Wy5w97a2cJg6W8P8YKOYfWn3PJ/UCSFcvCPtvBhcXieiN3M3ljguzG4XK7BnGgxG -acAQdY8e0cWz1n+zKPNeNn4Iu3GXAXz4/IPksHk54inl1//0Lv8ggZjljfjnf0q1SPftYI7lpZWT -/4aTCkimRrAIcwrQJPnZJRb7BPSC6kfn1QJHMv77mRMz2+4WbdfpyPQQ0CWLJsgVXtBsSMf2Awal -n+zZzhGpXyCbWTEw1ccqZcK5KaiKNqWv51N4yVXw9dzJoCvxbYtCFGZZJdx7c+ObDotaF1/9KY4C -xJjgK9/NgTXCZP1jYm0XIBnJsFSNg0pnMRETttTuGbOVi1/s/F1RGv5RNZsCUt21d9FhkWQQXsd2 -rOzDgTdag6BQCN3hSU9eKW/GhNBuMibRN9eS7Sm1y2qFU1HgGJBQfPPRPLKxXaNi++Zt0tnon2IU -8pg5rP/IvStXYQNUQ9SiFdfAUkLU5b1j8ltnka8xl+oXsleSG44GPz6kM0RmwUrGkl4z/+NfHSsI -K+TuvC7qOah0WLFhcsXWn2+dDV1bXuAeC769TkqkpHhdXfUHnVgK3Pv7u3rVPT5AMeFUGxRB2dP4 -CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I= +iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFuG0UUhr/szuxkxtlduzVx00jGUAhcI56CF0DiHXgFHqDiMfoCvAJC4gIJEBdVaZoGOY5dJ45r7zozmRlvzIVDm3Iu//PpP7/+s/OC/0+UREkEGZGr5N6mAX78MwISQEYJ6j6QYqZf7fzsYyRGJDISuQ9g6uMjW00WRCSRCP4DwNSEg496v828fC++B4yBGro7h+PliiiJHtR9B1vrmbtg359cOk+UqA8cDJm+eHu8yDfii6sAxK0u3hlsnPFn1WvT4XxYUqqtKu8cTMNKT8+nz3/aaWft3svKecBD/O9ETu2O//G7fXt5mHX2xwGP32aIEUNNvX/uh3z2pAkcKD+9XOLVNoMESw7YC+aPP8nqqSluyiuVaZRXCKLEQK3PxsP27UHe1lXV9eczCu2V8ippJA2gz+YTNTK9ttZOoYr+aeXwHp+k245cnFRNt1tmMJg3XV0dXQd3F5LcGn5fDnVQgwINRyFvtddnNmwBSW1qXfzNSDwoM+2A+aTbShfDd89Ka9ybiiLvGa33gK8THrWfVNUSSGLTALMRo/xBXnLbACu3QtRXY+sgkWnawKxCcLjPtr29gVZdPTidBUcSaXJeTfUL+mW4631Fmse+772xIGjSza/KTW/MYE/UCXPbEWs//Xxvqf8qZgYhG0jt8cnTnJpEgfDQ6rvE0n9tq66ICafil5OnOYt0hY94FUPoxI136uPpw4VIYNd+M5utrqvGpg0hc50bRl6BvLanX4pb/3347uWi/WgNHtYyyrdwszsX5fjZtwghy7C5sK2WgUoErcjIoEWr4fAHHhoRP+XZ86L/uMAarLFgsMBaAJhM8Ief9Ey7yHSmyXTp0GRoF7KQEdD/ArmtONKkgCleAAAAAElFTkSuQmCC ------=_NextPart_000_0003_01D622B3.CA753E60-- "#; @@ -2022,3 +1998,48 @@ async fn test_protected_date() -> Result<()> { assert_eq!(alice_msg.get_timestamp(), bob_msg.get_timestamp()); Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_huge_image_becomes_file() -> Result<()> { + let t = TestContext::new_alice().await; + let msg_id = receive_imf( + &t, + include_bytes!("../../test-data/message/image_huge_64M.eml"), + false, + ) + .await? + .unwrap() + .msg_ids[0]; + let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap(); + // Huge image should be treated as file: + assert_eq!(msg.viewtype, Viewtype::File); + assert!(msg.get_file(&t).is_some()); + assert_eq!(msg.get_filename().unwrap(), "huge_image.png"); + assert_eq!(msg.get_filemime().unwrap(), "image/png"); + // File has no width or height + assert!(msg.param.get_int(Param::Width).is_none()); + assert!(msg.param.get_int(Param::Height).is_none()); + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_4k_image_stays_image() -> Result<()> { + let t = TestContext::new_alice().await; + let msg_id = receive_imf( + &t, + include_bytes!("../../test-data/message/image_4k.eml"), + false, + ) + .await? + .unwrap() + .msg_ids[0]; + let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap(); + // 4K image should be treated as image: + assert_eq!(msg.viewtype, Viewtype::Image); + assert!(msg.get_file(&t).is_some()); + assert_eq!(msg.get_filename().unwrap(), "4k_image.png"); + assert_eq!(msg.get_filemime().unwrap(), "image/png"); + assert_eq!(msg.param.get_int(Param::Width).unwrap_or_default(), 3840); + assert_eq!(msg.param.get_int(Param::Height).unwrap_or_default(), 2160); + Ok(()) +} diff --git a/test-data/message/image_4k.eml b/test-data/message/image_4k.eml new file mode 100644 index 0000000000..7b2e3e7773 --- /dev/null +++ b/test-data/message/image_4k.eml @@ -0,0 +1,28 @@ +Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET) +From: Test Sender +Subject: Large image test +To: alice@example.org +Message-ID: +Date: Thu, 17 Dec 2020 15:38:45 +0100 +User-Agent: Test-Agent/1.0 +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------BIG_IMAGE_BOUNDARY" +Content-Language: en-US + +This is a multi-part message in MIME format. +--------------BIG_IMAGE_BOUNDARY +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit + +Here is a 4k image + +--------------BIG_IMAGE_BOUNDARY +Content-Type: image/png; + name="4k_image.png" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="4k_image.png" + +iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== +--------------BIG_IMAGE_BOUNDARY-- diff --git a/test-data/message/image_huge_64M.eml b/test-data/message/image_huge_64M.eml new file mode 100644 index 0000000000..6dde8a56fb --- /dev/null +++ b/test-data/message/image_huge_64M.eml @@ -0,0 +1,28 @@ +Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET) +From: Test Sender +Subject: Large image test +To: alice@example.org +Message-ID: +Date: Thu, 17 Dec 2020 15:38:45 +0100 +User-Agent: Test-Agent/1.0 +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------HUGE_IMAGE_BOUNDARY" +Content-Language: en-US + +This is a multi-part message in MIME format. +--------------HUGE_IMAGE_BOUNDARY +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit + +Here is a huge image + +--------------HUGE_IMAGE_BOUNDARY +Content-Type: image/png; + name="huge_image.png" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="huge_image.png" + +iVBORw0KGgoAAAANSUhEUgAAH0AAAB9ACAIAAACJkzqjAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== +--------------HUGE_IMAGE_BOUNDARY--