dmg_cracker/
passwords.rs

1use std::fs::File;
2use std::io::Read;
3use std::path::Path;
4
5pub fn read_password_list(
6    filepath: &str,
7) -> Result<Vec<String>, Box<dyn std::error::Error>> {
8    let path = Path::new(filepath);
9    let extension = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
10
11    match extension.to_lowercase().as_str() {
12        "csv" => read_csv_passwords(filepath),
13        _ => read_txt_passwords(filepath),
14    }
15}
16
17fn validate_password(password: &str) -> Option<String> {
18    let trimmed = password.trim();
19    if trimmed.is_empty() {
20        None
21    } else {
22        Some(trimmed.to_string())
23    }
24}
25
26fn read_txt_passwords(
27    filepath: &str,
28) -> Result<Vec<String>, Box<dyn std::error::Error>> {
29    let mut file = File::open(filepath)?;
30    let mut contents = String::new();
31    file.read_to_string(&mut contents)?;
32    let passwords: Vec<String> =
33        contents.lines().filter_map(validate_password).collect();
34    Ok(passwords)
35}
36
37fn read_csv_passwords(
38    filepath: &str,
39) -> Result<Vec<String>, Box<dyn std::error::Error>> {
40    let file = File::open(filepath)?;
41    let mut rdr = csv::ReaderBuilder::new()
42        .has_headers(false)
43        .from_reader(file);
44    let mut passwords = Vec::new();
45
46    for result in rdr.records() {
47        let record = result?;
48        // Take the first column as the password
49        if let Some(password) = record.get(0) {
50            if let Some(validated) = validate_password(password) {
51                passwords.push(validated);
52            }
53        }
54    }
55    Ok(passwords)
56}
57
58#[cfg(test)]
59mod test_read_password_list {
60    use super::*;
61    use std::io::Write;
62    use tempfile::NamedTempFile;
63
64    #[test]
65    fn test_read_password_list_txt() {
66        let file_contents = "password1\npassword2\npassword3";
67        let mut temp_file = NamedTempFile::new().unwrap();
68        temp_file.write_all(file_contents.as_bytes()).unwrap();
69
70        let password_list =
71            read_password_list(temp_file.path().to_str().unwrap()).unwrap();
72
73        assert_eq!(password_list, vec!["password1", "password2", "password3"]);
74    }
75
76    #[test]
77    fn test_read_password_list_csv() {
78        let file_contents = "password1\npassword2\npassword3\n";
79        let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
80        temp_file.write_all(file_contents.as_bytes()).unwrap();
81
82        let password_list =
83            read_password_list(temp_file.path().to_str().unwrap()).unwrap();
84
85        assert_eq!(password_list, vec!["password1", "password2", "password3"]);
86    }
87
88    #[test]
89    fn test_read_password_list_csv_with_headers() {
90        let file_contents = "password,description\npassword1,common\npassword2,weak\npassword3,medium\n";
91        let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
92        temp_file.write_all(file_contents.as_bytes()).unwrap();
93
94        let password_list =
95            read_password_list(temp_file.path().to_str().unwrap()).unwrap();
96
97        assert_eq!(
98            password_list,
99            vec!["password", "password1", "password2", "password3"]
100        );
101    }
102
103    #[test]
104    fn test_read_password_list_csv_with_quotes() {
105        let file_contents = "\"password1\",\"description1\"\n\"password,2\",\"description2\"\n\"password3\",\"description3\"\n";
106        let mut temp_file = NamedTempFile::with_suffix(".csv").unwrap();
107        temp_file.write_all(file_contents.as_bytes()).unwrap();
108
109        let password_list =
110            read_password_list(temp_file.path().to_str().unwrap()).unwrap();
111
112        assert_eq!(password_list, vec!["password1", "password,2", "password3"]);
113    }
114}