mail_parser/core/
message.rs1use std::{borrow::Cow, convert::TryInto};
8
9use crate::{
10 decoders::html::{html_to_text, text_to_html},
11 parsers::{
12 fields::thread::thread_name,
13 preview::{preview_html, preview_text},
14 MessageStream,
15 },
16 Address, AttachmentIterator, BodyPartIterator, DateTime, GetHeader, Header, HeaderForm,
17 HeaderName, HeaderValue, Message, MessageParser, MessagePart, PartType, Received,
18};
19
20impl<'x> Message<'x> {
21 pub fn root_part(&self) -> &MessagePart<'x> {
23 &self.parts[0]
24 }
25
26 pub fn header(&self, header: impl Into<HeaderName<'x>>) -> Option<&HeaderValue<'x>> {
28 self.parts[0].headers.header(header).map(|h| &h.value)
29 }
30
31 pub fn remove_header(&mut self, header: impl Into<HeaderName<'x>>) -> Option<HeaderValue<'x>> {
33 let header = header.into();
34 let headers = &mut self.parts[0].headers;
35 headers
36 .iter()
37 .position(|h| h.name == header)
38 .map(|pos| headers.swap_remove(pos).value)
39 }
40
41 pub fn header_raw(&self, header: impl Into<HeaderName<'x>>) -> Option<&str> {
43 self.parts[0].headers.header(header).and_then(|h| {
44 std::str::from_utf8(&self.raw_message[h.offset_start as usize..h.offset_end as usize])
45 .ok()
46 })
47 }
48
49 pub fn header_as(
51 &self,
52 header: impl Into<HeaderName<'x>>,
53 form: HeaderForm,
54 ) -> Vec<HeaderValue<'_>> {
55 let header = header.into();
56 let mut results = Vec::new();
57 for header_ in &self.parts[0].headers {
58 if header_.name == header {
59 results.push(
60 self.raw_message
61 .get(header_.offset_start as usize..header_.offset_end as usize)
62 .map_or(HeaderValue::Empty, |bytes| match form {
63 HeaderForm::Raw => HeaderValue::Text(
64 std::str::from_utf8(bytes).unwrap_or_default().trim().into(),
65 ),
66 HeaderForm::Text => MessageStream::new(bytes).parse_unstructured(),
67 HeaderForm::Addresses => MessageStream::new(bytes).parse_address(),
68 HeaderForm::GroupedAddresses => {
69 MessageStream::new(bytes).parse_address()
70 }
71 HeaderForm::MessageIds => MessageStream::new(bytes).parse_id(),
72 HeaderForm::Date => MessageStream::new(bytes).parse_date(),
73 HeaderForm::URLs => MessageStream::new(bytes).parse_address(),
74 }),
75 );
76 }
77 }
78
79 results
80 }
81
82 pub fn headers(&self) -> &[Header<'x>] {
84 &self.parts[0].headers
85 }
86
87 pub fn header_values(
89 &self,
90 name: impl Into<HeaderName<'x>>,
91 ) -> impl Iterator<Item = &HeaderValue<'x>> + Sync + Send {
92 let name = name.into();
93 self.parts[0].headers.iter().filter_map(move |header| {
94 if header.name == name {
95 Some(&header.value)
96 } else {
97 None
98 }
99 })
100 }
101
102 pub fn headers_raw(&self) -> impl Iterator<Item = (&str, &str)> + Sync + Send {
104 self.parts[0].headers.iter().filter_map(move |header| {
105 Some((
106 header.name.as_str(),
107 std::str::from_utf8(
108 &self.raw_message[header.offset_start as usize..header.offset_end as usize],
109 )
110 .ok()?,
111 ))
112 })
113 }
114
115 pub fn raw_message(&self) -> &[u8] {
117 let part = &self.parts[0];
118 self.raw_message
119 .get(part.offset_header as usize..part.offset_end as usize)
120 .unwrap_or_default()
121 }
122
123 pub fn bcc(&self) -> Option<&Address<'x>> {
125 self.parts[0]
126 .headers
127 .header_value(&HeaderName::Bcc)
128 .and_then(|a| a.as_address())
129 }
130
131 pub fn cc(&self) -> Option<&Address<'x>> {
133 self.parts[0]
134 .headers
135 .header_value(&HeaderName::Cc)
136 .and_then(|a| a.as_address())
137 }
138
139 pub fn comments(&self) -> &HeaderValue<'x> {
141 self.parts[0]
142 .headers
143 .header_value(&HeaderName::Comments)
144 .unwrap_or(&HeaderValue::Empty)
145 }
146
147 pub fn date(&self) -> Option<&DateTime> {
149 self.parts[0]
150 .headers
151 .header_value(&HeaderName::Date)
152 .and_then(|header| header.as_datetime())
153 }
154
155 pub fn from(&self) -> Option<&Address<'x>> {
157 self.parts[0]
158 .headers
159 .header_value(&HeaderName::From)
160 .and_then(|a| a.as_address())
161 }
162
163 pub fn in_reply_to(&self) -> &HeaderValue<'x> {
165 self.parts[0]
166 .headers
167 .header_value(&HeaderName::InReplyTo)
168 .unwrap_or(&HeaderValue::Empty)
169 }
170
171 pub fn keywords(&self) -> &HeaderValue<'x> {
173 self.parts[0]
174 .headers
175 .header_value(&HeaderName::Keywords)
176 .unwrap_or(&HeaderValue::Empty)
177 }
178
179 pub fn list_archive(&self) -> &HeaderValue<'x> {
181 self.parts[0]
182 .headers
183 .header_value(&HeaderName::ListArchive)
184 .unwrap_or(&HeaderValue::Empty)
185 }
186
187 pub fn list_help(&self) -> &HeaderValue<'x> {
189 self.parts[0]
190 .headers
191 .header_value(&HeaderName::ListHelp)
192 .unwrap_or(&HeaderValue::Empty)
193 }
194
195 pub fn list_id(&self) -> &HeaderValue<'x> {
197 self.parts[0]
198 .headers
199 .header_value(&HeaderName::ListId)
200 .unwrap_or(&HeaderValue::Empty)
201 }
202
203 pub fn list_owner(&self) -> &HeaderValue<'x> {
205 self.parts[0]
206 .headers
207 .header_value(&HeaderName::ListOwner)
208 .unwrap_or(&HeaderValue::Empty)
209 }
210
211 pub fn list_post(&self) -> &HeaderValue<'x> {
213 self.parts[0]
214 .headers
215 .header_value(&HeaderName::ListPost)
216 .unwrap_or(&HeaderValue::Empty)
217 }
218
219 pub fn list_subscribe(&self) -> &HeaderValue<'x> {
221 self.parts[0]
222 .headers
223 .header_value(&HeaderName::ListSubscribe)
224 .unwrap_or(&HeaderValue::Empty)
225 }
226
227 pub fn list_unsubscribe(&self) -> &HeaderValue<'x> {
229 self.parts[0]
230 .headers
231 .header_value(&HeaderName::ListUnsubscribe)
232 .unwrap_or(&HeaderValue::Empty)
233 }
234
235 pub fn message_id(&self) -> Option<&str> {
237 self.parts[0]
238 .headers
239 .header_value(&HeaderName::MessageId)
240 .and_then(|header| header.as_text())
241 }
242
243 pub fn mime_version(&self) -> &HeaderValue<'x> {
245 self.parts[0]
246 .headers
247 .header_value(&HeaderName::MimeVersion)
248 .unwrap_or(&HeaderValue::Empty)
249 }
250
251 pub fn received(&self) -> Option<&Received<'x>> {
253 self.parts[0]
254 .headers
255 .header_value(&HeaderName::Received)
256 .and_then(|header| header.as_received())
257 }
258
259 pub fn references(&self) -> &HeaderValue<'x> {
261 self.parts[0]
262 .headers
263 .header_value(&HeaderName::References)
264 .unwrap_or(&HeaderValue::Empty)
265 }
266
267 pub fn reply_to(&self) -> Option<&Address<'x>> {
269 self.parts[0]
270 .headers
271 .header_value(&HeaderName::ReplyTo)
272 .and_then(|a| a.as_address())
273 }
274
275 pub fn resent_bcc(&self) -> Option<&Address<'x>> {
277 self.parts[0]
278 .headers
279 .header_value(&HeaderName::ResentBcc)
280 .and_then(|a| a.as_address())
281 }
282
283 pub fn resent_cc(&self) -> Option<&Address<'x>> {
285 self.parts[0]
286 .headers
287 .header_value(&HeaderName::ResentTo)
288 .and_then(|a| a.as_address())
289 }
290
291 pub fn resent_date(&self) -> &HeaderValue<'x> {
293 self.parts[0]
294 .headers
295 .header_value(&HeaderName::ResentDate)
296 .unwrap_or(&HeaderValue::Empty)
297 }
298
299 pub fn resent_from(&self) -> Option<&Address<'x>> {
301 self.parts[0]
302 .headers
303 .header_value(&HeaderName::ResentFrom)
304 .and_then(|a| a.as_address())
305 }
306
307 pub fn resent_message_id(&self) -> &HeaderValue<'x> {
309 self.parts[0]
310 .headers
311 .header_value(&HeaderName::ResentMessageId)
312 .unwrap_or(&HeaderValue::Empty)
313 }
314
315 pub fn resent_sender(&self) -> Option<&Address<'x>> {
317 self.parts[0]
318 .headers
319 .header_value(&HeaderName::ResentSender)
320 .and_then(|a| a.as_address())
321 }
322
323 pub fn resent_to(&self) -> Option<&Address<'x>> {
325 self.parts[0]
326 .headers
327 .header_value(&HeaderName::ResentTo)
328 .and_then(|a| a.as_address())
329 }
330
331 pub fn return_path(&self) -> &HeaderValue<'x> {
333 self.parts[0]
334 .headers
335 .header_value(&HeaderName::ReturnPath)
336 .unwrap_or(&HeaderValue::Empty)
337 }
338
339 pub fn return_address(&self) -> Option<&str> {
342 match self.parts[0].headers.header_value(&HeaderName::ReturnPath) {
343 Some(HeaderValue::Text(text)) => Some(text.as_ref()),
344 Some(HeaderValue::TextList(text_list)) => text_list.last().map(|t| t.as_ref()),
345 _ => match self.parts[0].headers.header_value(&HeaderName::From) {
346 Some(HeaderValue::Address(addr)) => addr.first()?.address.as_deref(),
347 _ => None,
348 },
349 }
350 }
351
352 pub fn sender(&self) -> Option<&Address<'x>> {
354 self.parts[0]
355 .headers
356 .header_value(&HeaderName::Sender)
357 .and_then(|a| a.as_address())
358 }
359
360 pub fn subject(&self) -> Option<&str> {
362 self.parts[0]
363 .headers
364 .header_value(&HeaderName::Subject)
365 .and_then(|header| header.as_text())
366 }
367
368 pub fn thread_name(&self) -> Option<&str> {
371 thread_name(self.subject()?).into()
372 }
373
374 pub fn to(&self) -> Option<&Address<'x>> {
376 self.parts[0]
377 .headers
378 .header_value(&HeaderName::To)
379 .and_then(|a| a.as_address())
380 }
381
382 pub fn body_preview(&self, preview_len: usize) -> Option<Cow<'x, str>> {
384 if !self.text_body.is_empty() {
385 preview_text(self.body_text(0)?, preview_len).into()
386 } else if !self.html_body.is_empty() {
387 preview_html(self.body_html(0)?, preview_len).into()
388 } else {
389 None
390 }
391 }
392
393 pub fn body_html(&'x self, pos: usize) -> Option<Cow<'x, str>> {
395 let part = self.parts.get(*self.html_body.get(pos)? as usize)?;
396 match &part.body {
397 PartType::Html(html) => Some(html.as_ref().into()),
398 PartType::Text(text) => Some(text_to_html(text.as_ref()).into()),
399 _ => None,
400 }
401 }
402
403 pub fn body_text(&'x self, pos: usize) -> Option<Cow<'x, str>> {
405 let part = self.parts.get(*self.text_body.get(pos)? as usize)?;
406 match &part.body {
407 PartType::Text(text) => Some(text.as_ref().into()),
408 PartType::Html(html) => Some(html_to_text(html.as_ref()).into()),
409 _ => None,
410 }
411 }
412
413 pub fn part(&self, pos: u32) -> Option<&MessagePart<'x>> {
415 self.parts.get(pos as usize)
416 }
417
418 pub fn html_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
420 self.parts.get(*self.html_body.get(pos as usize)? as usize)
421 }
422
423 pub fn text_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
425 self.parts.get(*self.text_body.get(pos as usize)? as usize)
426 }
427
428 pub fn attachment(&self, pos: u32) -> Option<&MessagePart<'x>> {
430 self.parts
431 .get(*self.attachments.get(pos as usize)? as usize)
432 }
433
434 pub fn text_body_count(&self) -> usize {
436 self.text_body.len()
437 }
438
439 pub fn html_body_count(&self) -> usize {
441 self.html_body.len()
442 }
443
444 pub fn attachment_count(&self) -> usize {
446 self.attachments.len()
447 }
448
449 pub fn text_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
451 BodyPartIterator::new(self, &self.text_body)
452 }
453
454 pub fn html_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
456 BodyPartIterator::new(self, &self.html_body)
457 }
458
459 pub fn attachments(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
461 AttachmentIterator::new(self)
462 }
463
464 pub fn into_owned(self) -> Message<'static> {
466 Message {
467 html_body: self.html_body,
468 text_body: self.text_body,
469 attachments: self.attachments,
470 parts: self.parts.into_iter().map(|p| p.into_owned()).collect(),
471 raw_message: self.raw_message.into_owned().into(),
472 }
473 }
474}
475
476impl<'x> TryInto<Message<'x>> for &'x [u8] {
477 type Error = ();
478
479 fn try_into(self) -> Result<Message<'x>, Self::Error> {
480 MessageParser::default().parse(self).ok_or(())
481 }
482}