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 all_bcc(&self) -> impl Iterator<Item = &Address<'x>> {
133 self.header_values(HeaderName::Bcc)
134 .filter_map(|a| a.as_address())
135 }
136
137 pub fn cc(&self) -> Option<&Address<'x>> {
139 self.parts[0]
140 .headers
141 .header_value(&HeaderName::Cc)
142 .and_then(|a| a.as_address())
143 }
144
145 pub fn all_cc(&self) -> impl Iterator<Item = &Address<'x>> {
147 self.header_values(HeaderName::Cc)
148 .filter_map(|a| a.as_address())
149 }
150
151 pub fn comments(&self) -> &HeaderValue<'x> {
153 self.parts[0]
154 .headers
155 .header_value(&HeaderName::Comments)
156 .unwrap_or(&HeaderValue::Empty)
157 }
158
159 pub fn date(&self) -> Option<&DateTime> {
161 self.parts[0]
162 .headers
163 .header_value(&HeaderName::Date)
164 .and_then(|header| header.as_datetime())
165 }
166
167 pub fn from(&self) -> Option<&Address<'x>> {
169 self.parts[0]
170 .headers
171 .header_value(&HeaderName::From)
172 .and_then(|a| a.as_address())
173 }
174
175 pub fn in_reply_to(&self) -> &HeaderValue<'x> {
177 self.parts[0]
178 .headers
179 .header_value(&HeaderName::InReplyTo)
180 .unwrap_or(&HeaderValue::Empty)
181 }
182
183 pub fn keywords(&self) -> &HeaderValue<'x> {
185 self.parts[0]
186 .headers
187 .header_value(&HeaderName::Keywords)
188 .unwrap_or(&HeaderValue::Empty)
189 }
190
191 pub fn list_archive(&self) -> &HeaderValue<'x> {
193 self.parts[0]
194 .headers
195 .header_value(&HeaderName::ListArchive)
196 .unwrap_or(&HeaderValue::Empty)
197 }
198
199 pub fn list_help(&self) -> &HeaderValue<'x> {
201 self.parts[0]
202 .headers
203 .header_value(&HeaderName::ListHelp)
204 .unwrap_or(&HeaderValue::Empty)
205 }
206
207 pub fn list_id(&self) -> &HeaderValue<'x> {
209 self.parts[0]
210 .headers
211 .header_value(&HeaderName::ListId)
212 .unwrap_or(&HeaderValue::Empty)
213 }
214
215 pub fn list_owner(&self) -> &HeaderValue<'x> {
217 self.parts[0]
218 .headers
219 .header_value(&HeaderName::ListOwner)
220 .unwrap_or(&HeaderValue::Empty)
221 }
222
223 pub fn list_post(&self) -> &HeaderValue<'x> {
225 self.parts[0]
226 .headers
227 .header_value(&HeaderName::ListPost)
228 .unwrap_or(&HeaderValue::Empty)
229 }
230
231 pub fn list_subscribe(&self) -> &HeaderValue<'x> {
233 self.parts[0]
234 .headers
235 .header_value(&HeaderName::ListSubscribe)
236 .unwrap_or(&HeaderValue::Empty)
237 }
238
239 pub fn list_unsubscribe(&self) -> &HeaderValue<'x> {
241 self.parts[0]
242 .headers
243 .header_value(&HeaderName::ListUnsubscribe)
244 .unwrap_or(&HeaderValue::Empty)
245 }
246
247 pub fn message_id(&self) -> Option<&str> {
249 self.parts[0]
250 .headers
251 .header_value(&HeaderName::MessageId)
252 .and_then(|header| header.as_text())
253 }
254
255 pub fn mime_version(&self) -> &HeaderValue<'x> {
257 self.parts[0]
258 .headers
259 .header_value(&HeaderName::MimeVersion)
260 .unwrap_or(&HeaderValue::Empty)
261 }
262
263 pub fn received(&self) -> Option<&Received<'x>> {
265 self.parts[0]
266 .headers
267 .header_value(&HeaderName::Received)
268 .and_then(|header| header.as_received())
269 }
270
271 pub fn received_all(&self) -> impl Iterator<Item = &Received<'x>> + Sync + Send {
274 self.header_values(HeaderName::Received)
275 .filter_map(|header| header.as_received())
276 }
277
278 pub fn references(&self) -> &HeaderValue<'x> {
280 self.parts[0]
281 .headers
282 .header_value(&HeaderName::References)
283 .unwrap_or(&HeaderValue::Empty)
284 }
285
286 pub fn reply_to(&self) -> Option<&Address<'x>> {
288 self.parts[0]
289 .headers
290 .header_value(&HeaderName::ReplyTo)
291 .and_then(|a| a.as_address())
292 }
293
294 pub fn resent_bcc(&self) -> Option<&Address<'x>> {
296 self.parts[0]
297 .headers
298 .header_value(&HeaderName::ResentBcc)
299 .and_then(|a| a.as_address())
300 }
301
302 pub fn resent_cc(&self) -> Option<&Address<'x>> {
304 self.parts[0]
305 .headers
306 .header_value(&HeaderName::ResentTo)
307 .and_then(|a| a.as_address())
308 }
309
310 pub fn resent_date(&self) -> &HeaderValue<'x> {
312 self.parts[0]
313 .headers
314 .header_value(&HeaderName::ResentDate)
315 .unwrap_or(&HeaderValue::Empty)
316 }
317
318 pub fn resent_from(&self) -> Option<&Address<'x>> {
320 self.parts[0]
321 .headers
322 .header_value(&HeaderName::ResentFrom)
323 .and_then(|a| a.as_address())
324 }
325
326 pub fn resent_message_id(&self) -> &HeaderValue<'x> {
328 self.parts[0]
329 .headers
330 .header_value(&HeaderName::ResentMessageId)
331 .unwrap_or(&HeaderValue::Empty)
332 }
333
334 pub fn resent_sender(&self) -> Option<&Address<'x>> {
336 self.parts[0]
337 .headers
338 .header_value(&HeaderName::ResentSender)
339 .and_then(|a| a.as_address())
340 }
341
342 pub fn resent_to(&self) -> Option<&Address<'x>> {
344 self.parts[0]
345 .headers
346 .header_value(&HeaderName::ResentTo)
347 .and_then(|a| a.as_address())
348 }
349
350 pub fn return_path(&self) -> &HeaderValue<'x> {
352 self.parts[0]
353 .headers
354 .header_value(&HeaderName::ReturnPath)
355 .unwrap_or(&HeaderValue::Empty)
356 }
357
358 pub fn return_address(&self) -> Option<&str> {
361 match self.parts[0].headers.header_value(&HeaderName::ReturnPath) {
362 Some(HeaderValue::Text(text)) => Some(text.as_ref()),
363 Some(HeaderValue::TextList(text_list)) => text_list.last().map(|t| t.as_ref()),
364 _ => match self.parts[0].headers.header_value(&HeaderName::From) {
365 Some(HeaderValue::Address(addr)) => addr.first()?.address.as_deref(),
366 _ => None,
367 },
368 }
369 }
370
371 pub fn sender(&self) -> Option<&Address<'x>> {
373 self.parts[0]
374 .headers
375 .header_value(&HeaderName::Sender)
376 .and_then(|a| a.as_address())
377 }
378
379 pub fn subject(&self) -> Option<&str> {
381 self.parts[0]
382 .headers
383 .header_value(&HeaderName::Subject)
384 .and_then(|header| header.as_text())
385 }
386
387 pub fn thread_name(&self) -> Option<&str> {
390 thread_name(self.subject()?).into()
391 }
392
393 pub fn to(&self) -> Option<&Address<'x>> {
395 self.parts[0]
396 .headers
397 .header_value(&HeaderName::To)
398 .and_then(|a| a.as_address())
399 }
400
401 pub fn all_to(&self) -> impl Iterator<Item = &Address<'x>> {
403 self.header_values(HeaderName::To)
404 .filter_map(|a| a.as_address())
405 }
406
407 pub fn body_preview(&self, preview_len: usize) -> Option<Cow<'x, str>> {
409 if !self.text_body.is_empty() {
410 preview_text(self.body_text(0)?, preview_len).into()
411 } else if !self.html_body.is_empty() {
412 preview_html(self.body_html(0)?, preview_len).into()
413 } else {
414 None
415 }
416 }
417
418 pub fn body_html(&'x self, pos: usize) -> Option<Cow<'x, str>> {
420 let part = self.parts.get(*self.html_body.get(pos)? as usize)?;
421 match &part.body {
422 PartType::Html(html) => Some(html.as_ref().into()),
423 PartType::Text(text) => Some(text_to_html(text.as_ref()).into()),
424 _ => None,
425 }
426 }
427
428 pub fn body_text(&'x self, pos: usize) -> Option<Cow<'x, str>> {
430 let part = self.parts.get(*self.text_body.get(pos)? as usize)?;
431 match &part.body {
432 PartType::Text(text) => Some(text.as_ref().into()),
433 PartType::Html(html) => Some(html_to_text(html.as_ref()).into()),
434 _ => None,
435 }
436 }
437
438 pub fn part(&self, pos: u32) -> Option<&MessagePart<'x>> {
440 self.parts.get(pos as usize)
441 }
442
443 pub fn html_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
445 self.parts.get(*self.html_body.get(pos as usize)? as usize)
446 }
447
448 pub fn text_part(&self, pos: u32) -> Option<&MessagePart<'x>> {
450 self.parts.get(*self.text_body.get(pos as usize)? as usize)
451 }
452
453 pub fn attachment(&self, pos: u32) -> Option<&MessagePart<'x>> {
455 self.parts
456 .get(*self.attachments.get(pos as usize)? as usize)
457 }
458
459 pub fn text_body_count(&self) -> usize {
461 self.text_body.len()
462 }
463
464 pub fn html_body_count(&self) -> usize {
466 self.html_body.len()
467 }
468
469 pub fn attachment_count(&self) -> usize {
471 self.attachments.len()
472 }
473
474 pub fn text_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
476 BodyPartIterator::new(self, &self.text_body)
477 }
478
479 pub fn html_bodies(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
481 BodyPartIterator::new(self, &self.html_body)
482 }
483
484 pub fn attachments(&self) -> impl Iterator<Item = &'_ MessagePart<'_>> + Sync + Send {
486 AttachmentIterator::new(self)
487 }
488
489 pub fn into_owned(self) -> Message<'static> {
491 Message {
492 html_body: self.html_body,
493 text_body: self.text_body,
494 attachments: self.attachments,
495 parts: self.parts.into_iter().map(|p| p.into_owned()).collect(),
496 raw_message: self.raw_message.into_owned().into(),
497 }
498 }
499}
500
501impl<'x> TryInto<Message<'x>> for &'x [u8] {
502 type Error = ();
503
504 fn try_into(self) -> Result<Message<'x>, Self::Error> {
505 MessageParser::default().parse(self).ok_or(())
506 }
507}