mail_builder/encoders/
encode.rs1use std::io::{self, Write};
8
9use super::{base64::base64_encode_mime, quoted_printable::inline_quoted_printable_encode};
10
11pub enum EncodingType {
12 Base64,
13 QuotedPrintable(bool),
14 None,
15}
16
17pub fn get_encoding_type(input: &[u8], is_inline: bool, is_body: bool) -> EncodingType {
18 let base64_len = (input.len() * 4 / 3 + 3) & !3;
19 let mut qp_len = if !is_inline { input.len() / 76 } else { 0 };
20 let mut is_ascii = true;
21 let mut needs_encoding = false;
22 let mut line_len = 0;
23 let mut prev_ch = 0;
24
25 for (pos, &ch) in input.iter().enumerate() {
26 line_len += 1;
27
28 if ch >= 127
29 || ((ch == b' ' || ch == b'\t')
30 && ((is_body
31 && matches!(input.get(pos + 1..), Some([b'\n', ..] | [b'\r', b'\n', ..])))
32 || pos == input.len() - 1))
33 {
34 qp_len += 3;
35 if !needs_encoding {
36 needs_encoding = true;
37 }
38 if is_ascii && ch >= 127 {
39 is_ascii = false;
40 }
41 } else if ch == b'='
42 || (!is_body && ch == b'\r')
43 || (is_inline && (ch == b'\t' || ch == b'\r' || ch == b'\n' || ch == b'?'))
44 {
45 qp_len += 3;
46 } else if ch == b'\n' {
47 if !needs_encoding && line_len > 77 {
48 needs_encoding = true;
49 }
50 if is_body {
51 if prev_ch != b'\r' {
52 qp_len += 1;
53 }
54 qp_len += 1;
55 } else {
56 if !needs_encoding && prev_ch != b'\r' {
57 needs_encoding = true;
58 }
59 qp_len += 3;
60 }
61 line_len = 0;
62 } else {
63 qp_len += 1;
64 }
65
66 prev_ch = ch;
67 }
68
69 if !needs_encoding && line_len > 77 {
70 needs_encoding = true;
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 = inline_quoted_printable_encode(input.as_bytes(), &mut output)?
97 + 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}