decrypt_cookies/firefox/
mod.rs

1pub mod builder;
2pub mod items;
3
4use std::{
5    fmt::Display,
6    future::Future,
7    marker::{PhantomData, Sync},
8};
9
10use chrono::Utc;
11use rayon::prelude::{IntoParallelIterator, ParallelIterator};
12use sea_orm::{prelude::ColumnTrait, sea_query::IntoCondition, DbErr};
13use snafu::{Location, ResultExt, Snafu};
14
15pub use self::items::cookie::{
16    entities::moz_cookies::{Column as MozCookiesCol, ColumnIter as MozCookiesColIter},
17    MozCookie,
18};
19use self::items::{cookie::dao::CookiesQuery, I64ToMozTime};
20use crate::browser::{cookies::LeetCodeCookies, FirefoxPath};
21
22#[derive(Debug)]
23#[derive(Snafu)]
24#[snafu(visibility(pub))]
25pub enum FirefoxError {
26    #[snafu(display("{source}\n@:{location}"))]
27    Db {
28        source: DbErr,
29        #[snafu(implicit)]
30        location: Location,
31    },
32}
33
34type Result<T> = std::result::Result<T, FirefoxError>;
35
36#[derive(Clone)]
37#[derive(Debug)]
38#[derive(Default)]
39pub struct FirefoxGetter<T> {
40    pub(crate) cookies_query: CookiesQuery,
41    pub(crate) __browser: PhantomData<T>,
42}
43
44#[derive(Clone)]
45#[derive(Debug)]
46#[derive(Default)]
47pub struct FirefoxCookieGetter<T> {
48    pub(crate) cookies_query: CookiesQuery,
49    pub(crate) __browser: PhantomData<T>,
50}
51
52macro_rules! impl_display {
53    ($($s:ident),* $(,)?) => {
54        $(
55            impl<B: FirefoxPath> Display for $s<B> {
56                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57                    f.write_str(B::NAME)
58                }
59            }
60        )*
61    };
62}
63impl_display![FirefoxGetter, FirefoxCookieGetter,];
64
65impl<B> SealedCookies for FirefoxGetter<B> {
66    fn cookies_query(&self) -> &CookiesQuery {
67        &self.cookies_query
68    }
69}
70impl<B> SealedCookies for FirefoxCookieGetter<B> {
71    fn cookies_query(&self) -> &CookiesQuery {
72        &self.cookies_query
73    }
74}
75impl<B> GetCookies for FirefoxGetter<B> {}
76impl<B> GetCookies for FirefoxCookieGetter<B> {}
77
78trait SealedCookies {
79    fn cookies_query(&self) -> &CookiesQuery;
80}
81
82#[expect(private_bounds, reason = "impl details")]
83pub trait GetCookies: SealedCookies {
84    /// filter by condition
85    ///
86    /// # Example
87    ///
88    /// ```rust,ignore
89    /// use decrypt_cookies::{firefox::MozCookiesCol, Browser, FirefoxBuilder, ColumnTrait};
90    ///
91    /// #[tokio::main]
92    /// async fn main() {
93    ///     let ffget = FirefoxBuilder::new(Firefox::new().unwrap())
94    ///         .build()
95    ///         .await
96    ///         .unwrap();
97    ///     let res = ffget
98    ///         .cookies_filter(MozCookiesCol::Host.contains("mozilla.com"))
99    ///         .await
100    ///         .unwrap_or_default();
101    /// }
102    /// ```
103    fn cookies_filter<F>(&self, filter: F) -> impl Future<Output = Result<Vec<MozCookie>>> + Send
104    where
105        F: IntoCondition + Send,
106        Self: Sync,
107    {
108        async {
109            let res = self
110                .cookies_query()
111                .query_cookie_filter(filter)
112                .await
113                .context(DbSnafu)?;
114            let res = res
115                .into_par_iter()
116                .map(MozCookie::from)
117                .collect();
118            Ok(res)
119        }
120    }
121
122    fn cookies_all(&self) -> impl Future<Output = Result<Vec<MozCookie>>> + Send
123    where
124        Self: Sync,
125    {
126        async {
127            let res = self
128                .cookies_query()
129                .query_all_cookie()
130                .await
131                .context(DbSnafu)?;
132            let res = res
133                .into_par_iter()
134                .map(MozCookie::from)
135                .collect();
136            Ok(res)
137        }
138    }
139
140    /// Filter cookies by host
141    #[doc(alias = "cookies_by_domain", alias = "cookies_by_url")]
142    fn cookies_by_host<H>(&self, host: H) -> impl Future<Output = Result<Vec<MozCookie>>> + Send
143    where
144        Self: Sync,
145        H: AsRef<str> + Send + Sync,
146    {
147        async move {
148            let res = self
149                .cookies_query()
150                .query_cookie_by_host(host.as_ref())
151                .await
152                .context(DbSnafu)?;
153            let res = res
154                .into_par_iter()
155                .map(MozCookie::from)
156                .collect();
157            Ok(res)
158        }
159    }
160
161    /// get session csrf for leetcode
162    fn get_session_csrf<H>(&self, host: H) -> impl Future<Output = Result<LeetCodeCookies>> + Send
163    where
164        Self: Sync,
165        H: AsRef<str> + Send + Sync,
166    {
167        async move {
168            let cookies = self
169                .cookies_query()
170                .query_cookie_filter(
171                    MozCookiesCol::Host
172                        .contains(host.as_ref())
173                        .and(
174                            MozCookiesCol::Name
175                                .eq("csrftoken")
176                                .or(MozCookiesCol::Name.eq("LEETCODE_SESSION")),
177                        ),
178                )
179                .await
180                .context(DbSnafu)?;
181
182            let mut res = LeetCodeCookies::default();
183
184            for cookie in cookies {
185                if let Some(s) = cookie.name {
186                    if s == "csrftoken" {
187                        let expir = cookie
188                            .expiry
189                            .unwrap_or_default()
190                            .secs_to_moz_utc();
191                        if let Some(expir) = expir {
192                            if Utc::now() > expir {
193                                res.expiry = true;
194                                break;
195                            }
196                        }
197
198                        res.csrf = cookie.value.unwrap_or_default();
199                    }
200                    else if s == "LEETCODE_SESSION" {
201                        let expir = cookie
202                            .expiry
203                            .unwrap_or_default()
204                            .secs_to_moz_utc();
205                        if let Some(expir) = expir {
206                            if Utc::now() > expir {
207                                res.expiry = true;
208                                break;
209                            }
210                        }
211
212                        res.session = cookie.value.unwrap_or_default();
213                    }
214                }
215            }
216            Ok(res)
217        }
218    }
219}