1use crate::{
8 Address, Attribute, ContentType, DateTime, GetHeader, Greeting, Header, HeaderName,
9 HeaderValue, Host, Message, MessagePart, MessagePartId, MimeHeaders, PartType, Protocol,
10 Received, TlsVersion,
11};
12use core::fmt;
13use std::hash::Hash;
14use std::net::IpAddr;
15use std::{borrow::Cow, fmt::Display};
16
17impl<'x> Header<'x> {
18 pub fn name(&self) -> &str {
20 self.name.as_str()
21 }
22
23 pub fn value(&self) -> &HeaderValue<'x> {
25 &self.value
26 }
27
28 pub fn offset_start(&self) -> u32 {
30 self.offset_start
31 }
32
33 pub fn offset_end(&self) -> u32 {
35 self.offset_end
36 }
37
38 pub fn offset_field(&self) -> u32 {
40 self.offset_field
41 }
42
43 pub fn into_owned(self) -> Header<'static> {
45 Header {
46 name: self.name.into_owned(),
47 value: self.value.into_owned(),
48 offset_field: self.offset_field,
49 offset_start: self.offset_start,
50 offset_end: self.offset_end,
51 }
52 }
53}
54
55impl<'x> HeaderValue<'x> {
56 pub fn is_empty(&self) -> bool {
57 *self == HeaderValue::Empty
58 }
59
60 pub fn unwrap_text(self) -> Cow<'x, str> {
61 match self {
62 HeaderValue::Text(s) => s,
63 _ => panic!("HeaderValue::unwrap_text called on non-Text value"),
64 }
65 }
66
67 pub fn unwrap_text_list(self) -> Vec<Cow<'x, str>> {
68 match self {
69 HeaderValue::TextList(l) => l,
70 HeaderValue::Text(s) => vec![s],
71 _ => panic!("HeaderValue::unwrap_text_list called on non-TextList value"),
72 }
73 }
74
75 pub fn unwrap_datetime(self) -> DateTime {
76 match self {
77 HeaderValue::DateTime(d) => d,
78 _ => panic!("HeaderValue::unwrap_datetime called on non-DateTime value"),
79 }
80 }
81
82 pub fn unwrap_address(self) -> Address<'x> {
83 match self {
84 HeaderValue::Address(a) => a,
85 _ => panic!("HeaderValue::unwrap_address called on non-Address value"),
86 }
87 }
88
89 pub fn unwrap_content_type(self) -> ContentType<'x> {
90 match self {
91 HeaderValue::ContentType(c) => c,
92 _ => panic!("HeaderValue::unwrap_content_type called on non-ContentType value"),
93 }
94 }
95
96 pub fn unwrap_received(self) -> Received<'x> {
97 match self {
98 HeaderValue::Received(r) => *r,
99 _ => panic!("HeaderValue::unwrap_received called on non-Received value"),
100 }
101 }
102
103 pub fn into_text(self) -> Option<Cow<'x, str>> {
104 match self {
105 HeaderValue::Text(s) => Some(s),
106 _ => None,
107 }
108 }
109
110 pub fn into_text_list(self) -> Option<Vec<Cow<'x, str>>> {
111 match self {
112 HeaderValue::Text(s) => Some(vec![s]),
113 HeaderValue::TextList(l) => Some(l),
114 _ => None,
115 }
116 }
117
118 pub fn into_address(self) -> Option<Address<'x>> {
119 match self {
120 HeaderValue::Address(a) => Some(a),
121 _ => None,
122 }
123 }
124
125 pub fn into_datetime(self) -> Option<DateTime> {
126 match self {
127 HeaderValue::DateTime(d) => Some(d),
128 _ => None,
129 }
130 }
131
132 pub fn into_content_type(self) -> Option<ContentType<'x>> {
133 match self {
134 HeaderValue::ContentType(c) => Some(c),
135 _ => None,
136 }
137 }
138
139 pub fn into_received(self) -> Option<Received<'x>> {
140 match self {
141 HeaderValue::Received(r) => Some(*r),
142 _ => None,
143 }
144 }
145
146 pub fn as_text(&self) -> Option<&str> {
147 match *self {
148 HeaderValue::Text(ref s) => Some(s),
149 HeaderValue::TextList(ref l) => l.last()?.as_ref().into(),
150 _ => None,
151 }
152 }
153
154 pub fn as_text_list(&self) -> Option<&[Cow<'x, str>]> {
155 match *self {
156 HeaderValue::Text(ref s) => Some(std::slice::from_ref(s)),
157 HeaderValue::TextList(ref l) => Some(l.as_slice()),
158 _ => None,
159 }
160 }
161
162 pub fn as_address(&self) -> Option<&Address<'x>> {
163 match *self {
164 HeaderValue::Address(ref a) => Some(a),
165 _ => None,
166 }
167 }
168
169 pub fn as_received(&self) -> Option<&Received<'x>> {
170 match *self {
171 HeaderValue::Received(ref r) => Some(r),
172 _ => None,
173 }
174 }
175
176 pub fn as_content_type(&self) -> Option<&ContentType<'x>> {
177 match *self {
178 HeaderValue::ContentType(ref c) => Some(c),
179 _ => None,
180 }
181 }
182
183 pub fn as_datetime(&self) -> Option<&DateTime> {
184 match *self {
185 HeaderValue::DateTime(ref d) => Some(d),
186 _ => None,
187 }
188 }
189
190 pub fn into_owned(self) -> HeaderValue<'static> {
191 match self {
192 HeaderValue::Address(addr) => HeaderValue::Address(addr.into_owned()),
193 HeaderValue::Text(text) => HeaderValue::Text(text.into_owned().into()),
194 HeaderValue::TextList(list) => HeaderValue::TextList(
195 list.into_iter()
196 .map(|text| text.into_owned().into())
197 .collect(),
198 ),
199 HeaderValue::DateTime(datetime) => HeaderValue::DateTime(datetime),
200 HeaderValue::ContentType(ct) => HeaderValue::ContentType(ContentType {
201 c_type: ct.c_type.into_owned().into(),
202 c_subtype: ct.c_subtype.map(|s| s.into_owned().into()),
203 attributes: ct.attributes.map(|attributes| {
204 attributes
205 .into_iter()
206 .map(|a| Attribute {
207 name: a.name.into_owned().into(),
208 value: a.value.into_owned().into(),
209 })
210 .collect()
211 }),
212 }),
213 HeaderValue::Received(rcvd) => HeaderValue::Received(Box::new(rcvd.into_owned())),
214 HeaderValue::Empty => HeaderValue::Empty,
215 }
216 }
217
218 pub fn len(&self) -> usize {
219 match self {
220 HeaderValue::Text(text) => text.len(),
221 HeaderValue::TextList(list) => list.iter().map(|t| t.len()).sum(),
222 HeaderValue::Address(Address::List(list)) => list
223 .iter()
224 .map(|a| {
225 a.name.as_ref().map_or(0, |a| a.len())
226 + a.address.as_ref().map_or(0, |a| a.len())
227 })
228 .sum(),
229 HeaderValue::Address(Address::Group(grouplist)) => grouplist
230 .iter()
231 .flat_map(|g| g.addresses.iter())
232 .map(|a| {
233 a.name.as_ref().map_or(0, |a| a.len())
234 + a.address.as_ref().map_or(0, |a| a.len())
235 })
236 .sum(),
237 HeaderValue::DateTime(_) => 24,
238 HeaderValue::ContentType(ct) => {
239 ct.c_type.len()
240 + ct.c_subtype.as_ref().map_or(0, |s| s.len())
241 + ct.attributes.as_ref().map_or(0, |at| {
242 at.iter().map(|a| a.name.len() + a.value.len()).sum()
243 })
244 }
245 HeaderValue::Received(_) => 1,
246 HeaderValue::Empty => 0,
247 }
248 }
249}
250
251impl PartialEq for HeaderName<'_> {
252 fn eq(&self, other: &Self) -> bool {
253 match (self, other) {
254 (Self::Other(a), Self::Other(b)) => a.eq_ignore_ascii_case(b),
255 (Self::Other(_), _) | (_, Self::Other(_)) => false,
256 _ => self.id() == other.id(),
257 }
258 }
259}
260
261impl Hash for HeaderName<'_> {
262 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
263 match self {
264 HeaderName::Other(value) => {
265 for ch in value.as_bytes() {
266 ch.to_ascii_lowercase().hash(state)
267 }
268 }
269 _ => self.id().hash(state),
270 }
271 }
272}
273
274impl Eq for HeaderName<'_> {}
275
276impl<'x> From<HeaderName<'x>> for u8 {
277 fn from(name: HeaderName<'x>) -> Self {
278 name.id()
279 }
280}
281
282impl HeaderName<'_> {
283 pub fn into_string(self) -> String {
284 match self {
285 HeaderName::Other(name) => name.into_owned(),
286 _ => self.as_str().to_string(),
287 }
288 }
289
290 pub fn as_str(&self) -> &str {
291 match self {
292 HeaderName::Other(other) => other.as_ref(),
293 _ => self.as_static_str(),
294 }
295 }
296
297 pub fn len(&self) -> usize {
298 self.as_str().len()
299 }
300
301 pub fn is_structured(&self) -> bool {
303 matches!(
304 self,
305 HeaderName::Subject
306 | HeaderName::Comments
307 | HeaderName::ContentDescription
308 | HeaderName::ContentLocation
309 | HeaderName::ContentTransferEncoding
310 | HeaderName::From
311 | HeaderName::To
312 | HeaderName::Cc
313 | HeaderName::Bcc
314 | HeaderName::ReplyTo
315 | HeaderName::Sender
316 | HeaderName::ResentTo
317 | HeaderName::ResentFrom
318 | HeaderName::ResentBcc
319 | HeaderName::ResentCc
320 | HeaderName::ResentSender
321 | HeaderName::ListArchive
322 | HeaderName::ListHelp
323 | HeaderName::ListId
324 | HeaderName::ListOwner
325 | HeaderName::ListPost
326 | HeaderName::ListSubscribe
327 | HeaderName::ListUnsubscribe
328 | HeaderName::Date
329 | HeaderName::ResentDate
330 | HeaderName::MessageId
331 | HeaderName::References
332 | HeaderName::InReplyTo
333 | HeaderName::ReturnPath
334 | HeaderName::ContentId
335 | HeaderName::ResentMessageId
336 | HeaderName::Keywords
337 | HeaderName::ContentLanguage
338 | HeaderName::Received
339 | HeaderName::ContentType
340 | HeaderName::ContentDisposition
341 )
342 }
343
344 pub fn is_mime_header(&self) -> bool {
346 matches!(
347 self,
348 HeaderName::ContentDescription
349 | HeaderName::ContentId
350 | HeaderName::ContentLanguage
351 | HeaderName::ContentLocation
352 | HeaderName::ContentTransferEncoding
353 | HeaderName::ContentType
354 | HeaderName::ContentDisposition
355 )
356 }
357
358 pub fn is_other(&self) -> bool {
360 matches!(self, HeaderName::Other(_))
361 }
362
363 pub fn is_empty(&self) -> bool {
364 false
365 }
366}
367
368impl<'x> MimeHeaders<'x> for Message<'x> {
369 fn content_description(&self) -> Option<&str> {
370 self.parts[0]
371 .headers
372 .header_value(&HeaderName::ContentDescription)
373 .and_then(|header| header.as_text())
374 }
375
376 fn content_disposition(&self) -> Option<&ContentType<'x>> {
377 self.parts[0]
378 .headers
379 .header_value(&HeaderName::ContentDisposition)
380 .and_then(|header| header.as_content_type())
381 }
382
383 fn content_id(&self) -> Option<&str> {
384 self.parts[0]
385 .headers
386 .header_value(&HeaderName::ContentId)
387 .and_then(|header| header.as_text())
388 }
389
390 fn content_transfer_encoding(&self) -> Option<&str> {
391 self.parts[0]
392 .headers
393 .header_value(&HeaderName::ContentTransferEncoding)
394 .and_then(|header| header.as_text())
395 }
396
397 fn content_type(&self) -> Option<&ContentType<'x>> {
398 self.parts[0]
399 .headers
400 .header_value(&HeaderName::ContentType)
401 .and_then(|header| header.as_content_type())
402 }
403
404 fn content_language(&self) -> &HeaderValue<'x> {
405 self.parts[0]
406 .headers
407 .header_value(&HeaderName::ContentLanguage)
408 .unwrap_or(&HeaderValue::Empty)
409 }
410
411 fn content_location(&self) -> Option<&str> {
412 self.parts[0]
413 .headers
414 .header_value(&HeaderName::ContentLocation)
415 .and_then(|header| header.as_text())
416 }
417}
418
419impl<'x> MessagePart<'x> {
420 pub fn contents(&self) -> &[u8] {
422 match &self.body {
423 PartType::Text(text) | PartType::Html(text) => text.as_bytes(),
424 PartType::Binary(bin) | PartType::InlineBinary(bin) => bin.as_ref(),
425 PartType::Message(message) => message.raw_message(),
426 PartType::Multipart(_) => b"",
427 }
428 }
429
430 pub fn text_contents(&self) -> Option<&str> {
432 match &self.body {
433 PartType::Text(text) | PartType::Html(text) => text.as_ref().into(),
434 PartType::Binary(bin) | PartType::InlineBinary(bin) => {
435 std::str::from_utf8(bin.as_ref()).ok()
436 }
437 PartType::Message(message) => std::str::from_utf8(message.raw_message()).ok(),
438 PartType::Multipart(_) => None,
439 }
440 }
441
442 pub fn message(&self) -> Option<&Message<'x>> {
444 if let PartType::Message(message) = &self.body {
445 Some(message)
446 } else {
447 None
448 }
449 }
450
451 pub fn sub_parts(&self) -> Option<&[MessagePartId]> {
453 if let PartType::Multipart(parts) = &self.body {
454 Some(parts.as_ref())
455 } else {
456 None
457 }
458 }
459
460 pub fn len(&self) -> usize {
462 match &self.body {
463 PartType::Text(text) | PartType::Html(text) => text.len(),
464 PartType::Binary(bin) | PartType::InlineBinary(bin) => bin.len(),
465 PartType::Message(message) => message.raw_message().len(),
466 PartType::Multipart(_) => 0,
467 }
468 }
469
470 pub fn is_text(&self) -> bool {
472 matches!(self.body, PartType::Text(_) | PartType::Html(_))
473 }
474
475 pub fn is_text_html(&self) -> bool {
477 matches!(self.body, PartType::Html(_))
478 }
479
480 pub fn is_binary(&self) -> bool {
482 matches!(self.body, PartType::Binary(_) | PartType::InlineBinary(_))
483 }
484
485 pub fn is_multipart(&self) -> bool {
487 matches!(self.body, PartType::Multipart(_))
488 }
489
490 pub fn is_message(&self) -> bool {
492 matches!(self.body, PartType::Message(_))
493 }
494
495 pub fn is_empty(&self) -> bool {
497 self.len() == 0
498 }
499
500 pub fn headers(&self) -> &[Header<'x>] {
502 &self.headers
503 }
504
505 pub fn raw_len(&self) -> u32 {
507 self.offset_end.saturating_sub(self.offset_header)
508 }
509
510 pub fn raw_header_offset(&self) -> u32 {
512 self.offset_header
513 }
514
515 pub fn raw_body_offset(&self) -> u32 {
517 self.offset_body
518 }
519
520 pub fn raw_end_offset(&self) -> u32 {
522 self.offset_end
523 }
524
525 pub fn into_owned(self) -> MessagePart<'static> {
527 MessagePart {
528 headers: self.headers.into_iter().map(|h| h.into_owned()).collect(),
529 is_encoding_problem: self.is_encoding_problem,
530 body: match self.body {
531 PartType::Text(v) => PartType::Text(v.into_owned().into()),
532 PartType::Html(v) => PartType::Html(v.into_owned().into()),
533 PartType::Binary(v) => PartType::Binary(v.into_owned().into()),
534 PartType::InlineBinary(v) => PartType::InlineBinary(v.into_owned().into()),
535 PartType::Message(v) => PartType::Message(v.into_owned()),
536 PartType::Multipart(v) => PartType::Multipart(v),
537 },
538 encoding: self.encoding,
539 offset_header: self.offset_header,
540 offset_body: self.offset_body,
541 offset_end: self.offset_end,
542 }
543 }
544}
545
546impl fmt::Display for MessagePart<'_> {
547 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
548 fmt.write_str(self.text_contents().unwrap_or("[no contents]"))
549 }
550}
551
552impl<'x> MimeHeaders<'x> for MessagePart<'x> {
553 fn content_description(&self) -> Option<&str> {
554 self.headers
555 .header_value(&HeaderName::ContentDescription)
556 .and_then(|header| header.as_text())
557 }
558
559 fn content_disposition(&self) -> Option<&ContentType<'x>> {
560 self.headers
561 .header_value(&HeaderName::ContentDisposition)
562 .and_then(|header| header.as_content_type())
563 }
564
565 fn content_id(&self) -> Option<&str> {
566 self.headers
567 .header_value(&HeaderName::ContentId)
568 .and_then(|header| header.as_text())
569 }
570
571 fn content_transfer_encoding(&self) -> Option<&str> {
572 self.headers
573 .header_value(&HeaderName::ContentTransferEncoding)
574 .and_then(|header| header.as_text())
575 }
576
577 fn content_type(&self) -> Option<&ContentType<'x>> {
578 self.headers
579 .header_value(&HeaderName::ContentType)
580 .and_then(|header| header.as_content_type())
581 }
582
583 fn content_language(&self) -> &HeaderValue<'x> {
584 self.headers
585 .header_value(&HeaderName::ContentLanguage)
586 .unwrap_or(&HeaderValue::Empty)
587 }
588
589 fn content_location(&self) -> Option<&str> {
590 self.headers
591 .header_value(&HeaderName::ContentLocation)
592 .and_then(|header| header.as_text())
593 }
594}
595
596impl<'x> ContentType<'x> {
598 pub fn ctype(&self) -> &str {
600 &self.c_type
601 }
602
603 pub fn subtype(&self) -> Option<&str> {
605 self.c_subtype.as_ref()?.as_ref().into()
606 }
607
608 pub fn attribute(&self, name: &str) -> Option<&str> {
610 self.attributes
611 .as_ref()?
612 .iter()
613 .find(|a| a.name == name)?
614 .value
615 .as_ref()
616 .into()
617 }
618
619 pub fn remove_attribute(&mut self, name: &str) -> Option<Cow<'x, str>> {
621 let attributes = self.attributes.as_mut()?;
622
623 attributes
624 .iter()
625 .position(|a| a.name == name)
626 .map(|pos| attributes.swap_remove(pos).value)
627 }
628
629 pub fn attributes(&self) -> Option<&[Attribute<'x>]> {
631 self.attributes.as_deref()
632 }
633
634 pub fn has_attribute(&self, name: &str) -> bool {
636 self.attributes
637 .as_ref()
638 .is_some_and(|attr| attr.iter().any(|a| a.name == name))
639 }
640
641 pub fn is_attachment(&self) -> bool {
643 self.c_type.eq_ignore_ascii_case("attachment")
644 }
645
646 pub fn is_inline(&self) -> bool {
648 self.c_type.eq_ignore_ascii_case("inline")
649 }
650}
651
652impl<'x> Received<'x> {
654 pub fn into_owned(self) -> Received<'static> {
655 Received {
656 from: self.from.map(|s| s.into_owned()),
657 from_ip: self.from_ip,
658 from_iprev: self.from_iprev.map(|s| s.into_owned().into()),
659 by: self.by.map(|s| s.into_owned()),
660 for_: self.for_.map(|s| s.into_owned().into()),
661 with: self.with,
662 tls_version: self.tls_version,
663 tls_cipher: self.tls_cipher.map(|s| s.into_owned().into()),
664 id: self.id.map(|s| s.into_owned().into()),
665 ident: self.ident.map(|s| s.into_owned().into()),
666 helo: self.helo.map(|s| s.into_owned()),
667 helo_cmd: self.helo_cmd,
668 via: self.via.map(|s| s.into_owned().into()),
669 date: self.date,
670 }
671 }
672
673 pub fn from(&self) -> Option<&Host<'x>> {
675 self.from.as_ref()
676 }
677
678 pub fn from_ip(&self) -> Option<IpAddr> {
680 self.from_ip
681 }
682
683 pub fn from_iprev(&self) -> Option<&str> {
685 self.from_iprev.as_ref().map(|s| s.as_ref())
686 }
687
688 pub fn by(&self) -> Option<&Host<'x>> {
690 self.by.as_ref()
691 }
692
693 pub fn for_(&self) -> Option<&str> {
695 self.for_.as_ref().map(|s| s.as_ref())
696 }
697
698 pub fn with(&self) -> Option<Protocol> {
700 self.with
701 }
702
703 pub fn tls_version(&self) -> Option<TlsVersion> {
705 self.tls_version
706 }
707
708 pub fn tls_cipher(&self) -> Option<&str> {
710 self.tls_cipher.as_ref().map(|s| s.as_ref())
711 }
712
713 pub fn id(&self) -> Option<&str> {
715 self.id.as_ref().map(|s| s.as_ref())
716 }
717
718 pub fn ident(&self) -> Option<&str> {
720 self.ident.as_ref().map(|s| s.as_ref())
721 }
722
723 pub fn helo(&self) -> Option<&Host<'x>> {
725 self.helo.as_ref()
726 }
727
728 pub fn helo_cmd(&self) -> Option<Greeting> {
730 self.helo_cmd
731 }
732
733 pub fn via(&self) -> Option<&str> {
735 self.via.as_ref().map(|s| s.as_ref())
736 }
737
738 pub fn date(&self) -> Option<DateTime> {
740 self.date
741 }
742}
743
744impl Host<'_> {
746 pub fn into_owned(self) -> Host<'static> {
747 match self {
748 Host::Name(name) => Host::Name(name.into_owned().into()),
749 Host::IpAddr(ip) => Host::IpAddr(ip),
750 }
751 }
752}
753
754impl<'x> GetHeader<'x> for Vec<Header<'x>> {
755 fn header_value(&self, name: &HeaderName<'_>) -> Option<&HeaderValue<'x>> {
756 self.iter()
757 .rev()
758 .find(|header| &header.name == name)
759 .map(|header| &header.value)
760 }
761
762 fn header(&self, name: impl Into<HeaderName<'x>>) -> Option<&Header<'x>> {
763 let name = name.into();
764 self.iter().rev().find(|header| header.name == name)
765 }
766}
767
768impl<'x> From<&'x str> for HeaderName<'x> {
769 fn from(value: &'x str) -> Self {
770 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
771 }
772}
773
774impl<'x> From<Cow<'x, str>> for HeaderName<'x> {
775 fn from(value: Cow<'x, str>) -> Self {
776 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
777 }
778}
779
780impl From<String> for HeaderName<'_> {
781 fn from(value: String) -> Self {
782 HeaderName::parse(value).unwrap_or(HeaderName::Other("".into()))
783 }
784}
785
786impl From<HeaderName<'_>> for String {
787 fn from(header: HeaderName<'_>) -> Self {
788 header.to_string()
789 }
790}
791
792impl<'x> From<HeaderName<'x>> for Cow<'x, str> {
793 fn from(header: HeaderName<'x>) -> Self {
794 match header {
795 HeaderName::Other(value) => value,
796 _ => Cow::Borrowed(header.as_static_str()),
797 }
798 }
799}
800
801impl From<u8> for HeaderName<'_> {
802 fn from(value: u8) -> Self {
803 match value {
804 0 => HeaderName::Subject,
805 1 => HeaderName::From,
806 2 => HeaderName::To,
807 3 => HeaderName::Cc,
808 4 => HeaderName::Date,
809 5 => HeaderName::Bcc,
810 6 => HeaderName::ReplyTo,
811 7 => HeaderName::Sender,
812 8 => HeaderName::Comments,
813 9 => HeaderName::InReplyTo,
814 10 => HeaderName::Keywords,
815 11 => HeaderName::Received,
816 12 => HeaderName::MessageId,
817 13 => HeaderName::References,
818 14 => HeaderName::ReturnPath,
819 15 => HeaderName::MimeVersion,
820 16 => HeaderName::ContentDescription,
821 17 => HeaderName::ContentId,
822 18 => HeaderName::ContentLanguage,
823 19 => HeaderName::ContentLocation,
824 20 => HeaderName::ContentTransferEncoding,
825 21 => HeaderName::ContentType,
826 22 => HeaderName::ContentDisposition,
827 23 => HeaderName::ResentTo,
828 24 => HeaderName::ResentFrom,
829 25 => HeaderName::ResentBcc,
830 26 => HeaderName::ResentCc,
831 27 => HeaderName::ResentSender,
832 28 => HeaderName::ResentDate,
833 29 => HeaderName::ResentMessageId,
834 30 => HeaderName::ListArchive,
835 31 => HeaderName::ListHelp,
836 32 => HeaderName::ListId,
837 33 => HeaderName::ListOwner,
838 34 => HeaderName::ListPost,
839 35 => HeaderName::ListSubscribe,
840 36 => HeaderName::ListUnsubscribe,
841 38 => HeaderName::ArcAuthenticationResults,
842 39 => HeaderName::ArcMessageSignature,
843 40 => HeaderName::ArcSeal,
844 41 => HeaderName::DkimSignature,
845 _ => HeaderName::Other("".into()),
846 }
847 }
848}
849
850impl From<DateTime> for i64 {
851 fn from(value: DateTime) -> Self {
852 value.to_timestamp()
853 }
854}
855
856impl TlsVersion {
857 pub fn as_str(&self) -> &'static str {
858 match self {
859 TlsVersion::SSLv2 => "SSLv2",
860 TlsVersion::SSLv3 => "SSLv3",
861 TlsVersion::TLSv1_0 => "TLSv1.0",
862 TlsVersion::TLSv1_1 => "TLSv1.1",
863 TlsVersion::TLSv1_2 => "TLSv1.2",
864 TlsVersion::TLSv1_3 => "TLSv1.3",
865 TlsVersion::DTLSv1_0 => "DTLSv1.0",
866 TlsVersion::DTLSv1_2 => "DTLSv1.2",
867 TlsVersion::DTLSv1_3 => "DTLSv1.3",
868 }
869 }
870}
871
872impl Greeting {
873 pub fn as_str(&self) -> &'static str {
874 match self {
875 Greeting::Helo => "HELO",
876 Greeting::Ehlo => "EHLO",
877 Greeting::Lhlo => "LHLO",
878 }
879 }
880}
881
882impl Protocol {
883 pub fn as_str(&self) -> &'static str {
884 match self {
885 Protocol::SMTP => "SMTP",
886 Protocol::LMTP => "LMTP",
887 Protocol::ESMTP => "ESMTP",
888 Protocol::ESMTPS => "ESMTPS",
889 Protocol::ESMTPA => "ESMTPA",
890 Protocol::ESMTPSA => "ESMTPSA",
891 Protocol::LMTPA => "LMTPA",
892 Protocol::LMTPS => "LMTPS",
893 Protocol::LMTPSA => "LMTPSA",
894 Protocol::UTF8SMTP => "UTF8SMTP",
895 Protocol::UTF8SMTPA => "UTF8SMTPA",
896 Protocol::UTF8SMTPS => "UTF8SMTPS",
897 Protocol::UTF8SMTPSA => "UTF8SMTPSA",
898 Protocol::UTF8LMTP => "UTF8LMTP",
899 Protocol::UTF8LMTPA => "UTF8LMTPA",
900 Protocol::UTF8LMTPS => "UTF8LMTPS",
901 Protocol::UTF8LMTPSA => "UTF8LMTPSA",
902 Protocol::HTTP => "HTTP",
903 Protocol::HTTPS => "HTTPS",
904 Protocol::IMAP => "IMAP",
905 Protocol::POP3 => "POP3",
906 Protocol::MMS => "MMS",
907 Protocol::Local => "Local",
908 }
909 }
910}
911
912impl Display for Host<'_> {
913 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
914 match self {
915 Host::Name(name) => name.fmt(f),
916 Host::IpAddr(ip) => ip.fmt(f),
917 }
918 }
919}
920
921impl Display for Protocol {
922 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
923 f.write_str(self.as_str())
924 }
925}
926
927impl Display for Greeting {
928 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929 f.write_str(self.as_str())
930 }
931}
932
933impl Display for TlsVersion {
934 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
935 f.write_str(self.as_str())
936 }
937}
938
939impl Display for HeaderName<'_> {
940 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
941 write!(f, "{}", self.as_str())
942 }
943}