use std::{fs::create_dir_all, path::PathBuf};
use miette::{IntoDiagnostic, Result};
use tokio::fs::read_to_string;
use crate::Browser;
pub trait TempPath {
fn browser(&self) -> Browser;
fn temp_path_prefix(&self) -> PathBuf {
let mut temp_path = dirs::cache_dir().expect("get cache_dir failed");
temp_path.push(format!("tidy_browser/{}", self.browser(),));
create_dir_all(&temp_path).expect("create temp path failed");
temp_path
}
}
pub trait ChromiumInfo: TempPath {
const BOOKMARKS: &'static str = "Bookmarks"; const COOKIES: &'static str = "Cookies"; const EXTENSION_COOKIES: &'static str = "Extension Cookies";
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";
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;
fn local_state(&self) -> PathBuf {
let mut path = self.base().clone();
path.pop();
path.push(Self::LOCAL_STATE);
path
}
fn local_state_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::LOCAL_STATE)
}
fn credit(&self) -> PathBuf {
self.base().join(Self::WEB_DATA)
}
fn credit_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::WEB_DATA)
}
fn session(&self) -> PathBuf {
self.base()
.join(Self::SESSION_STORAGE)
}
fn session_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::SESSION_STORAGE)
}
fn extensions(&self) -> PathBuf {
self.base().join(Self::EXTENSIONS)
}
fn extensions_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::EXTENSIONS)
}
fn logindata(&self) -> PathBuf {
match self.browser() {
Browser::Yandex => self.base().join("Ya Passman Data"),
_ => self.base().join(Self::LOGIN_DATA),
}
}
fn logindata_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::LOGIN_DATA)
}
fn storage(&self) -> PathBuf {
self.base()
.join(Self::LOCAL_STORAGE)
}
fn storage_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::LOCAL_STORAGE)
}
fn bookmarks(&self) -> PathBuf {
self.base().join(Self::BOOKMARKS)
}
fn bookmarks_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::BOOKMARKS)
}
fn history(&self) -> PathBuf {
self.base().join(Self::HISTORY)
}
fn history_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::HISTORY)
}
fn cookies(&self) -> PathBuf {
self.base().join(Self::COOKIES)
}
fn cookies_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::COOKIES)
}
#[cfg(target_os = "macos")]
fn safe_name(&self) -> &str {
match self.browser() {
Browser::Chromium => "Chromium",
Browser::Chrome => "Chrome",
Browser::Edge => "Microsoft Edge",
Browser::Brave => "Brave",
Browser::Yandex => "Yandex",
Browser::Vivaldi => "Vivaldi",
Browser::Opera => "Opera",
#[cfg(not(target_os = "linux"))]
Browser::OperaGX => "Opera",
#[cfg(not(target_os = "linux"))]
Browser::CocCoc => "CocCoc",
#[cfg(not(target_os = "linux"))]
Browser::Arc => "Arc",
_ => panic!("safe storage not support: {}", self.browser()),
}
}
#[cfg(not(target_os = "windows"))]
fn safe_storage(&self) -> &str {
match self.browser() {
Browser::Chromium => "Chromium Safe Storage",
Browser::Chrome => "Chrome Safe Storage",
Browser::Edge => "Microsoft Edge Safe Storage",
Browser::Brave => "Brave Safe Storage",
Browser::Yandex => "Yandex Safe Storage",
Browser::Vivaldi => "Vivaldi Safe Storage",
Browser::Opera => "Opera Safe Storage",
#[cfg(not(target_os = "linux"))]
Browser::OperaGX => "Opera Safe Storage",
#[cfg(not(target_os = "linux"))]
Browser::CocCoc => "CocCoc Safe Storage",
#[cfg(not(target_os = "linux"))]
Browser::Arc => "Arc Safe Storage",
_ => panic!("safe storage not support: {}", self.browser()),
}
}
}
pub trait FfInfo: TempPath {
const COOKIES: &'static str = "cookies.sqlite";
const DATAS: &'static str = "places.sqlite"; const BOOKMARKBACKUPS: &'static str = "bookmarkbackups/bookmarks-date.jsonlz4";
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";
const CERT9: &'static str = "cert9.db"; fn base(&self) -> &PathBuf;
fn extensions(&self) -> PathBuf {
self.base().join(Self::EXTENSIONS)
}
fn extensions_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::EXTENSIONS)
}
fn passwd(&self) -> PathBuf {
self.base().join(Self::PASSWD)
}
fn passwd_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::PASSWD)
}
fn storage(&self) -> PathBuf {
self.base().join(Self::STORAGE)
}
fn storage_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::STORAGE)
}
fn key(&self) -> PathBuf {
self.base().join(Self::KEY)
}
fn key_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::KEY)
}
fn datas(&self) -> PathBuf {
self.base().join(Self::DATAS)
}
fn datas_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::DATAS)
}
fn cookies(&self) -> PathBuf {
self.base().join(Self::COOKIES)
}
fn cookies_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join(Self::COOKIES)
}
fn helper(
init_path: PathBuf,
base: &str,
) -> impl std::future::Future<Output = Result<PathBuf>> + Send {
let mut ini_path = init_path.clone();
ini_path.push(format!("{}/profiles.ini", base));
async move {
if !ini_path.exists() {
miette::bail!(
"{} not exists",
ini_path
.to_str()
.unwrap_or_default()
);
}
let str = read_to_string(ini_path)
.await
.into_diagnostic()?;
let ini_file = ini::Ini::load_from_str(&str).into_diagnostic()?;
let mut section = String::new();
for (sec, prop) in ini_file {
let Some(sec) = sec
else {
continue;
};
if sec.starts_with("Install") {
prop.get("Default")
.unwrap_or_default()
.clone_into(&mut section);
break;
}
}
tracing::debug!("section: {}", section);
let mut res = init_path;
res.push(format!("{}/{}", base, section));
tracing::debug!("path: {:?}", res);
Ok(res)
}
}
}
#[cfg(target_os = "linux")]
pub mod linux {
use std::path::PathBuf;
use miette::Result;
use super::{ChromiumInfo, FfInfo, TempPath};
use crate::Browser;
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct LinuxChromiumBase {
pub base: PathBuf,
pub browser: Browser,
}
impl TempPath for LinuxChromiumBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl ChromiumInfo for LinuxChromiumBase {
fn base(&self) -> &PathBuf {
&self.base
}
}
impl LinuxChromiumBase {
pub const EDGE_LINUX: &'static str = "microsoft-edge/Default";
pub const CHROME_LINUX: &'static str = "google-chrome/Default";
pub const OPERA_LINUX: &'static str = "opera/Default";
pub const BRAVE_LINUX: &'static str = "BraveSoftware/Brave-Browser/Default";
pub const CHROMIUM_LINUX: &'static str = "chromium/Default";
pub const YANDEX_LINUX: &'static str = "yandex-browser/Default";
pub const VIVALDI_LINUX: &'static str = "vivaldi/Default";
pub fn new(browser: Browser) -> Self {
let base = match browser {
Browser::Edge => Self::EDGE_LINUX,
Browser::Chromium => Self::CHROMIUM_LINUX,
Browser::Chrome => Self::CHROME_LINUX,
Browser::Brave => Self::BRAVE_LINUX,
Browser::Yandex => Self::YANDEX_LINUX,
Browser::Vivaldi => Self::VIVALDI_LINUX,
Browser::Opera => Self::OPERA_LINUX,
_ => panic!("Linux Chromium base not support: {browser}"),
};
let mut res = dirs::config_dir().expect("get config dir failed");
res.push(base);
Self { base: res, browser }
}
}
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct LinuxFFBase {
base: PathBuf,
browser: Browser,
}
impl TempPath for LinuxFFBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl FfInfo for LinuxFFBase {
fn base(&self) -> &PathBuf {
&self.base
}
}
impl LinuxFFBase {
const FF_BASE: &'static str = ".mozilla/firefox";
const LIBREWOLF_BASE: &'static str = ".librewolf";
pub async fn new(browser: Browser) -> Result<Self> {
let init = dirs::home_dir().ok_or_else(|| miette::miette!("get home dir failed"))?;
let base = match browser {
Browser::Librewolf => Self::LIBREWOLF_BASE,
Browser::Firefox => Self::FF_BASE,
_ => panic!("Linux Firefox base not support: {browser}"),
};
let base = Self::helper(init, base).await?;
Ok(Self { base, browser })
}
}
}
#[cfg(target_os = "macos")]
pub mod macos {
use std::path::PathBuf;
use miette::Result;
use super::{ChromiumInfo, FfInfo, TempPath};
use crate::Browser;
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct MacChromiumBase {
pub base: PathBuf,
pub browser: Browser,
}
impl TempPath for MacChromiumBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl MacChromiumBase {
pub const EDGE_MAC: &'static str = "Microsoft Edge/Default";
pub const CHROME_MAC: &'static str = "Google/Chrome/Default";
pub const CHROMIUM_MAC: &'static str = "Chromium/Default";
pub const BRAVE_MAC: &'static str = "BraveSoftware/Brave-Browser/Default";
pub const YANDEX_MAC: &'static str = "Yandex/YandexBrowser/Default";
pub const VIVALDI_MAC: &'static str = "Vivaldi/Default";
pub const OPERA_MAC: &'static str = "com.operasoftware.Opera/Default";
pub const OPERAGX_MAC: &'static str = "com.operasoftware.OperaGX";
pub const COCCOC_MAC: &'static str = "Coccoc/Default";
pub const ARC_MAC: &'static str = "Arc/User Data/Default";
pub fn new(browser: Browser) -> Self {
let mut cookie_dir = dirs::config_local_dir().expect("get config dir failed");
let v = match browser {
Browser::Chrome => Self::CHROME_MAC,
Browser::Edge => Self::EDGE_MAC,
Browser::Chromium => Self::CHROMIUM_MAC,
Browser::Brave => Self::BRAVE_MAC,
Browser::Yandex => Self::YANDEX_MAC,
Browser::Vivaldi => Self::VIVALDI_MAC,
Browser::Opera => Self::OPERA_MAC,
Browser::OperaGX => Self::OPERAGX_MAC,
Browser::CocCoc => Self::COCCOC_MAC,
Browser::Arc => Self::ARC_MAC,
_ => panic!("MacOs Chromium base not support: {browser}"),
};
cookie_dir.push(v);
Self { base: cookie_dir, browser }
}
}
impl ChromiumInfo for MacChromiumBase {
fn base(&self) -> &PathBuf {
&self.base
}
}
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct MacFFBase {
pub base: PathBuf,
browser: Browser,
}
impl TempPath for MacFFBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl FfInfo for MacFFBase {
fn base(&self) -> &PathBuf {
&self.base
}
}
impl MacFFBase {
const FIREFOX_BASE: &'static str = "Firefox";
const LIBREWOLF_BASE: &'static str = "librewolf";
pub async fn new(browser: Browser) -> Result<Self> {
let init = dirs::config_local_dir()
.ok_or_else(|| miette::miette!("get config local dir failed"))?;
let base = match browser {
Browser::Librewolf => Self::LIBREWOLF_BASE,
Browser::Firefox => Self::FIREFOX_BASE,
_ => panic!("MacOs Firefox base not support: {browser}"),
};
let base = Self::helper(init, base).await?;
Ok(Self { base, browser })
}
}
}
#[cfg(target_os = "windows")]
pub mod win {
use std::path::PathBuf;
use miette::Result;
use super::{ChromiumInfo, FfInfo, TempPath};
use crate::Browser;
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct WinChromiumBase {
base: PathBuf,
browser: Browser,
}
impl TempPath for WinChromiumBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl WinChromiumBase {
pub fn into_key(mut self) -> PathBuf {
self.base.push(Self::LOCAL_STATE);
self.base
}
}
impl WinChromiumBase {
const EDGE_WIN: &'static str = "Microsoft/Edge/User Data/Default";
const CHROME_WIN: &'static str = "Google/Chrome/User Data/Default";
const CHROMIUM_WIN: &'static str = "Chromium/User Data/Default";
const BRAVE_WIN: &'static str = "BraveSoftware/Brave-Browser/User Data/Default";
const VIVALDI_WIN: &'static str = "Vivaldi/User Data/Default";
const COCCOC_WIN: &'static str = "CocCoc/Browser/User Data/Default";
const YANDEX_WIN: &'static str = "Yandex/YandexBrowser/User Data/Default";
const OPERA_WIN: &'static str = "Opera Software/Opera Stable/Default";
const OPERAGX_WIN: &'static str = "Opera Software/Opera GX Stable";
pub fn new(browser: Browser) -> Self {
let mut cookie_dir = if matches!(browser, Browser::Opera | Browser::OperaGX) {
dirs::data_dir().expect("get config dir failed")
}
else {
dirs::data_local_dir().expect("get config dir failed")
};
let path_base = match browser {
Browser::Edge => Self::EDGE_WIN,
Browser::Chromium => Self::CHROMIUM_WIN,
Browser::Chrome => Self::CHROME_WIN,
Browser::Brave => Self::BRAVE_WIN,
Browser::Yandex => Self::YANDEX_WIN,
Browser::Vivaldi => Self::VIVALDI_WIN,
Browser::Opera => Self::OPERA_WIN,
Browser::OperaGX => Self::OPERAGX_WIN,
Browser::CocCoc => Self::COCCOC_WIN,
_ => panic!("Windows Chromium base not support: {browser}."),
};
cookie_dir.push(path_base);
Self { base: cookie_dir, browser }
}
}
impl ChromiumInfo for WinChromiumBase {
const COOKIES: &'static str = "Network/Cookies"; fn base(&self) -> &PathBuf {
&self.base
}
fn local_state(&self) -> PathBuf {
if self.browser == Browser::OperaGX {
self.base().join(Self::LOCAL_STATE)
}
else {
let mut path = self.base().clone();
path.pop();
path.push(Self::LOCAL_STATE);
path
}
}
fn cookies_temp(&self) -> PathBuf {
self.temp_path_prefix()
.join("Cookies")
}
}
#[derive(Clone)]
#[derive(Debug)]
#[derive(Default)]
#[derive(PartialEq, Eq)]
pub struct WinFFBase {
base: PathBuf,
browser: Browser,
}
impl TempPath for WinFFBase {
fn browser(&self) -> Browser {
self.browser
}
}
impl WinFFBase {
const FIREFOX_BASE: &'static str = r"Mozilla\Firefox";
const LIBREWOLF_BASE: &'static str = "librewolf";
pub async fn new(browser: Browser) -> Result<Self> {
let base = match browser {
Browser::Librewolf => Self::LIBREWOLF_BASE,
Browser::Firefox => Self::FIREFOX_BASE,
_ => panic!("Windows Firefox base not support: {browser}"),
};
let init =
dirs::data_dir().ok_or_else(|| miette::miette!("get data local dir failed"))?;
let base = Self::helper(init, base).await?;
Ok(Self { base, browser })
}
}
impl FfInfo for WinFFBase {
fn base(&self) -> &PathBuf {
&self.base
}
}
}