decrypt_cookies/chromium/
builder.rs

1use std::{fmt::Display, marker::PhantomData, path::PathBuf};
2
3use chromium_crypto::Decrypter;
4use tokio::{fs, join};
5
6use super::ChromiumGetter;
7use crate::{
8    browser::ChromiumPath,
9    chromium::items::{cookie::cookie_dao::CookiesQuery, passwd::login_data_dao::LoginDataQuery},
10};
11
12// TODO: add browser name in error
13#[derive(Debug)]
14#[derive(thiserror::Error)]
15pub enum ChromiumBuilderError {
16    #[error(transparent)]
17    Decrypter(#[from] chromium_crypto::error::CryptoError),
18    #[error(transparent)]
19    Db(#[from] sea_orm::DbErr),
20    #[error("Io: {source}, path: {path}")]
21    Io {
22        source: std::io::Error,
23        path: std::path::PathBuf,
24    },
25    #[error(transparent)]
26    Rawcopy(#[from] anyhow::Error),
27    #[error(transparent)]
28    TokioJoin(#[from] tokio::task::JoinError),
29    #[error("Can not found home dir")]
30    HOME,
31}
32
33pub type Result<T> = std::result::Result<T, ChromiumBuilderError>;
34
35#[derive(Clone)]
36#[derive(Debug)]
37#[derive(Default)]
38#[derive(PartialEq, Eq, PartialOrd, Ord)]
39pub(crate) struct TempPaths {
40    pub(crate) cookies_temp: PathBuf,
41    pub(crate) login_data_temp: PathBuf,
42    pub(crate) login_data_for_account_temp: Option<PathBuf>,
43    pub(crate) key_temp: PathBuf,
44}
45#[derive(Clone)]
46#[derive(Debug)]
47#[derive(Default)]
48#[derive(PartialEq, Eq)]
49pub struct ChromiumBuilder<T> {
50    pub(crate) base: Option<PathBuf>,
51    pub(crate) __browser: PhantomData<T>,
52}
53
54impl<B: ChromiumPath> Display for ChromiumGetter<B> {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        f.write_str(B::NAME)
57    }
58}
59
60impl<B: ChromiumPath> Display for ChromiumBuilder<B> {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        f.write_fmt(format_args!("{}Builder", B::NAME))
63    }
64}
65
66impl<B: ChromiumPath> ChromiumBuilder<B> {
67    pub const fn new() -> Self {
68        Self {
69            base: None,
70            __browser: core::marker::PhantomData::<B>,
71        }
72    }
73
74    /// When browser start with `--user-data-dir=DIR` or special other channel
75    pub const fn with_user_data_dir(base: PathBuf) -> Self {
76        Self {
77            base: Some(base),
78            __browser: core::marker::PhantomData::<B>,
79        }
80    }
81}
82
83impl<B: ChromiumPath + Send + Sync> ChromiumBuilder<B> {
84    pub async fn build(self) -> Result<ChromiumGetter<B>> {
85        let base = if let Some(base) = self.base {
86            base
87        }
88        else {
89            let Some(mut base) = dirs::home_dir()
90            else {
91                return Err(ChromiumBuilderError::HOME);
92            };
93
94            base.push(B::BASE);
95            base
96        };
97
98        let temp_paths = Self::cache_data(base).await?;
99
100        #[cfg(target_os = "linux")]
101        let crypto = Decrypter::build(B::SAFE_STORAGE, crate::browser::need_safe_storage).await?;
102
103        #[cfg(target_os = "macos")]
104        let crypto = Decrypter::build(B::SAFE_STORAGE, B::SAFE_NAME).await?;
105
106        #[cfg(target_os = "windows")]
107        let crypto = Decrypter::build(temp_paths.key_temp).await?;
108
109        let (cookies_query, login_data_query) = (
110            CookiesQuery::new(temp_paths.cookies_temp),
111            LoginDataQuery::new(temp_paths.login_data_temp),
112        );
113        let (cookies_query, login_data_query) = join!(cookies_query, login_data_query);
114        let (cookies_query, login_data_query) = (cookies_query?, login_data_query?);
115        let login_data_for_account_query =
116            if let Some(path) = temp_paths.login_data_for_account_temp {
117                LoginDataQuery::new(path)
118                    .await?
119                    .into()
120            }
121            else {
122                None
123            };
124
125        Ok(ChromiumGetter {
126            cookies_query,
127            login_data_query,
128            login_data_for_account_query,
129            crypto,
130            __browser: self.__browser,
131        })
132    }
133
134    async fn cache_data(base: PathBuf) -> Result<TempPaths> {
135        let cookies = B::cookies(base.clone());
136        let cookies_temp = B::cookies_temp();
137
138        let login_data = B::login_data(base.clone());
139        let login_data_temp = B::login_data_temp();
140
141        let login_data_for_account = B::login_data_for_account(base.clone());
142        let login_data_for_account_temp = B::login_data_for_account_temp();
143
144        let key = B::key(base.clone());
145        let key_temp = B::key_temp();
146
147        let ck_temp_p = cookies_temp
148            .parent()
149            .expect("Get parent dir failed");
150        let cd_ck = fs::create_dir_all(ck_temp_p);
151        let lg_temp_p = login_data_temp
152            .parent()
153            .expect("Get parent dir failed");
154        let cd_lg = fs::create_dir_all(lg_temp_p);
155        let k_temp_p = key_temp
156            .parent()
157            .expect("Get parent dir failed");
158        let cd_k = fs::create_dir_all(k_temp_p);
159        let (cd_ck, cd_lg, cd_k) = join!(cd_ck, cd_lg, cd_k);
160        cd_ck.map_err(|e| ChromiumBuilderError::Io {
161            source: e,
162            path: ck_temp_p.to_owned(),
163        })?;
164        cd_lg.map_err(|e| ChromiumBuilderError::Io {
165            source: e,
166            path: lg_temp_p.to_owned(),
167        })?;
168        cd_k.map_err(|e| ChromiumBuilderError::Io {
169            source: e,
170            path: k_temp_p.to_owned(),
171        })?;
172
173        #[cfg(target_os = "windows")]
174        let (cookies_cp, login_cp, lfac_cp, key_cp) = {
175            let cookies = cookies.clone();
176            let cookies_temp = cookies_temp.clone();
177            let cc = tokio::task::spawn_blocking(move || {
178                crate::utils::shadow_copy(&cookies, &cookies_temp)
179            });
180
181            let login = login_data.clone();
182            let login_temp = login_data_temp.clone();
183            let lc =
184                tokio::task::spawn_blocking(move || crate::utils::shadow_copy(&login, &login_temp));
185
186            let login_for_account = login_data_for_account.clone();
187            let login_for_account_temp = login_data_for_account_temp.clone();
188            let lfac = tokio::task::spawn_blocking(move || {
189                crate::utils::shadow_copy(&login_for_account, &login_for_account_temp)
190            });
191
192            let key = key.clone();
193            let key_temp = key_temp.clone();
194            let kc =
195                tokio::task::spawn_blocking(move || crate::utils::shadow_copy(&key, &key_temp));
196
197            (cc, lc, lfac, kc)
198        };
199
200        #[cfg(not(target_os = "windows"))]
201        let (cookies_cp, login_cp, lfac_cp, key_cp) = {
202            (
203                fs::copy(&cookies, &cookies_temp),
204                fs::copy(&login_data, &login_data_temp),
205                fs::copy(&login_data_for_account, &login_data_for_account_temp),
206                fs::copy(&key, &key_temp),
207            )
208        };
209
210        let (ck, lg, lfac, k) = join!(cookies_cp, login_cp, lfac_cp, key_cp);
211        #[cfg(target_os = "windows")]
212        {
213            ck??;
214            lg??;
215            k??;
216        };
217        #[cfg(not(target_os = "windows"))]
218        {
219            ck.map_err(|e| ChromiumBuilderError::Io { source: e, path: cookies })?;
220            lg.map_err(|e| ChromiumBuilderError::Io { source: e, path: login_data })?;
221            k.map_err(|e| ChromiumBuilderError::Io { source: e, path: key })?;
222        };
223        Ok(TempPaths {
224            cookies_temp,
225            login_data_temp,
226            login_data_for_account_temp: lfac
227                .map(|_| login_data_for_account_temp)
228                .ok(),
229            key_temp,
230        })
231    }
232}