Skip to main content

kodik_utils/
jar_ext.rs

1use reqwest::cookie::Jar;
2use std::{
3    fs::File,
4    io::{BufRead, BufReader},
5    path::Path,
6};
7
8pub trait JarExt {
9    /// Loads cookies from a Netscape-format cookie file.
10    ///
11    /// # Errors
12    ///
13    /// Returns a [`crate::Error`] if the file cannot be opened or is malformed.
14    fn load_netscape(path: &impl AsRef<Path>) -> crate::Result<Jar>;
15}
16
17impl JarExt for Jar {
18    fn load_netscape(path: &impl AsRef<Path>) -> crate::Result<Self> {
19        let jar = Self::default();
20        let file = File::open(path)?;
21        let mut reader = BufReader::new(file);
22        let mut line = String::new();
23
24        while reader.read_line(&mut line)? > 0 {
25            let trimmed = line.trim();
26
27            if trimmed.starts_with('#') || trimmed.is_empty() {
28                line.clear();
29                continue;
30            }
31
32            let mut parts = trimmed.splitn(7, '\t');
33            let domain = parts
34                .next()
35                .ok_or_else(|| crate::Error::NotFound("malformed cookie: missing domain".into()))?;
36            let key = parts
37                .nth(4)
38                .ok_or_else(|| crate::Error::NotFound("malformed cookie: missing name".into()))?;
39            let value = parts
40                .next()
41                .ok_or_else(|| crate::Error::NotFound("malformed cookie: missing value".into()))?;
42
43            let mut cookie = String::with_capacity(key.len() + value.len() + domain.len() + 10);
44            cookie.push_str(key);
45            cookie.push('=');
46            cookie.push_str(value);
47            cookie.push_str("; Domain=");
48            cookie.push_str(domain);
49
50            let domain = domain.trim_start_matches('.');
51            let mut url_str = String::with_capacity(8 + domain.len());
52            url_str.push_str("https://");
53            url_str.push_str(domain);
54
55            jar.add_cookie_str(
56                &cookie,
57                &reqwest::Url::parse(&url_str)
58                    .map_err(|e| crate::Error::NotFound(format!("invalid cookie domain '{domain}': {e}")))?,
59            );
60
61            line.clear();
62        }
63
64        Ok(jar)
65    }
66}