mail_builder/encoders/
encode.rs1use std::io::{self, Write};
13
14use super::{base64::base64_encode_mime, quoted_printable::quoted_printable_encode};
15
16pub enum EncodingType {
17 Base64,
18 QuotedPrintable(bool),
19 None,
20}
21
22pub fn get_encoding_type(input: &[u8], is_inline: bool, is_body: bool) -> EncodingType {
23 let base64_len = (input.len() * 4 / 3 + 3) & !3;
24 let mut qp_len = if !is_inline { input.len() / 76 } else { 0 };
25 let mut is_ascii = true;
26 let mut needs_encoding = false;
27 let mut line_len = 0;
28 let mut prev_ch = 0;
29
30 for (pos, &ch) in input.iter().enumerate() {
31 line_len += 1;
32
33 if ch >= 127
34 || ((ch == b' ' || ch == b'\t')
35 && ((is_body && matches!(input.get(pos + 1..), Some([b'\n', ..] | [b'\r', b'\n', ..])))
36 || pos == input.len() - 1))
37 {
38 qp_len += 3;
39 if !needs_encoding {
40 needs_encoding = true;
41 }
42 if is_ascii && ch >= 127 {
43 is_ascii = false;
44 }
45 } else if ch == b'='
46 || (!is_body && ch == b'\r')
47 || (is_inline && (ch == b'\t' || ch == b'\r' || ch == b'\n' || ch == b'?'))
48 {
49 qp_len += 3;
50 } else if ch == b'\n' {
51 if !needs_encoding && line_len > 997 {
52 needs_encoding = true;
53 }
54 if is_body {
55 if prev_ch != b'\r' {
56 qp_len += 1;
57 }
58 qp_len += 1;
59 } else {
60 if !needs_encoding && prev_ch != b'\r' {
61 needs_encoding = true;
62 }
63 qp_len += 3;
64 }
65 line_len = 0;
66 } else {
67 qp_len += 1;
68 }
69
70 prev_ch = ch;
71 }
72
73 if !needs_encoding {
74 EncodingType::None
75 } else if qp_len < base64_len {
76 EncodingType::QuotedPrintable(is_ascii)
77 } else {
78 EncodingType::Base64
79 }
80}
81
82pub fn rfc2047_encode(input: &str, mut output: impl Write) -> io::Result<usize> {
83 Ok(match get_encoding_type(input.as_bytes(), true, false) {
84 EncodingType::Base64 => {
85 output.write_all(b"\"=?utf-8?B?")?;
86 let bytes_written = base64_encode_mime(input.as_bytes(), &mut output, true)? + 14;
87 output.write_all(b"?=\"")?;
88 bytes_written
89 }
90 EncodingType::QuotedPrintable(is_ascii) => {
91 if !is_ascii {
92 output.write_all(b"\"=?utf-8?Q?")?;
93 } else {
94 output.write_all(b"\"=?us-ascii?Q?")?;
95 }
96 let bytes_written =
97 quoted_printable_encode(input.as_bytes(), &mut output, true, false)? + if is_ascii { 19 } else { 14 };
98 output.write_all(b"?=\"")?;
99 bytes_written
100 }
101 EncodingType::None => {
102 let mut bytes_written = 2;
103 output.write_all(b"\"")?;
104 for &ch in input.as_bytes() {
105 if ch == b'\\' || ch == b'"' {
106 output.write_all(b"\\")?;
107 bytes_written += 1;
108 } else if ch == b'\r' || ch == b'\n' {
109 continue;
110 }
111 output.write_all(&[ch])?;
112 bytes_written += 1;
113 }
114 output.write_all(b"\"")?;
115 bytes_written
116 }
117 })
118}