pub mod add;
pub mod change_password;
pub mod completions;
pub mod delete;
pub mod edit;
pub mod export;
pub mod generate;
pub mod help;
pub mod import;
pub mod init;
pub mod list;
pub mod lock;
pub mod show;
pub mod unlock;
use crate::keychain;
use crate::store;
use crate::ui::input::{ask_master_password, ask_totp_master_password};
use crate::vault::entities::Error;
use crate::vault::keepass_vault::KeepassVault;
use crate::vault::vault_trait::Vault;
use clap::ArgMatches;
use clipboard::ClipboardContext;
use clipboard::ClipboardProvider;
pub(crate) trait MatchHandlerTemplate
where
Self::ItemType: Clone,
{
type ItemType;
fn pre_handle_matches(&self, matches: &Vec<Self::ItemType>);
fn handle_one_match(&mut self, the_match: Self::ItemType) -> Result<Option<String>, Error>;
fn handle_many_matches(
&mut self,
matches: Vec<Self::ItemType>,
) -> Result<Option<String>, Error>;
}
pub(crate) fn handle_matches<H>(
matches: Vec<H::ItemType>,
handler: &mut Box<H>,
) -> Result<Option<String>, Error>
where
H: MatchHandlerTemplate,
H::ItemType: Clone,
{
if matches.is_empty() {
Ok(Some("No matches found".to_string()))
} else {
handler.pre_handle_matches(&matches.clone());
if matches.len() == 1 {
handler.handle_one_match(matches[0].clone())
} else {
handler.handle_many_matches(matches)
}
}
}
pub trait Action {
fn run(&self) -> Result<String, Error> {
Ok("Success".to_string())
}
}
fn get_vault_properties() -> (String, String, Option<String>) {
let stored_password = keychain::get_master_password();
let master_pwd = stored_password.unwrap_or_else(|_| ask_master_password(None));
let filepath = store::get_vault_path();
let keyfile_path = store::get_keyfile_path();
(master_pwd, filepath, keyfile_path)
}
fn unlock() -> Result<Box<dyn Vault>, Error> {
let (master_pwd, filepath, keyfile_path) = get_vault_properties();
println!("Unlocking vault...");
get_vault(&master_pwd, &filepath, keyfile_path)
}
fn unlock_totp_vault() -> Result<Box<dyn Vault>, Error> {
let stored_password = keychain::get_totp_master_password();
let master_pwd = stored_password.unwrap_or_else(|_| ask_totp_master_password());
let filepath = store::get_totp_vault_path();
let keyfile_path = store::get_totp_keyfile_path();
println!("Unlocking TOTP vault...");
get_vault(&master_pwd, &filepath, keyfile_path)
}
fn get_vault(
password: &str,
filepath: &str,
keyfile_path: Option<String>,
) -> Result<Box<dyn Vault>, Error> {
let vault = KeepassVault::open(password, filepath, keyfile_path)?;
Ok(Box::new(vault))
}
pub fn copy_to_clipboard(value: &str) {
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
ctx.set_contents(String::from(value)).unwrap();
}
pub fn copy_to_clipboard_timed(value: &str, timeout_secs: u64) {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
ctx.set_contents(String::from(value)).unwrap();
let interrupted = Arc::new(AtomicBool::new(false));
let interrupted_clone = interrupted.clone();
let original = String::from(value);
ctrlc::set_handler(move || {
interrupted_clone.store(true, Ordering::SeqCst);
})
.ok();
let total_ms = timeout_secs * 1000;
let mut elapsed = 0u64;
while elapsed < total_ms && !interrupted.load(Ordering::SeqCst) {
std::thread::sleep(std::time::Duration::from_millis(100));
elapsed += 100;
}
let result: Result<(), ()> = (|| {
let mut ctx: ClipboardContext = ClipboardProvider::new().map_err(|_| ())?;
let current = ctx.get_contents().map_err(|_| ())?;
if current == original {
ctx.set_contents(String::new()).map_err(|_| ())?;
}
Ok(())
})();
if result.is_err() {
log::debug!("Failed to clear clipboard after timeout");
}
if interrupted.load(Ordering::SeqCst) {
std::process::exit(0);
}
}
pub trait UnlockingAction {
fn execute(&self) -> Result<Option<String>, Error> {
if self.is_totp_vault() {
self.run_with_vault(&mut unlock_totp_vault()?)
} else {
let mut vault = unlock()?;
crate::completion_cache::ensure_cache_from_vault(&vault);
self.run_with_vault(&mut vault)
}
}
fn is_totp_vault(&self) -> bool {
false
}
fn run_with_vault(&self, _: &mut Box<dyn Vault>) -> Result<Option<String>, Error> {
Ok(Some("Success".to_string()))
}
}
#[derive(Debug, PartialEq)]
pub enum ItemType {
Credential,
Payment,
Note,
Totp,
}
impl ItemType {
pub fn new_from_args(matches: &ArgMatches) -> ItemType {
if matches.get_one::<bool>("payments").map_or(false, |v| *v) {
ItemType::Payment
} else if matches.get_one("notes").map_or(false, |v| *v) {
ItemType::Note
} else if matches.get_one("otp").map_or(false, |v| *v) {
ItemType::Totp
} else {
ItemType::Credential
}
}
}