add sync feature

Co-authored-by: Alpisc <Alpisc@users.noreply.github.com>
This commit is contained in:
Mystikfluu 2022-12-30 22:12:37 +01:00
parent 7fcbd71e99
commit 0c9f796658
6 changed files with 214 additions and 80 deletions

4
.gitignore vendored
View File

@ -1,4 +1,6 @@
/target /target
comp.bat comp.bat
comp.sh comp.sh
report.json report.json
*.ipass
*.ipassx

2
Cargo.lock generated
View File

@ -193,7 +193,7 @@ dependencies = [
[[package]] [[package]]
name = "ipass" name = "ipass"
version = "0.3.1" version = "0.4.0"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"brotli", "brotli",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ipass" name = "ipass"
version = "0.3.1" version = "0.4.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -19,4 +19,4 @@ opt-level = 'z' # Optimize for size
lto = true # Enable link-time optimization lto = true # Enable link-time optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations codegen-units = 1 # Reduce number of codegen units to increase optimizations
panic = 'abort' # Abort on panic panic = 'abort' # Abort on panic
strip = true # Strip symbols from binary; remove pdb # strip = true # Strip symbols from binary; remove pdb

View File

@ -1 +1,3 @@
cargo build --release @echo off
cargo build -r -j -10
@echo on

View File

@ -1,5 +1,10 @@
#![warn(missing_docs, single_use_lifetimes, unused_lifetimes, trivial_casts, trivial_numeric_casts)]
#![forbid(unsafe_code)]
//! IPass Password Manager
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{Read, Write}; use std::io::Write;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore; use rand::RngCore;
mod utils; mod utils;
@ -16,6 +21,19 @@ fn main() {
} }
let mode: &str = &args[1].trim().to_lowercase().to_owned(); let mode: &str = &args[1].trim().to_lowercase().to_owned();
let sync_file_loc = utils::get_home_folder_str()+"/.sync.ipass";
let sync_file_path = std::path::Path::new(&sync_file_loc);
let sync_enabled = sync_file_path.clone().exists();
let sync_loc: String;
if sync_enabled {
sync_loc = std::fs::read_to_string(sync_file_path).expect("Should have been able to read the sync file");
utils::import_file(&sync_loc);
} else {
sync_loc = "".to_string(); //sync is disabled, no location needed
}
match mode { match mode {
"list" => list(), "list" => list(),
@ -28,9 +46,14 @@ fn main() {
"export" => export(&args), "export" => export(&args),
"rename" => rename(&args), "rename" => rename(&args),
"version" => version_help(version), "version" => version_help(version),
"sync" => sync(&args),
"isync" => isync(&args),
"clear" => clear(), "clear" => clear(),
_ => help_message(&args), _ => help_message(&args),
} }
if sync_enabled {
utils::export_file(&sync_loc);
}
} }
fn version_help(version: &str) { fn version_help(version: &str) {
@ -90,6 +113,14 @@ fn help_message(args: &Vec<String>) {
"clear".to_string(), "clear".to_string(),
"clears all entries".to_string(), "clears all entries".to_string(),
); );
help_messages.insert(
"sync".to_string(),
"automatically sync your data with a specified file".to_string(),
);
help_messages.insert(
"isync".to_string(),
"not fully implemented yet; ignore this for now | Syncs the database to IPass servers".to_string(),
);
if args.len() < 3 { if args.len() < 3 {
println!("You can use the following commands:"); println!("You can use the following commands:");
@ -103,7 +134,7 @@ fn help_message(args: &Vec<String>) {
} }
fn list() { fn list() {
let mut paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap(); let mut paths: std::fs::ReadDir = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
let mut has_entry:bool = false; let mut has_entry:bool = false;
@ -123,6 +154,7 @@ fn list() {
} }
fn add(args: &Vec<String>) { fn add(args: &Vec<String>) {
//! arguments: program add [name] {password}
if args.len() < 4 || args.len() > 5 { if args.len() < 4 || args.len() > 5 {
println!("Incorrect usage of \"add\""); println!("Incorrect usage of \"add\"");
@ -135,12 +167,12 @@ fn add(args: &Vec<String>) {
if args.len() > 4 { if args.len() > 4 {
pw = username+";"+args[4].trim(); pw = username+";"+args[4].trim();
} else { } else {
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'"; let alphabet: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'";
let alph_len = alphabet.chars().count(); let alph_len: usize = alphabet.chars().count();
let char_set:Vec<char> = alphabet.chars().collect(); let char_set:Vec<char> = alphabet.chars().collect();
let mut chars_index: Vec<u8> = vec![0;20]; let mut chars_index: Vec<u8> = vec![0;20];
OsRng.fill_bytes(&mut chars_index); OsRng.fill_bytes(&mut chars_index);
let mut chars = String::new(); let mut chars: String = String::new();
for index in chars_index { for index in chars_index {
// println!("{} - {} - {}",index,(index as usize)%(alph_len-1),alph_len); // println!("{} - {} - {}",index,(index as usize)%(alph_len-1),alph_len);
@ -154,7 +186,7 @@ fn add(args: &Vec<String>) {
} }
println!("Adding password for {}",args[2]); println!("Adding password for {}",args[2]);
let succeeded = utils::create_entry(&args[2], pw); let succeeded: bool = utils::create_entry(&args[2], pw);
if !succeeded { if !succeeded {
println!("You already have an entry with that name! Did you mean to use \"edit\"?"); println!("You already have an entry with that name! Did you mean to use \"edit\"?");
return; return;
@ -163,15 +195,16 @@ fn add(args: &Vec<String>) {
} }
fn get(args: &Vec<String>) { fn get(args: &Vec<String>) {
//! arguments: program get [name]
if args.len() < 3 { if args.len() < 3 {
println!("Invalid usage of \"get\""); println!("Invalid usage of \"get\"");
return; return;
} }
let name = &args[2]; let name: &String = &args[2];
let filepath = &(utils::get_ipass_folder()+name+".ipass"); let filepath = &(utils::get_ipass_folder()+name+".ipass");
if std::path::Path::new(filepath).exists() { if std::path::Path::new(filepath).exists() {
println!("Getting entry"); println!("Getting entry");
let entry = utils::get_entry(name); let entry: String = utils::get_entry(name);
let mut data = entry.split(";"); let mut data = entry.split(";");
println!("Username: '{}' Password: '{}'",data.next().unwrap(),data.next().unwrap()); println!("Username: '{}' Password: '{}'",data.next().unwrap(),data.next().unwrap());
} else { } else {
@ -180,7 +213,8 @@ fn get(args: &Vec<String>) {
} }
} }
fn changepw(args: &Vec<String>) { //rename func to changepw fn changepw(args: &Vec<String>) {
//! arguments: program changepw [name] {new_password}
if args.len() < 3 { if args.len() < 3 {
println!("Invalid usage of \"changepw\""); println!("Invalid usage of \"changepw\"");
return; return;
@ -203,6 +237,7 @@ fn changepw(args: &Vec<String>) { //rename func to changepw
} }
fn changeuser(args: &Vec<String>) { fn changeuser(args: &Vec<String>) {
//! arguments: program changeuser [name] {new_username}
if args.len() < 3 { if args.len() < 3 {
println!("Invalid usage of \"changeuser\""); println!("Invalid usage of \"changeuser\"");
return; return;
@ -224,7 +259,8 @@ fn changeuser(args: &Vec<String>) {
} }
} }
fn rename(args: &Vec<String>) { // prog ren old new fn rename(args: &Vec<String>) {
//! arguments: program rename [name] [new_name]
if args.len() < 4 { if args.len() < 4 {
println!("Invalid usage of \"rename\""); println!("Invalid usage of \"rename\"");
return; return;
@ -239,6 +275,7 @@ fn rename(args: &Vec<String>) { // prog ren old new
} }
fn remove(args: &Vec<String>) { fn remove(args: &Vec<String>) {
//! arguments: program remove [name]
if args.len() < 3 { if args.len() < 3 {
println!("Invalid usage of \"remove\""); println!("Invalid usage of \"remove\"");
return; return;
@ -259,6 +296,7 @@ fn remove(args: &Vec<String>) {
} }
fn import(args: &Vec<String>) { fn import(args: &Vec<String>) {
//! arguments: program import {location}
let mut location = utils::get_home_folder_str(); let mut location = utils::get_home_folder_str();
if args.len() == 3 { if args.len() == 3 {
location = args[2].clone(); location = args[2].clone();
@ -268,42 +306,7 @@ fn import(args: &Vec<String>) {
return; return;
} }
} }
if std::path::Path::new(&(location.clone()+"/export.ipassx")).exists() { if utils::import_file(&location) {
let mut reader = brotli::Decompressor::new(
std::fs::File::open(location.clone()+"/export.ipassx").unwrap(),
4096, // buffer size
);
let mut content: String = String::new();
let mut buf = [0u8; 4096];
loop {
match reader.read(&mut buf[..]) {
Err(e) => {
if let std::io::ErrorKind::Interrupted = e.kind() {
continue;
}
panic!("{}", e);
}
Ok(size) => {
if size == 0 {
break;
}
content += &std::str::from_utf8(&buf[..size]).unwrap();
}
}
}
let lines = content.lines();
let mut name = "";
for i in lines {
if name == "" {
name = i;
continue;
}
let mut file = std::fs::File::create(format!("{}/{}.ipass",utils::get_ipass_folder(), name)).unwrap();
file.write_all(i.as_bytes()).unwrap();
name = "";
}
println!("Imported all entries!"); println!("Imported all entries!");
} else { } else {
println!("No such file found!"); println!("No such file found!");
@ -312,6 +315,7 @@ fn import(args: &Vec<String>) {
} }
fn export(args: &Vec<String>) { fn export(args: &Vec<String>) {
//! arguments: program export {location}
let mut location = utils::get_home_folder_str(); let mut location = utils::get_home_folder_str();
if args.len() == 3 { if args.len() == 3 {
location = args[2].clone(); location = args[2].clone();
@ -321,37 +325,11 @@ fn export(args: &Vec<String>) {
return; return;
} }
} }
if utils::export_file(&location) {
let mut collected_data: String = String::new();
let paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
for p in paths {
if let Ok(path) = p {
let content = &mut std::fs::read_to_string(utils::get_ipass_folder()+&path.file_name().into_string().unwrap()).expect("Should have been able to read the file");
collected_data += format!("{}\n{}\n", path.file_name().into_string().unwrap().replace(".ipass", ""),content).as_str();
}
}
if let Ok(file) = std::fs::File::create(location.clone()+"/export.ipassx") {
let mut writer = brotli::CompressorWriter::new(
file,
4096,
11,
22);
match writer.write_all(collected_data.as_bytes()) {
Err(e) => panic!("{}", e),
Ok(_) => {},
}
println!("Saved at: '{}/export.ipassx'", location); println!("Saved at: '{}/export.ipassx'", location);
} else { } else {
println!("Failed saving at '{}/export.ipassx' does it exist?",location) println!("Failed saving at '{}/export.ipassx' does it exist?",location);
} }
} }
fn clear() { fn clear() {
@ -368,4 +346,83 @@ fn clear() {
} }
} }
println!("Cleared all entries!"); println!("Cleared all entries!");
}
fn sync(args: &Vec<String>) {
//! arguments: program sync [on/off] {location if on}
let arg: String;
if args.len() < 3 {
println!("Invalid usage of \"sync\"");
return;
} else {
arg = args[2].to_lowercase();
}
match arg.as_str() {
"on" => {
let location: String;
if args.len() < 4 {
location = utils::prompt_answer("No location specified, please provide the location of the file you want to sync: ".to_string());
} else {
location = args[3].clone();
}
let mut sync_file = std::fs::File::create(utils::get_home_folder_str()+"/.sync.ipass").expect("could not open sync file");
sync_file.write(location.as_bytes()).expect("could not write to sync file");
if !utils::export_file(&location) {
eprintln!("Test sync error, make sure you specified a valid folder!");
} else {
println!("Sync is now Enabled!");
println!("Sync file: {}",location);
}
},
"off" => {
let sync_file_loc = utils::get_home_folder_str()+"/.sync.ipass";
let sync_file_path = std::path::Path::new(&sync_file_loc);
let sync_enabled = sync_file_path.clone().exists();
if sync_enabled {
std::fs::remove_file(utils::get_home_folder_str()+"/.sync.ipass").expect("could not disable sync, is it already disabled?");
println!("Sync is now Disabled!");
} else {
println!("Sync is already disabled!");
}
},
_ => {
println!("Invalid argument, check \"help\" for help");
}
}
}
fn isync(args: &Vec<String>) {
//! arguments: program isync [on/off]
if args.len() > 2 {
println!("Invalid usage of \"isync\"");
return;
}
let arg: String = args[2].clone().to_lowercase();
match arg.as_str() {
"on" => {
todo!("ISync");
// println!("ISync is now Enabled!");
},
"off" => {
println!("ISync is now Disabled!");
},
_ => {
println!("Invalid argument, check \"help\" for help");
}
}
} }

View File

@ -1,4 +1,4 @@
use std::io::Write; use std::io::{Read, Write};
use aes_gcm::{ use aes_gcm::{
aead::{Aead, KeyInit}, aead::{Aead, KeyInit},
Aes256Gcm, Nonce Aes256Gcm, Nonce
@ -9,6 +9,79 @@ pub fn get_args() -> Vec<String> {
std::env::args().collect() // [0] = file path; [n>0] = argument std::env::args().collect() // [0] = file path; [n>0] = argument
} }
pub fn import_file(location:&String) -> bool {
if std::path::Path::new(&(location.clone()+"/export.ipassx")).exists() {
let mut reader = brotli::Decompressor::new(
std::fs::File::open(location.clone()+"/export.ipassx").unwrap(),
4096, // buffer size
);
let mut content: String = String::new();
let mut buf = [0u8; 4096];
loop {
match reader.read(&mut buf[..]) {
Err(e) => {
if let std::io::ErrorKind::Interrupted = e.kind() {
continue;
}
panic!("{}", e);
}
Ok(size) => {
if size == 0 {
break;
}
content += &std::str::from_utf8(&buf[..size]).unwrap();
}
}
}
let lines = content.lines();
let mut name = "";
for i in lines {
if name == "" {
name = i;
continue;
}
let mut file = std::fs::File::create(format!("{}/{}.ipass",get_ipass_folder(), name)).unwrap();
file.write_all(i.as_bytes()).unwrap();
name = "";
}
return true;
} else {
return false;
}
}
pub fn export_file(location:&String) -> bool {
let mut collected_data: String = String::new();
let paths = std::fs::read_dir(get_ipass_folder()).unwrap();
for p in paths {
if let Ok(path) = p {
let content = &mut std::fs::read_to_string(get_ipass_folder()+&path.file_name().into_string().unwrap()).expect("Should have been able to read the file");
collected_data += format!("{}\n{}\n", path.file_name().into_string().unwrap().replace(".ipass", ""),content).as_str();
}
}
if let Ok(file) = std::fs::File::create(location.clone()+"/export.ipassx") {
let mut writer = brotli::CompressorWriter::new(
file,
4096,
11,
22);
match writer.write_all(collected_data.as_bytes()) {
Err(e) => panic!("{}", e),
Ok(_) => {},
}
return true;
} else {
return false;
}
}
fn vecu8_to_string(vec: Vec<u8>) -> String { fn vecu8_to_string(vec: Vec<u8>) -> String {
let mut do_print_warning = false; let mut do_print_warning = false;
let mut out: String = String::new(); let mut out: String = String::new();