blob: 3175029a65547f8b780e9a6b2663172f3f7603d2 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{anyhow, Result};
use fuchsia_hyper::new_https_client;
use hyper::body::HttpBody;
use hyper::header::LOCATION;
use hyper::{StatusCode, Uri};
use std::fs::{create_dir_all, set_permissions, File, Permissions};
use std::io::{copy, Write};
use std::path::PathBuf;
use zip::ZipArchive;
/// Downloads a package from CIPD. This handles 1 iteration of url re-direct.
///
/// # Arguments
///
/// * `url` - url to cipd package
/// * `dest` - zip file location to save the fetched file.
pub async fn download(url: Uri, dest: &PathBuf) -> Result<StatusCode> {
let client = new_https_client();
println!("{}", format!("Downloading... from {} to {}", url.clone(), dest.display()));
let mut res = client.get(url).await?;
let mut status = res.status();
if status == StatusCode::FOUND {
let redirect_url = res
.headers()
.get(LOCATION)
.ok_or(anyhow!("Cannot read http response header resp: {:?}", res))?;
res = client.get(redirect_url.to_str()?.parse::<Uri>()?).await?;
status = res.status();
}
if status == StatusCode::OK {
let mut output = File::create(dest)?;
while let Some(next) = res.data().await {
let chunk = next?;
output.write_all(&chunk)?;
}
}
Ok(status)
}
/// Extracts a zip file to a specified location.
///
/// # Arguments
///
/// * `zip_file` - path to the zip file to extract
/// * `dest_root` - path to the root location to extract the zip_file to
pub fn extract_zip(zip_file: &PathBuf, dest_root: &PathBuf) -> Result<()> {
let file = File::open(&zip_file)?;
let mut archive = ZipArchive::new(file)?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let outpath = dest_root.join(file.sanitized_name());
if file.name().ends_with('/') {
println!("File {} extracted to \"{}\"", i, outpath.display());
create_dir_all(&outpath)?;
} else {
println!("File {} extracted to \"{}\" ({} bytes)", i, outpath.display(), file.size());
if let Some(p) = outpath.parent() {
if !p.exists() {
create_dir_all(&p)?;
}
}
let mut outfile = File::create(&outpath)?;
copy(&mut file, &mut outfile)?;
}
// Get and Set permissions
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = file.unix_mode() {
set_permissions(&outpath, Permissions::from_mode(mode))?;
}
}
}
Ok(())
}