feat: init commit
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
.idea/
|
||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# 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