use std::fs::{File,read_to_string};
use std::io::prelude::*;
use serde_json;
use crate::utils;
use utils::{get_db_path,check_elements};
use crate::cryptograpy;
use crate::otp::otp_element::OTPElement;
pub fn read_from_file() -> Result<Vec<OTPElement>,String>{
let encrypted_contents = read_to_string(&get_db_path()).unwrap();
let contents = cryptograpy::decrypt_string(&encrypted_contents, &cryptograpy::prompt_for_passwords("Password: ",8));
match contents {
Ok(contents) => {
let vector: Vec<OTPElement> = serde_json::from_str(&contents).unwrap();
return Ok(vector);
},
Err(e) => {
return Err(e);
}
}
}
pub fn check_secret(secret: &str) -> bool{
if secret.is_empty(){
return false;
}
let upper_secret = secret.to_uppercase().replace("=", "");
return upper_secret.chars().all(char::is_alphanumeric);
}
pub fn add_element(secret: &String,issuer: &String,label: &String,algorithm: &str,digits: u64) -> Result<(),String>{
if !check_secret(&secret){
return Err(String::from("Bad secret"))
}
let otp_element = OTPElement::new(secret.to_string(), issuer.to_string(), label.to_string(),digits, String::from("TOTP"), String::from(algorithm).to_uppercase(),String::from("Default"),0,0,30,vec![]);
let mut elements;
match read_from_file(){
Ok(result) => elements = result,
Err(e) => return Err(e)
}
elements.push(otp_element);
match overwrite_database(elements){
Ok(()) => Ok(()),
Err(e) => Err(format!("{}",e))
}
}
pub fn remove_element_from_db(mut id: usize) -> Result<(),String>{
if id == 0{
return Err(String::from("0 is a bad index"));
}
id -= 1;
let mut elements: Vec<OTPElement>;
match read_from_file(){
Ok(result) => elements = result,
Err(e) => {
return Err(e);
}
}
match check_elements(id, &elements){
Ok(()) => {
for i in 0..elements.len(){
if i == id {
elements.remove(i);
break;
}
}
match overwrite_database(elements){
Ok(()) => Ok(()),
Err(e) => Err(format!("{}",e)),
}
},
Err(e) => Err(e)
}
}
pub fn edit_element(mut id: usize, secret: &str,issuer: &str,label: &str,algorithm: &str,digits: u64) -> Result<(), String> {
if id == 0{
return Err(String::from("Invalid element"));
}
id -= 1;
let mut elements: Vec<OTPElement>;
match read_from_file(){
Ok(result) => elements = result,
Err(_e) => return Err(String::from("Cannot decrypt existing database"))
}
match check_elements(id,&elements){
Ok(()) => {
for i in 0..elements.len() {
if i == id{
if secret != ""{
elements[i].set_secret(secret.to_string());
}
if issuer != "."{
elements[i].set_issuer(issuer.to_string());
}
if label != "."{
elements[i].set_label(label.to_string());
}
if algorithm != "."{
elements[i].set_algorithm(algorithm.to_string().to_uppercase());
}
if digits != 0{
elements[i].set_digits(digits);
}
break;
}
}
match overwrite_database(elements){
Ok(()) => Ok(()),
Err(e) => Err(format!("{}",e)),
}
},
Err(e) => Err(e)
}
}
pub fn export_database() -> Result<String, String> {
let mut exported_path = utils::get_home_folder().to_str().unwrap().to_string();
exported_path.push_str("/exported.cotp");
let mut file = File::create(&exported_path).expect("Cannot create file");
let encrypted_contents = read_to_string(&get_db_path()).unwrap();
let contents = cryptograpy::decrypt_string(&encrypted_contents, &cryptograpy::prompt_for_passwords("Password: ",8));
match contents {
Ok(contents) => {
if contents == "[]"{
return Err(String::from("there are no elements in your database, type \"cotp -h\" to get help"));
}
file.write_all(contents.as_bytes()).expect("Failed to write contents");
return Ok(exported_path);
},
Err(e) => {
return Err(format!("{}",e));
}
}
}
pub fn overwrite_database(elements: Vec<OTPElement>) -> Result<(),std::io::Error>{
let json_string: &str = &serde_json::to_string(&elements)?;
overwrite_database_json(json_string)
}
pub fn overwrite_database_json(json: &str) -> Result<(),std::io::Error>{
let encrypted = cryptograpy::encrypt_string(json.to_string(), &cryptograpy::prompt_for_passwords("Insert password for database encryption: ",8));
let mut file = File::create(utils::get_db_path())?;
utils::write_to_file(&encrypted, &mut file)
}