use soft_ascii_string::{
SoftAsciiStr,
SoftAsciiChar,
SoftAsciiString
};
use media_type::BOUNDARY;
use internals::{
encoder::{
EncodingBuffer, EncodingWriter,
},
error::{EncodingError, EncodingErrorKind, Place, UTF_8, US_ASCII}
};
use headers::{
HeaderName,
HeaderObj, HeaderObjTrait,
HeaderKind,
headers::{ContentTransferEncoding, ContentType}
};
use ::{
error::MailError,
mail::{
Mail,
EncodableMail,
assume_encoded
}
};
#[inline(always)]
pub(crate) fn encode_mail(
mail: &EncodableMail,
top: bool,
encoder: &mut EncodingBuffer
) -> Result<(), MailError> {
_encode_mail(&*mail, top, encoder)
.map_err(|err| {
let mail_type = encoder.mail_type();
use self::MailError::*;
match err {
Encoding(enc_err) => Encoding(enc_err.with_mail_type_or_else(||Some(mail_type))),
other => other
}
})
}
fn _encode_mail(
mail: &Mail,
top: bool,
encoder: &mut EncodingBuffer
) -> Result<(), MailError> {
encode_headers(&mail, top, encoder)?;
encoder.write_blank_line();
encode_mail_part(&mail, encoder)?;
Ok(())
}
fn encode_headers(
mail: &Mail,
top: bool,
encoder: &mut EncodingBuffer
) -> Result<(), MailError> {
use super::MailBody::*;
let mut handle = encoder.writer();
if top {
handle.write_str(SoftAsciiStr::from_unchecked(
"MIME-Version: 1.0"
))?;
handle.finish_header();
}
for (name, hbody) in mail.headers().iter() {
let name_as_str = name.as_str();
let ignored_header = !top &&
!(name_as_str.starts_with("Content-")
|| name_as_str.starts_with("X-") );
if ignored_header {
warn!("non `Content-` header in MIME body: {:?}: {:?}", name, hbody);
}
encode_header(&mut handle, name, hbody)?;
}
match mail.body() {
SingleBody { ref body } => {
let data = assume_encoded(body);
let header = ContentTransferEncoding::body(data.encoding());
encode_header(&mut handle, header.name(), &header)?;
let header = ContentType::body(data.media_type().clone());
encode_header(&mut handle, header.name(), &header)?;
},
MultipleBodies { hidden_text:_, bodies:_ } => {}
}
Ok(())
}
fn encode_header(
handle: &mut EncodingWriter,
name: HeaderName,
header: &HeaderObj
) -> Result<(), EncodingError> {
let res = (|| -> Result<(), EncodingError> {
handle.write_str(name.as_ascii_str())?;
handle.write_char(SoftAsciiChar::from_unchecked(':'))?;
handle.write_fws();
header.encode(handle)?;
handle.finish_header();
Ok(())
})();
res.map_err(|err| {
err.with_place_or_else(|| Some(Place::Header { name: name.as_str() }))
})
}
fn encode_mail_part(mail: &Mail, encoder: &mut EncodingBuffer )
-> Result<(), MailError>
{
use super::MailBody::*;
let minus = SoftAsciiChar::from_unchecked('-');
match mail.body() {
SingleBody { ref body } => {
let data = assume_encoded(body);
let buffer = data.transfer_encoded_buffer();
encoder.write_body_unchecked(buffer);
},
MultipleBodies { ref hidden_text, ref bodies } => {
if hidden_text.len() > 0 {
warn!("\"hidden text\" in multipart bodies is dropped")
}
let mail_was_validated_err_msg = "[BUG] mail was already validated";
let boundary = mail.headers()
.get_single(ContentType)
.expect(mail_was_validated_err_msg)
.expect(mail_was_validated_err_msg)
.get_param(BOUNDARY)
.expect(mail_was_validated_err_msg)
.to_content();
let boundary = SoftAsciiString
::from_string(boundary)
.map_err(|orig_string| EncodingError
::from(EncodingErrorKind::InvalidTextEncoding {
got_encoding: UTF_8,
expected_encoding: US_ASCII
})
.with_place_or_else(|| Some(Place::Header { name: "Content-Type" }))
.with_str_context(orig_string.into_source())
)?;
for mail in bodies.iter() {
encoder.write_header_line(|handle| {
handle.write_char(minus)?;
handle.write_char(minus)?;
handle.write_str(&*boundary)
})?;
_encode_mail(mail, false, encoder)?;
}
if bodies.len() > 0 {
encoder.write_header_line(|handle| {
handle.write_char(minus)?;
handle.write_char(minus)?;
handle.write_str(&*boundary)?;
handle.write_char(minus)?;
handle.write_char(minus)
})?;
}
}
}
Ok(())
}