1use crate::error::{Error, Result};
2
3const COOKIE_NAME: &str = "ponymail";
4
5pub fn parse_ponymail_cookie(input: &str) -> Result<String> {
6 if let Some(value) = parse_netscape_cookie(input) {
7 return Ok(value);
8 }
9
10 let normalized = input.trim();
11 let normalized = normalized
12 .strip_prefix("Cookie:")
13 .or_else(|| normalized.strip_prefix("cookie:"))
14 .unwrap_or(normalized);
15
16 if looks_like_cookie_value(normalized) {
17 return validate_cookie_value(normalized);
18 }
19
20 for part in normalized.split(';') {
21 let part = part.trim();
22 if let Some((name, value)) = part.split_once('=') {
23 if name.trim() == COOKIE_NAME {
24 return validate_cookie_value(value.trim());
25 }
26 }
27 }
28
29 Err(Error::InvalidCookie(
30 "could not find a ponymail cookie".to_string(),
31 ))
32}
33
34fn parse_netscape_cookie(input: &str) -> Option<String> {
35 for line in input.lines() {
36 let line = line.trim();
37 if line.is_empty() || line.starts_with('#') {
38 continue;
39 }
40
41 let fields: Vec<&str> = line.split('\t').collect();
42 if fields.len() < 7 {
43 continue;
44 }
45
46 let domain = fields[0].trim_start_matches('.');
47 let name = fields[5];
48 let value = fields[6];
49 if domain == "lists.apache.org" && name == COOKIE_NAME {
50 return validate_cookie_value(value).ok();
51 }
52 }
53
54 None
55}
56
57fn looks_like_cookie_value(input: &str) -> bool {
58 !input.is_empty()
59 && !input.contains('=')
60 && !input.contains(';')
61 && !input.contains(char::is_whitespace)
62}
63
64fn validate_cookie_value(value: &str) -> Result<String> {
65 let value = value.trim();
66 if value.is_empty() {
67 return Err(Error::InvalidCookie(
68 "ponymail cookie value is empty".to_string(),
69 ));
70 }
71 if value.contains(';') || value.contains('\n') || value.contains('\r') {
72 return Err(Error::InvalidCookie(
73 "ponymail cookie value contains invalid characters".to_string(),
74 ));
75 }
76 Ok(value.to_string())
77}
78
79#[cfg(test)]
80mod tests {
81 use super::parse_ponymail_cookie;
82
83 #[test]
84 fn parse_plain_cookie() {
85 let cookie = parse_ponymail_cookie("ponymail=abc-123").unwrap();
86 assert_eq!(cookie, "abc-123");
87 }
88
89 #[test]
90 fn parse_bare_cookie_value() {
91 let cookie = parse_ponymail_cookie("abc-123").unwrap();
92 assert_eq!(cookie, "abc-123");
93 }
94
95 #[test]
96 fn parse_cookie_header() {
97 let cookie = parse_ponymail_cookie("Cookie: other=x; ponymail=abc; foo=bar").unwrap();
98 assert_eq!(cookie, "abc");
99 }
100
101 #[test]
102 fn parse_netscape_cookie() {
103 let input = ".lists.apache.org\tTRUE\t/\tTRUE\t0\tponymail\tabc";
104 let cookie = parse_ponymail_cookie(input).unwrap();
105 assert_eq!(cookie, "abc");
106 }
107}