use crate::ffi;
use crate::io::{CliInputOutput, OutputType};
use crate::password::v2::{Password, PasswordStore};
use serde::{Deserialize, Serialize};
use serde_json;
use std::fs::File;
#[derive(Serialize, Deserialize)]
pub struct JsonExport {
passwords: Vec<Password>,
}
pub fn callback_exec(
matches: &clap::ArgMatches,
store: &mut PasswordStore,
io: &mut impl CliInputOutput,
) -> Result<(), i32> {
let subcommand_name = matches.subcommand_name().unwrap();
let subcommand_matches = matches.subcommand_matches(subcommand_name).unwrap();
let (valid, invalid) = if subcommand_name == "json" {
create_imported_passwords_from_json(subcommand_matches, io)
} else if subcommand_name == "csv" {
create_imported_passwords_from_csv(subcommand_matches, io)
} else if subcommand_name == "1password" {
create_imported_passwords_from_1password(subcommand_matches, io)
} else {
unimplemented!("Invalid import source")
}?;
import_passwords(valid, invalid, store, io)
}
fn import_passwords(
valid: Vec<Password>,
invalid: Vec<Password>,
store: &mut PasswordStore,
io: &mut impl CliInputOutput,
) -> Result<(), i32> {
let mut errors = 0;
let mut warnings = 0;
let mut successes = 0;
for password in invalid {
io.error(
format!("{}, invalid format, skipping", password.name),
OutputType::Error,
);
errors += 1;
}
for password in valid {
if store.get_password(&password.name).is_some() {
io.warning(
format!("{}, already in password store, skipping", password.name),
OutputType::Error,
);
warnings += 1;
continue;
}
if let Err(err) = store.add_password(password.clone()) {
io.error(
format!("{}, error ({:?})", password.name, err),
OutputType::Error,
);
errors += 1;
continue;
}
successes += 1;
}
io.success(format!("Imported: {}", successes), OutputType::Standard);
io.warning(format!("Warnings: {}", warnings), OutputType::Error);
io.error(format!("Errors: {}", errors), OutputType::Error);
Ok(())
}
fn create_imported_passwords_from_csv(
matches: &clap::ArgMatches,
io: &mut impl CliInputOutput,
) -> Result<(Vec<Password>, Vec<Password>), i32> {
let path_str = matches.get_one::<String>("path").unwrap();
let mut reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_path(path_str)
.map_err(|err| {
io.error(
format!("Uh oh, could not open or read the file (reason: {})", err),
OutputType::Error,
);
1
})?;
let mut valid = vec![];
for record_result in reader.records() {
if let Ok(record) = record_result {
valid.push(Password {
name: record[0].into(),
username: record[1].into(),
password: record[2].into(),
created_at: ffi::time(),
updated_at: ffi::time(),
});
} else {
return Err(1);
}
}
Ok((valid, vec![]))
}
fn create_imported_passwords_from_1password(
matches: &clap::ArgMatches,
io: &mut impl CliInputOutput,
) -> Result<(Vec<Password>, Vec<Password>), i32> {
let path_str = matches.get_one::<String>("path").unwrap();
let mut reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_path(path_str)
.map_err(|err| {
io.error(
format!("Uh oh, could not open or read the file (reason: {})", err),
OutputType::Error,
);
1
})?;
let mut valid = vec![];
let mut invalid = vec![];
for record_result in reader.records() {
if let Ok(record) = record_result {
if &record[3] != "Login" {
invalid.push(Password {
name: record[2].into(),
username: record[5].into(),
password: record[1].into(),
created_at: ffi::time(),
updated_at: ffi::time(),
});
continue;
}
valid.push(Password {
name: record[2].into(),
username: record[5].into(),
password: record[1].into(),
created_at: ffi::time(),
updated_at: ffi::time(),
});
} else {
return Err(1);
}
}
Ok((valid, invalid))
}
fn create_imported_passwords_from_json(
matches: &clap::ArgMatches,
io: &mut impl CliInputOutput,
) -> Result<(Vec<Password>, Vec<Password>), i32> {
let path_str = matches.get_one::<String>("path").unwrap();
let dump_file = File::open(path_str).map_err(|err| {
io.error(
format!("Uh oh, could not open the file (reason: {})", err),
OutputType::Error,
);
1
})?;
let export: JsonExport = serde_json::from_reader(dump_file).map_err(|json_err| {
io.error(
format!(
"Woops, I could not import the passwords from JSON (reason: {}).",
json_err
),
OutputType::Error,
);
1
})?;
Ok((export.passwords, vec![]))
}