mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
fix: prefer last part in multipart/alternative
This is recommended by RFC 2046 and does not require a separate loop looking for multipart subpart.
This commit is contained in:
@@ -1072,47 +1072,35 @@ impl MimeMessage {
|
|||||||
)?
|
)?
|
||||||
.0;
|
.0;
|
||||||
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
match (mimetype.type_(), mimetype.subtype().as_str()) {
|
||||||
/* Most times, multipart/alternative contains true alternatives
|
|
||||||
as text/plain and text/html. If we find a multipart/mixed
|
|
||||||
inside multipart/alternative, we use this (happens eg in
|
|
||||||
apple mail: "plaintext" as an alternative to "html+PDF attachment") */
|
|
||||||
(mime::MULTIPART, "alternative") => {
|
(mime::MULTIPART, "alternative") => {
|
||||||
for cur_data in &mail.subparts {
|
// multipart/alternative is described in
|
||||||
let mime_type = get_mime_type(
|
// <https://datatracker.ietf.org/doc/html/rfc2046#section-5.1.4>.
|
||||||
|
// Specification says that last part should be preferred,
|
||||||
|
// so we iterate over parts in reverse order.
|
||||||
|
|
||||||
|
// Search for plain text or multipart part.
|
||||||
|
//
|
||||||
|
// If we find a multipart inside multipart/alternative
|
||||||
|
// and it has usable subparts, we only parse multipart.
|
||||||
|
// This happens e.g. in Apple Mail:
|
||||||
|
// "plaintext" as an alternative to "html+PDF attachment".
|
||||||
|
for cur_data in mail.subparts.iter().rev() {
|
||||||
|
let (mime_type, _viewtype) = get_mime_type(
|
||||||
cur_data,
|
cur_data,
|
||||||
&get_attachment_filename(context, cur_data)?,
|
&get_attachment_filename(context, cur_data)?,
|
||||||
self.has_chat_version(),
|
self.has_chat_version(),
|
||||||
)?
|
)?;
|
||||||
.0;
|
|
||||||
if mime_type == "multipart/mixed" || mime_type == "multipart/related" {
|
if mime_type == mime::TEXT_PLAIN || mime_type.type_() == mime::MULTIPART {
|
||||||
any_part_added = self
|
any_part_added = self
|
||||||
.parse_mime_recursive(context, cur_data, is_related)
|
.parse_mime_recursive(context, cur_data, is_related)
|
||||||
.await?;
|
.await?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !any_part_added {
|
if !any_part_added {
|
||||||
/* search for text/plain and add this */
|
for cur_part in mail.subparts.iter().rev() {
|
||||||
for cur_data in &mail.subparts {
|
|
||||||
if get_mime_type(
|
|
||||||
cur_data,
|
|
||||||
&get_attachment_filename(context, cur_data)?,
|
|
||||||
self.has_chat_version(),
|
|
||||||
)?
|
|
||||||
.0
|
|
||||||
.type_()
|
|
||||||
== mime::TEXT
|
|
||||||
{
|
|
||||||
any_part_added = self
|
|
||||||
.parse_mime_recursive(context, cur_data, is_related)
|
|
||||||
.await?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !any_part_added {
|
|
||||||
/* `text/plain` not found - use the first part */
|
|
||||||
for cur_part in &mail.subparts {
|
|
||||||
if self
|
if self
|
||||||
.parse_mime_recursive(context, cur_part, is_related)
|
.parse_mime_recursive(context, cur_part, is_related)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@@ -2084,3 +2084,48 @@ async fn test_4k_image_stays_image() -> Result<()> {
|
|||||||
assert_eq!(msg.param.get_int(Param::Height).unwrap_or_default(), 2160);
|
assert_eq!(msg.param.get_int(Param::Height).unwrap_or_default(), 2160);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests that if multiple alternatives are available in multipart/alternative,
|
||||||
|
/// the last one is preferred.
|
||||||
|
///
|
||||||
|
/// RFC 2046 says the last supported alternative should be preferred:
|
||||||
|
/// <https://datatracker.ietf.org/doc/html/rfc2046#section-5.1.4>
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn prefer_last_alternative() {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let context = &tcm.alice().await;
|
||||||
|
let raw = br#"From: Bob <bob@example.net>
|
||||||
|
To: Alice <alice@example.org>
|
||||||
|
Subject: Alternatives
|
||||||
|
Date: Tue, 5 May 2020 01:23:45 +0000
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Chat-Version: 1.0
|
||||||
|
Content-Type: multipart/alternative; boundary="boundary"
|
||||||
|
|
||||||
|
This is a multipart message in MIME format.
|
||||||
|
|
||||||
|
--boundary
|
||||||
|
Content-Type: text/plain; charset="us-ascii"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
|
First alternative.
|
||||||
|
--boundary
|
||||||
|
Content-Type: text/plain; charset="us-ascii"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
|
Second alternative.
|
||||||
|
--boundary
|
||||||
|
Content-Type: text/plain; charset="us-ascii"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
|
Third alternative.
|
||||||
|
--boundary--
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let message = MimeMessage::from_bytes(context, &raw[..], None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(message.parts.len(), 1);
|
||||||
|
assert_eq!(message.parts[0].typ, Viewtype::Text);
|
||||||
|
assert_eq!(message.parts[0].msg, "Third alternative.");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user