1use crate::{canonicalization, hash, DKIMError};
2use nom::bytes::complete::{tag, take_while1};
3use nom::character::complete::alpha1;
4use nom::combinator::opt;
5use nom::multi::fold_many0;
6use nom::sequence::{delimited, pair, preceded, terminated};
7use nom::{IResult, Parser};
8
9#[derive(Clone, Debug, PartialEq)]
10pub struct Tag {
12 pub name: String,
14 pub value: String,
16 pub raw_value: String,
18}
19
20pub fn tag_list(input: &str) -> IResult<&str, Vec<Tag>> {
24 let (input, start) = tag_spec.parse(input)?;
25
26 terminated(
27 fold_many0(
28 preceded(tag(";"), tag_spec),
29 move || vec![start.clone()],
30 |mut acc: Vec<Tag>, item| {
31 acc.push(item);
32 acc
33 },
34 ),
35 opt(tag(";")),
36 )
37 .parse(input)
38}
39
40fn tag_spec(input: &str) -> IResult<&str, Tag> {
42 let (input, name) = delimited(opt(fws), tag_name, opt(fws)).parse(input)?;
43 let (input, _) = tag("=").parse(input)?;
44
45 let value_input = input;
47 let (_, raw_value) = delimited(opt(fws), raw_tag_value, opt(fws)).parse(value_input)?;
48 let (input, value) = delimited(opt(fws), tag_value, opt(fws)).parse(value_input)?;
49
50 Ok((
51 input,
52 Tag {
53 name: name.to_owned(),
54 value,
55 raw_value,
56 },
57 ))
58}
59
60fn tag_name(input: &str) -> IResult<&str, &str> {
63 alpha1.parse(input)
64}
65
66fn tag_value(input: &str) -> IResult<&str, String> {
70 let is_valchar = |c| ('!'..=':').contains(&c) || ('<'..='~').contains(&c);
71 match opt(take_while1(is_valchar)).parse(input)? {
72 (input, Some(start)) => fold_many0(
73 preceded(fws, take_while1(is_valchar)),
74 || start.to_owned(),
75 |mut acc: String, item| {
76 acc += item;
77 acc
78 },
79 )
80 .parse(input),
81 (input, None) => Ok((input, "".to_string())),
82 }
83}
84
85fn raw_tag_value(input: &str) -> IResult<&str, String> {
86 let is_valchar = |c| ('!'..=':').contains(&c) || ('<'..='~').contains(&c);
87 match opt(take_while1(is_valchar)).parse(input)? {
88 (input, Some(start)) => fold_many0(
89 pair(fws, take_while1(is_valchar)),
90 || start.to_owned(),
91 |mut acc: String, item| {
92 acc += &(item.0.to_owned() + item.1);
93 acc
94 },
95 )
96 .parse(input),
97 (input, None) => Ok((input, "".to_string())),
98 }
99}
100
101fn fws(input: &str) -> IResult<&str, &str> {
103 take_while1(|c| c == ' ' || c == '\t' || c == '\r' || c == '\n').parse(input)
104}
105
106pub(crate) fn parse_hash_algo(value: &str) -> Result<hash::HashAlgo, DKIMError> {
107 use hash::HashAlgo;
108 match value {
109 "rsa-sha1" => Ok(HashAlgo::RsaSha1),
110 "rsa-sha256" => Ok(HashAlgo::RsaSha256),
111 "ed25519-sha256" => Ok(HashAlgo::Ed25519Sha256),
112 e => Err(DKIMError::UnsupportedHashAlgorithm(e.to_string())),
113 }
114}
115
116pub(crate) fn parse_canonicalization(
119 value: Option<&str>,
120) -> Result<(canonicalization::Type, canonicalization::Type), DKIMError> {
121 use canonicalization::Type::{Relaxed, Simple};
122 match value {
123 None => Ok((Simple, Simple)),
124 Some(s) => match s {
125 "simple/simple" => Ok((Simple, Simple)),
126 "relaxed/simple" => Ok((Relaxed, Simple)),
127 "simple/relaxed" => Ok((Simple, Relaxed)),
128 "relaxed/relaxed" => Ok((Relaxed, Relaxed)),
129 "relaxed" => Ok((Relaxed, Simple)),
130 "simple" => Ok((Simple, Simple)),
131 v => Err(DKIMError::UnsupportedCanonicalizationType(v.to_owned())),
132 },
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_canonicalization_empty() {
142 use canonicalization::Type::Simple;
143 assert_eq!(parse_canonicalization(None).unwrap(), (Simple, Simple));
144 }
145
146 #[test]
147 fn test_canonicalization_one_algo() {
148 use canonicalization::Type::{Relaxed, Simple};
149
150 assert_eq!(
151 parse_canonicalization(Some("simple")).unwrap(),
152 (Simple, Simple)
153 );
154 assert_eq!(
155 parse_canonicalization(Some("relaxed")).unwrap(),
156 (Relaxed, Simple)
157 );
158 }
159
160 #[test]
161 fn test_tag_list() {
162 assert_eq!(
163 tag_list("a = a/1@.-:= ").unwrap(),
164 (
165 "",
166 vec![Tag {
167 name: "a".to_string(),
168 value: "a/1@.-:=".to_string(),
169 raw_value: "a/1@.-:=".to_string()
170 }]
171 )
172 );
173 assert_eq!(
174 tag_list("a= a ; b = a\n bc").unwrap(),
175 (
176 "",
177 vec![
178 Tag {
179 name: "a".to_string(),
180 value: "a".to_string(),
181 raw_value: "a".to_string()
182 },
183 Tag {
184 name: "b".to_string(),
185 value: "abc".to_string(),
186 raw_value: "a\n bc".to_string()
187 }
188 ]
189 )
190 );
191 }
192
193 #[test]
194 fn test_tag_spec() {
195 assert_eq!(
196 tag_spec("a=b").unwrap(),
197 (
198 "",
199 Tag {
200 name: "a".to_string(),
201 value: "b".to_string(),
202 raw_value: "b".to_string()
203 }
204 )
205 );
206 assert_eq!(
207 tag_spec("a=b c d e f").unwrap(),
208 (
209 "",
210 Tag {
211 name: "a".to_string(),
212 value: "bcdef".to_string(),
213 raw_value: "b c d e f".to_string()
214 }
215 )
216 );
217 }
218
219 #[test]
220 fn test_tag_list_dns() {
221 assert_eq!(
222 tag_list("k=rsa; p=kEy+/").unwrap(),
223 (
224 "",
225 vec![
226 Tag {
227 name: "k".to_string(),
228 value: "rsa".to_string(),
229 raw_value: "rsa".to_string()
230 },
231 Tag {
232 name: "p".to_string(),
233 value: "kEy+/".to_string(),
234 raw_value: "kEy+/".to_string()
235 }
236 ]
237 )
238 );
239 }
240}