use rand::seq::SliceRandom;
use std::path::{Path};
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader};
use clap::{Arg,App};
use regex::Regex;
fn no_word_file() -> io::Result<String> {
// Fall back to this if we cant find the word file
let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGIJKLMNOPQRSTUVWXYZ0123456789!@$%^&*()-+".chars().collect();
let mut password: String = String::new();
for _x in 0..63 {
let random_char = chars.choose(&mut rand::thread_rng());
password.push_str(&random_char.unwrap().to_string());
}
Ok(password)
}
fn character_weave(passphrase: String) -> io::Result<String> {
/*
* a - @
* i - !
* o - 0
* s - $
* e - 3
*/
let mut new_passphrase: String = String::new();
for c in passphrase.chars() {
// Probably a better way to do this as opposed to a giant if / elsif
if c.to_string() == "a" {
new_passphrase.push_str(&"@".to_string());
} else if c.to_string() == "i" {
new_passphrase.push_str(&"!".to_string());
} else if c.to_string() == "o" {
new_passphrase.push_str(&"0".to_string());
} else if c.to_string() == "s" {
new_passphrase.push_str(&"$".to_string());
} else if c.to_string() == "e" {
new_passphrase.push_str(&"3".to_string());
} else {
new_passphrase.push_str(&c.to_string());
}
}
Ok(new_passphrase)
}
// This is stupid TODO
fn print_password(password: String) {
println!("{}",password);
}
fn read_phrase_file(word_file: String) -> io::Result<Vec<String>> {
if ! Path::new(&word_file).exists() {
println!("Could not find {}, generating a password and exiting", word_file);
print_password(no_word_file().unwrap());
std::process::exit(1);
}
let words_fh = File::open(word_file)?;
let mut words = vec![];
let re = Regex::new(r"^([0-9]{5})\s(.*)$").unwrap();
for line in BufReader::new(words_fh).lines() {
for re_capture in re.captures_iter(&line.unwrap()) {
// println!("Capture 1: {}, Capture 2: {}", &re_capture[1], &re_capture[2]);
// Only push word for now, need to figure out multidimentional arrays
words.push(re_capture[2].to_string());
}
}
Ok(words)
}
fn main() {
// This variable is used as the default word file path, see Arg::with_name("words_file")
// This is probably wrong/bad, and limits the functionality to Linux only
let mut default_words_path = dirs::home_dir().unwrap().to_str().unwrap().to_string();
default_words_path.push_str(&"/.local/bin/eff_large_wordlist.txt".to_string());
let args = App::new("swmkp")
.version("0.1")
.about("My pw gen. Based on EFF passphrase guidelines\nhttps://www.eff.org/dice")
.arg(Arg::with_name("words-file")
.short("f")
.long("words-file")
.required(true)
.takes_value(true)
.default_value(&default_words_path)
.help("Path to passphrase file, expects file provided by EFF\nSee:\nhttps://www.eff.org/files/2016/07/18/eff_large_wordlist.txt\nhttps://eff.org/files/2016/09/08/eff_short_wordlist_1.txt\nhttps://eff.org/files/2016/09/08/eff_short_wordlist_2_0.txt\n"))
.arg(Arg::with_name("length")
.short("l")
.long("length")
.required(false)
.takes_value(true)
.default_value("5")
.help("How many words to use"))
.arg(Arg::with_name("delimiter")
.short("d")
.long("delimiter")
.required(false)
.takes_value(true)
.default_value("-")
.help("What word delimiter to use"))
.arg(Arg::with_name("with-characters")
.short("w")
.long("with-characters")
.required(false)
.takes_value(false)
.help("Replaces some letters with special characters"))
.arg(Arg::with_name("password")
.short("p")
.long("password")
.required(false)
.takes_value(false)
.help("Generate a 64 character password, as opposed to a passphrase\nFallback here if no passphrase file provided/found"))
.get_matches();
if args.is_present("password") {
print_password(no_word_file().unwrap());
std::process::exit(0);
}
let roll_count: usize = args.value_of("length").unwrap().parse().unwrap();
let delim: String = args.value_of("delimiter").unwrap().to_string();
let words = read_phrase_file(args.value_of("words-file").unwrap().to_string()).unwrap();
let mut passphrase: String = String::new();
for x in 0..roll_count {
let random_word = words.choose(&mut rand::thread_rng());
if x == ( roll_count - 1 ) {
passphrase.push_str(random_word.unwrap());
} else {
passphrase.push_str(random_word.unwrap());
passphrase.push_str(&delim);
}
}
if args.is_present("with-characters") {
let new_passphrase = character_weave(passphrase).unwrap();
println!("{}",new_passphrase);
} else {
println!("{}",passphrase);
}
}