extern crate artc;
extern crate clap;
extern crate cstr_argument;
extern crate flate2;
extern crate gpgme;
extern crate reqwest;
extern crate tar;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{BufRead, Error, Write};
use std::path::{Path, PathBuf};
use std::result::Result;
use artc::artifacts::*;
use artc::prelude::*;
use clap::{App, Arg, SubCommand};
use cstr_argument::CStrArgument;
use flate2::write::GzEncoder;
use flate2::Compression;
use gpgme::Context as GpgContext;
use gpgme::Protocol;
use tar::Builder;
use tar::HeaderMode;
fn main() -> Result<(), Error> {
let artc_dir = default_artc_dir();
fs::create_dir_all(&artc_dir)?;
let matches = App::new("artc")
.version("0.1.0")
.author("Shane Isbell <shane.isbell@gmail.com>")
.about("Program for downloading artifacts and verifying the sha hashes and pgp signatures.")
.subcommand(
SubCommand::with_name("download")
.about(
"Downloads artifacts, verifies hashes/configs and imports keys into keystore",
)
.arg(
Arg::with_name("input")
.help("input file containing URLs of artifacts")
.takes_value(true)
.short("i")
.long("input"),
),
)
.subcommand(
SubCommand::with_name("rbm")
.about("generates tor rbm config file for downloads")
.arg(
Arg::with_name("keyring")
.help("the keyring name to use in generated config files")
.takes_value(true)
.short("k")
.long("keyring"),
),
)
.subcommand(
SubCommand::with_name("package")
.about("Packages artifacts into a maven repo and archives repo into single file"),
)
.get_matches();
let command = match matches.subcommand_name() {
Some("download") => Command::DownloadArtifacts,
Some("rbm") => Command::RbmConfigs,
Some("package") => Command::PackageArtifacts,
_ => Command::NoCommand,
};
match command {
Command::DownloadArtifacts => {
let input_file = matches
.subcommand_matches("download")
.unwrap()
.value_of("input")
.unwrap();
let mut log_file = create_file(&artc_dir, "download.log").unwrap();
let mut sha_file = create_file(&artc_dir, "sha.tsv").unwrap();
let mut asc_file = create_file(&artc_dir, "asc.tsv").unwrap();
let reader = get_buffer(input_file).unwrap();
let download_dir = default_artifacts_dir();
let pgp_keys_dir = default_keys_dir();
match fs::create_dir_all(&pgp_keys_dir) {
Ok(()) => {
for line_result in reader.lines() {
match line_result {
Result::Ok(url) => manage_artifact_downloads(
url,
&download_dir,
&pgp_keys_dir,
&log_file,
&sha_file,
&asc_file,
),
Result::Err(err) => {
log_file.write_fmt(format_args!(
"Unable to download artifact: {}",
err.to_string()
))?;
}
}
}
}
Err(e) => {
log(e.to_string(), &log_file);
}
}
}
Command::RbmConfigs => {
let rbm_dir = default_rbm_dir();
fs::create_dir_all(&rbm_dir)?;
let mut rbm_file = create_file(&rbm_dir, "config").unwrap();
let mut repo_file = create_file(&rbm_dir, "create_maven_repo.sh").unwrap();
let keyring = matches
.subcommand_matches("rbm")
.unwrap()
.value_of("keyring")
.unwrap();
match fs::copy(default_keyring(), PathBuf::new().join(default_artc_dir()).join(keyring)) {
Ok(_) => println!("Copied keyring {}", keyring),
Err(_) => { println!("Failed to copy keyring {}", keyring)}
}
let artifact_map = collect_artifacts(&artc_dir);
for (url, artifact_info) in &artifact_map {
match artifact_info.sig_type {
SigType::Asc => {
let out = format!(" - URL: {}\r\n sig_ext: asc\r\n file_gpg_id: {}\r\n gpg_keyring: {}\r\n", &url, &artifact_info.value, keyring);
rbm_file.write_all(out.as_bytes())?;
}
SigType::Sha => {
let out = format!(
" - URL: {}\r\n sha256Sum: {}\r\n",
&url, &artifact_info.value
);
if !artifact_info.verified {
rbm_file.write_all(
" #Sha not verified from original source\r\n".as_bytes(),
)?;
}
rbm_file.write_all(out.as_bytes())?;
}
}
}
println!("Wrote rbm config to {:?}", rbm_file);
repo_file
.write_all("# TODO: Set $M2_REPO to location of maven repository\r\n".as_bytes())?;
for (url, _artifact_info) in &artifact_map {
let filename = get_filename_from_url(&url, Path::new("/")).unwrap();
let out = format!(
"mkdir -p $M2_REPO{} && cp {:?} \"$_\"\r\n",
filename.parent().unwrap().to_str().unwrap(),
filename.file_name().unwrap()
);
repo_file.write_all(out.as_bytes())?;
}
println!("Wrote maven repo script to {:?}", repo_file);
}
Command::PackageArtifacts => {
let artifact_map = collect_artifacts(&artc_dir);
let base_dir = default_artifacts_dir();
for (url, _artifact_info) in &artifact_map {
let src_filename = get_filename_from_url(&url, &base_dir).unwrap();
let x = get_filename_from_url(&url, Path::new("")).unwrap();
let target_filename = default_maven_repo_dir().join(x);
fs::create_dir_all(&target_filename.parent().unwrap())?;
match fs::copy(&src_filename, &target_filename) {
Ok(_) => println!("Copied file: {:?}", target_filename),
Err(e) => {
println!("{} {:?} {:?}", e.to_string(), src_filename, target_filename)
}
}
}
let archive_path = PathBuf::new().join(&artc_dir).join("maven-repo.tar.gz");
let tar_gz = File::create(&archive_path).unwrap();
let enc = GzEncoder::new(&tar_gz, Compression::default());
let mut tar = Builder::new(enc);
tar.mode(HeaderMode::Deterministic);
tar.append_dir_all("m2", default_maven_repo_dir())?;
println!(
"Created archive {} with hash: {}",
&archive_path.display(),
get_hash(archive_path.as_path()).unwrap()
);
}
Command::NoCommand => {}
}
Ok(())
}
fn collect_artifacts(artc_dir: &PathBuf) -> HashMap<String, ArtifactInfo> {
let sha_file = open_file(&artc_dir, "sha.tsv").unwrap();
let asc_file = open_file(&artc_dir, "asc.tsv").unwrap();
let asc_reader = get_buffer_from_file(asc_file).unwrap();
let mut asc_map = HashMap::new();
for line_result in asc_reader.lines() {
let v: Vec<String> = split_by_tab(line_result.unwrap());
let (fingerprint, url) = (&v[0], &v[1]);
asc_map.insert(
url.to_string(),
ArtifactInfo {
sig_type: SigType::Asc,
value: fingerprint.to_string(),
verified: true,
},
);
}
let sha_reader = get_buffer_from_file(sha_file).unwrap();
let mut sha_map = HashMap::new();
for line_result in sha_reader.lines() {
let v: Vec<String> = split_by_tab(line_result.unwrap());
let (hash, verified, url) = (&v[0], &v[1], &v[2]);
sha_map.insert(
url.to_string(),
ArtifactInfo {
sig_type: SigType::Sha,
value: hash.to_string(),
verified: (" ver " == verified),
},
);
}
merge(sha_map, asc_map)
}
fn manage_artifact_downloads(
url: String,
download_dir: &PathBuf,
keys_dir: &PathBuf,
log_file: &File,
sha_file: &File,
asc_file: &File,
) {
let main_artifact_url = url.as_str();
let main_artifact_filename = get_filename_from_url(&main_artifact_url, &download_dir).unwrap();
let main_artifact: MainArtifact =
Artifact::new(main_artifact_url, main_artifact_filename.as_path(), None);
match &main_artifact.download() {
Ok(_) => {}
Err(e) => {
log(e.to_string(), &log_file);
()
}
}
let sha_url = main_artifact.append_url_ext("sha2");
let sha_path = main_artifact.append_file_ext("sha2");
let sha_artifact: ArtifactSha =
Artifact::new(sha_url.as_str(), sha_path.as_path(), Some(&main_artifact));
match &sha_artifact.download() {
Ok(_) => match sha_artifact.check_hash() {
Ok(sha_hash) => {
tab_fmt3(sha_hash.hash, "ver".to_string(), sha_hash.url, &sha_file);
}
Err(e) => {
log(e.to_string(), &log_file);
}
},
Err(e) => {
log(e.to_string(), &log_file);
match &sha_artifact.get_hash() {
Ok(hash) => {
tab_fmt3(
hash.to_string(),
"gen".to_string(),
sha_artifact.attached_artifact.url.to_string(),
&sha_file,
);
}
Err(e) => {
log(e.to_string(), &log_file);
}
}
}
}
let mut ctx = GpgContext::from_protocol(Protocol::OpenPgp).unwrap();
ctx.set_engine_home_dir("target/artc".to_string().into_cstr());
let asc_url = main_artifact.append_url_ext("asc");
let asc_path = main_artifact.append_file_ext("asc");
let asc_artifact: ArtifactAsc =
Artifact::new(asc_url.as_str(), asc_path.as_path(), Some(&main_artifact));
match &asc_artifact.download() {
Ok(_) => match verify_artifact(&asc_artifact, &keys_dir, &mut ctx) {
Ok(key_id) => {
tab_fmt2(key_id.id, key_id.url, &asc_file);
}
Err(e) => {
log(e.to_string(), &log_file);
}
},
Err(e) => {
log(e.to_string(), &log_file);
}
}
}
enum Command {
DownloadArtifacts,
RbmConfigs,
PackageArtifacts,
NoCommand,
}