mail_builder/encoders/
base64.rs1use std::io::{self, Write};
8
9const CHARPAD: u8 = b'=';
10
11#[inline(always)]
12pub fn base64_encode(input: &[u8]) -> io::Result<Vec<u8>> {
13 let mut buf = Vec::with_capacity(4 * (input.len() / 3));
14 base64_encode_mime(input, &mut buf, true)?;
15 Ok(buf)
16}
17
18pub fn base64_encode_mime(
19 input: &[u8],
20 mut output: impl Write,
21 is_inline: bool,
22) -> io::Result<usize> {
23 let mut i = 0;
24 let mut t1;
25 let mut t2;
26 let mut t3;
27 let mut bytes_written = 0;
28
29 if input.len() > 2 {
30 while i < input.len() - 2 {
31 t1 = input[i];
32 t2 = input[i + 1];
33 t3 = input[i + 2];
34
35 output.write_all(&[
36 E0[t1 as usize],
37 E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize],
38 E1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) as usize],
39 E2[t3 as usize],
40 ])?;
41
42 bytes_written += 4;
43
44 if !is_inline && bytes_written % 19 == 0 {
45 output.write_all(b"\r\n")?;
46 }
47
48 i += 3;
49 }
50 }
51
52 let remaining = input.len() - i;
53 if remaining > 0 {
54 t1 = input[i];
55 if remaining == 1 {
56 output.write_all(&[
57 E0[t1 as usize],
58 E1[((t1 & 0x03) << 4) as usize],
59 CHARPAD,
60 CHARPAD,
61 ])?;
62 } else {
63 t2 = input[i + 1];
64 output.write_all(&[
65 E0[t1 as usize],
66 E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize],
67 E2[((t2 & 0x0F) << 2) as usize],
68 CHARPAD,
69 ])?;
70 }
71
72 bytes_written += 4;
73
74 if !is_inline && bytes_written % 19 == 0 {
75 output.write_all(b"\r\n")?;
76 }
77 }
78
79 if !is_inline && bytes_written % 19 != 0 {
80 output.write_all(b"\r\n")?;
81 }
82
83 Ok(bytes_written)
84}
85
86#[cfg(test)]
87#[allow(clippy::items_after_test_module)]
88mod tests {
89
90 #[test]
91 fn encode_base64() {
92 for (input, expected_result, is_inline) in [
93 ("Test".to_string(), "VGVzdA==\r\n", false),
94 ("Ye".to_string(), "WWU=\r\n", false),
95 ("A".to_string(), "QQ==\r\n", false),
96 ("ro".to_string(), "cm8=\r\n", false),
97 (
98 "Are you a Shimano or Campagnolo person?".to_string(),
99 "QXJlIHlvdSBhIFNoaW1hbm8gb3IgQ2FtcGFnbm9sbyBwZXJzb24/\r\n",
100 false,
101 ),
102 (
103 "<!DOCTYPE html>\n<html>\n<body>\n</body>\n</html>\n".to_string(),
104 "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8Ym9keT4KPC9ib2R5Pgo8L2h0bWw+Cg==\r\n",
105 false,
106 ),
107 ("áéíóú".to_string(), "w6HDqcOtw7PDug==\r\n", false),
108 (
109 " ".repeat(100),
110 concat!(
111 "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg",
112 "ICAgICAgICAgICAgICAgICAgICAgICAgICAg\r\n",
113 "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg",
114 "ICAgICAgICAgICAgIA==\r\n",
115 ),
116 false,
117 ),
118 ] {
119 let mut output = Vec::new();
120 super::base64_encode_mime(input.as_bytes(), &mut output, is_inline).unwrap();
121 assert_eq!(std::str::from_utf8(&output).unwrap(), expected_result);
122 }
123 }
124}
125
126pub static E0: &[u8] = b"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZaaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzz0000111122223333444455556666777788889999++++////";
139pub static E1: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
140pub static E2: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";