Skip to main content

verifyos_cli/parsers/
zip_extractor.rs

1use std::fs;
2use std::io;
3use std::path::{Path, PathBuf};
4use tempfile::TempDir;
5use zip::ZipArchive;
6
7#[derive(Debug, thiserror::Error)]
8pub enum ExtractionError {
9    #[error("IO Error: {0}")]
10    Io(#[from] io::Error),
11    #[error("Zip Error: {0}")]
12    Zip(#[from] zip::result::ZipError),
13    #[error("Invalid IPA path: {0}")]
14    InvalidPath(PathBuf),
15}
16
17pub struct ExtractedIpa {
18    pub temp_dir: TempDir,
19    pub payload_dir: PathBuf,
20}
21
22impl ExtractedIpa {
23    pub fn get_app_bundle_path(&self) -> io::Result<Option<PathBuf>> {
24        if !self.payload_dir.exists() {
25            return Ok(None);
26        }
27
28        // Try direct root search first
29        for entry in fs::read_dir(&self.payload_dir)? {
30            let entry = entry?;
31            let path = entry.path();
32            if path.extension().and_then(|e| e.to_str()) == Some("app") {
33                return Ok(Some(path));
34            }
35        }
36
37        // Fallback: recursive search
38        let mut queue = vec![self.payload_dir.clone()];
39        while let Some(dir) = queue.pop() {
40            if let Ok(entries) = fs::read_dir(dir) {
41                for entry in entries.flatten() {
42                    let path = entry.path();
43                    if path.is_dir() {
44                        if path.extension().and_then(|e| e.to_str()) == Some("app") {
45                            return Ok(Some(path));
46                        }
47                        queue.push(path);
48                    }
49                }
50            }
51        }
52
53        Ok(None)
54    }
55}
56
57pub fn extract_ipa<P: AsRef<Path>>(ipa_path: P) -> Result<ExtractedIpa, ExtractionError> {
58    let path = ipa_path.as_ref();
59    if !path.exists() {
60        return Err(ExtractionError::InvalidPath(path.to_path_buf()));
61    }
62    let file = fs::File::open(path)?;
63    let mut archive = ZipArchive::new(file)?;
64    let temp_dir = tempfile::tempdir()?;
65    let extract_path = temp_dir.path();
66
67    archive.extract(extract_path)?;
68
69    let mut payload_dir = extract_path.join("Payload");
70    if !payload_dir.exists() {
71        payload_dir = extract_path.to_path_buf();
72    }
73
74    Ok(ExtractedIpa {
75        temp_dir,
76        payload_dir,
77    })
78}