refactor value encoding and update all deps to latest version
This commit is contained in:
+39
-65
@@ -5,7 +5,7 @@ use std::fmt::{self, Display, Write};
|
|||||||
use charset::decode_latin1;
|
use charset::decode_latin1;
|
||||||
use email::rfc2045::Rfc2045Parser;
|
use email::rfc2045::Rfc2045Parser;
|
||||||
use email::FromHeader;
|
use email::FromHeader;
|
||||||
use email_encoding::headers::EmailWriter;
|
use email_encoding::headers::writer::EmailWriter;
|
||||||
|
|
||||||
fn allowed_str(s: &str) -> bool {
|
fn allowed_str(s: &str) -> bool {
|
||||||
s.bytes().all(allowed_char)
|
s.bytes().all(allowed_char)
|
||||||
@@ -15,70 +15,47 @@ const fn allowed_char(c: u8) -> bool {
|
|||||||
c >= 1 && c <= 9 || c == 11 || c == 12 || c >= 14 && c <= 127
|
c >= 1 && c <= 9 || c == 11 || c == 12 || c >= 14 && c <= 127
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HeaderValueEncoder<'a> {
|
fn encode<'a>(encode_buf: &mut String, writer: &'a mut EmailWriter) {
|
||||||
writer: EmailWriter<'a>,
|
if encode_buf.is_empty() {
|
||||||
encode_buf: String,
|
// nothing to encode
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = encode_buf.trim_end_matches(' ');
|
||||||
|
email_encoding::headers::rfc2047::encode(prefix, writer).expect("we can always write to a string");
|
||||||
|
|
||||||
|
// TODO: add a better API for doing this in email-encoding
|
||||||
|
let spaces = encode_buf.len() - prefix.len();
|
||||||
|
for _ in 0..spaces {
|
||||||
|
writer.space();
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_buf.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HeaderValueEncoder<'a> {
|
fn encode_header_value(key: &str, value: &str) -> String {
|
||||||
fn encode(name: &str, value: &'a str, f: &'a mut impl fmt::Write) -> fmt::Result {
|
let mut encoded_value = String::new();
|
||||||
let encoder = Self::new(name, f);
|
let line_len = key.len() + ": ".len();
|
||||||
encoder.format(value.split_inclusive(' '))
|
let mut writer = EmailWriter::new(&mut encoded_value, line_len, 0, false);
|
||||||
}
|
let mut encode_buf = String::new();
|
||||||
|
for next_word in value.split_inclusive(' ') {
|
||||||
|
let allowed = allowed_str(next_word);
|
||||||
|
|
||||||
fn new(name: &str, writer: &'a mut dyn Write) -> Self {
|
if allowed {
|
||||||
let line_len = name.len() + ": ".len();
|
// This word only contains allowed characters
|
||||||
let writer = EmailWriter::new(writer, line_len, 0, false, false);
|
|
||||||
|
|
||||||
Self {
|
// the next word is allowed, but we may have accumulated some words to encode
|
||||||
writer,
|
encode(&mut encode_buf, &mut writer);
|
||||||
encode_buf: String::new(),
|
|
||||||
|
writer.folding().write_str(next_word).expect("we can always write to a string");
|
||||||
|
} else {
|
||||||
|
// This word contains unallowed characters
|
||||||
|
encode_buf.push_str(next_word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
encode(&mut encode_buf, &mut writer);
|
||||||
fn format(mut self, words_iter: impl Iterator<Item = &'a str>) -> fmt::Result {
|
drop(writer);
|
||||||
for next_word in words_iter {
|
encoded_value
|
||||||
let allowed = allowed_str(next_word);
|
|
||||||
|
|
||||||
if allowed {
|
|
||||||
// This word only contains allowed characters
|
|
||||||
|
|
||||||
// the next word is allowed, but we may have accumulated some words to encode
|
|
||||||
self.flush_encode_buf()?;
|
|
||||||
|
|
||||||
self.writer.folding().write_str(next_word)?;
|
|
||||||
} else {
|
|
||||||
// This word contains unallowed characters
|
|
||||||
self.encode_buf.push_str(next_word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.flush_encode_buf()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_encode_buf(&mut self) -> fmt::Result {
|
|
||||||
if self.encode_buf.is_empty() {
|
|
||||||
// nothing to encode
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefix = self.encode_buf.trim_end_matches(' ');
|
|
||||||
email_encoding::headers::rfc2047::encode(prefix, &mut self.writer)?;
|
|
||||||
|
|
||||||
let spaces = self.encode_buf.len() - prefix.len();
|
|
||||||
for _ in 0..spaces {
|
|
||||||
self.writer.space();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.encode_buf.clear();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_header_value(key: &str, value: &str, encoded_value: &mut String) {
|
|
||||||
HeaderValueEncoder::encode(key, value, encoded_value).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Header<'a>: Clone + Display {
|
pub trait Header<'a>: Clone + Display {
|
||||||
@@ -132,8 +109,7 @@ pub trait Header<'a>: Clone + Display {
|
|||||||
fn set_value<V: ToString>(&mut self, value: V) {
|
fn set_value<V: ToString>(&mut self, value: V) {
|
||||||
let value = value.to_string();
|
let value = value.to_string();
|
||||||
assert!(!value.is_empty());
|
assert!(!value.is_empty());
|
||||||
let mut encoded_value = String::new();
|
let encoded_value = encode_header_value(&self.get_key(), &value);
|
||||||
encode_header_value(&self.get_key(), &value, &mut encoded_value);
|
|
||||||
self.set_value_raw(encoded_value.into_bytes());
|
self.set_value_raw(encoded_value.into_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +150,7 @@ impl<T: ToString> Display for RFC2045HeaderValue<T> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
let key = key.to_string();
|
let key = key.to_string();
|
||||||
let mut encoded_value = String::new();
|
let encoded_value = encode_header_value(&key, &value.to_string());
|
||||||
encode_header_value(&key, &value.to_string(), &mut encoded_value);
|
|
||||||
format!("{key}=\"{encoded_value}\"")
|
format!("{key}=\"{encoded_value}\"")
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -211,8 +186,7 @@ impl<'a> Header<'a> for CowHeader<'a> {
|
|||||||
let value = value.to_string();
|
let value = value.to_string();
|
||||||
assert!(!key.is_empty());
|
assert!(!key.is_empty());
|
||||||
assert!(!value.is_empty());
|
assert!(!value.is_empty());
|
||||||
let mut encoded_value = String::new();
|
let encoded_value = encode_header_value(&key, &value);
|
||||||
encode_header_value(&key, &value, &mut encoded_value);
|
|
||||||
|
|
||||||
let mut key_bytes = key.into_bytes();
|
let mut key_bytes = key.into_bytes();
|
||||||
let mut value_bytes = encoded_value.into_bytes();
|
let mut value_bytes = encoded_value.into_bytes();
|
||||||
|
|||||||
+2
-2
@@ -749,7 +749,7 @@ pub fn parse_mail<'a, H: Header<'a> + 'a>(raw_data: &'a [u8]) -> Result<CowMail<
|
|||||||
|
|
||||||
fn parse_mail_recursive<'a, H: Header<'a> + 'a>(
|
fn parse_mail_recursive<'a, H: Header<'a> + 'a>(
|
||||||
raw_data: &'a [u8],
|
raw_data: &'a [u8],
|
||||||
mut in_multipart_digest: bool,
|
in_multipart_digest: bool,
|
||||||
) -> Result<CowMail<H>, &'static str> {
|
) -> Result<CowMail<H>, &'static str> {
|
||||||
let (headers, body_idx) = parse_headers::<H>(raw_data)?;
|
let (headers, body_idx) = parse_headers::<H>(raw_data)?;
|
||||||
|
|
||||||
@@ -769,7 +769,7 @@ fn parse_mail_recursive<'a, H: Header<'a> + 'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ctype.mimetype.starts_with("multipart/") && ctype.params.get("boundary").is_some() && raw_data.len() > body_idx {
|
if ctype.mimetype.starts_with("multipart/") && ctype.params.get("boundary").is_some() && raw_data.len() > body_idx {
|
||||||
in_multipart_digest = ctype.mimetype == "multipart/digest";
|
let in_multipart_digest = ctype.mimetype == "multipart/digest";
|
||||||
let boundary = String::from("--") + &ctype.params["boundary"];
|
let boundary = String::from("--") + &ctype.params["boundary"];
|
||||||
if let Some(body_end_idx) = find_from_u8_line_prefix(raw_data, body_idx, boundary.as_bytes()) {
|
if let Some(body_end_idx) = find_from_u8_line_prefix(raw_data, body_idx, boundary.as_bytes()) {
|
||||||
mail.body_bytes = Cow::Borrowed(&raw_data[body_idx..body_end_idx]);
|
mail.body_bytes = Cow::Borrowed(&raw_data[body_idx..body_end_idx]);
|
||||||
|
|||||||
Reference in New Issue
Block a user