mirror of
https://github.com/chatmail/core.git
synced 2026-05-03 13:26:28 +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;
|
||||
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") => {
|
||||
for cur_data in &mail.subparts {
|
||||
let mime_type = get_mime_type(
|
||||
// multipart/alternative is described in
|
||||
// <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,
|
||||
&get_attachment_filename(context, cur_data)?,
|
||||
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
|
||||
.parse_mime_recursive(context, cur_data, is_related)
|
||||
.await?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !any_part_added {
|
||||
/* search for text/plain and add this */
|
||||
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 {
|
||||
for cur_part in mail.subparts.iter().rev() {
|
||||
if self
|
||||
.parse_mime_recursive(context, cur_part, is_related)
|
||||
.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);
|
||||
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