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#[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 encoder.write_blank_line();
63
64 encode_mail_part(&mail, encoder)?;
65
66 Ok(())
67}
68
69fn 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 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
136fn 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 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}