tidy_browser/
firefox.rs

1use std::{collections::HashSet, fmt::Display, path::PathBuf};
2
3use decrypt_cookies::{firefox::builder::FirefoxBuilderError, prelude::*};
4use snafu::ResultExt;
5use strum::IntoEnumIterator;
6use tokio::task;
7
8use crate::{
9    args::{FirefoxName, Format, Value},
10    error::{self, Result},
11    utils::{self},
12};
13
14#[derive(Clone, Copy)]
15#[derive(Debug)]
16#[derive(Default)]
17#[derive(PartialEq, Eq, PartialOrd, Ord)]
18pub struct FirefoxBased;
19
20impl FirefoxBased {
21    pub async fn multi_data<H>(
22        names: impl Iterator<Item = FirefoxName>,
23        output_dir: PathBuf,
24        sep: String,
25        host: H,
26        format: Format,
27    ) -> Result<()>
28    where
29        H: Into<Option<String>>,
30    {
31        let host = host.into();
32        for task in names.map(|name| {
33            let host = host.clone();
34            let output_dir = output_dir.clone();
35            let sep = sep.clone();
36            let values = HashSet::from_iter(Value::iter());
37
38            tokio::task::spawn(async move {
39                Self::write_data(
40                    name, None, None, None, host, values, output_dir, sep, format,
41                )
42                .await
43            })
44        }) {
45            if let Err(e) = task
46                .await
47                .context(error::TokioTaskSnafu)?
48            {
49                match e {
50                    error::Error::FirefoxBuilder {
51                        source: FirefoxBuilderError::NotFoundBase { path, .. },
52                        ..
53                    } => {
54                        tracing::info!(
55                            r#"Not found {},
56The browser is not installed or started with `-P`/`-profile` arg."#,
57                            path.display()
58                        );
59                        todo!()
60                    },
61                    e => tracing::error!("{e}"),
62                }
63            }
64        }
65
66        Ok(())
67    }
68
69    #[tracing::instrument(skip_all, fields(name), level = "info")]
70    #[expect(clippy::too_many_arguments, reason = "bin not lib")]
71    pub async fn write_data<B, P, PP, H, S>(
72        name: FirefoxName,
73        base: B,
74        profile: P,
75        profile_path: PP,
76        host: H,
77        values: HashSet<Value>,
78        mut output_dir: PathBuf,
79        sep: S,
80        format: Format,
81    ) -> Result<()>
82    where
83        B: Into<Option<PathBuf>>,
84        P: Into<Option<String>>,
85        PP: Into<Option<PathBuf>>,
86        H: Into<Option<String>>,
87        S: Display + Send + Clone + 'static,
88    {
89        let base: Option<PathBuf> = base.into();
90        let profile: Option<String> = profile.into();
91        let profile_path: Option<PathBuf> = profile_path.into();
92        let host: Option<String> = host.into();
93
94        macro_rules! firefoxes {
95            ($($browser:ident,) *) => {
96                match name {
97                    $(
98                        FirefoxName::$browser => {
99                            let firefox = if let Some(pp) = profile_path {
100                                FirefoxBuilder::<$browser>::with_profile_path(pp)
101                            }
102                            else {
103                                let mut builder = FirefoxBuilder::<$browser>::new();
104                                if let Some(base) = base {
105                                    builder.base(base);
106                                }
107                                if let Some(profile) = &profile {
108                                    builder.profile(profile);
109                                }
110                                builder
111                            }
112                            .build()
113                                .await
114                                .context(error::FirefoxBuilderSnafu)?;
115
116                            let task = if values.contains(&Value::Cookie) {
117                                let host = host.clone();
118                                let firefox = firefox.clone();
119                                let task = task::spawn(async move {
120                                    let cookies = if let Some(host) = host {
121                                        firefox.cookies_by_host(host).await
122                                    }
123                                    else {
124                                        firefox.cookies_all().await
125                                    }
126                                    .context(error::FirefoxSnafu)?;
127                                    Ok::<_, error::Error>(cookies)
128                                });
129                                Some(task)
130                            }
131                            else {
132                                None
133                            };
134
135                            if values.contains(&Value::Login) {
136                                // TODO:
137                            }
138                            (task, $browser::NAME)
139                        },
140                    )*
141                }
142            };
143        }
144
145        let (ff, name) = firefoxes![Firefox, Librewolf, Floorp, Zen,];
146
147        output_dir.push(name);
148        tokio::fs::create_dir_all(&output_dir)
149            .await
150            .with_context(|_| error::IoSnafu { path: output_dir.clone() })?;
151
152        if let Some(ff) = ff {
153            let cookies = ff
154                .await
155                .context(error::TokioTaskSnafu)??;
156            let out_file = output_dir.join(crate::COOKIES_FILE_CSV);
157            let sep = sep.clone();
158
159            utils::write_cookies(out_file, cookies, sep, format)
160                .await
161                .context(error::TokioTaskSnafu)??;
162        }
163
164        Ok(())
165    }
166}