mirror of
https://github.com/002Hub/IPass.git
synced 2025-04-19 22:01:21 +02:00
add most basic functionality
Co-authored-by: Alpisc <Alpisc@users.noreply.github.com>
This commit is contained in:
parent
c86ab9447a
commit
23f80e8c3c
88
Cargo.lock
generated
88
Cargo.lock
generated
@ -2,6 +2,23 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
@ -16,8 +33,79 @@ name = "ipass"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"home",
|
"home",
|
||||||
|
"rand",
|
||||||
|
"rpassword",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.139"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "7.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rtoolbox",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtoolbox"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -7,3 +7,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
home = "0.5.4"
|
home = "0.5.4"
|
||||||
|
rpassword = "7.2"
|
||||||
|
rand = "0.8.5"
|
4
ipass.bat
Normal file
4
ipass.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
REM this is just a shortcut
|
||||||
|
cargo run --release %*
|
||||||
|
@echo on
|
248
src/main.rs
248
src/main.rs
@ -1,58 +1,36 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use home::home_dir;
|
use rand::rngs::OsRng;
|
||||||
use std::fs;
|
use rand::RngCore;
|
||||||
|
use std::io::Write;
|
||||||
fn get_args() -> Vec<String> {
|
mod utils;
|
||||||
std::env::args().collect() // [0] = file path; [n>0] = argument
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_home_folder_str() -> String {
|
|
||||||
match home::home_dir() {
|
|
||||||
Some(path) => {
|
|
||||||
let p = path.to_str();
|
|
||||||
match p {
|
|
||||||
Some(pa) => return pa.to_owned(),
|
|
||||||
None => return "".to_owned(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => return "".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ipass_folder() -> String {
|
|
||||||
let path = get_home_folder_str()+"/.IPass/";
|
|
||||||
fs::create_dir_all(&path).unwrap();
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = get_args();
|
let version = "0.1.0";
|
||||||
|
println!("IPass v{}\n", version);
|
||||||
|
|
||||||
if args.len()<2 {
|
let args = utils::get_args();
|
||||||
help_message()
|
|
||||||
|
if args.len() < 2 {
|
||||||
|
help_message(&args);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mode: &str = &args[1].trim().to_lowercase().to_owned();
|
let mode: &str = &args[1].trim().to_lowercase().to_owned();
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
"list" => list(),
|
"list" => list(),
|
||||||
"add" => add(),
|
"add" => add(&args),
|
||||||
"get" => get(),
|
"get" => get(&args),
|
||||||
"edit" => edit(),
|
"edit" => edit(&args),
|
||||||
"remove" => remove(),
|
"remove" => remove(&args),
|
||||||
_ => help_message(),
|
"import" => import(&args),
|
||||||
|
"export" => export(&args),
|
||||||
|
"rename" => rename(&args),
|
||||||
|
_ => help_message(&args),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help_message() {
|
fn help_message(args: &Vec<String>) {
|
||||||
|
|
||||||
let version = "0.0.1";
|
|
||||||
println!("IPass v{}\n", version);
|
|
||||||
|
|
||||||
let args = get_args();
|
|
||||||
|
|
||||||
let mut help_messages:HashMap<String, String> = HashMap::new();
|
let mut help_messages:HashMap<String, String> = HashMap::new();
|
||||||
help_messages.insert(
|
help_messages.insert(
|
||||||
@ -79,53 +57,183 @@ fn help_message() {
|
|||||||
"remove".to_string(),
|
"remove".to_string(),
|
||||||
"removes an existing entry".to_string(),
|
"removes an existing entry".to_string(),
|
||||||
);
|
);
|
||||||
|
help_messages.insert(
|
||||||
|
"rename".to_string(),
|
||||||
|
"renames an existing entry".to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
if args.len()<3 {
|
if args.len() < 3 {
|
||||||
print!("The possible commands are: ");
|
println!("You can use the following commands:");
|
||||||
for i in help_messages.keys() {
|
for i in help_messages.keys() {
|
||||||
print!("\"{i}\" ");
|
println!("\"{i}\"{}- {}"," ".repeat(8-i.len()),help_messages[i]);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
println!("{} {}", &args[2], &help_messages[&args[2]])
|
println!("{} {}", &args[2], &help_messages[&args[2]])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pw() -> String {
|
|
||||||
let mut output: String = String::new();
|
|
||||||
std::io::stdin().read_line(&mut output).expect("Failed to read line");
|
|
||||||
return output.replace("\n", "").replace("\r","");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list() {
|
fn list() {
|
||||||
todo!("List websites/names");
|
let paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
println!("Entry: \"{}\"", path.unwrap().file_name().into_string().unwrap().replace(".ipass", ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add() {
|
fn add(args: &Vec<String>) {
|
||||||
todo!("Add password")
|
if args.len() < 3 || args.len() > 4 {
|
||||||
// create_entry(args[3],encrypt_pass(args[4]));
|
println!("Incorrect usage of \"add\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pw: String;
|
||||||
|
|
||||||
|
if args.len() > 3 {
|
||||||
|
pw = args[3].to_owned();
|
||||||
|
} else {
|
||||||
|
let char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"§$%&/()=?´`²³{[]}\\,.-;:_><|+*#'".as_bytes();
|
||||||
|
let mut chars_index: Vec<u8> = vec![0;20];
|
||||||
|
OsRng.fill_bytes(&mut chars_index);
|
||||||
|
let mut chars = String::new();
|
||||||
|
|
||||||
|
for index in chars_index {
|
||||||
|
chars += std::str::from_utf8(&[char_set[(index%(char_set.len() as u8)) as usize]]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pw = chars;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Adding password for {}",args[2]);
|
||||||
|
let succeeded = utils::create_entry(&args[2],&mut pw);
|
||||||
|
if !succeeded {
|
||||||
|
println!("You already have an entry with that name! Did you mean to use \"edit\"?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("Added password for {}",args[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get() {
|
fn get(args: &Vec<String>) {
|
||||||
todo!("Get password")
|
let name = &args[2];
|
||||||
|
let filepath = &(utils::get_ipass_folder()+name+".ipass");
|
||||||
|
if std::path::Path::new(filepath).exists() {
|
||||||
|
println!("Getting entry");
|
||||||
|
println!("{}",utils::get_entry(filepath));
|
||||||
|
} else {
|
||||||
|
println!("No such entry!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edit() {
|
fn edit(args: &Vec<String>) {
|
||||||
todo!("Edit entry")
|
if args.len() < 3 {
|
||||||
|
println!("Invalid usage of \"edit\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass");
|
||||||
|
if std::path::Path::new(filepath).exists() {
|
||||||
|
let output: String;
|
||||||
|
if args.len() != 4 {
|
||||||
|
output = rpassword::prompt_password("Please enter the new password: ").unwrap();
|
||||||
|
} else {
|
||||||
|
output = args[3].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create(format!("{}/{}.ipass",utils::get_ipass_folder(),args[2])).unwrap();
|
||||||
|
file.write_all(output.replace("\n", "").replace("\r","").as_bytes()).unwrap();
|
||||||
|
|
||||||
|
println!("Changed Password of {}!", args[2]);
|
||||||
|
} else {
|
||||||
|
println!("No such file!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove() {
|
fn rename(args: &Vec<String>) { // prog ren old new
|
||||||
todo!("Removes entry")
|
if args.len() < 4 {
|
||||||
|
println!("Invalid usage of \"rename\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass");
|
||||||
|
if std::path::Path::new(filepath).exists() {
|
||||||
|
std::fs::rename(format!("{}/{}.ipass",utils::get_ipass_folder(),args[2]), format!("{}/{}.ipass",utils::get_ipass_folder(),args[3])).unwrap();
|
||||||
|
println!("Renamed {} to {}", args[2], args[3]);
|
||||||
|
} else {
|
||||||
|
println!("No such file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_pass(pass: &str) -> &str {
|
fn remove(args: &Vec<String>) {
|
||||||
return pass;
|
let name = &args[2];
|
||||||
|
let filepath = &(utils::get_ipass_folder()+name+".ipass");
|
||||||
|
if std::path::Path::new(filepath).exists() {
|
||||||
|
if utils::prompt_answer(format!("Are you sure you want to delete {}? [y/N]", name)) == "y" {
|
||||||
|
std::fs::remove_file(filepath).unwrap();
|
||||||
|
println!("Removed entry \"{}\"", name);
|
||||||
|
} else{
|
||||||
|
println!("Operation cancelled!")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("No entry named \"{}\"", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
fn import(args: &Vec<String>) {
|
||||||
prog.exe list -> all saved entries
|
let mut location = utils::get_home_folder_str();
|
||||||
prog.exe add [Name] \n **in stars** {password}
|
if args.len() == 3 {
|
||||||
prog.exe get [Name]
|
location = args[2].clone();
|
||||||
*/
|
} else {
|
||||||
|
if utils::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
||||||
|
println!("Operation cancelled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if std::path::Path::new(&(location.clone()+"/export.ipassx")).exists() {
|
||||||
|
let content = &mut std::fs::read_to_string(location.clone()+"/export.ipassx").expect("Should have been able to read the file");
|
||||||
|
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 = "";
|
||||||
|
}
|
||||||
|
print!("Imported all entries!");
|
||||||
|
} else {
|
||||||
|
println!("No such file found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(args: &Vec<String>) { //TODO: compress data
|
||||||
|
let mut location = utils::get_home_folder_str();
|
||||||
|
if args.len() == 3 {
|
||||||
|
location = args[2].clone();
|
||||||
|
} else {
|
||||||
|
if utils::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
||||||
|
println!("Operation cancelled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create(location.clone()+"/export.ipassx").unwrap();
|
||||||
|
file.write_all(collected_data.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
println!("Saved at: {}/export.ipassx", location);
|
||||||
|
}
|
68
src/utils.rs
Normal file
68
src/utils.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_args() -> Vec<String> {
|
||||||
|
std::env::args().collect() // [0] = file path; [n>0] = argument
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt_pass(pass: &mut String,_mpw: String) -> &mut String {
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt_pass(pass: &mut String, _mpw: String) -> &mut String {
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_home_folder_str() -> String {
|
||||||
|
match home::home_dir() {
|
||||||
|
Some(path) => {
|
||||||
|
let p = path.to_str();
|
||||||
|
match p {
|
||||||
|
Some(pa) => return pa.to_owned(),
|
||||||
|
None => return "".to_owned(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return "".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ipass_folder() -> String {
|
||||||
|
let path = get_home_folder_str()+"/.IPass/";
|
||||||
|
std::fs::create_dir_all(&path).unwrap();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_entry(name:&String, pw:&mut String) -> bool {
|
||||||
|
if std::path::Path::new(&(get_ipass_folder()+name+".ipass")).exists() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
edit_entry(name, pw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entry(filepath:&String) -> String {
|
||||||
|
let content = &mut std::fs::read_to_string(filepath).expect("Should have been able to read the file");
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
return decrypt_pass(content,mpw).to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edit_entry(name:&String,mut pw:&mut String) {
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
pw = encrypt_pass(pw,mpw);
|
||||||
|
let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
file.write_all(pw.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_for_pw() -> String {
|
||||||
|
let output = rpassword::prompt_password("Please enter the master password: ").unwrap();
|
||||||
|
return output.replace("\n", "").replace("\r","");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_answer(toprint: String) -> String {
|
||||||
|
print!("{toprint}");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
let mut choice = String::new();
|
||||||
|
std::io::stdin().read_line(&mut choice).expect("Failed to read choice");
|
||||||
|
|
||||||
|
return choice.trim().to_lowercase();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user