feat: init commit
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.idea/
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
|
||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@@ -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"
|
||||
|
||||
7
src/home.rs
Normal file
7
src/home.rs
Normal file
@@ -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<PathBuf> {
|
||||
Some(PathBuf::from("."))
|
||||
}
|
||||
132
src/lib.rs
Normal file
132
src/lib.rs
Normal file
@@ -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<Option<String>> {
|
||||
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<Vec<PathBuf>> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
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::<Vec<_>>().is_empty();
|
||||
if need_encode {
|
||||
// TODO ...
|
||||
format!("E"{}, filename)
|
||||
} else {
|
||||
format!("e{}", filename)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_stored_password_dir() -> XResult<PathBuf> {
|
||||
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)
|
||||
}
|
||||
3
src/main.rs
Normal file
3
src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
Reference in New Issue
Block a user