use rcgen::{CertificateParams, DistinguishedName, DnType, DnValue};
use std::{
fs::{File, Permissions},
io::{stdin, stdout, Write},
os::unix::prelude::PermissionsExt,
path::PathBuf,
};
use crate::err::AppErr;
const OK: &str = "\x1b[0m\x1b[33m[fluffer]\x1b[0m";
const FAIL: &str = "\x1b[31m[fluffer]\x1b[0m";
const PROMPT: &str = "\x1b[33;3m→ ";
pub fn gen_cert(cert: &PathBuf, key: &PathBuf) -> Result<(), AppErr> {
let cert_display = cert.display();
let key_display = key.display();
match (File::open(cert).is_ok(), File::open(key).is_ok()) {
(false, true) => {
eprintln!("{FAIL} Missing certificate: [{cert_display}].");
return Err(AppErr::RcGenStop);
}
(true, false) => {
eprintln!("{FAIL} Missing private key: [{key_display}].");
return Err(AppErr::RcGenStop);
}
(true, true) => return Ok(()),
(false, false) => (),
}
let stdin = stdin();
let mut stdout = stdout();
print!(
"\n{OK} Missing certificate files!
Expected two files: [{cert_display}] and [{key_display}].
\x1b[1mDo you want to generate a keypair interactively?\x1b[0m [y/n]
{PROMPT}"
);
stdout.flush()?;
let mut yorn = String::new();
stdin.read_line(&mut yorn)?;
if yorn != "y\n" {
return Err(AppErr::RcGenStop);
}
print!(
"\n{OK} Enter a comma-separated list of domain name(s) you will be using.
e.g. localhost, *.localhost, sub.example.com
{PROMPT}"
);
stdout.flush()?;
let mut domains = String::new();
stdin.read_line(&mut domains)?;
let domains: Vec<String> = domains.split(',').map(|d| d.trim().to_string()).collect();
if domains.iter().all(|x| x.is_empty()) {
return Err(AppErr::RcGenNoDomains);
}
println!("\x1b[3m{domains:#?}\x1b[0m");
let subject_name = domains.first().ok_or(AppErr::RcGenNoDomains)?.clone();
let subject_alt_names = domains;
let mut params = CertificateParams::new(subject_alt_names);
params.distinguished_name = DistinguishedName::new();
params
.distinguished_name
.push(DnType::CommonName, DnValue::Utf8String(subject_name));
let gen_pair = rcgen::Certificate::from_params(params)?;
let pem = gen_pair.serialize_pem()?;
let mut file = File::create(cert)?;
write!(file, "{pem}")?;
println!("{OK} 📜 Wrote cert.pem");
let mut file = File::create("key.pem")?;
file.set_permissions(Permissions::from_mode(0o600))?;
write!(file, "{}", gen_pair.serialize_private_key_pem())?;
println!("{OK} 🔑 Wrote {key_display}");
Ok(())
}