Files
ElyOffice/crates/ely-mail-engine/tests/smtp_utf8.rs
T
ZacharyZhang-NYandClaude Opus 5.5 fe90221c11 C12.2: read and write MIME by the tree
The reader walks the MIME tree with its container context: alternative
picks versions, related finds its root by start, attached multiparts
stay whole, digest children are messages. Nesting deeper than 64
levels, counting enclosed messages, is refused, and over-deep parse
results are taken apart iteratively so dropping them cannot overflow
the stack. Composite parts with a Content-ID are cid targets.

The writer keeps attachment parameters and Content-IDs, writes
multipart and message attachments unencoded, and makes its own
Message-ID. SMTP adds BODY=8BITMIME for 8-bit bodies and SMTPUTF8
when any header block at any depth is 8-bit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 17:35:05 -04:00

156 lines
4.5 KiB
Rust

mod fake;
use ely_mail_engine::{Draft, Error, Mailbox, Part, build};
use fake::smtp::{logged_in, transaction};
#[tokio::test]
async fn a_utf8_header_needs_smtputf8() {
let message = "Subject: 您好\r\n\r\nhi\r\n";
let (mut smtp, script) = logged_in(
"250-mx\n250-SMTPUTF8\n250 AUTH PLAIN\n",
transaction(
"MAIL FROM:<ann@example.com> SMTPUTF8",
"RCPT TO:<bo@example.com>",
"Subject: 您好\n\nhi",
),
)
.await;
let to = ["bo@example.com".to_owned()];
smtp.send("ann@example.com", &to, message.as_bytes())
.await
.unwrap();
script.await.unwrap().unwrap();
let (mut smtp, script) = logged_in("250-mx\n250 AUTH PLAIN\n", vec![]).await;
let err = smtp
.send("ann@example.com", &to, message.as_bytes())
.await
.unwrap_err();
assert!(
matches!(&err, Error::Invalid(text) if text.contains("headers")),
"{err}"
);
script.await.unwrap().unwrap();
}
#[tokio::test]
async fn an_8bit_body_needs_8bitmime() {
let message = "Subject: hi\r\n\r\n您好\r\n".as_bytes();
let to = ["bo@example.com".to_owned()];
let (mut smtp, script) = logged_in(
"250-mx\n250-8BITMIME\n250 AUTH PLAIN\n",
transaction(
"MAIL FROM:<ann@example.com> BODY=8BITMIME",
"RCPT TO:<bo@example.com>",
"Subject: hi\n\n您好",
),
)
.await;
smtp.send("ann@example.com", &to, message).await.unwrap();
script.await.unwrap().unwrap();
let (mut smtp, script) = logged_in("250-mx\n250 AUTH PLAIN\n", vec![]).await;
let err = smtp
.send("ann@example.com", &to, message)
.await
.unwrap_err();
assert!(
matches!(&err, Error::Invalid(text) if text.contains("8-bit")),
"{err}"
);
script.await.unwrap().unwrap();
}
#[tokio::test]
async fn a_utf8_address_needs_smtputf8() {
let to = "用户@例子.广告";
let (mut smtp, script) = logged_in(
"250-mx\n250-SMTPUTF8\n250 AUTH PLAIN\n",
transaction(
"MAIL FROM:<ann@example.com> SMTPUTF8",
&format!("RCPT TO:<{to}>"),
"Subject: x\n\nx",
),
)
.await;
smtp.send(
"ann@example.com",
&[to.to_owned()],
b"Subject: x\r\n\r\nx\r\n",
)
.await
.unwrap();
script.await.unwrap().unwrap();
let (mut smtp, script) = logged_in("250-mx\n250 AUTH PLAIN\n", vec![]).await;
let err = smtp
.send(
"ann@example.com",
&[to.to_owned()],
b"Subject: x\r\n\r\nx\r\n",
)
.await
.unwrap_err();
assert!(
matches!(&err, Error::Invalid(text) if text.contains("non-ASCII")),
"{err}"
);
script.await.unwrap().unwrap();
}
/// A forwarded message/global under ASCII outer headers.
fn forwarded() -> Vec<u8> {
let enclosed = Part {
name: Some("old.eml".into()),
content_type: "message/global".into(),
params: Vec::new(),
content_id: None,
bytes: "Subject: 您好\r\n\r\nhi\r\n".as_bytes().to_vec(),
};
let draft = Draft {
from: Mailbox {
name: None,
email: "ann@example.com".into(),
},
to: Vec::new(),
cc: Vec::new(),
subject: "fwd".into(),
text: "see attached".into(),
html: None,
attachments: vec![enclosed],
in_reply_to: None,
references: Vec::new(),
date: 0,
};
build(&draft).unwrap().bytes
}
#[tokio::test]
async fn an_enclosed_utf8_header_needs_smtputf8() {
let to = ["bo@example.com".to_owned()];
let built = forwarded();
let (mut smtp, script) = logged_in("250-mx\n250-8BITMIME\n250 AUTH PLAIN\n", vec![]).await;
let err = smtp.send("ann@example.com", &to, &built).await.unwrap_err();
assert!(
matches!(&err, Error::Invalid(text) if text.contains("headers")),
"{err}"
);
script.await.unwrap().unwrap();
let message = "Content-Type: multipart/mixed; boundary=b\r\n\r\n--b\r\n\
Content-Type: message/global\r\n\r\nSubject: 您好\r\n\r\nhi\r\n--b--\r\n";
let (mut smtp, script) = logged_in(
"250-mx\n250-8BITMIME\n250-SMTPUTF8\n250 AUTH PLAIN\n",
transaction(
"MAIL FROM:<ann@example.com> BODY=8BITMIME SMTPUTF8",
"RCPT TO:<bo@example.com>",
message.replace("\r\n", "\n").trim_end(),
),
)
.await;
smtp.send("ann@example.com", &to, message.as_bytes())
.await
.unwrap();
script.await.unwrap().unwrap();
}