diff options
Diffstat (limited to 'core/src/encode.rs')
-rw-r--r-- | core/src/encode.rs | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/core/src/encode.rs b/core/src/encode.rs new file mode 100644 index 0000000..1ea34c0 --- /dev/null +++ b/core/src/encode.rs @@ -0,0 +1,203 @@ +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 + } +}; + + +/// +/// # Panics +/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture` +/// on `Mail` to prevent this from happening +/// +#[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)?; + + //the empty line between the headers and the body + encoder.write_blank_line(); + + encode_mail_part(&mail, encoder)?; + + Ok(()) +} + +/// +/// # Panics +/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture` +/// on `Mail` to prevent this from happening +/// +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> { + //FIXME[rust/catch] use catch block + 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() })) + }) +} + +/// +/// # Panics +/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture` +/// on `Mail` to prevent this from happening +/// +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 { + //TODO find out if there is any source using the hidden text + // (e.g. for some form of validation, prove of senders validity etc.) + // if not drop the "hidden_text" field + 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(()) +} |