decrypt_cookies/chromium/
builder.rs1use 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#[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 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}