diff --git a/crates/kumod/src/http_server/inject_v1.rs b/crates/kumod/src/http_server/inject_v1.rs index 16d90d96..3a3e9d61 100644 --- a/crates/kumod/src/http_server/inject_v1.rs +++ b/crates/kumod/src/http_server/inject_v1.rs @@ -554,7 +554,7 @@ impl<'a> Compiled<'a> { if need_to { builder.set_to(Address::Mailbox(Mailbox { - name: recip.name.clone(), + name: recip.name.clone().map(Into::into), address: AddrSpec::parse(&recip.email)?, }))?; } @@ -586,7 +586,7 @@ impl InjectV1Request { } => { if let Some(from) = from { let mailbox = Address::Mailbox(Mailbox { - name: from.name.clone(), + name: from.name.clone().map(Into::into), address: AddrSpec::parse(&from.email) .context("failed parsing content.from")?, }); @@ -595,7 +595,7 @@ impl InjectV1Request { } if let Some(reply_to) = reply_to { let mailbox = Address::Mailbox(Mailbox { - name: reply_to.name.clone(), + name: reply_to.name.clone().map(Into::into), address: AddrSpec::parse(&reply_to.email) .context("failed parsing content.reply_to")?, }); diff --git a/crates/mailparsing/src/header.rs b/crates/mailparsing/src/header.rs index 3c74b790..a1529c33 100644 --- a/crates/mailparsing/src/header.rs +++ b/crates/mailparsing/src/header.rs @@ -577,7 +577,7 @@ Ok( fn assign_mailbox() { let mut sender = Header::with_name_value("Sender", ""); sender.assign(Mailbox { - name: Some("John Smith".to_string()), + name: Some("John Smith".into()), address: AddrSpec::new("john.smith", "example.com"), }); assert_eq!( @@ -586,7 +586,7 @@ Ok( ); sender.assign(Mailbox { - name: Some("John \"the smith\" Smith".to_string()), + name: Some("John \"the smith\" Smith".into()), address: AddrSpec::new("john.smith", "example.com"), }); assert_eq!( @@ -600,7 +600,7 @@ Ok( let sender = Header::new( "Sender", Mailbox { - name: Some("John".to_string()), + name: Some("John".into()), address: AddrSpec::new("john.smith", "example.com"), }, ); @@ -612,7 +612,7 @@ Ok( let sender = Header::new( "Sender", Mailbox { - name: Some("John".to_string()), + name: Some("John".into()), address: AddrSpec::new("john smith", "example.com"), }, ); @@ -627,7 +627,7 @@ Ok( let sender = Header::new( "Sender", Mailbox { - name: Some("André Pirard".to_string()), + name: Some("André Pirard".into()), address: AddrSpec::new("andre", "example.com"), }, ); diff --git a/crates/mailparsing/src/rfc5322_parser.rs b/crates/mailparsing/src/rfc5322_parser.rs index 166f69b7..6e243415 100644 --- a/crates/mailparsing/src/rfc5322_parser.rs +++ b/crates/mailparsing/src/rfc5322_parser.rs @@ -313,7 +313,7 @@ fn name_addr(input: Span) -> IResult { context( "name_addr", map((opt(display_name), angle_addr), |(name, address)| Mailbox { - name, + name: name.map(Into::into), address, }), ) @@ -1705,7 +1705,7 @@ impl MailboxList { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct Mailbox { - pub name: Option, + pub name: Option, pub address: AddrSpec, } @@ -2170,20 +2170,21 @@ fn test_qp_encode() { /// Quote input string `s`, using a backslash escape, if any /// of the characters is NOT atext. When quoting, the input /// string is enclosed in quotes. -fn quote_string(s: &str) -> String { +fn quote_string(s: impl AsRef<[u8]>) -> BString { + let s = s.as_ref(); if s.chars().any(|c| !is_atext(c)) { - let mut result = String::with_capacity(s.len() + 4); - result.push('"'); - for c in s.chars() { + let mut result = Vec::::with_capacity(s.len() + 4); + result.push(b'"'); + for (start, end, c) in s.char_indices() { if !c.is_ascii_whitespace() && !is_qtext(c) && !is_atext(c) { - result.push('\\'); + result.push(b'\\'); } - result.push(c); + result.push_str(&s[start..end]); } - result.push('"'); - result + result.push(b'"'); + result.into() } else { - s.to_string() + s.into() } } @@ -2209,12 +2210,11 @@ impl EncodeHeaderValue for Mailbox { fn encode_value(&self) -> SharedString<'static> { match &self.name { Some(name) => { - let mut value = if name.is_ascii() { - quote_string(name) + let mut value: Vec = if name.is_ascii() { + quote_string(name).into() } else { - qp_encode(name.as_bytes()) - } - .into_bytes(); + qp_encode(name.as_bytes()).into_bytes() + }; value.push_str(" <"); value.push_str(self.address.encode_value().as_bytes()); @@ -2282,7 +2282,7 @@ mod test { #[test] fn mailbox_encodes_at() { let mbox = Mailbox { - name: Some("foo@bar.com".to_string()), + name: Some("foo@bar.com".into()), address: AddrSpec { local_part: "foo".into(), domain: "bar.com".into(), diff --git a/crates/message/src/address.rs b/crates/message/src/address.rs index 5af97218..1026eba9 100644 --- a/crates/message/src/address.rs +++ b/crates/message/src/address.rs @@ -1,3 +1,4 @@ +use bstr::{BStr, BString, ByteSlice}; use config::any_err; use dns_resolver::DomainClassification; use mailparsing::{Address, AddressList, EncodeHeaderValue, Mailbox}; @@ -164,9 +165,9 @@ impl HeaderAddressList { /// If the address list is comprised of a single entry, /// returns just the display name portion, if any - pub fn name(&self) -> anyhow::Result> { + pub fn name(&self) -> anyhow::Result> { let addr = self.single_address()?; - Ok(addr.name.as_deref()) + Ok(addr.name.as_ref().map(|b| b.as_bstr())) } pub fn email(&self) -> anyhow::Result> { @@ -285,7 +286,7 @@ impl From<&Address> for HeaderAddressEntry { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct HeaderAddress { - pub name: Option, + pub name: Option, pub address: Option, } @@ -310,8 +311,8 @@ impl HeaderAddress { pub fn email(&self) -> Option<&str> { self.address.as_deref() } - pub fn name(&self) -> Option<&str> { - self.name.as_deref() + pub fn name(&self) -> Option<&BStr> { + self.name.as_ref().map(|b| b.as_bstr()) } pub fn crack_address(&self) -> anyhow::Result<(&str, &str)> {