1ef4a21bf3
Signed-off-by: Ash Svitan <selfsigned-ash@proton.me>
176 lines
5.0 KiB
Rust
176 lines
5.0 KiB
Rust
mod pass;
|
|
mod rbw;
|
|
mod sh;
|
|
|
|
use std::env;
|
|
use std::io;
|
|
use std::io::{Error, ErrorKind, Write};
|
|
use clap::Parser;
|
|
|
|
const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
|
|
|
#[derive(Parser)]
|
|
#[command(version, about, long_about = None)]
|
|
struct Cli {}
|
|
|
|
fn ask_consent(question: String, line1: String, line2: String) -> Result<bool, Error> {
|
|
println!("{}", question);
|
|
println!("\t{}", line1);
|
|
println!("\t{}", line2);
|
|
print!("Proceed? [y/N] ");
|
|
io::stdout().flush()?;
|
|
let mut input = String::new();
|
|
io::stdin().read_line(&mut input)?;
|
|
|
|
return Ok(input.to_lowercase() == "y\n");
|
|
}
|
|
|
|
fn sync() -> Result<(), Error> {
|
|
pass::check_pass()?;
|
|
rbw::check_rbw()?;
|
|
|
|
let vaults = pass::get_vaults()?.vaults;
|
|
if vaults.len() <= 0 {
|
|
return Err(Error::new(ErrorKind::Other, "No vaults found"));
|
|
}
|
|
let vault_names = vaults
|
|
.iter()
|
|
.map(|vault| vault.name.clone())
|
|
.collect::<Vec<String>>();
|
|
|
|
let vault;
|
|
match env::var(ENV_VAR_DEFAULT_VAULT) {
|
|
Ok(default_vault) => {
|
|
if vault_names.contains(&default_vault) {
|
|
vault = default_vault;
|
|
} else {
|
|
return Err(Error::new(
|
|
ErrorKind::Other,
|
|
format!("Vault {} does not exist", default_vault),
|
|
));
|
|
}
|
|
}
|
|
Err(_) => {
|
|
vault = vault_names.get(0).unwrap().clone();
|
|
println!(
|
|
"No default vault found (you can set it with {}), using {}...",
|
|
ENV_VAR_DEFAULT_VAULT, vault
|
|
);
|
|
}
|
|
};
|
|
|
|
let pass_items = pass::get_items(&vault)?;
|
|
let mut pass_logins = pass::get_logins(pass_items);
|
|
|
|
let rbw_items = rbw::get_items()?;
|
|
let mut rbw_logins = rbw::get_logins(rbw_items)?;
|
|
|
|
println!();
|
|
println!(
|
|
"Got {} rbw logins and {} pass logins...",
|
|
rbw_logins.len(),
|
|
pass_logins.len()
|
|
);
|
|
|
|
for rbw_login in rbw_logins.clone().iter() {
|
|
let mut pass_login: Option<&pass::LoginItem> = None;
|
|
|
|
'inner: for pass_login_iter in pass_logins.iter() {
|
|
if rbw_login.name == pass_login_iter.title {
|
|
pass_login = Some(pass_login_iter);
|
|
break 'inner;
|
|
}
|
|
}
|
|
|
|
if let None = pass_login {
|
|
continue;
|
|
}
|
|
let pass_login = pass_login.unwrap();
|
|
|
|
let rbw_password = rbw_login.password.clone();
|
|
let pass_password = pass_login.password.clone();
|
|
|
|
let rbw_user = rbw_login.user.clone();
|
|
let pass_user = match pass_login.username.is_empty() {
|
|
true => pass_login.email.clone(),
|
|
false => pass_login.username.clone(),
|
|
};
|
|
let pass_user_is_actually_email = pass_user == pass_login.email;
|
|
|
|
if rbw_user != pass_user || rbw_password != pass_password {
|
|
let consent = ask_consent(
|
|
format!("Attempting to update {}:", rbw_login.name),
|
|
format!("{} -> {}", pass_user, rbw_user.clone()),
|
|
format!("{} -> {}", pass_password, rbw_password.clone()),
|
|
)?;
|
|
if consent {
|
|
let updated_pass_login = pass::LoginItem {
|
|
id: pass_login.id.clone(),
|
|
title: pass_login.title.clone(),
|
|
username: rbw_user.clone(),
|
|
email: rbw_user.clone(),
|
|
password: rbw_password,
|
|
};
|
|
pass::update(&vault, updated_pass_login, pass_user_is_actually_email);
|
|
}
|
|
}
|
|
|
|
let rbw_index = rbw_logins
|
|
.iter()
|
|
.position(|x| x.id == rbw_login.id)
|
|
.unwrap();
|
|
rbw_logins.remove(rbw_index);
|
|
|
|
let pass_index = pass_logins
|
|
.iter()
|
|
.position(|x| x.id == pass_login.id)
|
|
.unwrap();
|
|
pass_logins.remove(pass_index);
|
|
}
|
|
|
|
// TODO: need to delete these
|
|
println!();
|
|
println!(
|
|
"Remaining {} rbw logins and {} pass logins...",
|
|
rbw_logins.len(),
|
|
pass_logins.len()
|
|
);
|
|
|
|
for rbw_login in rbw_logins {
|
|
let consent = ask_consent(
|
|
format!("Attempting to create {}:", rbw_login.name),
|
|
format!("{}", rbw_login.user),
|
|
format!("{}", rbw_login.password),
|
|
)?;
|
|
if consent {
|
|
pass::create(&vault, rbw_login.name, rbw_login.user, rbw_login.password);
|
|
}
|
|
}
|
|
|
|
for pass_login in pass_logins {
|
|
let consent = ask_consent(
|
|
format!("Attempting to delete {}:", pass_login.title),
|
|
format!(
|
|
"{}",
|
|
if pass_login.username.is_empty() {
|
|
pass_login.email
|
|
} else {
|
|
pass_login.username
|
|
}
|
|
),
|
|
format!("{}", pass_login.password),
|
|
)?;
|
|
if consent {
|
|
pass::trash(&vault, pass_login.id);
|
|
}
|
|
}
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
fn main() -> Result<(), Error> {
|
|
Cli::parse();
|
|
sync()?;
|
|
return Ok(());
|
|
}
|