mail_parser/core/
message.rs1use std::{borrow::Cow, convert::TryInto};
8
9use crate::{
10 Address, AttachmentIterator, BodyPartIterator, DateTime, GetHeader, Header, HeaderForm,
11 HeaderName, HeaderValue, Message, MessageParser, MessagePart, PartType, Received,
12 decoders::html::{html_to_text, text_to_html},
13 parsers::{
14 MessageStream,
15 fields::thread::thread_name,
16 preview::{preview_html, preview_text},
17 },
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 received_all(&self) -> impl Iterator<Item = &Received<'x>> + Sync + Send {
262 self.header_values(HeaderName::Received)
263 .filter_map(|header| header.as_received())
264 }
265
266 pub fn references(&self) -> &HeaderValue<'x> {
268 self.parts[0]
269 .headers
270 .header_value(&HeaderName::References)
271 .unwrap_or(&HeaderValue::Empty)
272 }
273
274 pub fn reply_to(&self) -> Option<&Address<'x>> {
276 self.parts[0]
277 .headers
278 .header_value(&HeaderName::ReplyTo)
279 .and_then(|a| a.as_address())
280 }
281
282 pub fn resent_bcc(&self) -> Option<&Address<'x>> {
284 self.parts[0]
285 .headers
286 .header_value(&HeaderName::ResentBcc)
287 .and_then(|a| a.as_address())
288 }
289
290 pub fn resent_cc(&self) -> Option<&Address<'x>> {
292 self.parts[0]
293 .headers
294 .header_value(&HeaderName::ResentTo)
295 .and_then(|a| a.as_address())
296 }
297
298 pub fn resent_date(&self) -> &HeaderValue<'x> {
300 self.parts[0]
301 .headers
302 .header_value(&HeaderName::ResentDate)
303 .unwrap_or(&HeaderValue::Empty)
304 }
305
306 pub fn resent_from(&self) -> Option<&Address<'x>> {
308 self.parts[0]
309 .headers
310 .header_value(&HeaderName::ResentFrom)
311 .and_then(|a| a.as_address())
312 }
313
314 pub fn resent_message_id(&self) -> &HeaderValue<'x> {
316 self.parts[0]
317 .headers
318 .header_value(&HeaderName::ResentMessageId)
319 .unwrap_or(&HeaderValue::Empty)
320 }
321
322 pub fn resent_sender(&self) -> Option<&Address<'x>> {
324 self.parts[0]
325 .headers
326 .header_value(&HeaderName::ResentSender)
327 .and_then(|a| a.as_address())
328 }
329
330 pub fn resent_to(&self) -> Option<&Address<'x>> {
332 self.parts[0]
333 .headers
334 .header_value(&HeaderName::ResentTo)
335 .and_then(|a| a.as_address())
336 }
337
338 pub fn return_path(&self) -> &HeaderValue<'x> {
340 self.parts[0]
341 .headers
342 .header_value(&HeaderName::ReturnPath)
343 .unwrap_or(&HeaderValue::Empty)
344 }
345
346 pub fn return_address(&self) -> Option<&str> {
349 match self.parts[0].headers.header_value(&HeaderName::ReturnPath) {
350 Some(HeaderValue::Text(text)) => Some(text.as_ref()),
351 Some(HeaderValue::TextList(text_list)) => text_list.last().map(|t| t.as_ref()),
352 _ => match self.parts[0].headers.header_value(&HeaderName::From) {
353 Some(HeaderValue::Address(addr)) => addr.first()?.address.as_deref(),
354 _ => None,
355 },
356 }
357 }
358
359 pub fn sender(&self) -> Option<&Address<'x>> {
361 self.parts[0]
362 .headers
363 .header_value(&HeaderName::Sender)
364 .and_then(|a| a.as_address())
365 }
366
367 pub fn subject(&self) -> Option<&str> {
369 self.parts[0]
370 .headers
371 .header_value(&HeaderName::Subject)
372 .and_then(|header| header.as_text())
373 }
374
375 pub fn thread_name(&self) -> Option<&str> {
378 thread_name(self.subject()?).into()
379 }
380
381 pub fn to(&self) -> Option<&Address<'x>> {
383 self.parts[0]
384 .headers
385 .header_value(&HeaderName::To)
386 .and_then(|a| a.as_address())
387 }
388
389 pub fn body_preview(&self, preview_len: usize) -> Option<Cow<'x, str>> {
391 if !self.text_body.is_empty() {
392 preview_text(self.body_text(0)?, preview_len).into()
393 } else if !self.html_body.is_empty() {
394 preview_html(self.body_html(0)?, preview_len).into()
395 } else {
396 None
397 }
398 }
399
400 pub fn body_html(&'x self, pos: usize) -> Option<Cow<'x, str>> {
402 let part = self.parts.get(*self.html_body.get(pos)? as usize)?;
403 match &part.body {
404 PartType::Html(html) => Some(html.as_ref().into()),
405 PartType::Text(text) => Some(text_to_html(text.as_ref()).into()),
406 _ => None,
407 }
408 }
409
410 pub fn body_text(&'x self, pos: usize) -> Option<Cow<'x, str>> {
412 let part = self.parts.get(*self.text_body.get(pos)? as usize)?;
413 match &part.body {
414 PartType::Text(text) => Some(text.as_ref().into()),
415 PartType::Html(html) => Some(html_to_text(html.as_ref()).into()),
416 _ => None,
417 }
418 }
419
420 pub fn part(&self, pos: u32) -> Option<&MessagePart<'x>> {
422 self.parts.get(pos as usize)
423 }
424
425 pub fn html_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
427 self.parts.get(*self.html_body.get(pos as usize)? as usize)
428 }
429
430 pub fn text_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
432 self.parts.get(*self.text_body.get(pos as usize)? as usize)
433 }
434
435 pub fn attachment(&self, pos: u32) -> Option<&MessagePart<'x>> {
437 self.parts
438 .get(*self.attachments.get(pos as usize)? as usize)
439 }
440
441 pub fn text_body_count(&self) -> usize {
443 self.text_body.len()
444 }
445
446 pub fn html_body_count(&self) -> usize {
448 self.html_body.len()
449 }
450
451 pub fn attachment_count(&self) -> usize {
453 self.attachments.len()
454 }
455
456 pub fn text_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
458 BodyPartIterator::new(self, &self.text_body)
459 }
460
461 pub fn html_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
463 BodyPartIterator::new(self, &self.html_body)
464 }
465
466 pub fn attachments(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
468 AttachmentIterator::new(self)
469 }
470
471 pub fn into_owned(self) -> Message<'static> {
473 Message {
474 html_body: self.html_body,
475 text_body: self.text_body,
476 attachments: self.attachments,
477 parts: self.parts.into_iter().map(|p| p.into_owned()).collect(),
478 raw_message: self.raw_message.into_owned().into(),
479 }
480 }
481}
482
483impl<'x> TryInto<Message<'x>> for &'x [u8] {
484 type Error = ();
485
486 fn try_into(self) -> Result<Message<'x>, Self::Error> {
487 MessageParser::default().parse(self).ok_or(())
488 }
489}