1pub fn canonicalize_headers_simple(headers: &[(&str, &str, &str)], signed_headers: &[String]) -> String {
3 let mut canonicalized_headers = String::new();
4 let mut already_used = Vec::new();
5
6 for signed_header in signed_headers {
7 for (idx, (name, separator, value)) in headers
8 .iter()
9 .enumerate()
10 .filter(|(idx, _)| !already_used.contains(idx))
11 {
12 if unicase::eq_ascii(signed_header.as_str(), name) {
13 canonicalized_headers.push_str(name);
14 canonicalized_headers.push_str(separator);
15 canonicalized_headers.push_str(value);
16 canonicalized_headers.push_str("\r\n");
17
18 already_used.push(idx);
19 break;
20 }
21 }
22 }
23 canonicalized_headers
24}
25
26pub fn canonicalize_body_simple(mut body: &str) -> &str {
30 if body.is_empty() {
31 return "\r\n";
32 }
33
34 while body.ends_with("\r\n\r\n") {
35 body = &body[..body.len() - 2];
36 }
37
38 body
39}
40
41pub fn canonicalize_header_relaxed(mut value: String) -> String {
45 value = value.replace('\t', " ");
46 value = value.replace("\r\n", "");
47
48 while value.ends_with(' ') {
49 value.remove(value.len() - 1);
50 }
51 while value.starts_with(' ') {
52 value.remove(0);
53 }
54 let mut previous = false;
55 value.retain(|c| {
56 if c == ' ' {
57 if previous {
58 false
59 } else {
60 previous = true;
61 true
62 }
63 } else {
64 previous = false;
65 true
66 }
67 });
68
69 value
70}
71
72pub fn canonicalize_headers_relaxed(headers: &[(&str, &str, &str)], signed_headers: &[String]) -> String {
74 let mut canonicalized_headers = String::new();
75 let mut already_used = Vec::new();
76
77 for signed_header in signed_headers {
78 for (idx, (name, _separator, value)) in headers
79 .iter()
80 .enumerate()
81 .filter(|(idx, _)| !already_used.contains(idx))
82 {
83 if unicase::eq_ascii(signed_header.as_str(), name) {
84 canonicalized_headers.push_str(&name.to_lowercase());
85 canonicalized_headers.push_str(":");
86 canonicalized_headers.push_str(&canonicalize_header_relaxed(value.to_string()));
87 canonicalized_headers.push_str("\r\n");
88
89 already_used.push(idx);
90 break;
91 }
92 }
93 }
94 canonicalized_headers
95}
96
97pub fn canonicalize_body_relaxed(mut body: String) -> String {
101 body = body.replace('\t', " ");
105 let mut previous = false;
106 body.retain(|c| {
107 if c == ' ' {
108 if previous {
109 false
110 } else {
111 previous = true;
112 true
113 }
114 } else {
115 previous = false;
116 true
117 }
118 });
119
120 while let Some(idx) = body.find(" \r\n") {
122 body.remove(idx);
123 }
124
125 while body.ends_with("\r\n\r\n") {
127 body.remove(body.len() - 1);
128 body.remove(body.len() - 1);
129 }
130
131 if !body.is_empty() && !body.ends_with("\r\n") {
133 body.push_str("\r\n");
134 }
135
136 body
137}
138
139#[cfg(test)]
148mod test {
149 use super::*;
150 use pretty_assertions::assert_eq;
151 use std::convert::TryFrom;
152 use string_tools::get_all_after;
153 use crate::email::Email;
154
155 const MAIL: &str = "A: X\r\nB : Y\t\r\n\tZ \r\n\r\n C \r\nD \t E\r\n\r\n\r\n";
156
157 #[test]
158 fn canonicalize_body_relaxed_test() {
159 assert_eq!(
160 canonicalize_body_relaxed(get_all_after(MAIL, "\r\n\r\n").to_string()),
161 " C\r\nD E\r\n"
162 );
163 }
164
165 #[test]
166 fn canonicalize_headers_relaxed_test() {
167 let mail = Email::try_from(MAIL).unwrap();
168 assert_eq!(
169 canonicalize_headers_relaxed(&mail.parsed.0, &["a".to_string(), "b".to_string()]),
170 "a:X\r\nb:Y Z\r\n"
171 );
172 assert_eq!(
173 canonicalize_headers_relaxed(&mail.parsed.0, &["b".to_string(), "a".to_string()]),
174 "b:Y Z\r\na:X\r\n"
175 );
176 }
177
178 #[test]
179 fn canonicalize_body_simple_test() {
180 assert_eq!(
181 canonicalize_body_simple(get_all_after(MAIL, "\r\n\r\n")),
182 " C \r\nD \t E\r\n"
183 );
184 }
185
186 #[test]
187 fn canonicalize_headers_simple_test() {
188 let mail = Email::try_from(MAIL).unwrap();
189 assert_eq!(
190 canonicalize_headers_simple(&mail.parsed.0, &["a".to_string(), "b".to_string()]),
191 "A: X\r\nB : Y\t\r\n\tZ \r\n"
192 );
193 assert_eq!(
194 canonicalize_headers_simple(&mail.parsed.0, &["b".to_string(), "a".to_string()]),
195 "B : Y\t\r\n\tZ \r\nA: X\r\n"
196 );
197 }
198}