mod eipc {
pub mod auth;
}
mod pin {
pub mod entry;
}
use std::io::{self, Write, Read};
use std::sync::Arc;
use std::path::Path;
use eipc::auth::EIPC_Auth;
use flypto::hkdf_sha256::HkdfSha256;
use flypto::x25519::X25519;
use flypto::aes::AES;
use flypto::argon2::Argon2Hash;
use pin::entry::PinEntry;
use std::os::unix::net::UnixStream;
use base64::{encode, decode};
use crate::config::Config;
pub struct SPMG {
socket: EIPC_Auth,
config: Arc<Config>,
}
impl SPMG {
pub fn new(config: Config) -> Self {
let cfg_rc = Arc::new(config);
let mut x25519 = X25519::new();
let socket_path = format!("{}/socket", cfg_rc.spmg_path);
let mut socket = EIPC_Auth::new(&socket_path, cfg_rc.clone());
socket.eipc_client.connect();
SPMG {
socket,
config: cfg_rc
}
}
pub fn init(&mut self, mut lock_pwd: Option<String>) {
println!("Creating a new lock...");
let lock_path_str = format!("{}/pwds.lock", self.config.spmg_path);
let lock_path = Path::new(&lock_path_str);
if lock_path.exists() {
println!("Password lock file {}/pwds.lock already exists!", self.config.spmg_path);
return;
}
if lock_pwd.is_none() {
lock_pwd = Some(
PinEntry::prompt_repeat("Please provide a password to lock spmg").unwrap()
);
}
lock_pwd = Some(
urlencoding::decode(&lock_pwd.clone().unwrap()).unwrap().to_string()
);
self.socket.eipc_client.send_event("init", &lock_pwd.unwrap());
println!("Password lock created!");
}
pub fn add(&mut self, pwd_id: String) -> Result<(), io::Error> {
match PinEntry::prompt_repeat("Please enter your new password to store") {
Ok(npwd) => {
match self.socket.auth_command(|key: &str| -> String {
format!(
"get {} {}",
"spmg-lock",
key
)
}) {
Ok(mut lock_token) => {
lock_token = urlencoding::decode(&lock_token).unwrap().to_string();
if lock_token == self.config.lock_token {
self.socket.auth_command(|key: &str| -> String {
format!(
"add {} {} {}",
pwd_id,
npwd,
key
)
});
Ok(())
} else {
return Err(io::Error::new(io::ErrorKind::PermissionDenied, "Unauthorized!"))
}
}
Err(error) => {
return Err(error)
}
}
}
Err(error) => {
return Err(error)
}
}
}
pub fn set(&mut self, pwd_id: String, mut password: String) {
self.socket.auth_command(|key: &str| -> String {
format!(
"add {} {} {}",
pwd_id,
password,
key
)
});
println!("Password added!");
}
pub fn get(&mut self, pwd_id: String, lock_pwd: Option<String>) -> Option<String> {
if lock_pwd.is_none() {
match self.socket.auth_command(|key: &str| -> String {
format!(
"get {} {}",
pwd_id,
key
)
}) {
Ok(mut pwd) => {
pwd = urlencoding::decode(&pwd).unwrap().to_string();
if pwd == "ERR NOTFOUND" {
eprintln!("No entry with identifier '{}'!", pwd_id);
None
} else {
Some(pwd)
}
}
Err(error) => {
eprintln!("{}", error);
return None;
}
}
} else {
let dkey = Argon2Hash::derive_key(&lock_pwd.unwrap(), &self.config.argon_salt);
let enc_key = encode(dkey);
let command = format!(
"get {} {}",
pwd_id,
enc_key
);
let mut pwd = self.socket.eipc_client.send_event(&command, "").unwrap();
pwd = urlencoding::decode(&pwd).unwrap().to_string();
if pwd == "ERR NOTFOUND" {
eprintln!("No entry with identifier '{}'!", pwd_id);
None
} else {
Some(pwd)
}
}
}
pub fn clear_cache(&mut self) -> Option<String> {
match self.socket.eipc_client.send_event("clear-cache", "") {
Ok(mut response) => {
if (response == "OK") {
Some(response)
} else {
return None;
}
}
Err(error) => {
eprintln!("{}", error);
return None;
}
}
}
pub fn cache_expiration(&mut self) -> Option<String> {
let command = "cache-expiration";
let mut cache_expiration = self.socket.eipc_client.send_event(&command, "").unwrap();
if cache_expiration == "NONE" {
None
} else {
Some(cache_expiration)
}
}
pub fn kill(&mut self) {
let cmd_res = self.socket.eipc_client.send_event("kill", "");
println!("cmd_res: {}", cmd_res.unwrap());
}
pub fn disconnect(&mut self) {
self.socket.eipc_client.disconnect();
}
}