use super::BasicOptions;
use clap::{AppSettings, Arg, ArgMatches, SubCommand};
use commands::StaticSubcommand;
use error::Error;
use meta;
use meta::{load_signing_key, Meta};
use std::io::{stderr, Write};
use std::process::exit;
pub fn invocation() -> StaticSubcommand {
return SubCommand::with_name("key")
.about("Manage signing and SSH keys")
.subcommand(
SubCommand::with_name("upload")
.about("Upload keys to a remote server")
.arg(Arg::with_name("port")
.long("port")
.short("p")
.help("Port of the SSH server.")
.takes_value(true)
.required(false))
.arg(Arg::with_name("local")
.long("local")
.help("Save keys for the local repository only")
.takes_value(false)
.required(false))
.arg(Arg::with_name("address")
.help("Address to use, for instance pijul_org@nest.pijul.com.")
.takes_value(true)
.required(true))
)
.subcommand(
SubCommand::with_name("gen")
.about("Generate keys. This command generates an SSH key if --signing-id is not given")
.arg(Arg::with_name("signing-id")
.long("signing-id")
.help("Generate a signing key for this user id (user ids are email addresses)")
.takes_value(true))
.arg(Arg::with_name("repository")
.long("for-repository")
.help("Save keys for the given repository only")
.takes_value(true)
.required(false))
)
.subcommand(
SubCommand::with_name("register")
.setting(AppSettings::Hidden)
.about("Register a signature key given in binary on the standard input")
);
}
pub enum Params<'a> {
Upload { address: &'a str, port: Option<u16> },
Gen { signing: Option<&'a str> },
Register,
None,
}
pub fn parse_args<'a>(args: &'a ArgMatches) -> Result<Params<'a>, Error> {
match args.subcommand() {
("upload", Some(args)) => Ok(Params::Upload {
address: args.value_of("address").unwrap(),
port: args.value_of("port").and_then(|x| x.parse().ok()),
}),
("gen", Some(args)) => Ok(Params::Gen {
signing: args.value_of("signing-id"),
}),
("register", _) => Ok(Params::Register),
_ => Ok(Params::None),
}
}
pub fn run(arg_matches: &ArgMatches) -> Result<(), Error> {
let mut global = meta::Global::load().unwrap_or_else(|_| meta::Global::new());
match parse_args(arg_matches)? {
Params::Upload { address, port } => {
let local_key = BasicOptions::from_args(arg_matches).ok().and_then(|opts| {
Meta::load(&opts.repo_root)
.ok()
.and_then(|meta| meta.signing_key)
});
let key = local_key
.or(global.signing_key)
.map(|s| load_signing_key(s));
match key {
Some(Ok(keys)) => {
if keys.keys.is_empty() {
return Ok(());
}
if let Some(remote) = super::remote::parse_ssh_remote_nopath(address, port) {
debug!("sending key");
remote.session()?.send_key(keys)?
}
}
Some(Err(e)) => return Err(e),
None => return Ok(()),
}
}
Params::Gen { signing } => {
if let Some(identity) = signing {
global.generate_global_signing_key(identity, None)?
} else {
meta::generate_global_ssh_key()?
}
}
Params::Register => unimplemented!(),
Params::None => {}
}
Ok(())
}
pub fn explain(r: Result<(), Error>) {
if let Err(e) = r {
if let Error::InARepository { path } = e {
writeln!(stderr(), "Repository {:?} already exists", path).unwrap();
} else {
writeln!(stderr(), "error: {}", e).unwrap();
}
exit(1)
}
}