1use std::{
4 borrow::Cow,
5 error::Error,
6 fmt::{self, Display, Formatter, Write},
7 ops::Deref,
8};
9
10use email_encoding::headers::writer::EmailWriter;
11
12pub use self::{
13 content::*,
14 content_disposition::ContentDisposition,
15 content_type::{ContentType, ContentTypeErr},
16 date::Date,
17 mailbox::*,
18 special::*,
19 textual::*,
20};
21use crate::BoxError;
22
23mod content;
24mod content_disposition;
25mod content_type;
26mod date;
27mod mailbox;
28mod special;
29mod textual;
30
31pub trait Header: Clone {
35 fn name() -> HeaderName;
36
37 fn parse(s: &str) -> Result<Self, BoxError>;
38
39 fn display(&self) -> HeaderValue;
40}
41
42#[derive(Debug, Clone, Default)]
44pub struct Headers {
45 headers: Vec<HeaderValue>,
46}
47
48impl Headers {
49 #[inline]
53 pub const fn new() -> Self {
54 Self {
55 headers: Vec::new(),
56 }
57 }
58
59 #[inline]
63 pub fn with_capacity(capacity: usize) -> Self {
64 Self {
65 headers: Vec::with_capacity(capacity),
66 }
67 }
68
69 pub fn get<H: Header>(&self) -> Option<H> {
73 self.get_raw(&H::name())
74 .and_then(|raw_value| H::parse(raw_value).ok())
75 }
76
77 pub fn set<H: Header>(&mut self, header: H) {
80 self.insert_raw(header.display());
81 }
82
83 pub fn remove<H: Header>(&mut self) -> Option<H> {
87 self.remove_raw(&H::name())
88 .and_then(|value| H::parse(&value.raw_value).ok())
89 }
90
91 #[inline]
95 pub fn clear(&mut self) {
96 self.headers.clear();
97 }
98
99 pub fn get_raw(&self, name: &str) -> Option<&str> {
103 self.find_header(name).map(|value| value.raw_value.as_str())
104 }
105
106 pub fn insert_raw(&mut self, value: HeaderValue) {
109 match self.find_header_mut(&value.name) {
110 Some(current_value) => {
111 *current_value = value;
112 }
113 None => {
114 self.headers.push(value);
115 }
116 }
117 }
118
119 pub fn remove_raw(&mut self, name: &str) -> Option<HeaderValue> {
123 self.find_header_index(name).map(|i| self.headers.remove(i))
124 }
125
126 pub(crate) fn find_header(&self, name: &str) -> Option<&HeaderValue> {
127 self.headers.iter().find(|value| name == value.name)
128 }
129
130 fn find_header_mut(&mut self, name: &str) -> Option<&mut HeaderValue> {
131 self.headers.iter_mut().find(|value| name == value.name)
132 }
133
134 fn find_header_index(&self, name: &str) -> Option<usize> {
135 self.headers
136 .iter()
137 .enumerate()
138 .find(|(_i, value)| name == value.name)
139 .map(|(i, _)| i)
140 }
141}
142
143impl Display for Headers {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 for value in &self.headers {
147 f.write_str(&value.name)?;
148 f.write_str(": ")?;
149 f.write_str(&value.encoded_value)?;
150 f.write_str("\r\n")?;
151 }
152
153 Ok(())
154 }
155}
156
157#[allow(missing_copy_implementations)]
160#[derive(Debug, Clone)]
161#[non_exhaustive]
162pub struct InvalidHeaderName;
163
164impl fmt::Display for InvalidHeaderName {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str("invalid header name")
167 }
168}
169
170impl Error for InvalidHeaderName {}
171
172#[derive(Debug, Clone)]
174pub struct HeaderName(Cow<'static, str>);
175
176impl HeaderName {
177 pub fn new_from_ascii(ascii: String) -> Result<Self, InvalidHeaderName> {
179 if !ascii.is_empty() && ascii.len() <= 76 && ascii.is_ascii() && !ascii.contains([':', ' '])
180 {
181 Ok(Self(Cow::Owned(ascii)))
182 } else {
183 Err(InvalidHeaderName)
184 }
185 }
186
187 pub const fn new_from_ascii_str(ascii: &'static str) -> Self {
189 macro_rules! static_assert {
190 ($condition:expr) => {
191 let _ = [()][(!($condition)) as usize];
192 };
193 }
194
195 static_assert!(!ascii.is_empty());
196 static_assert!(ascii.len() <= 76);
197
198 let bytes = ascii.as_bytes();
199 let mut i = 0;
200 while i < bytes.len() {
201 static_assert!(bytes[i].is_ascii());
202 static_assert!(bytes[i] != b' ');
203 static_assert!(bytes[i] != b':');
204
205 i += 1;
206 }
207
208 Self(Cow::Borrowed(ascii))
209 }
210}
211
212impl Display for HeaderName {
213 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
214 f.write_str(self)
215 }
216}
217
218impl Deref for HeaderName {
219 type Target = str;
220
221 #[inline]
222 fn deref(&self) -> &Self::Target {
223 &self.0
224 }
225}
226
227impl AsRef<[u8]> for HeaderName {
228 #[inline]
229 fn as_ref(&self) -> &[u8] {
230 let s: &str = self.as_ref();
231 s.as_bytes()
232 }
233}
234
235impl AsRef<str> for HeaderName {
236 #[inline]
237 fn as_ref(&self) -> &str {
238 &self.0
239 }
240}
241
242impl PartialEq<HeaderName> for HeaderName {
243 fn eq(&self, other: &HeaderName) -> bool {
244 self.eq_ignore_ascii_case(other)
245 }
246}
247
248impl PartialEq<&str> for HeaderName {
249 fn eq(&self, other: &&str) -> bool {
250 self.eq_ignore_ascii_case(other)
251 }
252}
253
254impl PartialEq<HeaderName> for &str {
255 fn eq(&self, other: &HeaderName) -> bool {
256 self.eq_ignore_ascii_case(other)
257 }
258}
259
260#[derive(Debug, Clone, PartialEq)]
262pub struct HeaderValue {
263 name: HeaderName,
264 raw_value: String,
265 encoded_value: String,
266}
267
268impl HeaderValue {
269 pub fn new(name: HeaderName, raw_value: String) -> Self {
276 let mut encoded_value = String::with_capacity(raw_value.len());
277 HeaderValueEncoder::encode(&name, &raw_value, &mut encoded_value).unwrap();
278
279 Self {
280 name,
281 raw_value,
282 encoded_value,
283 }
284 }
285
286 pub fn dangerous_new_pre_encoded(
295 name: HeaderName,
296 raw_value: String,
297 encoded_value: String,
298 ) -> Self {
299 Self {
300 name,
301 raw_value,
302 encoded_value,
303 }
304 }
305
306 #[cfg(feature = "dkim")]
307 pub(crate) fn get_raw(&self) -> &str {
308 &self.raw_value
309 }
310
311 #[cfg(feature = "dkim")]
312 pub(crate) fn get_encoded(&self) -> &str {
313 &self.encoded_value
314 }
315}
316
317struct HeaderValueEncoder<'a> {
319 writer: EmailWriter<'a>,
320 encode_buf: String,
321}
322
323impl<'a> HeaderValueEncoder<'a> {
324 fn encode(name: &str, value: &'a str, f: &'a mut impl fmt::Write) -> fmt::Result {
325 let encoder = Self::new(name, f);
326 encoder.format(value.split_inclusive(' '))
327 }
328
329 fn new(name: &str, writer: &'a mut dyn Write) -> Self {
330 let line_len = name.len() + ": ".len();
331 let writer = EmailWriter::new(writer, line_len, 0, false);
332
333 Self {
334 writer,
335 encode_buf: String::new(),
336 }
337 }
338
339 fn format(mut self, words_iter: impl Iterator<Item = &'a str>) -> fmt::Result {
340 for next_word in words_iter {
341 let allowed = allowed_str(next_word);
342
343 if allowed {
344 self.flush_encode_buf()?;
348
349 self.writer.folding().write_str(next_word)?;
350 } else {
351 self.encode_buf.push_str(next_word);
353 }
354 }
355
356 self.flush_encode_buf()?;
357
358 Ok(())
359 }
360
361 fn flush_encode_buf(&mut self) -> fmt::Result {
362 if self.encode_buf.is_empty() {
363 return Ok(());
365 }
366
367 let prefix = self.encode_buf.trim_end_matches(' ');
368 email_encoding::headers::rfc2047::encode(prefix, &mut self.writer)?;
369
370 let spaces = self.encode_buf.len() - prefix.len();
372 for _ in 0..spaces {
373 self.writer.space();
374 }
375
376 self.encode_buf.clear();
377 Ok(())
378 }
379}
380
381fn allowed_str(s: &str) -> bool {
382 s.bytes().all(allowed_char)
383}
384
385const fn allowed_char(c: u8) -> bool {
386 c >= 1 && c <= 9 || c == 11 || c == 12 || c >= 14 && c <= 127
387}
388
389#[cfg(test)]
390mod tests {
391 use pretty_assertions::assert_eq;
392
393 use super::{HeaderName, HeaderValue, Headers, To};
394 use crate::message::Mailboxes;
395
396 #[test]
397 fn valid_headername() {
398 assert!(HeaderName::new_from_ascii(String::from("From")).is_ok());
399 }
400
401 #[test]
402 fn non_ascii_headername() {
403 assert!(HeaderName::new_from_ascii(String::from("🌎")).is_err());
404 }
405
406 #[test]
407 fn spaces_in_headername() {
408 assert!(HeaderName::new_from_ascii(String::from("From ")).is_err());
409 }
410
411 #[test]
412 fn colons_in_headername() {
413 assert!(HeaderName::new_from_ascii(String::from("From:")).is_err());
414 }
415
416 #[test]
417 fn empty_headername() {
418 assert!(HeaderName::new_from_ascii("".to_owned()).is_err());
419 }
420
421 #[test]
422 fn const_valid_headername() {
423 let _ = HeaderName::new_from_ascii_str("From");
424 }
425
426 #[test]
427 #[should_panic]
428 fn const_non_ascii_headername() {
429 let _ = HeaderName::new_from_ascii_str("🌎");
430 }
431
432 #[test]
433 #[should_panic]
434 fn const_spaces_in_headername() {
435 let _ = HeaderName::new_from_ascii_str("From ");
436 }
437
438 #[test]
439 #[should_panic]
440 fn const_colons_in_headername() {
441 let _ = HeaderName::new_from_ascii_str("From:");
442 }
443
444 #[test]
445 #[should_panic]
446 fn const_empty_headername() {
447 let _ = HeaderName::new_from_ascii_str("");
448 }
449
450 #[test]
451 fn headername_headername_eq() {
452 assert_eq!(
453 HeaderName::new_from_ascii_str("From"),
454 HeaderName::new_from_ascii_str("From")
455 );
456 }
457
458 #[test]
459 fn headername_str_eq() {
460 assert_eq!(HeaderName::new_from_ascii_str("From"), "From");
461 }
462
463 #[test]
464 fn str_headername_eq() {
465 assert_eq!("From", HeaderName::new_from_ascii_str("From"));
466 }
467
468 #[test]
469 fn headername_headername_eq_case_insensitive() {
470 assert_eq!(
471 HeaderName::new_from_ascii_str("From"),
472 HeaderName::new_from_ascii_str("from")
473 );
474 }
475
476 #[test]
477 fn headername_str_eq_case_insensitive() {
478 assert_eq!(HeaderName::new_from_ascii_str("From"), "from");
479 }
480
481 #[test]
482 fn str_headername_eq_case_insensitive() {
483 assert_eq!("from", HeaderName::new_from_ascii_str("From"));
484 }
485
486 #[test]
487 fn headername_headername_ne() {
488 assert_ne!(
489 HeaderName::new_from_ascii_str("From"),
490 HeaderName::new_from_ascii_str("To")
491 );
492 }
493
494 #[test]
495 fn headername_str_ne() {
496 assert_ne!(HeaderName::new_from_ascii_str("From"), "To");
497 }
498
499 #[test]
500 fn str_headername_ne() {
501 assert_ne!("From", HeaderName::new_from_ascii_str("To"));
502 }
503
504 #[test]
507 fn format_ascii() {
508 let mut headers = Headers::new();
509 headers.insert_raw(HeaderValue::new(
510 HeaderName::new_from_ascii_str("To"),
511 "John Doe <example@example.com>, Jean Dupont <jean@example.com>".to_owned(),
512 ));
513
514 assert_eq!(
515 headers.to_string(),
516 "To: John Doe <example@example.com>, Jean Dupont <jean@example.com>\r\n"
517 );
518 }
519
520 #[test]
521 fn format_ascii_with_folding() {
522 let mut headers = Headers::new();
523 headers.insert_raw(HeaderValue::new(
524 HeaderName::new_from_ascii_str("To"),
525 "Ascii <example@example.com>, John Doe <johndoe@example.com, John Smith <johnsmith@example.com>, Pinco Pallino <pincopallino@example.com>, Jemand <jemand@example.com>, Jean Dupont <jean@example.com>".to_owned(),
526 ));
527
528 assert_eq!(
529 headers.to_string(),
530 concat!(
531 "To: Ascii <example@example.com>, John Doe <johndoe@example.com, John Smith\r\n",
532 " <johnsmith@example.com>, Pinco Pallino <pincopallino@example.com>, Jemand\r\n",
533 " <jemand@example.com>, Jean Dupont <jean@example.com>\r\n"
534 )
535 );
536 }
537
538 #[test]
539 fn format_ascii_with_folding_long_line() {
540 let mut headers = Headers::new();
541 headers.insert_raw(HeaderValue::new(
542 HeaderName::new_from_ascii_str("Subject"),
543 "Hello! This is lettre, and this IsAVeryLongLineDoYouKnowWhatsGoingToHappenIGuessWeAreGoingToFindOut. Ok I guess that's it!".to_owned()
544 ));
545
546 assert_eq!(
547 headers.to_string(),
548 concat!(
549 "Subject: Hello! This is lettre, and this\r\n",
550 " IsAVeryLongLineDoYouKnowWhatsGoingToHappenIGuessWeAreGoingToFindOut. Ok I\r\n",
551 " guess that's it!\r\n"
552 )
553 );
554 }
555
556 #[test]
557 fn format_ascii_with_folding_very_long_line() {
558 let mut headers = Headers::new();
559 headers.insert_raw(
560 HeaderValue::new(
561 HeaderName::new_from_ascii_str("Subject"),
562 "Hello! IGuessTheLastLineWasntLongEnoughSoLetsTryAgainShallWeWhatDoYouThinkItsGoingToHappenIGuessWereAboutToFindOut! I don't know".to_owned()
563 ));
564
565 assert_eq!(
566 headers.to_string(),
567 concat!(
568 "Subject: Hello!\r\n",
569 " IGuessTheLastLineWasntLongEnoughSoLetsTryAgainShallWeWhatDoYouThinkItsGoingToHappenIGuessWereAboutToFindOut!\r\n",
570 " I don't know\r\n",
571 )
572 );
573 }
574
575 #[test]
576 fn format_ascii_with_folding_giant_word() {
577 let mut headers = Headers::new();
578 headers.insert_raw(HeaderValue::new(
579 HeaderName::new_from_ascii_str("Subject"),
580 "1abcdefghijklmnopqrstuvwxyz2abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz4abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz6abcdefghijklmnopqrstuvwxyz".to_owned()
581 ));
582
583 assert_eq!(
584 headers.to_string(),
585 "Subject: 1abcdefghijklmnopqrstuvwxyz2abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz4abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz6abcdefghijklmnopqrstuvwxyz\r\n",
586 );
587 }
588
589 #[test]
590 fn format_special() {
591 let mut headers = Headers::new();
592 headers.insert_raw(HeaderValue::new(
593 HeaderName::new_from_ascii_str("To"),
594 "Seán <sean@example.com>".to_owned(),
595 ));
596
597 assert_eq!(
598 headers.to_string(),
599 "To: =?utf-8?b?U2XDoW4=?= <sean@example.com>\r\n"
600 );
601 }
602
603 #[test]
604 fn format_special_emoji() {
605 let mut headers = Headers::new();
606 headers.insert_raw(HeaderValue::new(
607 HeaderName::new_from_ascii_str("To"),
608 "🌎 <world@example.com>".to_owned(),
609 ));
610
611 assert_eq!(
612 headers.to_string(),
613 "To: =?utf-8?b?8J+Mjg==?= <world@example.com>\r\n"
614 );
615 }
616
617 #[test]
618 fn format_special_with_folding() {
619 let mut headers = Headers::new();
620 let to = To::from(Mailboxes::from_iter([
621 "🌍 <world@example.com>".parse().unwrap(),
622 "🦆 Everywhere <ducks@example.com>".parse().unwrap(),
623 "Иванов Иван Иванович <ivanov@example.com>".parse().unwrap(),
624 "Jānis Bērziņš <janis@example.com>".parse().unwrap(),
625 "Seán Ó Rudaí <sean@example.com>".parse().unwrap(),
626 ]));
627 headers.set(to);
628
629 assert_eq!(
630 headers.to_string(),
631 concat!(
632 "To: =?utf-8?b?8J+MjQ==?= <world@example.com>, =?utf-8?b?8J+mhiBFdmVyeXdo?=\r\n",
633 " =?utf-8?b?ZXJl?= <ducks@example.com>, =?utf-8?b?0JjQstCw0L3QvtCyINCY0LI=?=\r\n",
634 " =?utf-8?b?0LDQvSDQmNCy0LDQvdC+0LLQuNGH?= <ivanov@example.com>,\r\n",
635 " =?utf-8?b?SsSBbmlzIELEk3J6acWGxaE=?= <janis@example.com>, =?utf-8?b?U2U=?=\r\n",
636 " =?utf-8?b?w6FuIMOTIFJ1ZGHDrQ==?= <sean@example.com>\r\n",
637 )
638 );
639 }
640
641 #[test]
642 fn format_special_with_folding_raw() {
643 let mut headers = Headers::new();
644 headers.insert_raw(HeaderValue::new(
645 HeaderName::new_from_ascii_str("To"),
646 "🌍 <world@example.com>, 🦆 Everywhere <ducks@example.com>, Иванов Иван Иванович <ivanov@example.com>, Jānis Bērziņš <janis@example.com>, Seán Ó Rudaí <sean@example.com>".to_owned(),
647 ));
648
649 assert_eq!(
650 headers.to_string(),
651 concat!(
652 "To: =?utf-8?b?8J+MjQ==?= <world@example.com>, =?utf-8?b?8J+mhg==?=\r\n",
653 " Everywhere <ducks@example.com>, =?utf-8?b?0JjQstCw0L3QvtCyINCY0LLQsNC9?=\r\n",
654 " =?utf-8?b?INCY0LLQsNC90L7QstC40Yc=?= <ivanov@example.com>,\r\n",
655 " =?utf-8?b?SsSBbmlzIELEk3J6acWGxaE=?= <janis@example.com>, =?utf-8?b?U2U=?=\r\n",
656 " =?utf-8?b?w6FuIMOTIFJ1ZGHDrQ==?= <sean@example.com>\r\n",
657 )
658 );
659 }
660
661 #[test]
662 fn format_slice_on_char_boundary_bug() {
663 let mut headers = Headers::new();
664 headers.insert_raw(
665 HeaderValue::new(
666 HeaderName::new_from_ascii_str("Subject"),
667 "🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳".to_owned(),)
668 );
669
670 assert_eq!(
671 headers.to_string(),
672 concat!(
673 "Subject: =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz?=\r\n",
674 " =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbM=?=\r\n",
675 " =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbM=?=\r\n",
676 " =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbM=?=\r\n",
677 " =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+ls/CfpbM=?=\r\n",
678 " =?utf-8?b?8J+ls/CfpbPwn6Wz8J+ls/CfpbPwn6Wz8J+lsw==?=\r\n"
679 )
680 );
681 }
682
683 #[test]
684 fn format_bad_stuff() {
685 let mut headers = Headers::new();
686 headers.insert_raw(HeaderValue::new(
687 HeaderName::new_from_ascii_str("Subject"),
688 "Hello! \r\n This is \" bad \0. 👋".to_owned(),
689 ));
690
691 assert_eq!(
692 headers.to_string(),
693 "Subject: Hello! =?utf-8?b?DQo=?= This is \" bad =?utf-8?b?AC4g8J+Riw==?=\r\n"
694 );
695 }
696
697 #[test]
698 fn format_everything() {
699 let mut headers = Headers::new();
700 headers.insert_raw(
701 HeaderValue::new(
702 HeaderName::new_from_ascii_str("Subject"),
703 "Hello! This is lettre, and this IsAVeryLongLineDoYouKnowWhatsGoingToHappenIGuessWeAreGoingToFindOut. Ok I guess that's it!".to_owned()
704 )
705 );
706 headers.insert_raw(
707 HeaderValue::new(
708 HeaderName::new_from_ascii_str("To"),
709 "🌍 <world@example.com>, 🦆 Everywhere <ducks@example.com>, Иванов Иван Иванович <ivanov@example.com>, Jānis Bērziņš <janis@example.com>, Seán Ó Rudaí <sean@example.com>".to_owned(),
710 )
711 );
712 headers.insert_raw(HeaderValue::new(
713 HeaderName::new_from_ascii_str("From"),
714 "Someone <somewhere@example.com>".to_owned(),
715 ));
716 headers.insert_raw(HeaderValue::new(
717 HeaderName::new_from_ascii_str("Content-Transfer-Encoding"),
718 "quoted-printable".to_owned(),
719 ));
720
721 assert_eq!(
722 headers.to_string(),
723 concat!(
724 "Subject: Hello! This is lettre, and this\r\n",
725 " IsAVeryLongLineDoYouKnowWhatsGoingToHappenIGuessWeAreGoingToFindOut. Ok I\r\n",
726 " guess that's it!\r\n",
727 "To: =?utf-8?b?8J+MjQ==?= <world@example.com>, =?utf-8?b?8J+mhg==?=\r\n",
728 " Everywhere <ducks@example.com>, =?utf-8?b?0JjQstCw0L3QvtCyINCY0LLQsNC9?=\r\n",
729 " =?utf-8?b?INCY0LLQsNC90L7QstC40Yc=?= <ivanov@example.com>,\r\n",
730 " =?utf-8?b?SsSBbmlzIELEk3J6acWGxaE=?= <janis@example.com>, =?utf-8?b?U2U=?=\r\n",
731 " =?utf-8?b?w6FuIMOTIFJ1ZGHDrQ==?= <sean@example.com>\r\n",
732 "From: Someone <somewhere@example.com>\r\n",
733 "Content-Transfer-Encoding: quoted-printable\r\n",
734 )
735 );
736 }
737
738 #[test]
739 fn issue_653() {
740 let mut headers = Headers::new();
741 headers.insert_raw(HeaderValue::new(
742 HeaderName::new_from_ascii_str("Subject"),
743 "+仮名 :a;go; ;;;;;s;;;;;;;;;;;;;;;;fffeinmjgggggggggfっ".to_owned(),
744 ));
745
746 assert_eq!(
747 headers.to_string(),
748 concat!(
749 "Subject: =?utf-8?b?77yL5Luu5ZCN?= :a;go; =?utf-8?b?Ozs7OztzOzs7Ozs7Ozs7?=\r\n",
750 " =?utf-8?b?Ozs7Ozs7O2ZmZmVpbm1qZ2dnZ2dnZ2dn772G44Gj?=\r\n",
751 )
752 );
753 }
754}