1use random_string::generate;
2use std::io::Write;
3use std::process::{Command, Stdio};
4
5const BASE_MOUNT_PATH: &str = "/tmp/";
6
7pub struct Dmg {
8 pub dmg_path: String,
9 pub mount_path: String,
10}
11
12impl Dmg {
13 pub fn new(dmg_path: &str) -> Self {
14 Self {
15 dmg_path: dmg_path.to_string(),
16 mount_path: generate_random_string(),
17 }
18 }
19
20 pub fn attempt_password(&self, password: &str) -> bool {
21 self.attempt_attach(password)
22 }
23
24 fn attempt_attach(&self, password: &str) -> bool {
25 let mut child = match Command::new("hdiutil")
26 .arg("verify")
27 .arg("-stdinpass")
28 .arg("-quiet")
29 .arg(&self.dmg_path)
30 .stdin(Stdio::piped())
31 .stdout(Stdio::null())
32 .stderr(Stdio::null())
33 .spawn()
34 {
35 Ok(child) => child,
36 Err(e) => {
37 eprintln!("❌ Error: Failed to execute hdiutil command: {e}");
38 eprintln!("💡 Please ensure:");
39 eprintln!(" - You're running on macOS (hdiutil is required)");
40 eprintln!(" - You have permission to access the DMG file");
41 eprintln!(" - The DMG file exists and is not corrupted");
42 return false;
43 }
44 };
45
46 if let Some(stdin) = child.stdin.as_mut() {
47 if let Err(e) = stdin.write_all(password.as_bytes()) {
48 eprintln!("❌ Error: Failed to write password to hdiutil: {e}");
49 return false;
50 }
51 } else {
52 eprintln!("❌ Error: Failed to get stdin handle for hdiutil");
53 return false;
54 }
55
56 match child.wait_with_output() {
57 Ok(output) => output.status.success(),
58 Err(e) => {
59 eprintln!("❌ Error: Failed to wait for hdiutil process: {e}");
60 false
61 }
62 }
63 }
64}
65
66pub fn generate_random_string() -> String {
67 format!(
68 "{}{}",
69 BASE_MOUNT_PATH,
70 generate(6, "abcdefghijklmnopqrstuvwxyz")
71 )
72}