mail_core/
encode.rs

1use soft_ascii_string::{
2    SoftAsciiStr,
3    SoftAsciiChar,
4    SoftAsciiString
5};
6use media_type::BOUNDARY;
7
8use internals::{
9    encoder::{
10        EncodingBuffer, EncodingWriter,
11    },
12    error::{EncodingError, EncodingErrorKind, Place, UTF_8, US_ASCII}
13};
14use headers::{
15    HeaderName,
16    HeaderObj, HeaderObjTrait,
17    HeaderKind,
18    headers::{ContentTransferEncoding, ContentType}
19};
20
21use ::{
22    error::MailError,
23    mail::{
24        Mail,
25        EncodableMail,
26        assume_encoded
27    }
28};
29
30
31///
32/// # Panics
33/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture`
34/// on `Mail` to prevent this from happening
35///
36#[inline(always)]
37pub(crate) fn encode_mail(
38    mail: &EncodableMail,
39    top: bool,
40    encoder: &mut EncodingBuffer
41) -> Result<(), MailError> {
42    _encode_mail(&*mail, top, encoder)
43        .map_err(|err| {
44            let mail_type = encoder.mail_type();
45            use self::MailError::*;
46
47            match err {
48                 Encoding(enc_err) => Encoding(enc_err.with_mail_type_or_else(||Some(mail_type))),
49                 other => other
50            }
51        })
52}
53
54fn _encode_mail(
55    mail: &Mail,
56    top: bool,
57    encoder: &mut EncodingBuffer
58) -> Result<(), MailError> {
59    encode_headers(&mail, top, encoder)?;
60
61    //the empty line between the headers and the body
62    encoder.write_blank_line();
63
64    encode_mail_part(&mail, encoder)?;
65
66    Ok(())
67}
68
69///
70/// # Panics
71/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture`
72/// on `Mail` to prevent this from happening
73///
74fn encode_headers(
75    mail: &Mail,
76    top: bool,
77    encoder:  &mut EncodingBuffer
78) -> Result<(), MailError> {
79    use super::MailBody::*;
80
81    let mut handle = encoder.writer();
82    if top {
83        handle.write_str(SoftAsciiStr::from_unchecked(
84            "MIME-Version: 1.0"
85        ))?;
86        handle.finish_header();
87    }
88
89    for (name, hbody) in mail.headers().iter() {
90        let name_as_str = name.as_str();
91        let ignored_header = !top &&
92            !(name_as_str.starts_with("Content-")
93                || name_as_str.starts_with("X-") );
94
95        if ignored_header {
96            warn!("non `Content-` header in MIME body: {:?}: {:?}", name, hbody);
97        }
98
99        encode_header(&mut handle, name, hbody)?;
100    }
101
102
103    match mail.body() {
104        SingleBody { ref body } => {
105            let data = assume_encoded(body);
106            let header = ContentTransferEncoding::body(data.encoding());
107            encode_header(&mut handle, header.name(), &header)?;
108            let header = ContentType::body(data.media_type().clone());
109            encode_header(&mut handle, header.name(), &header)?;
110        },
111        MultipleBodies { hidden_text:_, bodies:_ } => {}
112    }
113    Ok(())
114}
115
116fn encode_header(
117    handle: &mut EncodingWriter,
118    name: HeaderName,
119    header: &HeaderObj
120) -> Result<(), EncodingError> {
121    //FIXME[rust/catch] use catch block
122    let res = (|| -> Result<(), EncodingError> {
123        handle.write_str(name.as_ascii_str())?;
124        handle.write_char(SoftAsciiChar::from_unchecked(':'))?;
125        handle.write_fws();
126        header.encode(handle)?;
127        handle.finish_header();
128        Ok(())
129    })();
130
131    res.map_err(|err| {
132        err.with_place_or_else(|| Some(Place::Header { name: name.as_str() }))
133    })
134}
135
136///
137/// # Panics
138/// if the body is not yet resolved use `Body::poll_body` or `IntoFuture`
139/// on `Mail` to prevent this from happening
140///
141fn encode_mail_part(mail: &Mail, encoder:  &mut EncodingBuffer )
142    -> Result<(), MailError>
143{
144    use super::MailBody::*;
145
146    let minus = SoftAsciiChar::from_unchecked('-');
147
148    match mail.body() {
149        SingleBody { ref body } => {
150            let data = assume_encoded(body);
151            let buffer = data.transfer_encoded_buffer();
152            encoder.write_body_unchecked(buffer);
153        },
154        MultipleBodies { ref hidden_text, ref bodies } => {
155            if hidden_text.len() > 0 {
156                //TODO find out if there is any source using the hidden text
157                // (e.g. for some form of validation, prove of senders validity etc.)
158                // if not drop the "hidden_text" field
159                warn!("\"hidden text\" in multipart bodies is dropped")
160            }
161
162            let mail_was_validated_err_msg = "[BUG] mail was already validated";
163            let boundary = mail.headers()
164                .get_single(ContentType)
165                .expect(mail_was_validated_err_msg)
166                .expect(mail_was_validated_err_msg)
167                .get_param(BOUNDARY)
168                .expect(mail_was_validated_err_msg)
169                .to_content();
170
171            let boundary = SoftAsciiString
172                ::from_string(boundary)
173                .map_err(|orig_string| EncodingError
174                    ::from(EncodingErrorKind::InvalidTextEncoding {
175                        got_encoding: UTF_8,
176                        expected_encoding: US_ASCII
177                    })
178                    .with_place_or_else(|| Some(Place::Header { name: "Content-Type" }))
179                    .with_str_context(orig_string.into_source())
180                )?;
181
182            for mail in bodies.iter() {
183                encoder.write_header_line(|handle| {
184                    handle.write_char(minus)?;
185                    handle.write_char(minus)?;
186                    handle.write_str(&*boundary)
187                })?;
188                _encode_mail(mail, false, encoder)?;
189            }
190
191            if bodies.len() > 0 {
192                encoder.write_header_line(|handle| {
193                    handle.write_char(minus)?;
194                    handle.write_char(minus)?;
195                    handle.write_str(&*boundary)?;
196                    handle.write_char(minus)?;
197                    handle.write_char(minus)
198                })?;
199            }
200        }
201    }
202    Ok(())
203}