use toml;
use libpijul::fs_representation::meta_file;
use libpijul::KeyPair;
use error::{ErrorKind, Result};
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::collections::BTreeMap;
use commands::remote::{Remote, parse_remote};
use std::path::{Path, PathBuf};
use std;
pub const DEFAULT_REMOTE: &'static str = "remote";
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Repository {
pub address: String,
pub port: Option<u16>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Meta {
pub authors: Vec<String>,
pub signing_key: Option<String>,
pub pull: Option<String>,
pub push: Option<String>,
pub remote: BTreeMap<String, Repository>
}
impl Meta {
pub fn load(r: &Path) -> Result<Meta> {
let mut str = String::new();
{
let mut f = try!(File::open(meta_file(r)));
try!(f.read_to_string(&mut str));
}
Ok(toml::from_str(&str)?)
}
pub fn new() -> Meta {
Meta {
authors: Vec::new(),
signing_key: None,
pull: None,
push: None,
remote: BTreeMap::new(),
}
}
pub fn save(&self, r: &Path) -> Result<()> {
let mut f = try!(File::create(meta_file(r)));
let s: String = toml::to_string(&self)?;
try!(f.write_all(s.as_bytes()));
Ok(())
}
fn parse_remote<'a>(&'a self, remote: &'a str, port: Option<u16>, base_path: Option<&'a Path>) -> Remote<'a> {
if let Some(repo) = self.remote.get(remote) {
parse_remote(&repo.address, port.or(repo.port), None, base_path)
} else {
parse_remote(remote, port, None, base_path)
}
}
fn get_remote<'a>(&'a self, remote: Option<&'a str>, default_remote: Option<&'a String>,
port: Option<u16>,
base_path: Option<&'a Path>) -> Result<Remote<'a>> {
if let Some(remote) = remote {
Ok(self.parse_remote(remote, port, base_path))
} else if let Some(ref remote) = default_remote {
Ok(self.parse_remote(remote, port, base_path))
} else if self.remote.len() == 1 {
let remote = self.remote.keys().next().unwrap();
Ok(self.parse_remote(remote, port, base_path))
} else {
Err(ErrorKind::MissingRemoteRepository.into())
}
}
pub fn pull<'a>(&'a self, remote: Option<&'a str>, port: Option<u16>,
base_path: Option<&'a Path>) -> Result<Remote<'a>> {
self.get_remote(remote, self.pull.as_ref(), port, base_path)
}
pub fn push<'a>(&'a self, remote: Option<&'a str>, port: Option<u16>, base_path: Option<&'a Path>) -> Result<Remote<'a>> {
self.get_remote(remote, self.push.as_ref(), port, base_path)
}
pub fn signing_key(&self) -> Result<Option<KeyPair>> {
if let Some(ref path) = self.signing_key {
let mut f = File::open(path)?;
let mut key = [0; 85];
f.read_exact(&mut key)?;
Ok(Some(KeyPair::from_pkcs8(&key)?))
} else {
Ok(None)
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Global {
pub author: String,
pub signing_key: Option<String>,
}
fn global_path() -> PathBuf {
let mut path: PathBuf = std::env::home_dir().unwrap();
path.push(".pijulconfig");
path
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyType {
SSH,
Signing
}
pub fn generate_key<P:AsRef<Path>>(dot_pijul: P, keytype: KeyType) -> Result<()> {
use ring::signature::Ed25519KeyPair;
use ring::rand::SystemRandom;
let key = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new())?;
create_dir_all(dot_pijul.as_ref())?;
let f = dot_pijul.as_ref().join(match keytype {
KeyType::SSH => "id_ed25519.pkcs8",
KeyType::Signing => "sig_ed25519.pkcs8",
});
debug!("generate_key: {:?}", f);
if std::fs::metadata(&f).is_err() {
let mut f = File::create(&f)?;
f.write_all(&key)?;
Ok(())
} else {
Err(ErrorKind::WillNotOverwriteKeyFile(f).into())
}
}
pub fn load_key<P:AsRef<Path>>(dot_pijul: P, keytype: KeyType) -> Result<KeyPair> {
let f = dot_pijul.as_ref().join(match keytype {
KeyType::SSH => "id_ed25519.pkcs8",
KeyType::Signing => "sig_ed25519.pkcs8",
});
debug!("load_key: {:?}", f);
let mut f = File::open(&f)?;
let mut key = Vec::new();
f.read_to_end(&mut key)?;
Ok(KeyPair::from_pkcs8(&key)?)
}
pub fn generate_global_key(keytype: KeyType) -> Result<()> {
generate_key(&global_path(), keytype)
}
pub fn load_global_key(keytype: KeyType) -> Result<KeyPair> {
load_key(&global_path(), keytype)
}
pub fn load_global_or_local_key<P:AsRef<Path>>(dot_pijul: Option<P>, keytype: KeyType) -> Result<KeyPair> {
if let Some(dot_pijul) = dot_pijul {
if let Ok(key) = load_key(dot_pijul.as_ref(), keytype) {
return Ok(key)
}
}
load_global_key(keytype)
}
impl Global {
pub fn new() -> Self {
Global {
author: String::new(),
signing_key: None
}
}
pub fn load() -> Result<Self> {
let mut path = global_path();
path.push("config");
let mut str = String::new();
{
let mut f = try!(File::open(&path));
try!(f.read_to_string(&mut str));
}
Ok(toml::from_str(&str)?)
}
pub fn save(&self) -> Result<()> {
let mut path = global_path();
create_dir_all(&path)?;
path.push("config");
let mut f = try!(File::create(&path));
let s: String = toml::to_string(&self)?;
try!(f.write_all(s.as_bytes()));
Ok(())
}
}