From 9c9abd1def23c0f8baea450b02bf35a2c98293a1 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 15 Oct 2023 00:19:02 +0800 Subject: [PATCH] feat: init commit --- .gitignore | 1 + Cargo.toml | 14 ++++++ src/home.rs | 7 +++ src/lib.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 ++ 5 files changed, 157 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/home.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 3bf25c0..409abaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ # ---> Rust # Generated by Cargo # will have compiled files and executables diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ac4e789 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "local-stored-password" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rust_util = "0.6.42" + +[target.'cfg(any(windows, unix))'.dependencies] +rpassword = "7.2.0" +dirs = "5.0.1" + diff --git a/src/home.rs b/src/home.rs new file mode 100644 index 0000000..af50588 --- /dev/null +++ b/src/home.rs @@ -0,0 +1,7 @@ +#[cfg(any(windows, unix))] +pub(crate) use dirs::home_dir as get_home_dir; + +#[cfg(not(any(windows, unix)))] +pub(crate) fn get_home_dir() -> Option { + Some(PathBuf::from(".")) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ebf15bd --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,132 @@ +use std::{fs, io}; +use std::fs::create_dir; +use std::io::Write; +use std::path::PathBuf; + +use rust_util::{information, opt_result, simple_error, warning, XResult}; + +mod home; + +const STORED_PASSWORD_DIR: &str = ".stored_passwords"; +const STORED_PASSWORD_EXT: &str = ".spass"; + +enum StoredPasswordType { + Hmac, + HmacWithPass, +} + +struct StoredPasswordBody { + ty: String, + content: String, +} + +pub fn read_stored_password() -> XResult> { + let stored_pass_files = list_stored_passwords()?; + if stored_pass_files.is_empty() { + return Ok(None); + } + information!("Found {} stored passwords:", stored_pass_files.len()); + for (i, entry) in stored_pass_files.iter().enumerate() { + println!("#{} : {}", (i + 1), entry.display()); + } + let num = read_number("Please select(0 for skip)", 0, stored_pass_files.len() + 1); + if num == 0 { return Ok(None); } + let pass_file = &stored_pass_files[num - 1]; + + // TODO ... + + Ok(None) +} + +pub fn write_stored_password() { + // TODO ... +} + +fn read_number(hint: &str, from: usize, to: usize) -> usize { + loop { + print!("{} ({}-{}): ", hint, from, to); + io::stdout().flush().ok(); + let mut buff = String::new(); + let _ = io::stdin().read_line(&mut buff).expect("Read line from stdin"); + let buff = buff.trim(); + match buff.parse() { + Err(_) => warning!("Input number error!"), + Ok(number) => if number < from || number > to { + warning!("Input number is not in range."); + } else { + return number; + }, + } + } +} + +fn list_stored_passwords() -> XResult> { + let dir = get_stored_password_dir()?; + if !dir.is_dir() { + return Ok(vec![]); + } + let read_dir = match fs::read_dir(dir) { + Ok(read_dir) => read_dir, + Err(e) => return simple_error!("can't read dir: {}", dir.display()), + }; + let mut stored_pass_files = vec![]; + for entry in read_dir { + if let Ok(entry) = entry { + let path = entry.path(); + if path.is_file() && path.display().to_string().ends_with(STORED_PASSWORD_EXT) { + stored_pass_files.push(path); + } + } + } + stored_pass_files.sort(); + Ok(stored_pass_files) +} + +fn extract_password_name(file: &PathBuf) -> XResult { + let filename = file.display().to_string(); + if !filename.ends_with(STORED_PASSWORD_EXT) { + return simple_error!("Not a stored password file: {}", filename); + } + + Ok("".to_string()) +} + +fn decode_filename(filename: &str) -> XResult { + if filename.starts_with("E") { + // TODO ... + Ok("".to_string()) + } else if filename.starts_with("e") { + Ok(filename.chars().skip(1).collect()) + } else { + Ok(filename.to_string()) + } +} + +fn encode_filename(filename: &str) -> String { + let need_encode = !filename.chars().filter(|c| { + let lower = *c >= 'a' && *c <= 'z'; + let upper = *c >= 'A' && *c <= 'Z'; + let digit = *c >= '0' && *c <= '9'; + let other_chars = match *c { + '-' | '_' | '.' => true, + _ => false, + }; + !lower && !upper && !digit && !other_chars + }).collect::>().is_empty(); + if need_encode { + // TODO ... + format!("E"{}, filename) + } else { + format!("e{}", filename) + } +} + +fn get_stored_password_dir() -> XResult { + let home_path = opt_result!(get_home_dir(), "can't find home dir: {}"); + let mut complete_path = home_path; + complete_path.push(STORED_PASSWORD_DIR); + if !complete_path.exists() { + create_dir(&complete_path)?; + } + Ok(complete_path) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}