email_encoding/headers/
rfc2047.rs1use std::fmt::{self, Write};
6
7use super::{utils, writer::EmailWriter, MAX_LINE_LEN};
8
9const ENCODING_START_PREFIX: &str = "=?utf-8?b?";
10const ENCODING_END_SUFFIX: &str = "?=";
11
12pub fn encode(mut s: &str, w: &mut EmailWriter<'_>) -> fmt::Result {
31 let mut wrote = false;
32
33 while !s.is_empty() {
34 let remaining_line_len = MAX_LINE_LEN.saturating_sub(
35 ENCODING_START_PREFIX.len() + ENCODING_END_SUFFIX.len() + w.line_len() + "\r\n".len(),
36 );
37 let unencoded_remaining_line_len = remaining_line_len / 4 * 3;
38
39 let mut word =
40 utils::truncate_to_char_boundary(s, unencoded_remaining_line_len.min(s.len()));
41 if word.is_empty() {
42 if wrote || w.has_spaces() {
43 w.new_line()?;
45 if !w.has_spaces() {
46 w.space();
51 }
52 continue;
53 }
54
55 word = &s[..s.chars().next().expect("`s` is empty").len_utf8()];
58 }
59
60 w.write_str(ENCODING_START_PREFIX)?;
62
63 let encoder = base64::display::Base64Display::new(
65 word.as_bytes(),
66 &base64::engine::general_purpose::STANDARD,
67 );
68 write!(w, "{}", encoder)?;
69
70 w.write_str(ENCODING_END_SUFFIX)?;
72
73 s = &s[word.len()..];
74 wrote = true;
75 }
76
77 Ok(())
78}
79
80#[cfg(test)]
81mod tests {
82 use pretty_assertions::assert_eq;
83
84 use super::*;
85
86 #[test]
87 fn empty() {
88 let mut s = String::new();
89 let line_len = s.len();
90
91 {
92 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
93 encode("", &mut w).unwrap();
94 }
95
96 assert_eq!(s, "");
97 }
98
99 #[test]
100 fn basic() {
101 let mut s = String::new();
102 let line_len = s.len();
103
104 {
105 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
106 encode("abcd", &mut w).unwrap();
107 }
108
109 assert_eq!(s, "=?utf-8?b?YWJjZA==?=");
110 }
111
112 #[test]
113 fn basic_nopad() {
114 let mut s = String::new();
115 let line_len = s.len();
116
117 {
118 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
119 encode("abcdef", &mut w).unwrap();
120 }
121
122 assert_eq!(s, "=?utf-8?b?YWJjZGVm?=");
123 }
124
125 #[test]
126 fn long() {
127 let mut s = String::new();
128 let line_len = s.len();
129
130 {
131 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
132 encode(&"lettre".repeat(20), &mut w).unwrap();
133 }
134
135 assert_eq!(
136 s,
137 concat!(
138 "=?utf-8?b?bGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0?=\r\n",
139 " =?utf-8?b?dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJl?=\r\n",
140 " =?utf-8?b?bGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJl?="
141 )
142 );
143 }
144
145 #[test]
146 fn long_encoded() {
147 let mut s = String::new();
148 let line_len = s.len();
149
150 {
151 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
152 encode(&"hétérogénéité".repeat(16), &mut w).unwrap();
153 }
154
155 assert_eq!(
156 s,
157 concat!(
158 "=?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
159 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
160 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
161 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
162 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
163 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
164 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOp?=",
165 )
166 );
167 }
168}