1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::path::PathBuf;

use miette::{IntoDiagnostic, Result};
use tokio::fs::read_to_string;

use crate::{browser::BrowserFile, firefox, Browser};

/// just impl the `base` method
pub trait FFPath {
    const COOKIES: &'static str = "cookies.sqlite";
    const DATAS: &'static str = "places.sqlite"; // bookmarks etc.
    const KEY: &'static str = "key4.db"; // key sqlite3
    const STORAGE: &'static str = "webappsstore.sqlite"; // web storage data
    const PASSWD: &'static str = "logins.json"; // passwd
    const EXTENSIONS: &'static str = "extensions.json";

    fn base(&self) -> &PathBuf;

    /// json
    fn extensions(&self) -> PathBuf {
        self.base().join(Self::EXTENSIONS)
    }
    /// json
    fn passwd(&self) -> PathBuf {
        self.base().join(Self::PASSWD)
    }
    /// sqlite3
    fn storage(&self) -> PathBuf {
        self.base().join(Self::STORAGE)
    }
    /// sqlite3
    fn key(&self) -> PathBuf {
        self.base().join(Self::KEY)
    }
    /// sqlite3
    fn datas(&self) -> PathBuf {
        self.base().join(Self::DATAS)
    }
    /// sqlite3
    fn cookies(&self) -> PathBuf {
        self.base().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)
        }
    }
}

pub async fn file_path(browser: Browser, file: BrowserFile) -> Result<PathBuf> {
    #[cfg(target_os = "linux")]
    let res = firefox::linux::path::LinuxFFBase::new(browser).await?;
    #[cfg(target_os = "macos")]
    let res = firefox::macos::path::MacFFBase::new(browser).await?;
    #[cfg(target_os = "windows")]
    let res = firefox::win::path::WinFFBase::new(browser).await?;
    let pt = match file {
        BrowserFile::Cookies => res.cookies(),
        BrowserFile::Key => res.key(),
        BrowserFile::Storage => res.storage(),
        BrowserFile::Passwd => res.passwd(),
        BrowserFile::Extensions => res.extensions(),
        BrowserFile::Bookmarks | BrowserFile::History => res.datas(),
        // Bookmarks: moz_bookmarks,  History: moz_places table
        _ => miette::bail!("just chromium base have"),
    };
    Ok(pt)
}