1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::fs;
use std::io;
use std::fs::File;
use std::path::{Component, PathBuf, Path};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use flate2::read::GzDecoder;
use tar::Archive;
use zip;

pub fn extract_zip(filename: &PathBuf, install_dir: &PathBuf) -> Result<(), io::Error> {
    let file = File::open(filename)?;
    let mut archive = zip::ZipArchive::new(file)?;
    for i in 0..archive.len() {
        let mut file = archive.by_index(i)?;
        let outpath = sanitize_filename(file.name());
        let install_path = Path::new(install_dir.as_path()).join(outpath);
        println!("{}", install_path.display());

        create_directory(install_path.parent().unwrap_or(Path::new("")), None)?;
        let perms = convert_permissions(file.unix_mode());

        if (&*file.name()).ends_with("/") {
            create_directory(&install_path, perms)?;
        } else {
            write_file(&mut file, &install_path, perms)?;
        }
    }
    Ok(())
}

pub fn extract_targz(targz: &PathBuf, install_dir: &PathBuf) -> Result<(), io::Error> {
    let tarball = File::open(targz)?;
    let gz = GzDecoder::new(tarball)?;
    let mut tar = Archive::new(gz);
    tar.unpack(install_dir)?;
    Ok(())
}

fn write_file(file: &mut zip::read::ZipFile,
              outpath: &Path,
              perms: Option<fs::Permissions>)
              -> Result<(), io::Error> {
    let mut outfile = File::create(&outpath)?;
    io::copy(file, &mut outfile)?;
    if let Some(perms) = perms {
        fs::set_permissions(outpath, perms)?;
    }
    Ok(())
}

fn create_directory(outpath: &Path, perms: Option<fs::Permissions>) -> Result<(), io::Error> {
    fs::create_dir_all(&outpath)?;
    if let Some(perms) = perms {
        fs::set_permissions(outpath, perms)?;
    }
    Ok(())
}

#[cfg(unix)]
fn convert_permissions(mode: Option<u32>) -> Option<fs::Permissions> {
    match mode {
        Some(mode) => Some(fs::Permissions::from_mode(mode)),
        None => None,
    }
}

#[cfg(not(unix))]
fn convert_permissions(_mode: Option<u32>) -> Option<fs::Permissions> {
    None
}

fn sanitize_filename(filename: &str) -> PathBuf {
    let no_null_filename = match filename.find('\0') {
        Some(index) => &filename[0..index],
        None => filename,
    };

    Path::new(no_null_filename)
        .components()
        .filter(|component| *component != Component::ParentDir)
        .fold(PathBuf::new(), |mut path, ref cur| {
            path.push(cur.as_os_str());
            path
        })
}

#[cfg(test)]
mod test {
    use super::*;
    use std::env;
    use tempdir::TempDir;

    #[test]
    fn test_zip_extractor() {
        let pwd = env::current_dir().unwrap();
        let zipfile = pwd.join("fixture").join("test-extractor.zip");
        let extract_dir = TempDir::new("grnenv-rs").unwrap().into_path();
        assert!(extract_zip(&zipfile, &extract_dir).is_ok());
        assert!(extract_dir.is_dir());
        assert!(extract_dir.join("test-extractor").is_dir());
        assert!(extract_dir.join("test-extractor").join("test.txt").exists());
        assert!(extract_dir.join("test-extractor").join("nested").is_dir());
        assert!(extract_dir.join("test-extractor").join("nested").join("test.txt").exists());
    }

    #[test]
    fn test_targz_extractor() {
        let pwd = env::current_dir().unwrap();
        let targz = pwd.join("fixture").join("test-extractor.tar.gz");
        let extract_dir = TempDir::new("grnenv-rs").unwrap().into_path();
        assert!(extract_targz(&targz, &extract_dir).is_ok());
        assert!(extract_dir.is_dir());
        assert!(extract_dir.join("test-extractor").is_dir());
        assert!(extract_dir.join("test-extractor").join("test.txt").exists());
        assert!(extract_dir.join("test-extractor").join("nested").is_dir());
        assert!(extract_dir.join("test-extractor").join("nested").join("test.txt").exists());
    }
}