1use std::{fs::create_dir_all, path::PathBuf};
2
3use chrono::{DateTime, Utc};
4use miette::{IntoDiagnostic, Result};
5use tokio::fs::read_to_string;
6
7use crate::Browser;
8
9#[cfg(any(target_os = "linux", target_os = "macos"))]
10const CHROMIUM_SAFE: &str = "Chromium Safe Storage";
11#[cfg(any(target_os = "linux", target_os = "macos"))]
12const CHROME_SAFE: &str = "Chrome Safe Storage";
13#[cfg(any(target_os = "linux", target_os = "macos"))]
14const EDGE_SAFE: &str = "Microsoft Edge Safe Storage";
15#[cfg(any(target_os = "linux", target_os = "macos"))]
16const BRAVE_SAFE: &str = "Brave Safe Storage";
17#[cfg(any(target_os = "linux", target_os = "macos"))]
18const YANDEX_SAFE: &str = "Yandex Safe Storage";
19#[cfg(any(target_os = "linux", target_os = "macos"))]
20const VIVALDI_SAFE: &str = "Vivaldi Safe Storage";
21#[cfg(any(target_os = "linux", target_os = "macos"))]
22const OPERA_SAFE: &str = "Opera Safe Storage";
23#[cfg(target_os = "macos")]
24const OPERAGX_SAFE: &str = "Opera Safe Storage";
25#[cfg(target_os = "macos")]
26const COCCOC_SAFE: &str = "CocCoc Safe Storage";
27#[cfg(target_os = "macos")]
28const ARC_SAFE: &str = "Arc Safe Storage";
29
30pub trait BrowserTime {
31 const MAX_TIME: DateTime<Utc> = chrono::DateTime::<Utc>::MAX_UTC;
32 const MIN_TIME: DateTime<Utc> = chrono::DateTime::<Utc>::MIN_UTC;
33}
34
35impl BrowserTime for i64 {}
36
37pub trait TempPath {
39 fn browser(&self) -> Browser;
40
41 fn temp_path_prefix(&self) -> PathBuf {
43 let mut temp_path = dirs::cache_dir().expect("get cache_dir failed");
44 temp_path.push(format!("tidy_browser/{}", self.browser(),));
45 create_dir_all(&temp_path).expect("create temp path failed");
46
47 temp_path
48 }
49}
50
51pub trait ChromiumInfo: TempPath {
53 const BOOKMARKS: &'static str = "Bookmarks"; const COOKIES: &'static str = "Cookies"; const EXTENSION_COOKIES: &'static str = "Extension Cookies";
58 const HISTORY: &'static str = "History"; const LOAD_STATISTICS: &'static str = "load_statistics.db"; const LOGIN_DATA: &'static str = "Login Data"; const MEDIA_DEVICE_SALTS: &'static str = "MediaDeviceSalts"; const NETWORK_ACTION_PREDICTOR: &'static str = "Network Action Predictor"; const LOCAL_STORAGE: &'static str = "Local Storage/leveldb";
65 const EXTENSIONS: &'static str = "Extensions"; const SESSION_STORAGE: &'static str = "Session Storage"; const WEB_DATA: &'static str = "Web Data"; const LOCAL_STATE: &'static str = "Local State"; fn base(&self) -> &PathBuf;
74
75 fn local_state(&self) -> PathBuf {
77 let mut path = self.base().clone();
78 path.pop();
79 path.push(Self::LOCAL_STATE);
80 path
81 }
82 fn local_state_temp(&self) -> PathBuf {
83 self.temp_path_prefix()
84 .join(Self::LOCAL_STATE)
85 }
86
87 fn credit(&self) -> PathBuf {
89 self.base().join(Self::WEB_DATA)
90 }
91 fn credit_temp(&self) -> PathBuf {
92 self.temp_path_prefix()
93 .join(Self::WEB_DATA)
94 }
95
96 fn session(&self) -> PathBuf {
98 self.base()
99 .join(Self::SESSION_STORAGE)
100 }
101 fn session_temp(&self) -> PathBuf {
102 self.temp_path_prefix()
103 .join(Self::SESSION_STORAGE)
104 }
105
106 fn extensions(&self) -> PathBuf {
108 self.base().join(Self::EXTENSIONS)
109 }
110 fn extensions_temp(&self) -> PathBuf {
111 self.temp_path_prefix()
112 .join(Self::EXTENSIONS)
113 }
114
115 fn logindata(&self) -> PathBuf {
117 match self.browser() {
118 Browser::Yandex => self.base().join("Ya Passman Data"),
119 _ => self.base().join(Self::LOGIN_DATA),
120 }
121 }
122 fn logindata_temp(&self) -> PathBuf {
123 self.temp_path_prefix()
124 .join(Self::LOGIN_DATA)
125 }
126
127 fn storage(&self) -> PathBuf {
129 self.base()
130 .join(Self::LOCAL_STORAGE)
131 }
132 fn storage_temp(&self) -> PathBuf {
133 self.temp_path_prefix()
134 .join(Self::LOCAL_STORAGE)
135 }
136
137 fn bookmarks(&self) -> PathBuf {
139 self.base().join(Self::BOOKMARKS)
140 }
141 fn bookmarks_temp(&self) -> PathBuf {
142 self.temp_path_prefix()
143 .join(Self::BOOKMARKS)
144 }
145
146 fn history(&self) -> PathBuf {
148 self.base().join(Self::HISTORY)
149 }
150 fn history_temp(&self) -> PathBuf {
151 self.temp_path_prefix()
152 .join(Self::HISTORY)
153 }
154
155 fn cookies(&self) -> PathBuf {
157 self.base().join(Self::COOKIES)
158 }
159 fn cookies_temp(&self) -> PathBuf {
160 self.temp_path_prefix()
161 .join(Self::COOKIES)
162 }
163
164 #[cfg(target_os = "macos")]
166 fn safe_name(&self) -> &str {
167 match self.browser() {
168 Browser::Chromium => "Chromium",
169 Browser::Chrome => "Chrome",
170 Browser::Edge => "Microsoft Edge",
171 Browser::Brave => "Brave",
172 Browser::Yandex => "Yandex",
173 Browser::Vivaldi => "Vivaldi",
174 Browser::Opera => "Opera",
175 #[cfg(not(target_os = "linux"))]
176 Browser::OperaGX => "Opera",
177 #[cfg(not(target_os = "linux"))]
178 Browser::CocCoc => "CocCoc",
179 #[cfg(not(target_os = "linux"))]
180 Browser::Arc => "Arc",
181 _ => panic!("safe storage not support: {}", self.browser()),
182 }
183 }
184
185 #[cfg(not(target_os = "windows"))]
187 fn safe_storage(&self) -> &str {
188 match self.browser() {
189 Browser::Chromium => CHROMIUM_SAFE,
190 Browser::Chrome => CHROME_SAFE,
191 Browser::Edge => EDGE_SAFE,
192 Browser::Brave => BRAVE_SAFE,
193 Browser::Yandex => YANDEX_SAFE,
194 Browser::Vivaldi => VIVALDI_SAFE,
195 Browser::Opera => OPERA_SAFE,
196 #[cfg(not(target_os = "linux"))]
197 Browser::OperaGX => OPERAGX_SAFE,
198 #[cfg(not(target_os = "linux"))]
199 Browser::CocCoc => COCCOC_SAFE,
200 #[cfg(not(target_os = "linux"))]
201 Browser::Arc => ARC_SAFE,
202 _ => panic!("safe storage not support: {}", self.browser()),
203 }
204 }
205}
206
207#[cfg(target_os = "linux")]
209pub(crate) fn need_safe_storage(lab: &str) -> bool {
210 matches!(
211 lab,
212 CHROMIUM_SAFE
213 | CHROME_SAFE
214 | EDGE_SAFE
215 | BRAVE_SAFE
216 | YANDEX_SAFE
217 | VIVALDI_SAFE
218 | OPERA_SAFE
219 )
220}
221
222pub trait FfInfo: TempPath {
223 const COOKIES: &'static str = "cookies.sqlite";
224 const DATAS: &'static str = "places.sqlite"; const BOOKMARKBACKUPS: &'static str = "bookmarkbackups/bookmarks-date.jsonlz4";
226 const FAVICONS: &'static str = "favicons.sqlite"; const KEY: &'static str = "key4.db"; const PASSWD: &'static str = "logins.json"; const SEARCH: &'static str = "search.json.mozlz4"; const STORAGE: &'static str = "webappsstore.sqlite"; const EXTENSIONS: &'static str = "extensions.json";
232 const CERT9: &'static str = "cert9.db"; fn base(&self) -> &PathBuf;
235
236 fn extensions(&self) -> PathBuf {
238 self.base().join(Self::EXTENSIONS)
239 }
240 fn extensions_temp(&self) -> PathBuf {
241 self.temp_path_prefix()
242 .join(Self::EXTENSIONS)
243 }
244
245 fn passwd(&self) -> PathBuf {
247 self.base().join(Self::PASSWD)
248 }
249 fn passwd_temp(&self) -> PathBuf {
250 self.temp_path_prefix()
251 .join(Self::PASSWD)
252 }
253
254 fn storage(&self) -> PathBuf {
256 self.base().join(Self::STORAGE)
257 }
258 fn storage_temp(&self) -> PathBuf {
259 self.temp_path_prefix()
260 .join(Self::STORAGE)
261 }
262
263 fn key(&self) -> PathBuf {
265 self.base().join(Self::KEY)
266 }
267 fn key_temp(&self) -> PathBuf {
268 self.temp_path_prefix()
269 .join(Self::KEY)
270 }
271
272 fn datas(&self) -> PathBuf {
274 self.base().join(Self::DATAS)
275 }
276 fn datas_temp(&self) -> PathBuf {
277 self.temp_path_prefix()
278 .join(Self::DATAS)
279 }
280
281 fn cookies(&self) -> PathBuf {
283 self.base().join(Self::COOKIES)
284 }
285 fn cookies_temp(&self) -> PathBuf {
286 self.temp_path_prefix()
287 .join(Self::COOKIES)
288 }
289
290 fn helper(
291 init_path: PathBuf,
292 base: &str,
293 ) -> impl std::future::Future<Output = Result<PathBuf>> + Send {
294 let mut ini_path = init_path.clone();
295 ini_path.push(format!("{}/profiles.ini", base));
296 async move {
297 if !ini_path.exists() {
298 miette::bail!(
299 "{} not exists",
300 ini_path
301 .to_str()
302 .unwrap_or_default()
303 );
304 }
305 let str = read_to_string(ini_path)
306 .await
307 .into_diagnostic()?;
308 let ini_file = ini::Ini::load_from_str(&str).into_diagnostic()?;
309 let mut section = String::new();
310 for (sec, prop) in ini_file {
311 let Some(sec) = sec
312 else {
313 continue;
314 };
315 if sec.starts_with("Install") {
316 prop.get("Default")
317 .unwrap_or_default()
318 .clone_into(&mut section);
319 break;
320 }
321 }
322
323 tracing::debug!("section: {}", section);
324
325 let mut res = init_path;
326 res.push(format!("{}/{}", base, section));
327 tracing::debug!("path: {:?}", res);
328
329 Ok(res)
330 }
331 }
332}
333
334#[cfg(target_os = "linux")]
335pub mod linux {
336 use std::path::PathBuf;
337
338 use miette::Result;
339
340 use super::{ChromiumInfo, FfInfo, TempPath};
341 use crate::Browser;
342
343 #[allow(clippy::exhaustive_structs)]
344 #[derive(Clone)]
345 #[derive(Debug)]
346 #[derive(Default)]
347 #[derive(PartialEq, Eq)]
348 pub struct LinuxChromiumBase {
349 pub base: PathBuf,
350 pub browser: Browser,
351 }
352
353 impl TempPath for LinuxChromiumBase {
354 fn browser(&self) -> Browser {
355 self.browser
356 }
357 }
358
359 impl ChromiumInfo for LinuxChromiumBase {
360 fn base(&self) -> &PathBuf {
361 &self.base
362 }
363 }
364
365 impl LinuxChromiumBase {
367 pub const EDGE_LINUX: &'static str = "microsoft-edge/Default";
368 pub const CHROME_LINUX: &'static str = "google-chrome/Default";
369 pub const OPERA_LINUX: &'static str = "opera/Default";
370 pub const BRAVE_LINUX: &'static str = "BraveSoftware/Brave-Browser/Default";
371 pub const CHROMIUM_LINUX: &'static str = "chromium/Default";
372 pub const YANDEX_LINUX: &'static str = "yandex-browser/Default";
373 pub const VIVALDI_LINUX: &'static str = "vivaldi/Default";
374
375 pub fn new(browser: Browser) -> Self {
376 let base = match browser {
377 Browser::Edge => Self::EDGE_LINUX,
378 Browser::Chromium => Self::CHROMIUM_LINUX,
379 Browser::Chrome => Self::CHROME_LINUX,
380 Browser::Brave => Self::BRAVE_LINUX,
381 Browser::Yandex => Self::YANDEX_LINUX,
382 Browser::Vivaldi => Self::VIVALDI_LINUX,
383 Browser::Opera => Self::OPERA_LINUX,
384 _ => panic!("Linux Chromium base not support: {browser}"),
385 };
386 let mut res = dirs::config_dir().expect("get config dir failed");
387 res.push(base);
388
389 Self { base: res, browser }
390 }
391 }
392
393 #[derive(Clone)]
394 #[derive(Debug)]
395 #[derive(Default)]
396 #[derive(PartialEq, Eq)]
397 pub struct LinuxFFBase {
398 base: PathBuf,
399 browser: Browser,
400 }
401
402 impl TempPath for LinuxFFBase {
403 fn browser(&self) -> Browser {
404 self.browser
405 }
406 }
407
408 impl FfInfo for LinuxFFBase {
409 fn base(&self) -> &PathBuf {
410 &self.base
411 }
412 }
413
414 impl LinuxFFBase {
415 const FF_BASE: &'static str = ".mozilla/firefox";
416 const LIBREWOLF_BASE: &'static str = ".librewolf";
417
418 pub async fn new(browser: Browser) -> Result<Self> {
419 let init = dirs::home_dir().ok_or_else(|| miette::miette!("get home dir failed"))?;
420 let base = match browser {
421 Browser::Librewolf => Self::LIBREWOLF_BASE,
422 Browser::Firefox => Self::FF_BASE,
423 _ => panic!("Linux Firefox base not support: {browser}"),
424 };
425 let base = Self::helper(init, base).await?;
426
427 Ok(Self { base, browser })
428 }
429 }
430}
431
432#[cfg(target_os = "macos")]
433pub mod macos {
434 use std::path::PathBuf;
435
436 use miette::Result;
437
438 use super::{ChromiumInfo, FfInfo, TempPath};
439 use crate::Browser;
440
441 #[allow(clippy::exhaustive_structs)]
442 #[derive(Clone)]
443 #[derive(Debug)]
444 #[derive(Default)]
445 #[derive(PartialEq, Eq)]
446 pub struct MacChromiumBase {
447 pub base: PathBuf,
448 pub browser: Browser,
449 }
450
451 impl TempPath for MacChromiumBase {
452 fn browser(&self) -> Browser {
453 self.browser
454 }
455 }
456
457 impl MacChromiumBase {
458 pub const EDGE_MAC: &'static str = "Microsoft Edge/Default";
459 pub const CHROME_MAC: &'static str = "Google/Chrome/Default";
460 pub const CHROMIUM_MAC: &'static str = "Chromium/Default";
461 pub const BRAVE_MAC: &'static str = "BraveSoftware/Brave-Browser/Default";
462 pub const YANDEX_MAC: &'static str = "Yandex/YandexBrowser/Default";
463 pub const VIVALDI_MAC: &'static str = "Vivaldi/Default";
464 pub const OPERA_MAC: &'static str = "com.operasoftware.Opera/Default";
465 pub const OPERAGX_MAC: &'static str = "com.operasoftware.OperaGX";
466 pub const COCCOC_MAC: &'static str = "Coccoc/Default";
467 pub const ARC_MAC: &'static str = "Arc/User Data/Default";
468
469 pub fn new(browser: Browser) -> Self {
470 let mut cookie_dir = dirs::config_local_dir().expect("get config dir failed");
471 let v = match browser {
472 Browser::Chrome => Self::CHROME_MAC,
473 Browser::Edge => Self::EDGE_MAC,
474 Browser::Chromium => Self::CHROMIUM_MAC,
475 Browser::Brave => Self::BRAVE_MAC,
476 Browser::Yandex => Self::YANDEX_MAC,
477 Browser::Vivaldi => Self::VIVALDI_MAC,
478 Browser::Opera => Self::OPERA_MAC,
479 Browser::OperaGX => Self::OPERAGX_MAC,
480 Browser::CocCoc => Self::COCCOC_MAC,
481 Browser::Arc => Self::ARC_MAC,
482 _ => panic!("MacOs Chromium base not support: {browser}"),
483 };
484 cookie_dir.push(v);
485 Self { base: cookie_dir, browser }
486 }
487 }
488
489 impl ChromiumInfo for MacChromiumBase {
490 fn base(&self) -> &PathBuf {
491 &self.base
492 }
493 }
494
495 #[derive(Clone)]
496 #[derive(Debug)]
497 #[derive(Default)]
498 #[derive(PartialEq, Eq)]
499 pub struct MacFFBase {
500 pub base: PathBuf,
501 browser: Browser,
502 }
503
504 impl TempPath for MacFFBase {
505 fn browser(&self) -> Browser {
506 self.browser
507 }
508 }
509
510 impl FfInfo for MacFFBase {
511 fn base(&self) -> &PathBuf {
512 &self.base
513 }
514 }
515
516 impl MacFFBase {
517 const FIREFOX_BASE: &'static str = "Firefox";
518 const LIBREWOLF_BASE: &'static str = "librewolf";
519
520 pub async fn new(browser: Browser) -> Result<Self> {
521 let init = dirs::config_local_dir()
522 .ok_or_else(|| miette::miette!("get config local dir failed"))?;
523 let base = match browser {
524 Browser::Librewolf => Self::LIBREWOLF_BASE,
525 Browser::Firefox => Self::FIREFOX_BASE,
526 _ => panic!("MacOs Firefox base not support: {browser}"),
527 };
528 let base = Self::helper(init, base).await?;
529
530 Ok(Self { base, browser })
531 }
532 }
533}
534
535#[cfg(target_os = "windows")]
536pub mod win {
537 use std::path::PathBuf;
538
539 use miette::Result;
540
541 use super::{ChromiumInfo, FfInfo, TempPath};
542 use crate::Browser;
543
544 #[derive(Clone)]
545 #[derive(Debug)]
546 #[derive(Default)]
547 #[derive(PartialEq, Eq)]
548 pub struct WinChromiumBase {
549 base: PathBuf,
550 browser: Browser,
551 }
552
553 impl TempPath for WinChromiumBase {
554 fn browser(&self) -> Browser {
555 self.browser
556 }
557 }
558
559 impl WinChromiumBase {
560 pub fn into_key(mut self) -> PathBuf {
562 self.base.push(Self::LOCAL_STATE);
563 self.base
564 }
565 }
566
567 impl WinChromiumBase {
568 const EDGE_WIN: &'static str = "Microsoft/Edge/User Data/Default";
569 const CHROME_WIN: &'static str = "Google/Chrome/User Data/Default";
570 const CHROMIUM_WIN: &'static str = "Chromium/User Data/Default";
571 const BRAVE_WIN: &'static str = "BraveSoftware/Brave-Browser/User Data/Default";
572 const VIVALDI_WIN: &'static str = "Vivaldi/User Data/Default";
573 const COCCOC_WIN: &'static str = "CocCoc/Browser/User Data/Default";
574 const YANDEX_WIN: &'static str = "Yandex/YandexBrowser/User Data/Default";
575 const OPERA_WIN: &'static str = "Opera Software/Opera Stable/Default";
576 const OPERAGX_WIN: &'static str = "Opera Software/Opera GX Stable";
577 pub fn new(browser: Browser) -> Self {
580 let mut cookie_dir = if matches!(browser, Browser::Opera | Browser::OperaGX) {
581 dirs::data_dir().expect("get config dir failed")
582 }
583 else {
584 dirs::data_local_dir().expect("get config dir failed")
585 };
586
587 let path_base = match browser {
588 Browser::Edge => Self::EDGE_WIN,
589 Browser::Chromium => Self::CHROMIUM_WIN,
590 Browser::Chrome => Self::CHROME_WIN,
591 Browser::Brave => Self::BRAVE_WIN,
592 Browser::Yandex => Self::YANDEX_WIN,
593 Browser::Vivaldi => Self::VIVALDI_WIN,
594 Browser::Opera => Self::OPERA_WIN,
595 Browser::OperaGX => Self::OPERAGX_WIN,
596 Browser::CocCoc => Self::COCCOC_WIN,
597 _ => panic!("Windows Chromium base not support: {browser}."),
599 };
600 cookie_dir.push(path_base);
601
602 Self { base: cookie_dir, browser }
603 }
604 }
605
606 impl ChromiumInfo for WinChromiumBase {
607 const COOKIES: &'static str = "Network/Cookies"; fn base(&self) -> &PathBuf {
610 &self.base
611 }
612 fn local_state(&self) -> PathBuf {
613 if self.browser == Browser::OperaGX {
615 self.base().join(Self::LOCAL_STATE)
616 }
617 else {
618 let mut path = self.base().clone();
619 path.pop();
620 path.push(Self::LOCAL_STATE);
621 path
622 }
623 }
624 fn cookies_temp(&self) -> PathBuf {
625 self.temp_path_prefix()
626 .join("Cookies")
627 }
628 }
629
630 #[derive(Clone)]
631 #[derive(Debug)]
632 #[derive(Default)]
633 #[derive(PartialEq, Eq)]
634 pub struct WinFFBase {
635 base: PathBuf,
636 browser: Browser,
637 }
638
639 impl TempPath for WinFFBase {
640 fn browser(&self) -> Browser {
641 self.browser
642 }
643 }
644
645 impl WinFFBase {
646 const FIREFOX_BASE: &'static str = r"Mozilla\Firefox";
647 const LIBREWOLF_BASE: &'static str = "librewolf";
648
649 pub async fn new(browser: Browser) -> Result<Self> {
650 let base = match browser {
651 Browser::Librewolf => Self::LIBREWOLF_BASE,
652 Browser::Firefox => Self::FIREFOX_BASE,
653 _ => panic!("Windows Firefox base not support: {browser}"),
654 };
655 let init =
656 dirs::data_dir().ok_or_else(|| miette::miette!("get data local dir failed"))?;
657 let base = Self::helper(init, base).await?;
658
659 Ok(Self { base, browser })
660 }
661 }
662
663 impl FfInfo for WinFFBase {
664 fn base(&self) -> &PathBuf {
665 &self.base
666 }
667 }
668}