| extern crate clap; |
| extern crate env_logger; |
| #[macro_use] |
| extern crate log; |
| #[cfg(test)] |
| extern crate tempdir; |
| extern crate tuf as _tuf; |
| extern crate url; |
| |
| use clap::{App, AppSettings, SubCommand, Arg, ArgMatches, ArgGroup}; |
| use std::path::PathBuf; |
| use _tuf::{Tuf, Config, Error, RemoteRepo}; |
| use url::Url; |
| |
| // TODO logging |
| |
| fn main() { |
| let matches = parser().get_matches(); |
| env_logger::init().unwrap(); |
| |
| match run_main(matches) { |
| Ok(()) => std::process::exit(0), |
| Err(e) => { |
| error!("{:?}", e); |
| std::process::exit(1); |
| } |
| } |
| } |
| |
| fn run_main(matches: ArgMatches) -> Result<(), Error> { |
| let remote = matches.value_of("url").map(|u| RemoteRepo::Http(Url::parse(u).unwrap())) |
| .or_else(|| matches.value_of("file").map(|p| RemoteRepo::File(PathBuf::from(p)))) |
| .unwrap(); |
| let config = Config::build().remote(remote) |
| .local_path(PathBuf::from(matches.value_of("path").unwrap())) |
| .finish()?; |
| |
| if let Some(_) = matches.subcommand_matches("init") { |
| let path = PathBuf::from(matches.value_of("path").unwrap()); |
| cmd_init(&path) |
| } else if let Some(_) = matches.subcommand_matches("list") { |
| let mut tuf = Tuf::new(config)?; |
| cmd_list(&mut tuf) |
| } else if let Some(_) = matches.subcommand_matches("update") { |
| let mut tuf = Tuf::new(config)?; |
| cmd_update(&mut tuf) |
| } else if let Some(matches) = matches.subcommand_matches("verify") { |
| let mut tuf = Tuf::new(config)?; |
| cmd_verify(&mut tuf, matches.value_of("target").unwrap()) |
| } else { |
| unreachable!() // because of AppSettings::SubcommandRequiredElseHelp |
| } |
| } |
| |
| fn url_validator(url: String) -> Result<(), String> { |
| Url::parse(&url) |
| .map(|_| ()) |
| .map_err(|_| "URL was not valid".into()) |
| } |
| |
| fn parser<'a, 'b>() -> App<'a, 'b> { |
| App::new("tuf") |
| .version(env!("CARGO_PKG_VERSION")) |
| .about("CLI tool for verifying TUF metadata and downloading targets") |
| .settings(&[AppSettings::SubcommandRequiredElseHelp]) |
| .arg(Arg::with_name("verbose") |
| .short("v") |
| .long("verbose") |
| .multiple(true) |
| .help("Increase the verbosity of output to stderr")) |
| .arg(Arg::with_name("url") |
| .short("U") |
| .long("url") |
| .takes_value(true) |
| .validator(url_validator) |
| .help("URL of the TUF repo")) |
| .arg(Arg::with_name("file") |
| .short("f") |
| .long("file") |
| .takes_value(true) |
| .help("Path to the TUF repo (remote)")) |
| .arg(Arg::with_name("path") |
| .short("p") |
| .long("path") |
| .takes_value(true) |
| .required(true) |
| .help("Local path the TUF repo")) |
| .group(ArgGroup::with_name("remote_repo") |
| .args(&["url", "file"]) |
| .required(true)) |
| .subcommand(SubCommand::with_name("init").about("Initializes a new TUF repo")) |
| .subcommand(SubCommand::with_name("list").about("Lists available targets")) |
| .subcommand(SubCommand::with_name("update").about("Updates metadata from remotes")) |
| .subcommand(SubCommand::with_name("verify") |
| .about("Verifies a target") |
| .arg(Arg::with_name("target") |
| .takes_value(true) |
| .required(true) |
| .help("The full (non-local) path of the target to verify"))) |
| } |
| |
| fn cmd_init(local_path: &PathBuf) -> Result<(), Error> { |
| Tuf::initialize(local_path) |
| } |
| |
| fn cmd_list(tuf: &mut Tuf) -> Result<(), Error> { |
| let mut targets = tuf.list_targets(); |
| targets.sort(); |
| |
| for target in targets.iter() { |
| println!("{}", target); |
| } |
| |
| Ok(()) |
| } |
| |
| fn cmd_update(tuf: &mut Tuf) -> Result<(), Error> { |
| tuf.update() |
| } |
| |
| fn cmd_verify(tuf: &mut Tuf, target: &str) -> Result<(), Error> { |
| tuf.fetch_target(target) |
| .map(|_| ()) |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use std::fs::{self, DirBuilder}; |
| use std::path::{Path, PathBuf}; |
| use tempdir::TempDir; |
| |
| fn vector_path() -> PathBuf { |
| PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| .join("tests") |
| .join("tuf-test-vectors") |
| .join("tuf") |
| .join("001") |
| .join("repo") |
| } |
| |
| #[test] |
| fn test_clap() { |
| let _ = parser(); |
| } |
| |
| fn init_temp(temp: &Path) { |
| let dir = PathBuf::from("metadata").join("current"); |
| DirBuilder::new() |
| .recursive(true) |
| .create(temp.join(dir.clone())) |
| .expect(&format!("couldn't create path {}:", temp.join(dir).to_string_lossy())); |
| |
| let copy_path = vector_path().join("root.json"); |
| fs::copy(copy_path, |
| temp.join("metadata").join("current").join("root.json")) |
| .expect(&format!("copy failed for target")); |
| } |
| |
| #[test] |
| fn run_it() { |
| let temp = TempDir::new("rust-tuf").expect("couldn't make temp dir"); |
| init_temp(temp.path()); |
| let path = vector_path(); |
| println!("Temp path: {:?}", temp.path()); |
| println!("Test path: {:?}", path); |
| |
| let matches = parser() |
| .get_matches_from_safe(vec!["tuf", |
| "--file", |
| &path.to_string_lossy(), |
| "--path", |
| temp.path().to_str().expect("path not utf-8"), |
| "init"]) |
| .expect("parse error"); |
| assert_eq!(run_main(matches), Ok(())); |
| |
| let matches = parser() |
| .get_matches_from_safe(vec!["tuf", |
| "--file", |
| &path.to_string_lossy(), |
| "--path", |
| temp.path().to_str().expect("path not utf-8"), |
| "update"]) |
| .expect("parse error"); |
| assert_eq!(run_main(matches), Ok(())); |
| |
| let matches = parser() |
| .get_matches_from_safe(vec!["tuf", |
| "--file", |
| &path.to_string_lossy(), |
| "--path", |
| temp.path().to_str().expect("path not utf-8"), |
| "verify", |
| "targets/file.txt"]) |
| .expect("parse error"); |
| assert_eq!(run_main(matches), Ok(())); |
| } |
| } |