1use core::fmt;
8use std::hash::Hash;
9use std::net::IpAddr;
10use std::{borrow::Cow, fmt::Display};
11
12use crate::{
13 Address, Attribute, ContentType, DateTime, GetHeader, Greeting, Header, HeaderName,
14 HeaderValue, Host, Message, MessagePart, MessagePartId, MimeHeaders, PartType, Protocol,
15 Received, TlsVersion,
16};
17
18impl<'x> Header<'x> {
19 pub fn name(&self) -> &str {
21 self.name.as_str()
22 }
23
24 pub fn value(&self) -> &HeaderValue<'x> {
26 &self.value
27 }
28
29 pub fn offset_start(&self) -> u32 {
31 self.offset_start
32 }
33
34 pub fn offset_end(&self) -> u32 {
36 self.offset_end
37 }
38
39 pub fn offset_field(&self) -> u32 {
41 self.offset_field
42 }
43
44 pub fn into_owned(self) -> Header<'static> {
46 Header {
47 name: self.name.into_owned(),
48 value: self.value.into_owned(),
49 offset_field: self.offset_field,
50 offset_start: self.offset_start,
51 offset_end: self.offset_end,
52 }
53 }
54}
55
56impl<'x> HeaderValue<'x> {
57 pub fn is_empty(&self) -> bool {
58 *self == HeaderValue::Empty
59 }
60
61 pub fn unwrap_text(self) -> Cow<'x, str> {
62 match self {
63 HeaderValue::Text(s) => s,
64 _ => panic!("HeaderValue::unwrap_text called on non-Text value"),
65 }
66 }
67
68 pub fn unwrap_text_list(self) -> Vec<Cow<'x, str>> {
69 match self {
70 HeaderValue::TextList(l) => l,
71 HeaderValue::Text(s) => vec![s],
72 _ => panic!("HeaderValue::unwrap_text_list called on non-TextList value"),
73 }
74 }
75
76 pub fn unwrap_datetime(self) -> DateTime {
77 match self {
78 HeaderValue::DateTime(d) => d,
79 _ => panic!("HeaderValue::unwrap_datetime called on non-DateTime value"),
80 }
81 }
82
83 pub fn unwrap_address(self) -> Address<'x> {
84 match self {
85 HeaderValue::Address(a) => a,
86 _ => panic!("HeaderValue::unwrap_address called on non-Address value"),
87 }
88 }
89
90 pub fn unwrap_content_type(self) -> ContentType<'x> {
91 match self {
92 HeaderValue::ContentType(c) => c,
93 _ => panic!("HeaderValue::unwrap_content_type called on non-ContentType value"),
94 }
95 }
96
97 pub fn unwrap_received(self) -> Received<'x> {
98 match self {
99 HeaderValue::Received(r) => *r,
100 _ => panic!("HeaderValue::unwrap_received called on non-Received value"),
101 }
102 }
103
104 pub fn into_text(self) -> Option<Cow<'x, str>> {
105 match self {
106 HeaderValue::Text(s) => Some(s),
107 _ => None,
108 }
109 }
110
111 pub fn into_text_list(self) -> Option<Vec<Cow<'x, str>>> {
112 match self {
113 HeaderValue::Text(s) => Some(vec![s]),
114 HeaderValue::TextList(l) => Some(l),
115 _ => None,
116 }
117 }
118
119 pub fn into_address(self) -> Option<Address<'x>> {
120 match self {
121 HeaderValue::Address(a) => Some(a),
122 _ => None,
123 }
124 }
125
126 pub fn into_datetime(self) -> Option<DateTime> {
127 match self {
128 HeaderValue::DateTime(d) => Some(d),
129 _ => None,
130 }
131 }
132
133 pub fn into_content_type(self) -> Option<ContentType<'x>> {
134 match self {
135 HeaderValue::ContentType(c) => Some(c),
136 _ => None,
137 }
138 }
139
140 pub fn into_received(self) -> Option<Received<'x>> {
141 match self {
142 HeaderValue::Received(r) => Some(*r),
143 _ => None,
144 }
145 }
146
147 pub fn as_text(&self) -> Option<&str> {
148 match *self {
149 HeaderValue::Text(ref s) => Some(s),
150 HeaderValue::TextList(ref l) => l.last()?.as_ref().into(),
151 _ => None,
152 }
153 }
154
155 pub fn as_text_list(&self) -> Option<&[Cow<'x, str>]> {
156 match *self {
157 HeaderValue::Text(ref s) => Some(std::slice::from_ref(s)),
158 HeaderValue::TextList(ref l) => Some(l.as_slice()),
159 _ => None,
160 }
161 }
162
163 pub fn as_address(&self) -> Option<&Address<'x>> {
164 match *self {
165 HeaderValue::Address(ref a) => Some(a),
166 _ => None,
167 }
168 }
169
170 pub fn as_received(&self) -> Option<&Received<'x>> {
171 match *self {
172 HeaderValue::Received(ref r) => Some(r),
173 _ => None,
174 }
175 }
176
177 pub fn as_content_type(&self) -> Option<&ContentType<'x>> {
178 match *self {
179 HeaderValue::ContentType(ref c) => Some(c),
180 _ => None,
181 }
182 }
183
184 pub fn as_datetime(&self) -> Option<&DateTime> {
185 match *self {
186 HeaderValue::DateTime(ref d) => Some(d),
187 _ => None,
188 }
189 }
190
191 pub fn into_owned(self) -> HeaderValue<'static> {
192 match self {
193 HeaderValue::Address(addr) => HeaderValue::Address(addr.into_owned()),
194 HeaderValue::Text(text) => HeaderValue::Text(text.into_owned().into()),
195 HeaderValue::TextList(list) => HeaderValue::TextList(
196 list.into_iter()
197 .map(|text| text.into_owned().into())
198 .collect(),
199 ),
200 HeaderValue::DateTime(datetime) => HeaderValue::DateTime(datetime),
201 HeaderValue::ContentType(ct) => HeaderValue::ContentType(ContentType {
202 c_type: ct.c_type.into_owned().into(),
203 c_subtype: ct.c_subtype.map(|s| s.into_owned().into()),
204 attributes: ct.attributes.map(|attributes| {
205 attributes
206 .into_iter()
207 .map(|a| Attribute {
208 name: a.name.into_owned().into(),
209 value: a.value.into_owned().into(),
210 })
211 .collect()
212 }),
213 }),
214 HeaderValue::Received(rcvd) => HeaderValue::Received(Box::new(rcvd.into_owned())),
215 HeaderValue::Empty => HeaderValue::Empty,
216 }
217 }
218
219 pub fn len(&self) -> usize {
220 match self {
221 HeaderValue::Text(text) => text.len(),
222 HeaderValue::TextList(list) => list.iter().map(|t| t.len()).sum(),
223 HeaderValue::Address(Address::List(list)) => list
224 .iter()
225 .map(|a| {
226 a.name.as_ref().map_or(0, |a| a.len())
227 + a.address.as_ref().map_or(0, |a| a.len())
228 })
229 .sum(),
230 HeaderValue::Address(Address::Group(grouplist)) => grouplist
231 .iter()
232 .flat_map(|g| g.addresses.iter())
233 .map(|a| {
234 a.name.as_ref().map_or(0, |a| a.len())
235 + a.address.as_ref().map_or(0, |a| a.len())
236 })
237 .sum(),
238 HeaderValue::DateTime(_) => 24,
239 HeaderValue::ContentType(ct) => {
240 ct.c_type.len()
241 + ct.c_subtype.as_ref().map_or(0, |s| s.len())
242 + ct.attributes.as_ref().map_or(0, |at| {
243 at.iter().map(|a| a.name.len() + a.value.len()).sum()
244 })
245 }
246 HeaderValue::Received(_) => 1,
247 HeaderValue::Empty => 0,
248 }
249 }
250}
251
252impl PartialEq for HeaderName<'_> {
253 fn eq(&self, other: &Self) -> bool {
254 match (self, other) {
255 (Self::Other(a), Self::Other(b)) => a.eq_ignore_ascii_case(b),
256 (Self::Subject, Self::Subject) => true,
257 (Self::From, Self::From) => true,
258 (Self::To, Self::To) => true,
259 (Self::Cc, Self::Cc) => true,
260 (Self::Date, Self::Date) => true,
261 (Self::Bcc, Self::Bcc) => true,
262 (Self::ReplyTo, Self::ReplyTo) => true,
263 (Self::Sender, Self::Sender) => true,
264 (Self::Comments, Self::Comments) => true,
265 (Self::InReplyTo, Self::InReplyTo) => true,
266 (Self::Keywords, Self::Keywords) => true,
267 (Self::Received, Self::Received) => true,
268 (Self::MessageId, Self::MessageId) => true,
269 (Self::References, Self::References) => true,
270 (Self::ReturnPath, Self::ReturnPath) => true,
271 (Self::MimeVersion, Self::MimeVersion) => true,
272 (Self::ContentDescription, Self::ContentDescription) => true,
273 (Self::ContentId, Self::ContentId) => true,
274 (Self::ContentLanguage, Self::ContentLanguage) => true,
275 (Self::ContentLocation, Self::ContentLocation) => true,
276 (Self::ContentTransferEncoding, Self::ContentTransferEncoding) => true,
277 (Self::ContentType, Self::ContentType) => true,
278 (Self::ContentDisposition, Self::ContentDisposition) => true,
279 (Self::ResentTo, Self::ResentTo) => true,
280 (Self::ResentFrom, Self::ResentFrom) => true,
281 (Self::ResentBcc, Self::ResentBcc) => true,
282 (Self::ResentCc, Self::ResentCc) => true,
283 (Self::ResentSender, Self::ResentSender) => true,
284 (Self::ResentDate, Self::ResentDate) => true,
285 (Self::ResentMessageId, Self::ResentMessageId) => true,
286 (Self::ListArchive, Self::ListArchive) => true,
287 (Self::ListHelp, Self::ListHelp) => true,
288 (Self::ListId, Self::ListId) => true,
289 (Self::ListOwner, Self::ListOwner) => true,
290 (Self::ListPost, Self::ListPost) => true,
291 (Self::ListSubscribe, Self::ListSubscribe) => true,
292 (Self::ListUnsubscribe, Self::ListUnsubscribe) => true,
293 (Self::ArcAuthenticationResults, Self::ArcAuthenticationResults) => true,
294 (Self::ArcMessageSignature, Self::ArcMessageSignature) => true,
295 (Self::ArcSeal, Self::ArcSeal) => true,
296 (Self::DkimSignature, Self::DkimSignature) => true,
297 (Self::Dkim2Signature, Self::Dkim2Signature) => true,
298 (Self::MessageInstance, Self::MessageInstance) => true,
299 _ => false,
300 }
301 }
302}
303
304impl Hash for HeaderName<'_> {
305 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
306 match self {
307 HeaderName::Other(value) => {
308 for ch in value.as_bytes() {
309 ch.to_ascii_lowercase().hash(state)
310 }
311 }
312 _ => self.id().hash(state),
313 }
314 }
315}
316
317impl Eq for HeaderName<'_> {}
318
319impl<'x> From<HeaderName<'x>> for u8 {
320 fn from(name: HeaderName<'x>) -> Self {
321 name.id()
322 }
323}
324
325impl HeaderName<'_> {
326 pub fn to_owned(&self) -> HeaderName<'static> {
327 match self {
328 HeaderName::Other(name) => HeaderName::Other(name.to_string().into()),
329 HeaderName::Subject => HeaderName::Subject,
330 HeaderName::From => HeaderName::From,
331 HeaderName::To => HeaderName::To,
332 HeaderName::Cc => HeaderName::Cc,
333 HeaderName::Date => HeaderName::Date,
334 HeaderName::Bcc => HeaderName::Bcc,
335 HeaderName::ReplyTo => HeaderName::ReplyTo,
336 HeaderName::Sender => HeaderName::Sender,
337 HeaderName::Comments => HeaderName::Comments,
338 HeaderName::InReplyTo => HeaderName::InReplyTo,
339 HeaderName::Keywords => HeaderName::Keywords,
340 HeaderName::Received => HeaderName::Received,
341 HeaderName::MessageId => HeaderName::MessageId,
342 HeaderName::References => HeaderName::References,
343 HeaderName::ReturnPath => HeaderName::ReturnPath,
344 HeaderName::MimeVersion => HeaderName::MimeVersion,
345 HeaderName::ContentDescription => HeaderName::ContentDescription,
346 HeaderName::ContentId => HeaderName::ContentId,
347 HeaderName::ContentLanguage => HeaderName::ContentLanguage,
348 HeaderName::ContentLocation => HeaderName::ContentLocation,
349 HeaderName::ContentTransferEncoding => HeaderName::ContentTransferEncoding,
350 HeaderName::ContentType => HeaderName::ContentType,
351 HeaderName::ContentDisposition => HeaderName::ContentDisposition,
352 HeaderName::ResentTo => HeaderName::ResentTo,
353 HeaderName::ResentFrom => HeaderName::ResentFrom,
354 HeaderName::ResentBcc => HeaderName::ResentBcc,
355 HeaderName::ResentCc => HeaderName::ResentCc,
356 HeaderName::ResentSender => HeaderName::ResentSender,
357 HeaderName::ResentDate => HeaderName::ResentDate,
358 HeaderName::ResentMessageId => HeaderName::ResentMessageId,
359 HeaderName::ListArchive => HeaderName::ListArchive,
360 HeaderName::ListHelp => HeaderName::ListHelp,
361 HeaderName::ListId => HeaderName::ListId,
362 HeaderName::ListOwner => HeaderName::ListOwner,
363 HeaderName::ListPost => HeaderName::ListPost,
364 HeaderName::ListSubscribe => HeaderName::ListSubscribe,
365 HeaderName::ListUnsubscribe => HeaderName::ListUnsubscribe,
366 HeaderName::ArcAuthenticationResults => HeaderName::ArcAuthenticationResults,
367 HeaderName::ArcMessageSignature => HeaderName::ArcMessageSignature,
368 HeaderName::ArcSeal => HeaderName::ArcSeal,
369 HeaderName::DkimSignature => HeaderName::DkimSignature,
370 HeaderName::Dkim2Signature => HeaderName::Dkim2Signature,
371 HeaderName::MessageInstance => HeaderName::MessageInstance,
372 }
373 }
374
375 pub fn into_owned(self) -> HeaderName<'static> {
376 match self {
377 HeaderName::Other(name) => HeaderName::Other(name.into_owned().into()),
378 HeaderName::Subject => HeaderName::Subject,
379 HeaderName::From => HeaderName::From,
380 HeaderName::To => HeaderName::To,
381 HeaderName::Cc => HeaderName::Cc,
382 HeaderName::Date => HeaderName::Date,
383 HeaderName::Bcc => HeaderName::Bcc,
384 HeaderName::ReplyTo => HeaderName::ReplyTo,
385 HeaderName::Sender => HeaderName::Sender,
386 HeaderName::Comments => HeaderName::Comments,
387 HeaderName::InReplyTo => HeaderName::InReplyTo,
388 HeaderName::Keywords => HeaderName::Keywords,
389 HeaderName::Received => HeaderName::Received,
390 HeaderName::MessageId => HeaderName::MessageId,
391 HeaderName::References => HeaderName::References,
392 HeaderName::ReturnPath => HeaderName::ReturnPath,
393 HeaderName::MimeVersion => HeaderName::MimeVersion,
394 HeaderName::ContentDescription => HeaderName::ContentDescription,
395 HeaderName::ContentId => HeaderName::ContentId,
396 HeaderName::ContentLanguage => HeaderName::ContentLanguage,
397 HeaderName::ContentLocation => HeaderName::ContentLocation,
398 HeaderName::ContentTransferEncoding => HeaderName::ContentTransferEncoding,
399 HeaderName::ContentType => HeaderName::ContentType,
400 HeaderName::ContentDisposition => HeaderName::ContentDisposition,
401 HeaderName::ResentTo => HeaderName::ResentTo,
402 HeaderName::ResentFrom => HeaderName::ResentFrom,
403 HeaderName::ResentBcc => HeaderName::ResentBcc,
404 HeaderName::ResentCc => HeaderName::ResentCc,
405 HeaderName::ResentSender => HeaderName::ResentSender,
406 HeaderName::ResentDate => HeaderName::ResentDate,
407 HeaderName::ResentMessageId => HeaderName::ResentMessageId,
408 HeaderName::ListArchive => HeaderName::ListArchive,
409 HeaderName::ListHelp => HeaderName::ListHelp,
410 HeaderName::ListId => HeaderName::ListId,
411 HeaderName::ListOwner => HeaderName::ListOwner,
412 HeaderName::ListPost => HeaderName::ListPost,
413 HeaderName::ListSubscribe => HeaderName::ListSubscribe,
414 HeaderName::ListUnsubscribe => HeaderName::ListUnsubscribe,
415 HeaderName::ArcAuthenticationResults => HeaderName::ArcAuthenticationResults,
416 HeaderName::ArcMessageSignature => HeaderName::ArcMessageSignature,
417 HeaderName::ArcSeal => HeaderName::ArcSeal,
418 HeaderName::DkimSignature => HeaderName::DkimSignature,
419 HeaderName::Dkim2Signature => HeaderName::Dkim2Signature,
420 HeaderName::MessageInstance => HeaderName::MessageInstance,
421 }
422 }
423
424 pub fn into_string(self) -> String {
425 match self {
426 HeaderName::Other(name) => name.into_owned(),
427 _ => self.as_str().to_string(),
428 }
429 }
430
431 pub fn as_str(&self) -> &str {
432 match self {
433 HeaderName::Other(other) => other.as_ref(),
434 _ => self.as_static_str(),
435 }
436 }
437
438 pub fn as_static_str(&self) -> &'static str {
439 match self {
440 HeaderName::Subject => "Subject",
441 HeaderName::From => "From",
442 HeaderName::To => "To",
443 HeaderName::Cc => "Cc",
444 HeaderName::Date => "Date",
445 HeaderName::Bcc => "Bcc",
446 HeaderName::ReplyTo => "Reply-To",
447 HeaderName::Sender => "Sender",
448 HeaderName::Comments => "Comments",
449 HeaderName::InReplyTo => "In-Reply-To",
450 HeaderName::Keywords => "Keywords",
451 HeaderName::Received => "Received",
452 HeaderName::MessageId => "Message-ID",
453 HeaderName::References => "References",
454 HeaderName::ReturnPath => "Return-Path",
455 HeaderName::MimeVersion => "MIME-Version",
456 HeaderName::ContentDescription => "Content-Description",
457 HeaderName::ContentId => "Content-ID",
458 HeaderName::ContentLanguage => "Content-Language",
459 HeaderName::ContentLocation => "Content-Location",
460 HeaderName::ContentTransferEncoding => "Content-Transfer-Encoding",
461 HeaderName::ContentType => "Content-Type",
462 HeaderName::ContentDisposition => "Content-Disposition",
463 HeaderName::ResentTo => "Resent-To",
464 HeaderName::ResentFrom => "Resent-From",
465 HeaderName::ResentBcc => "Resent-Bcc",
466 HeaderName::ResentCc => "Resent-Cc",
467 HeaderName::ResentSender => "Resent-Sender",
468 HeaderName::ResentDate => "Resent-Date",
469 HeaderName::ResentMessageId => "Resent-Message-ID",
470 HeaderName::ListArchive => "List-Archive",
471 HeaderName::ListHelp => "List-Help",
472 HeaderName::ListId => "List-ID",
473 HeaderName::ListOwner => "List-Owner",
474 HeaderName::ListPost => "List-Post",
475 HeaderName::ListSubscribe => "List-Subscribe",
476 HeaderName::ListUnsubscribe => "List-Unsubscribe",
477 HeaderName::ArcAuthenticationResults => "ARC-Authentication-Results",
478 HeaderName::ArcMessageSignature => "ARC-Message-Signature",
479 HeaderName::ArcSeal => "ARC-Seal",
480 HeaderName::DkimSignature => "DKIM-Signature",
481 HeaderName::Dkim2Signature => "DKIM2-Signature",
482 HeaderName::MessageInstance => "Message-Instance",
483 HeaderName::Other(_) => "",
484 }
485 }
486
487 pub fn len(&self) -> usize {
488 match self {
489 HeaderName::Subject => "Subject".len(),
490 HeaderName::From => "From".len(),
491 HeaderName::To => "To".len(),
492 HeaderName::Cc => "Cc".len(),
493 HeaderName::Date => "Date".len(),
494 HeaderName::Bcc => "Bcc".len(),
495 HeaderName::ReplyTo => "Reply-To".len(),
496 HeaderName::Sender => "Sender".len(),
497 HeaderName::Comments => "Comments".len(),
498 HeaderName::InReplyTo => "In-Reply-To".len(),
499 HeaderName::Keywords => "Keywords".len(),
500 HeaderName::Received => "Received".len(),
501 HeaderName::MessageId => "Message-ID".len(),
502 HeaderName::References => "References".len(),
503 HeaderName::ReturnPath => "Return-Path".len(),
504 HeaderName::MimeVersion => "MIME-Version".len(),
505 HeaderName::ContentDescription => "Content-Description".len(),
506 HeaderName::ContentId => "Content-ID".len(),
507 HeaderName::ContentLanguage => "Content-Language".len(),
508 HeaderName::ContentLocation => "Content-Location".len(),
509 HeaderName::ContentTransferEncoding => "Content-Transfer-Encoding".len(),
510 HeaderName::ContentType => "Content-Type".len(),
511 HeaderName::ContentDisposition => "Content-Disposition".len(),
512 HeaderName::ResentTo => "Resent-To".len(),
513 HeaderName::ResentFrom => "Resent-From".len(),
514 HeaderName::ResentBcc => "Resent-Bcc".len(),
515 HeaderName::ResentCc => "Resent-Cc".len(),
516 HeaderName::ResentSender => "Resent-Sender".len(),
517 HeaderName::ResentDate => "Resent-Date".len(),
518 HeaderName::ResentMessageId => "Resent-Message-ID".len(),
519 HeaderName::ListArchive => "List-Archive".len(),
520 HeaderName::ListHelp => "List-Help".len(),
521 HeaderName::ListId => "List-ID".len(),
522 HeaderName::ListOwner => "List-Owner".len(),
523 HeaderName::ListPost => "List-Post".len(),
524 HeaderName::ListSubscribe => "List-Subscribe".len(),
525 HeaderName::ListUnsubscribe => "List-Unsubscribe".len(),
526 HeaderName::ArcAuthenticationResults => "ARC-Authentication-Results".len(),
527 HeaderName::ArcMessageSignature => "ARC-Message-Signature".len(),
528 HeaderName::ArcSeal => "ARC-Seal".len(),
529 HeaderName::DkimSignature => "DKIM-Signature".len(),
530 HeaderName::Dkim2Signature => "DKIM2-Signature".len(),
531 HeaderName::MessageInstance => "Message-Instance".len(),
532 HeaderName::Other(other) => other.len(),
533 }
534 }
535
536 pub fn is_mime_header(&self) -> bool {
538 matches!(
539 self,
540 HeaderName::ContentDescription
541 | HeaderName::ContentId
542 | HeaderName::ContentLanguage
543 | HeaderName::ContentLocation
544 | HeaderName::ContentTransferEncoding
545 | HeaderName::ContentType
546 | HeaderName::ContentDisposition
547 )
548 }
549
550 pub fn is_other(&self) -> bool {
552 matches!(self, HeaderName::Other(_))
553 }
554
555 pub fn is_empty(&self) -> bool {
556 false
557 }
558
559 pub fn id(&self) -> u8 {
560 match self {
561 HeaderName::Subject => 0,
562 HeaderName::From => 1,
563 HeaderName::To => 2,
564 HeaderName::Cc => 3,
565 HeaderName::Date => 4,
566 HeaderName::Bcc => 5,
567 HeaderName::ReplyTo => 6,
568 HeaderName::Sender => 7,
569 HeaderName::Comments => 8,
570 HeaderName::InReplyTo => 9,
571 HeaderName::Keywords => 10,
572 HeaderName::Received => 11,
573 HeaderName::MessageId => 12,
574 HeaderName::References => 13,
575 HeaderName::ReturnPath => 14,
576 HeaderName::MimeVersion => 15,
577 HeaderName::ContentDescription => 16,
578 HeaderName::ContentId => 17,
579 HeaderName::ContentLanguage => 18,
580 HeaderName::ContentLocation => 19,
581 HeaderName::ContentTransferEncoding => 20,
582 HeaderName::ContentType => 21,
583 HeaderName::ContentDisposition => 22,
584 HeaderName::ResentTo => 23,
585 HeaderName::ResentFrom => 24,
586 HeaderName::ResentBcc => 25,
587 HeaderName::ResentCc => 26,
588 HeaderName::ResentSender => 27,
589 HeaderName::ResentDate => 28,
590 HeaderName::ResentMessageId => 29,
591 HeaderName::ListArchive => 30,
592 HeaderName::ListHelp => 31,
593 HeaderName::ListId => 32,
594 HeaderName::ListOwner => 33,
595 HeaderName::ListPost => 34,
596 HeaderName::ListSubscribe => 35,
597 HeaderName::ListUnsubscribe => 36,
598 HeaderName::Other(_) => 37,
599 HeaderName::ArcAuthenticationResults => 38,
600 HeaderName::ArcMessageSignature => 39,
601 HeaderName::ArcSeal => 40,
602 HeaderName::DkimSignature => 41,
603 HeaderName::Dkim2Signature => 42,
604 HeaderName::MessageInstance => 43,
605 }
606 }
607}
608
609impl<'x> MimeHeaders<'x> for Message<'x> {
610 fn content_description(&self) -> Option<&str> {
611 self.parts[0]
612 .headers
613 .header_value(&HeaderName::ContentDescription)
614 .and_then(|header| header.as_text())
615 }
616
617 fn content_disposition(&self) -> Option<&ContentType<'x>> {
618 self.parts[0]
619 .headers
620 .header_value(&HeaderName::ContentDisposition)
621 .and_then(|header| header.as_content_type())
622 }
623
624 fn content_id(&self) -> Option<&str> {
625 self.parts[0]
626 .headers
627 .header_value(&HeaderName::ContentId)
628 .and_then(|header| header.as_text())
629 }
630
631 fn content_transfer_encoding(&self) -> Option<&str> {
632 self.parts[0]
633 .headers
634 .header_value(&HeaderName::ContentTransferEncoding)
635 .and_then(|header| header.as_text())
636 }
637
638 fn content_type(&self) -> Option<&ContentType<'x>> {
639 self.parts[0]
640 .headers
641 .header_value(&HeaderName::ContentType)
642 .and_then(|header| header.as_content_type())
643 }
644
645 fn content_language(&self) -> &HeaderValue<'x> {
646 self.parts[0]
647 .headers
648 .header_value(&HeaderName::ContentLanguage)
649 .unwrap_or(&HeaderValue::Empty)
650 }
651
652 fn content_location(&self) -> Option<&str> {
653 self.parts[0]
654 .headers
655 .header_value(&HeaderName::ContentLocation)
656 .and_then(|header| header.as_text())
657 }
658}
659
660impl<'x> MessagePart<'x> {
661 pub fn contents(&self) -> &[u8] {
663 match &self.body {
664 PartType::Text(text) | PartType::Html(text) => text.as_bytes(),
665 PartType::Binary(bin) | PartType::InlineBinary(bin) => bin.as_ref(),
666 PartType::Message(message) => message.raw_message(),
667 PartType::Multipart(_) => b"",
668 }
669 }
670
671 pub fn text_contents(&self) -> Option<&str> {
673 match &self.body {
674 PartType::Text(text) | PartType::Html(text) => text.as_ref().into(),
675 PartType::Binary(bin) | PartType::InlineBinary(bin) => {
676 std::str::from_utf8(bin.as_ref()).ok()
677 }
678 PartType::Message(message) => std::str::from_utf8(message.raw_message()).ok(),
679 PartType::Multipart(_) => None,
680 }
681 }
682
683 pub fn message(&self) -> Option<&Message<'x>> {
685 if let PartType::Message(message) = &self.body {
686 Some(message)
687 } else {
688 None
689 }
690 }
691
692 pub fn sub_parts(&self) -> Option<&[MessagePartId]> {
694 if let PartType::Multipart(parts) = &self.body {
695 Some(parts.as_ref())
696 } else {
697 None
698 }
699 }
700
701 pub fn len(&self) -> usize {
703 match &self.body {
704 PartType::Text(text) | PartType::Html(text) => text.len(),
705 PartType::Binary(bin) | PartType::InlineBinary(bin) => bin.len(),
706 PartType::Message(message) => message.raw_message().len(),
707 PartType::Multipart(_) => 0,
708 }
709 }
710
711 pub fn is_text(&self) -> bool {
713 matches!(self.body, PartType::Text(_) | PartType::Html(_))
714 }
715
716 pub fn is_text_html(&self) -> bool {
718 matches!(self.body, PartType::Html(_))
719 }
720
721 pub fn is_binary(&self) -> bool {
723 matches!(self.body, PartType::Binary(_) | PartType::InlineBinary(_))
724 }
725
726 pub fn is_multipart(&self) -> bool {
728 matches!(self.body, PartType::Multipart(_))
729 }
730
731 pub fn is_message(&self) -> bool {
733 matches!(self.body, PartType::Message(_))
734 }
735
736 pub fn is_empty(&self) -> bool {
738 self.len() == 0
739 }
740
741 pub fn headers(&self) -> &[Header<'x>] {
743 &self.headers
744 }
745
746 pub fn raw_len(&self) -> u32 {
748 self.offset_end.saturating_sub(self.offset_header)
749 }
750
751 pub fn raw_header_offset(&self) -> u32 {
753 self.offset_header
754 }
755
756 pub fn raw_body_offset(&self) -> u32 {
758 self.offset_body
759 }
760
761 pub fn raw_end_offset(&self) -> u32 {
763 self.offset_end
764 }
765
766 pub fn into_owned(self) -> MessagePart<'static> {
768 MessagePart {
769 headers: self.headers.into_iter().map(|h| h.into_owned()).collect(),
770 is_encoding_problem: self.is_encoding_problem,
771 body: match self.body {
772 PartType::Text(v) => PartType::Text(v.into_owned().into()),
773 PartType::Html(v) => PartType::Html(v.into_owned().into()),
774 PartType::Binary(v) => PartType::Binary(v.into_owned().into()),
775 PartType::InlineBinary(v) => PartType::InlineBinary(v.into_owned().into()),
776 PartType::Message(v) => PartType::Message(v.into_owned()),
777 PartType::Multipart(v) => PartType::Multipart(v),
778 },
779 encoding: self.encoding,
780 offset_header: self.offset_header,
781 offset_body: self.offset_body,
782 offset_end: self.offset_end,
783 }
784 }
785}
786
787impl fmt::Display for MessagePart<'_> {
788 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
789 fmt.write_str(self.text_contents().unwrap_or("[no contents]"))
790 }
791}
792
793impl<'x> MimeHeaders<'x> for MessagePart<'x> {
794 fn content_description(&self) -> Option<&str> {
795 self.headers
796 .header_value(&HeaderName::ContentDescription)
797 .and_then(|header| header.as_text())
798 }
799
800 fn content_disposition(&self) -> Option<&ContentType<'x>> {
801 self.headers
802 .header_value(&HeaderName::ContentDisposition)
803 .and_then(|header| header.as_content_type())
804 }
805
806 fn content_id(&self) -> Option<&str> {
807 self.headers
808 .header_value(&HeaderName::ContentId)
809 .and_then(|header| header.as_text())
810 }
811
812 fn content_transfer_encoding(&self) -> Option<&str> {
813 self.headers
814 .header_value(&HeaderName::ContentTransferEncoding)
815 .and_then(|header| header.as_text())
816 }
817
818 fn content_type(&self) -> Option<&ContentType<'x>> {
819 self.headers
820 .header_value(&HeaderName::ContentType)
821 .and_then(|header| header.as_content_type())
822 }
823
824 fn content_language(&self) -> &HeaderValue<'x> {
825 self.headers
826 .header_value(&HeaderName::ContentLanguage)
827 .unwrap_or(&HeaderValue::Empty)
828 }
829
830 fn content_location(&self) -> Option<&str> {
831 self.headers
832 .header_value(&HeaderName::ContentLocation)
833 .and_then(|header| header.as_text())
834 }
835}
836
837impl<'x> ContentType<'x> {
839 pub fn ctype(&self) -> &str {
841 &self.c_type
842 }
843
844 pub fn subtype(&self) -> Option<&str> {
846 self.c_subtype.as_ref()?.as_ref().into()
847 }
848
849 pub fn attribute(&self, name: &str) -> Option<&str> {
851 self.attributes
852 .as_ref()?
853 .iter()
854 .find(|a| a.name == name)?
855 .value
856 .as_ref()
857 .into()
858 }
859
860 pub fn remove_attribute(&mut self, name: &str) -> Option<Cow<'x, str>> {
862 let attributes = self.attributes.as_mut()?;
863
864 attributes
865 .iter()
866 .position(|a| a.name == name)
867 .map(|pos| attributes.swap_remove(pos).value)
868 }
869
870 pub fn attributes(&self) -> Option<&[Attribute<'x>]> {
872 self.attributes.as_deref()
873 }
874
875 pub fn has_attribute(&self, name: &str) -> bool {
877 self.attributes
878 .as_ref()
879 .is_some_and(|attr| attr.iter().any(|a| a.name == name))
880 }
881
882 pub fn is_attachment(&self) -> bool {
884 self.c_type.eq_ignore_ascii_case("attachment")
885 }
886
887 pub fn is_inline(&self) -> bool {
889 self.c_type.eq_ignore_ascii_case("inline")
890 }
891}
892
893impl<'x> Received<'x> {
895 pub fn into_owned(self) -> Received<'static> {
896 Received {
897 from: self.from.map(|s| s.into_owned()),
898 from_ip: self.from_ip,
899 from_iprev: self.from_iprev.map(|s| s.into_owned().into()),
900 by: self.by.map(|s| s.into_owned()),
901 for_: self.for_.map(|s| s.into_owned().into()),
902 with: self.with,
903 tls_version: self.tls_version,
904 tls_cipher: self.tls_cipher.map(|s| s.into_owned().into()),
905 id: self.id.map(|s| s.into_owned().into()),
906 ident: self.ident.map(|s| s.into_owned().into()),
907 helo: self.helo.map(|s| s.into_owned()),
908 helo_cmd: self.helo_cmd,
909 via: self.via.map(|s| s.into_owned().into()),
910 date: self.date,
911 }
912 }
913
914 pub fn from(&self) -> Option<&Host<'x>> {
916 self.from.as_ref()
917 }
918
919 pub fn from_ip(&self) -> Option<IpAddr> {
921 self.from_ip
922 }
923
924 pub fn from_iprev(&self) -> Option<&str> {
926 self.from_iprev.as_ref().map(|s| s.as_ref())
927 }
928
929 pub fn by(&self) -> Option<&Host<'x>> {
931 self.by.as_ref()
932 }
933
934 pub fn for_(&self) -> Option<&str> {
936 self.for_.as_ref().map(|s| s.as_ref())
937 }
938
939 pub fn with(&self) -> Option<Protocol> {
941 self.with
942 }
943
944 pub fn tls_version(&self) -> Option<TlsVersion> {
946 self.tls_version
947 }
948
949 pub fn tls_cipher(&self) -> Option<&str> {
951 self.tls_cipher.as_ref().map(|s| s.as_ref())
952 }
953
954 pub fn id(&self) -> Option<&str> {
956 self.id.as_ref().map(|s| s.as_ref())
957 }
958
959 pub fn ident(&self) -> Option<&str> {
961 self.ident.as_ref().map(|s| s.as_ref())
962 }
963
964 pub fn helo(&self) -> Option<&Host<'x>> {
966 self.helo.as_ref()
967 }
968
969 pub fn helo_cmd(&self) -> Option<Greeting> {
971 self.helo_cmd
972 }
973
974 pub fn via(&self) -> Option<&str> {
976 self.via.as_ref().map(|s| s.as_ref())
977 }
978
979 pub fn date(&self) -> Option<DateTime> {
981 self.date
982 }
983}
984
985impl Host<'_> {
987 pub fn into_owned(self) -> Host<'static> {
988 match self {
989 Host::Name(name) => Host::Name(name.into_owned().into()),
990 Host::IpAddr(ip) => Host::IpAddr(ip),
991 }
992 }
993}
994
995impl<'x> GetHeader<'x> for Vec<Header<'x>> {
996 fn header_value(&self, name: &HeaderName<'_>) -> Option<&HeaderValue<'x>> {
997 self.iter()
998 .rev()
999 .find(|header| &header.name == name)
1000 .map(|header| &header.value)
1001 }
1002
1003 fn header(&self, name: impl Into<HeaderName<'x>>) -> Option<&Header<'x>> {
1004 let name = name.into();
1005 self.iter().rev().find(|header| header.name == name)
1006 }
1007}
1008
1009impl<'x> From<&'x str> for HeaderName<'x> {
1010 fn from(value: &'x str) -> Self {
1011 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
1012 }
1013}
1014
1015impl<'x> From<Cow<'x, str>> for HeaderName<'x> {
1016 fn from(value: Cow<'x, str>) -> Self {
1017 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
1018 }
1019}
1020
1021impl From<String> for HeaderName<'_> {
1022 fn from(value: String) -> Self {
1023 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
1024 }
1025}
1026
1027impl From<HeaderName<'_>> for String {
1028 fn from(header: HeaderName<'_>) -> Self {
1029 header.to_string()
1030 }
1031}
1032
1033impl<'x> From<HeaderName<'x>> for Cow<'x, str> {
1034 fn from(header: HeaderName<'x>) -> Self {
1035 match header {
1036 HeaderName::Other(value) => value,
1037 _ => Cow::Borrowed(header.as_static_str()),
1038 }
1039 }
1040}
1041
1042impl From<u8> for HeaderName<'_> {
1043 fn from(value: u8) -> Self {
1044 match value {
1045 0 => HeaderName::Subject,
1046 1 => HeaderName::From,
1047 2 => HeaderName::To,
1048 3 => HeaderName::Cc,
1049 4 => HeaderName::Date,
1050 5 => HeaderName::Bcc,
1051 6 => HeaderName::ReplyTo,
1052 7 => HeaderName::Sender,
1053 8 => HeaderName::Comments,
1054 9 => HeaderName::InReplyTo,
1055 10 => HeaderName::Keywords,
1056 11 => HeaderName::Received,
1057 12 => HeaderName::MessageId,
1058 13 => HeaderName::References,
1059 14 => HeaderName::ReturnPath,
1060 15 => HeaderName::MimeVersion,
1061 16 => HeaderName::ContentDescription,
1062 17 => HeaderName::ContentId,
1063 18 => HeaderName::ContentLanguage,
1064 19 => HeaderName::ContentLocation,
1065 20 => HeaderName::ContentTransferEncoding,
1066 21 => HeaderName::ContentType,
1067 22 => HeaderName::ContentDisposition,
1068 23 => HeaderName::ResentTo,
1069 24 => HeaderName::ResentFrom,
1070 25 => HeaderName::ResentBcc,
1071 26 => HeaderName::ResentCc,
1072 27 => HeaderName::ResentSender,
1073 28 => HeaderName::ResentDate,
1074 29 => HeaderName::ResentMessageId,
1075 30 => HeaderName::ListArchive,
1076 31 => HeaderName::ListHelp,
1077 32 => HeaderName::ListId,
1078 33 => HeaderName::ListOwner,
1079 34 => HeaderName::ListPost,
1080 35 => HeaderName::ListSubscribe,
1081 36 => HeaderName::ListUnsubscribe,
1082 38 => HeaderName::ArcAuthenticationResults,
1083 39 => HeaderName::ArcMessageSignature,
1084 40 => HeaderName::ArcSeal,
1085 41 => HeaderName::DkimSignature,
1086 _ => HeaderName::Other("".into()),
1087 }
1088 }
1089}
1090
1091impl From<DateTime> for i64 {
1092 fn from(value: DateTime) -> Self {
1093 value.to_timestamp()
1094 }
1095}
1096
1097impl TlsVersion {
1098 pub fn as_str(&self) -> &'static str {
1099 match self {
1100 TlsVersion::SSLv2 => "SSLv2",
1101 TlsVersion::SSLv3 => "SSLv3",
1102 TlsVersion::TLSv1_0 => "TLSv1.0",
1103 TlsVersion::TLSv1_1 => "TLSv1.1",
1104 TlsVersion::TLSv1_2 => "TLSv1.2",
1105 TlsVersion::TLSv1_3 => "TLSv1.3",
1106 TlsVersion::DTLSv1_0 => "DTLSv1.0",
1107 TlsVersion::DTLSv1_2 => "DTLSv1.2",
1108 TlsVersion::DTLSv1_3 => "DTLSv1.3",
1109 }
1110 }
1111}
1112
1113impl Greeting {
1114 pub fn as_str(&self) -> &'static str {
1115 match self {
1116 Greeting::Helo => "HELO",
1117 Greeting::Ehlo => "EHLO",
1118 Greeting::Lhlo => "LHLO",
1119 }
1120 }
1121}
1122
1123impl Protocol {
1124 pub fn as_str(&self) -> &'static str {
1125 match self {
1126 Protocol::SMTP => "SMTP",
1127 Protocol::LMTP => "LMTP",
1128 Protocol::ESMTP => "ESMTP",
1129 Protocol::ESMTPS => "ESMTPS",
1130 Protocol::ESMTPA => "ESMTPA",
1131 Protocol::ESMTPSA => "ESMTPSA",
1132 Protocol::LMTPA => "LMTPA",
1133 Protocol::LMTPS => "LMTPS",
1134 Protocol::LMTPSA => "LMTPSA",
1135 Protocol::UTF8SMTP => "UTF8SMTP",
1136 Protocol::UTF8SMTPA => "UTF8SMTPA",
1137 Protocol::UTF8SMTPS => "UTF8SMTPS",
1138 Protocol::UTF8SMTPSA => "UTF8SMTPSA",
1139 Protocol::UTF8LMTP => "UTF8LMTP",
1140 Protocol::UTF8LMTPA => "UTF8LMTPA",
1141 Protocol::UTF8LMTPS => "UTF8LMTPS",
1142 Protocol::UTF8LMTPSA => "UTF8LMTPSA",
1143 Protocol::HTTP => "HTTP",
1144 Protocol::HTTPS => "HTTPS",
1145 Protocol::IMAP => "IMAP",
1146 Protocol::POP3 => "POP3",
1147 Protocol::MMS => "MMS",
1148 Protocol::Local => "Local",
1149 }
1150 }
1151}
1152
1153impl Display for Host<'_> {
1154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1155 match self {
1156 Host::Name(name) => name.fmt(f),
1157 Host::IpAddr(ip) => ip.fmt(f),
1158 }
1159 }
1160}
1161
1162impl Display for Protocol {
1163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1164 f.write_str(self.as_str())
1165 }
1166}
1167
1168impl Display for Greeting {
1169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1170 f.write_str(self.as_str())
1171 }
1172}
1173
1174impl Display for TlsVersion {
1175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1176 f.write_str(self.as_str())
1177 }
1178}
1179
1180impl Display for HeaderName<'_> {
1181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1182 write!(f, "{}", self.as_str())
1183 }
1184}