1#[cfg(target_os = "macos")]
2use decrypt_cookies::SafariGetter;
3use decrypt_cookies::{Browser, ChromiumBuilder, ChromiumGetter, FirefoxBuilder, FirefoxGetter};
4use miette::{IntoDiagnostic, Result};
5use strum::IntoEnumIterator;
6use tokio::{
7 fs::{create_dir_all, File, OpenOptions},
8 io::{AsyncWriteExt, BufWriter},
9};
10
11const BASE_DIR: &str = "./results";
12
13async fn open_file(browser: Browser, item: &str) -> Result<BufWriter<File>> {
14 Ok(BufWriter::new(
15 OpenOptions::new()
16 .create(true)
17 .write(true)
18 .open(format!("{BASE_DIR}/{browser}-{item}.csv"))
19 .await
20 .into_diagnostic()?,
21 ))
22}
23
24async fn write_chromium_password(getter: &ChromiumGetter) -> Result<()> {
25 println!("{} password", getter.browser());
26 let getter = ChromiumBuilder::new(getter.browser())
27 .build()
28 .await?;
29 let all_passwords = getter.get_logins_all().await?;
30 let head = b"Url,Username,Password,CreateDate,LastUsedDate,PasswordModifiedDate\n";
31 let mut buf_writer = open_file(getter.browser(), "password").await?;
32 buf_writer
33 .write_all(head)
34 .await
35 .into_diagnostic()?;
36
37 for ck in all_passwords {
38 let pass_str = format!(
39 "{},{},{},{},{},{}\n",
40 ck.origin_url,
41 ck.username_value
42 .unwrap_or_default(),
43 ck.password_value
44 .unwrap_or_default(),
45 ck.date_created.unwrap_or_default(),
46 ck.date_last_used
47 .unwrap_or_default(),
48 ck.date_password_modified
49 .unwrap_or_default()
50 );
51 buf_writer
52 .write_all(pass_str.as_bytes())
53 .await
54 .into_diagnostic()?;
55 }
56 buf_writer
57 .flush()
58 .await
59 .into_diagnostic()?;
60 buf_writer
61 .get_ref()
62 .sync_all()
63 .await
64 .into_diagnostic()?;
65 println!("{} password done", getter.browser());
66
67 Ok(())
68}
69
70async fn write_chromium_cookies(getter: &ChromiumGetter) -> Result<()> {
71 println!("{} cookies", getter.browser());
72 let getter = ChromiumBuilder::new(getter.browser())
73 .build()
74 .await?;
75 let cks = getter.get_cookies_all().await?;
76 let mut buf_writer = open_file(getter.browser(), "cookies").await?;
77 let head = b"Url,Name,Path,Value,DecryptedValue,IsSecure,IsHttponly,SourcePort,CreationUtc,ExpiresUtc,LastAccessUtc,LastUpdateUtc,HasExpires,IsPersistent\n";
78 buf_writer
79 .write_all(head)
80 .await
81 .into_diagnostic()?;
82 for ck in cks {
83 let ck_str = format!(
84 "{},{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
85 ck.host_key,
86 ck.name,
87 ck.path,
88 ck.value,
89 ck.decrypted_value
90 .unwrap_or_default(),
91 ck.is_secure,
92 ck.is_httponly,
93 ck.source_port,
94 ck.creation_utc.unwrap_or_default(),
95 ck.expires_utc.unwrap_or_default(),
96 ck.last_access_utc
97 .unwrap_or_default(),
98 ck.last_update_utc
99 .unwrap_or_default(),
100 ck.has_expires,
101 ck.is_persistent,
102 );
103 buf_writer
104 .write_all(ck_str.as_bytes())
105 .await
106 .into_diagnostic()?;
107 }
108
109 buf_writer
110 .flush()
111 .await
112 .into_diagnostic()?;
113 buf_writer
114 .get_ref()
115 .sync_all()
116 .await
117 .into_diagnostic()?;
118 println!("{} cookies done", getter.browser());
119
120 Ok(())
121}
122
123async fn write_firefox_cookies(getter: &FirefoxGetter) -> Result<()> {
124 println!("{} cookies", getter.browser());
125 let head = b"Host,Name,Path,Value,CreationTime,LastAccessed,Expiry,IsSecure,IsHttpOnly,OriginAttributes";
126 let mut buf_writer = open_file(getter.browser(), "cookies").await?;
127 buf_writer
128 .write_all(head)
129 .await
130 .into_diagnostic()?;
131 let cks = getter.get_cookies_all().await?;
132 for ck in cks {
133 let ck_str = format!(
134 "{},{},{},{},{},{},{},{},{},{}\n",
135 ck.host,
136 ck.name,
137 ck.path,
138 ck.value,
139 ck.creation_time
140 .unwrap_or_default(),
141 ck.last_accessed
142 .unwrap_or_default(),
143 ck.expiry.unwrap_or_default(),
144 ck.is_secure,
145 ck.is_http_only,
146 ck.origin_attributes
147 );
148 buf_writer
149 .write_all(ck_str.as_bytes())
150 .await
151 .into_diagnostic()?;
152 }
153
154 buf_writer
155 .flush()
156 .await
157 .into_diagnostic()?;
158 buf_writer
159 .get_ref()
160 .sync_all()
161 .await
162 .into_diagnostic()?;
163 println!("{} cookies done", getter.browser());
164 Ok(())
165}
166
167#[cfg(target_os = "macos")]
168async fn write_safari_cookies(getter: &SafariGetter) -> Result<()> {
169 println!("{} cookies", getter.browser());
170 let head = b"Domain,Name,Path,Value,Creation,Expires,IsSecure,IsHttpOnly,Comment\n";
171 let mut buf_writer = open_file(getter.browser(), "cookies").await?;
172 buf_writer
173 .write_all(head)
174 .await
175 .into_diagnostic()?;
176 let cks = getter.all_cookies();
177 for ck in cks {
178 let ck_str = format!(
179 "{},{},{},{},{},{},{},{},{}\n",
180 ck.domain,
181 ck.name,
182 ck.path,
183 ck.value,
184 ck.creation.unwrap_or_default(),
185 ck.expires.unwrap_or_default(),
186 ck.is_secure,
187 ck.is_httponly,
188 ck.comment,
189 );
190 buf_writer
191 .write_all(ck_str.as_bytes())
192 .await
193 .into_diagnostic()?;
194 }
195
196 buf_writer
197 .flush()
198 .await
199 .into_diagnostic()?;
200 buf_writer
201 .get_ref()
202 .sync_all()
203 .await
204 .into_diagnostic()?;
205 println!("{} cookies done", getter.browser());
206 Ok(())
207}
208
209pub async fn run() -> Result<()> {
210 create_dir_all(BASE_DIR)
211 .await
212 .into_diagnostic()?;
213 let mut jds = vec![];
214
215 for browser in Browser::iter() {
216 match browser {
217 Browser::Firefox | Browser::Librewolf => {
218 let hd = tokio::spawn(async move {
219 let getter = match FirefoxBuilder::new(browser)
220 .build()
221 .await
222 {
223 Ok(it) => it,
224 Err(err) => {
225 tracing::warn!("Firefox Getter wrong: {err}");
226 return;
227 },
228 };
229 match write_firefox_cookies(&getter).await {
230 Ok(()) => {},
231 Err(err) => tracing::warn!("{err}"),
232 }
233 });
234 jds.push(hd);
235 },
236 #[cfg(target_os = "macos")]
237 Browser::Safari => {
238 use decrypt_cookies::SafariBuilder;
239 let hd = tokio::spawn(async move {
240 let getter = match SafariBuilder::new().build().await {
241 Ok(it) => it,
242 Err(err) => {
243 tracing::warn!("{browser} wrong: {err}");
244 return;
245 },
246 };
247 match write_safari_cookies(&getter).await {
248 Ok(()) => {},
249 Err(err) => tracing::warn!("{browser} wrong: {err}"),
250 };
251 });
252 jds.push(hd);
253 },
254
255 browser => {
256 let hd = tokio::spawn(async move {
257 let getter = match ChromiumBuilder::new(browser)
258 .build()
259 .await
260 {
261 Ok(it) => it,
262 Err(err) => {
263 tracing::warn!("{browser} wrong: {err}");
264 return;
265 },
266 };
267 match write_chromium_cookies(&getter).await {
268 Ok(()) => {},
269 Err(err) => tracing::warn!("{browser} Cookies wrong: {err}"),
270 };
271 match write_chromium_password(&getter).await {
272 Ok(()) => {},
273 Err(err) => tracing::warn!("{browser} Cookies wrong: {err}"),
274 };
275 });
276 jds.push(hd);
277 },
278 }
279 }
280 for ele in jds {
281 match ele.await {
282 Ok(()) => {},
283 Err(err) => tracing::warn!("{err}"),
284 }
285 }
286
287 Ok(())
288}