diff --git a/Cargo.lock b/Cargo.lock index c3cc6954..668d962c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,9 +182,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" [[package]] name = "bindgen" @@ -519,18 +519,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstyle", "clap_lex", @@ -1499,7 +1499,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2340,9 +2340,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.178" +version = "0.2.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" [[package]] name = "libgit2-sys" @@ -2546,7 +2546,7 @@ checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -2934,9 +2934,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", "ucd-trie", @@ -2944,9 +2944,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" dependencies = [ "pest", "pest_generator", @@ -2954,9 +2954,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", @@ -2967,9 +2967,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" dependencies = [ "pest", "sha2", @@ -3445,7 +3445,6 @@ dependencies = [ "gettext", "glob", "hex", - "lazy_static", "locale_config", "man", "ripasso", @@ -3994,9 +3993,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.112" +version = "2.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" +checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4" dependencies = [ "proc-macro2", "quote", @@ -4270,9 +4269,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -4294,9 +4293,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4627,6 +4626,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -4638,9 +4646,12 @@ dependencies = [ [[package]] name = "wasite" -version = "0.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" +checksum = "313fb64fed616e75426cf738284efe4e85017243bccfae42698be1d7fa9b05d5" +dependencies = [ + "wasi 0.14.7+wasi-0.2.4", +] [[package]] name = "wasm-bindgen" @@ -4792,9 +4803,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" dependencies = [ "rustls-pki-types", ] @@ -4807,9 +4818,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "whoami" -version = "1.6.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +checksum = "86dc1eeef7866078951fc09f1857d3d33a37432fe376d7ff45449c8bb50318c1" dependencies = [ "libredox", "wasite", @@ -5442,9 +5453,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9211a9f64b825911bdf0240f58b7a8dac217fe260fc61f080a07f61372fbd5" +checksum = "30e0d8dffbae3d840f64bda38e28391faef673a7b5a6017840f2a106c8145868" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index cc834610..650b7b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ gpgme = "0.11" chrono = { version = "0.4", default-features = false, features = ["clock"] } git2 = "0.20" rand = "0.9" -whoami = "1" +whoami = "2" toml = "0.9" reqwest = { version = "0.13", features = ["blocking"] } hex = "0.4" @@ -36,6 +36,9 @@ flate2 = "1" tar = "0.4" criterion = "0.8" +[lints.clippy] +pedantic = "warn" + [workspace] members = [ diff --git a/benches/library_benchmark.rs b/benches/library_benchmark.rs index cbb34f6e..165f9038 100644 --- a/benches/library_benchmark.rs +++ b/benches/library_benchmark.rs @@ -30,12 +30,12 @@ fn cleanup(mut base_path: PathBuf, path_name: &str) -> Result<(), std::io::Error fn pop_list(password_dir: PathBuf) -> pass::Result<()> { let store = pass::PasswordStore::new( "", - &Some(password_dir), - &None, - &None, - &None, - &CryptoImpl::GpgMe, - &None, + Some(&password_dir), + None, + None, + None, + CryptoImpl::GpgMe, + None, )?; let results = store.all_passwords().unwrap(); diff --git a/cursive/Cargo.toml b/cursive/Cargo.toml index 80cc9c75..23a9ca4a 100644 --- a/cursive/Cargo.toml +++ b/cursive/Cargo.toml @@ -17,7 +17,6 @@ ripasso = { path = "../", version = "0.8.0" } locale_config = "0.3" unic-langid = "0.9" gettext = "0.4" -lazy_static = "1" terminal_size = "0.4" hex = "0.4" zeroize = { version = "1", features = ["zeroize_derive", "alloc"] } @@ -28,9 +27,12 @@ default-features = false features = ["toml"] [dev-dependencies] -tempfile = "3.10.1" +tempfile = "3" chrono = { version = "0.4", default-features = false, features = ["clock"] } [build-dependencies] glob = "0.3" man = "0.3" + +[lints.clippy] +pedantic = "warn" diff --git a/cursive/build.rs b/cursive/build.rs index eb3e3075..0d0a1a3a 100644 --- a/cursive/build.rs +++ b/cursive/build.rs @@ -65,7 +65,7 @@ fn generate_man_page_file() { dest_path.pop(); dest_path.pop(); dest_path.push("man-page"); - print!("creating directory: {:?} ", &dest_path); + print!("creating directory: {} ", dest_path.display()); let res = std::fs::create_dir(&dest_path); if res.is_ok() { println!("success"); @@ -73,7 +73,7 @@ fn generate_man_page_file() { println!("error: {:?}", res.err().unwrap()); } dest_path.push("cursive"); - print!("creating directory: {:?} ", &dest_path); + print!("creating directory: {} ", dest_path.display()); let res = std::fs::create_dir(&dest_path); if res.is_ok() { println!("success"); @@ -94,7 +94,7 @@ fn generate_translation_files() { dest_path.pop(); dest_path.pop(); dest_path.push("translations"); - print!("creating directory: {:?} ", &dest_path); + print!("creating directory: {} ", dest_path.display()); let res = std::fs::create_dir(&dest_path); if res.is_ok() { println!("success"); @@ -102,7 +102,7 @@ fn generate_translation_files() { println!("error: {:?}", res.err().unwrap()); } dest_path.push("cursive"); - print!("creating directory: {:?} ", &dest_path); + print!("creating directory: {} ", dest_path.display()); let res = std::fs::create_dir(&dest_path); if res.is_ok() { println!("success"); @@ -128,8 +128,8 @@ fn generate_translation_files() { filename.replace_range(3..4, "m"); print!( - "generating .mo file for {:?} to {}/{} ", - &file, + "generating .mo file for {} to {}/{} ", + file.display(), dest_path.display(), &filename ); diff --git a/cursive/src/helpers.rs b/cursive/src/helpers.rs index 5ce11856..f49763cc 100644 --- a/cursive/src/helpers.rs +++ b/cursive/src/helpers.rs @@ -14,7 +14,7 @@ along with this program. If not, see . */ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, LazyLock, Mutex}; use arboard::Clipboard; use cursive::{ @@ -22,13 +22,11 @@ use cursive::{ event::Key, views::{Checkbox, Dialog, EditView, OnEventView, RadioButton, TextView}, }; -use lazy_static::lazy_static; use pass::Result; use ripasso::{crypto::CryptoImpl, pass, pass::Recipient}; -lazy_static! { - static ref CLIPBOARD: Arc> = Arc::new(Mutex::new(Clipboard::new().unwrap())); -} +static CLIPBOARD: LazyLock>> = + LazyLock::new(|| Arc::new(Mutex::new(Clipboard::new().unwrap()))); /// Displays an error in a cursive dialog pub fn errorbox(ui: &mut Cursive, err: &pass::Error) { @@ -52,7 +50,7 @@ pub fn errorbox(ui: &mut Cursive, err: &pass::Error) { } /// Copies content to the clipboard. -pub fn set_clipboard(content: &String) -> Result<()> { +pub fn set_clipboard(content: &str) -> Result<()> { Ok(CLIPBOARD.lock().unwrap().set_text(content)?) } diff --git a/cursive/src/main.rs b/cursive/src/main.rs index d38e85eb..56a33d3e 100644 --- a/cursive/src/main.rs +++ b/cursive/src/main.rs @@ -14,14 +14,7 @@ along with this program. If not, see . */ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - process, - sync::{Arc, Mutex}, - thread, time, -}; - +use config::Config; use cursive::{ Cursive, CursiveExt, direction::Orientation, @@ -44,31 +37,39 @@ use ripasso::{ passphrase_generator::passphrase_generator, password_generator::password_generator, }; +use std::sync::{LazyLock, MutexGuard}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + process, + sync::{Arc, Mutex}, + thread, time, +}; use unic_langid::LanguageIdentifier; -mod helpers; -mod wizard; - use crate::helpers::{ get_value_from_input, is_checkbox_checked, is_radio_button_selected, recipients_widths, }; -use lazy_static::lazy_static; use ripasso::crypto::Fingerprint; +use ripasso::password_generator::PasswordGenerationCategory; use zeroize::Zeroize; -/// The 'pointer' to the current PasswordStore is of this convoluted type. +mod helpers; +mod wizard; + +/// The 'pointer' to the current `PasswordStore` is of this convoluted type. type PasswordStoreType = Arc>>>; /// The list of stores that the user have. type StoreListType = Arc>>>>; -lazy_static! { - static ref CATALOG: gettext::Catalog = get_translation_catalog(); - static ref DEFAULT_TERMINAL_SIZE: (usize, usize) = match terminal_size::terminal_size() { - Some((terminal_size::Width(w), terminal_size::Height(h))) => - (usize::from(w + 8), usize::from(h)), +static CATALOG: LazyLock = LazyLock::new(get_translation_catalog); +static DEFAULT_TERMINAL_SIZE: LazyLock<(usize, usize)> = + LazyLock::new(|| match terminal_size::terminal_size() { + Some((terminal_size::Width(w), terminal_size::Height(h))) => { + (usize::from(w + 8), usize::from(h)) + } _ => (0, 0), - }; -} + }); fn screen_width(ui: &Cursive) -> usize { match ui.screen_size().x { @@ -134,7 +135,7 @@ fn page_up(ui: &mut Cursive) { ); } -fn copy(ui: &mut Cursive, store: PasswordStoreType) { +fn copy(ui: &mut Cursive, store: &PasswordStoreType) { let sel = ui .find_name::>("results") .unwrap() @@ -155,14 +156,14 @@ fn copy(ui: &mut Cursive, store: PasswordStoreType) { thread::spawn(|| { thread::sleep(time::Duration::from_secs(40)); - helpers::set_clipboard(&String::new()).unwrap(); + helpers::set_clipboard("").unwrap(); }); ui.call_on_name("status_bar", |l: &mut TextView| { l.set_content(CATALOG.gettext("Copied password to copy buffer for 40 seconds")); }); } -fn copy_first_line(ui: &mut Cursive, store: PasswordStoreType) { +fn copy_first_line(ui: &mut Cursive, store: &PasswordStoreType) { let sel = ui .find_name::>("results") .unwrap() @@ -183,7 +184,7 @@ fn copy_first_line(ui: &mut Cursive, store: PasswordStoreType) { thread::spawn(|| { thread::sleep(time::Duration::from_secs(40)); - helpers::set_clipboard(&String::new()).unwrap(); + helpers::set_clipboard("").unwrap(); }); ui.call_on_name("status_bar", |l: &mut TextView| { l.set_content( @@ -192,7 +193,7 @@ fn copy_first_line(ui: &mut Cursive, store: PasswordStoreType) { }); } -fn copy_mfa(ui: &mut Cursive, store: PasswordStoreType) { +fn copy_mfa(ui: &mut Cursive, store: &PasswordStoreType) { let sel = ui .find_name::>("results") .unwrap() @@ -229,7 +230,7 @@ fn copy_name(ui: &mut Cursive) { if let Err(err) = || -> Result<()> { let name = sel.name.split('/').next_back(); - helpers::set_clipboard(&name.unwrap_or("").to_string())?; + helpers::set_clipboard(name.unwrap_or(""))?; Ok(()) }() { helpers::errorbox(ui, &err); @@ -241,7 +242,7 @@ fn copy_name(ui: &mut Cursive) { }); } -fn do_delete(ui: &mut Cursive, store: PasswordStoreType) { +fn do_delete(ui: &mut Cursive, store: &PasswordStoreType) { ui.call_on_name( "results", |l: &mut SelectView| -> Result<()> { @@ -269,13 +270,14 @@ fn do_delete(ui: &mut Cursive, store: PasswordStoreType) { ui.pop_layer(); } -fn delete(ui: &mut Cursive, store: PasswordStoreType) { +fn delete(ui: &mut Cursive, store: &PasswordStoreType) { + let store = store.clone(); ui.add_layer(CircularFocus::new( Dialog::around(TextView::new( CATALOG.gettext("Are you sure you want to delete the password?"), )) .button(CATALOG.gettext("Yes"), move |ui: &mut Cursive| { - do_delete(ui, store.clone()); + do_delete(ui, &store); ui.call_on_name("status_bar", |l: &mut TextView| { l.set_content(CATALOG.gettext("Password deleted")); }); @@ -299,7 +301,7 @@ fn get_selected_password_entry(ui: &mut Cursive) -> Option Some(password_entry) } -fn show_file_history(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn show_file_history(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let password_entry_opt = get_selected_password_entry(ui); if password_entry_opt.is_none() { return Ok(()); @@ -345,7 +347,7 @@ fn show_file_history(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { Ok(()) } -fn do_show_file_history(ui: &mut Cursive, store: PasswordStoreType) { +fn do_show_file_history(ui: &mut Cursive, store: &PasswordStoreType) { let res = show_file_history(ui, store); if let Err(err) = res { @@ -353,7 +355,7 @@ fn do_show_file_history(ui: &mut Cursive, store: PasswordStoreType) { } } -fn do_password_save(ui: &mut Cursive, password: &str, store: PasswordStoreType, do_pop: bool) { +fn do_password_save(ui: &mut Cursive, password: &str, store: &PasswordStoreType, do_pop: bool) { let res = password_save(ui, password, store, do_pop); if let Err(err) = res { helpers::errorbox(ui, &err); @@ -363,7 +365,7 @@ fn do_password_save(ui: &mut Cursive, password: &str, store: PasswordStoreType, fn password_save( ui: &mut Cursive, password: &str, - store: PasswordStoreType, + store: &PasswordStoreType, do_pop: bool, ) -> Result<()> { let password_entry_opt = get_selected_password_entry(ui); @@ -376,7 +378,7 @@ fn password_save( let r = password_entry.update(password.to_string(), &*store.lock()?.lock()?); if let Err(err) = r { - helpers::errorbox(ui, &err) + helpers::errorbox(ui, &err); } else { if do_pop { ui.pop_layer(); @@ -391,14 +393,14 @@ fn password_save( Ok(()) } -fn do_open(ui: &mut Cursive, store: PasswordStoreType) { +fn do_open(ui: &mut Cursive, store: &PasswordStoreType) { let res = open(ui, store); if let Err(err) = res { helpers::errorbox(ui, &err); } } -fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn open(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let password_entry_opt = get_selected_password_entry(ui); if password_entry_opt.is_none() { return Ok(()); @@ -415,6 +417,7 @@ fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { } } }; + let store = store.clone(); let d = Dialog::around(TextArea::new().content(&password).with_name("editbox")) .button(CATALOG.gettext("Save"), move |s| { let mut new_secret = s @@ -428,7 +431,7 @@ fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { let mut confirmed_new_secret = s .call_on_name("editbox", |e: &mut TextArea| e.get_content().to_string()) .unwrap(); - do_password_save(s, &confirmed_new_secret, store.clone(), true); + do_password_save(s, &confirmed_new_secret, &store, true); confirmed_new_secret.zeroize(); }) .dismiss_button(CATALOG.gettext("Close")); @@ -438,17 +441,17 @@ fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { }); s.add_layer(ev); } else { - do_password_save(s, &new_secret, store.clone(), false); - }; + do_password_save(s, &new_secret, &store, false); + } new_secret.zeroize(); }) .button(CATALOG.gettext("Generate Password"), move |s| { - let mut new_password = password_generator(20, 0); + let mut new_password = password_generator(20, PasswordGenerationCategory::AsciiOnly); s.call_on_name("editbox", |e: &mut TextArea| { e.set_content(&new_password); }); - new_password.zeroize() + new_password.zeroize(); }) @@ -456,7 +459,7 @@ fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { let mut new_password = match passphrase_generator(6) { Ok(words) => words.join(" "), Err(err) => { - helpers::errorbox(s, &ripasso::pass::Error::from(err)); + helpers::errorbox(s, &err); return; } }; @@ -476,7 +479,7 @@ fn open(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { Ok(()) } -fn do_rename_file(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn do_rename_file(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let old_name = ui .find_name::("old_name_input") .unwrap() @@ -517,7 +520,7 @@ fn do_rename_file(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { Ok(()) } -fn rename_file_dialog(ui: &mut Cursive, store: PasswordStoreType) { +fn rename_file_dialog(ui: &mut Cursive, store: &PasswordStoreType) { let sel = ui .find_name::>("results") .unwrap() @@ -556,12 +559,13 @@ fn rename_file_dialog(ui: &mut Cursive, store: PasswordStoreType) { fields.add_child(old_name_fields); fields.add_child(new_name_fields); + let store = store.clone(); let store2 = store.clone(); let d = Dialog::around(fields) .title(CATALOG.gettext("Rename File")) .button(CATALOG.gettext("Rename"), move |ui: &mut Cursive| { - if let Err(e) = do_rename_file(ui, store.clone()) { + if let Err(e) = do_rename_file(ui, &store) { helpers::errorbox(ui, &e); } }) @@ -572,7 +576,7 @@ fn rename_file_dialog(ui: &mut Cursive, store: PasswordStoreType) { s.pop_layer(); }) .on_event(Key::Enter, move |ui: &mut Cursive| { - if let Err(e) = do_rename_file(ui, store2.clone()) { + if let Err(e) = do_rename_file(ui, &store2) { helpers::errorbox(ui, &e); } }); @@ -584,7 +588,7 @@ fn do_new_password_save( s: &mut Cursive, path: &str, password: &str, - store: PasswordStoreType, + store: &PasswordStoreType, do_pop: bool, ) { let res = new_password_save(s, path, password, store, do_pop); @@ -597,7 +601,7 @@ fn new_password_save( s: &mut Cursive, path: &str, password: &str, - store: PasswordStoreType, + store: &PasswordStoreType, do_pop: bool, ) -> Result<()> { let entry = store @@ -629,7 +633,7 @@ fn new_password_save( Ok(()) } -fn create_save(s: &mut Cursive, store: PasswordStoreType) { +fn create_save(s: &mut Cursive, store: &PasswordStoreType) { let password = get_value_from_input(s, "new_password_input"); if password.is_none() { return; @@ -654,9 +658,10 @@ fn create_save(s: &mut Cursive, store: PasswordStoreType) { } if password.contains("otpauth://") { + let store = store.clone(); let d = Dialog::around(TextView::new(CATALOG.gettext("It seems like you are trying to save a TOTP code to the password store. This will reduce your 2FA solution to just 1FA, do you want to proceed?"))) .button(CATALOG.gettext("Save"), move |s| { - do_new_password_save(s, path.as_ref(), password.as_ref(), store.clone(), true); + do_new_password_save(s, path.as_ref(), password.as_ref(), &store, true); }) .dismiss_button(CATALOG.gettext("Close")); @@ -669,7 +674,109 @@ fn create_save(s: &mut Cursive, store: PasswordStoreType) { do_new_password_save(s, path.as_ref(), password.as_ref(), store, false); } } -fn create(ui: &mut Cursive, store: PasswordStoreType) { + +fn generate_password_callback( + category_value: &Arc>, + password_length: &Arc>, + s: &mut Cursive, +) { + let category = *category_value.lock().unwrap(); + let length = *password_length.lock().unwrap(); + let category = if category == 0 { + PasswordGenerationCategory::AsciiOnly + } else { + PasswordGenerationCategory::AsciiExtended + }; + let new_password = password_generator(length, category); + + s.call_on_name("new_password_input", |e: &mut EditView| { + e.set_content(new_password); + }); +} + +fn generate_passphrase_callback(s: &mut Cursive) { + let new_password = match passphrase_generator(6) { + Ok(words) => words.join(" "), + Err(err) => { + helpers::errorbox(s, &err); + return; + } + }; + s.call_on_name("new_password_input", |e: &mut EditView| { + e.set_content(new_password); + }); +} + +fn create_password_options_dialog( + category_value: &Arc>, + reveal_flag: &Arc>, + password_length: &Arc>, + s: &mut Cursive, +) { + let mut select = SelectView::::new(); + select.add_item("Category 0 (ASCII 33–126)", 0); + select.add_item("Category 1 (ASCII 33–255)", 1); + select.set_selection(*category_value.lock().unwrap()); + let select = select.with_name("password_category"); + + let length_input = EditView::new() + .content(password_length.lock().unwrap().to_string()) + .with_name("password_length") + .fixed_width(5); + + let reveal_checkbox = LinearLayout::horizontal() + .child(Checkbox::new().on_change({ + let reveal_flag = reveal_flag.clone(); + move |siv, checked| { + siv.call_on_name("new_password_input", |e: &mut EditView| { + e.set_secret(!checked); + }); + *reveal_flag.lock().unwrap() = checked; + } + })) + .child(TextView::new("Reveal password")); + + let dialog_content = LinearLayout::vertical() + .child(select.scrollable().fixed_size((30, 5))) + .child( + LinearLayout::horizontal() + .child(TextView::new("Length: ")) + .child(length_input), + ) + .child(reveal_checkbox); + + let save_selection = { + let category_value = category_value.clone(); + let password_length = password_length.clone(); + move |s: &mut Cursive| { + s.call_on_name("password_category", |view: &mut SelectView| { + if let Some(sel) = view.selection() { + *category_value.lock().unwrap() = *sel; + } + }); + + s.call_on_name("password_length", |view: &mut EditView| { + if let Ok(len) = view.get_content().parse::() { + *password_length.lock().unwrap() = len; + } + }); + + s.pop_layer(); + } + }; + + let popup = OnEventView::new( + Dialog::around(dialog_content) + .title("Password Options") + .button("OK", save_selection.clone()) + .dismiss_button("Cancel"), + ) + .on_event(Key::Enter, save_selection); + + s.add_layer(popup); +} + +fn create(ui: &mut Cursive, store: &PasswordStoreType) { let mut fields = LinearLayout::vertical(); let mut path_fields = LinearLayout::horizontal(); let mut password_fields = LinearLayout::horizontal(); @@ -715,6 +822,7 @@ fn create(ui: &mut Cursive, store: PasswordStoreType) { let reveal_flag = Arc::new(Mutex::new(false)); let password_length = Arc::new(Mutex::new(20_usize)); + let store = store.clone(); let d = Dialog::around(fields) .title(CATALOG.gettext("Add new password")) .button(CATALOG.gettext("Password Options"), { @@ -722,97 +830,17 @@ fn create(ui: &mut Cursive, store: PasswordStoreType) { let reveal_flag = reveal_flag.clone(); let password_length = password_length.clone(); move |s| { - let mut select = SelectView::::new(); - select.add_item("Category 0 (ASCII 33–126)", 0); - select.add_item("Category 1 (ASCII 33–255)", 1); - select.set_selection(*category_value.lock().unwrap()); - let select = select.with_name("password_category"); - - let length_input = EditView::new() - .content(password_length.lock().unwrap().to_string()) - .with_name("password_length") - .fixed_width(5); - - let reveal_checkbox = LinearLayout::horizontal() - .child(cursive::views::Checkbox::new().on_change({ - let reveal_flag = reveal_flag.clone(); - move |siv, checked| { - siv.call_on_name("new_password_input", |e: &mut EditView| { - e.set_secret(!checked); - }); - *reveal_flag.lock().unwrap() = checked; - } - })) - .child(TextView::new("Reveal password")); - - let dialog_content = LinearLayout::vertical() - .child(select.scrollable().fixed_size((30, 5))) - .child( - LinearLayout::horizontal() - .child(TextView::new("Length: ")) - .child(length_input), - ) - .child(reveal_checkbox); - - let save_selection = { - let category_value = category_value.clone(); - let password_length = password_length.clone(); - move |s: &mut Cursive| { - s.call_on_name("password_category", |view: &mut SelectView| { - if let Some(sel) = view.selection() { - *category_value.lock().unwrap() = *sel; - } - }); - - s.call_on_name("password_length", |view: &mut EditView| { - if let Ok(len) = view.get_content().parse::() { - *password_length.lock().unwrap() = len; - } - }); - - s.pop_layer(); - } - }; - - let popup = OnEventView::new( - Dialog::around(dialog_content) - .title("Password Options") - .button("OK", save_selection.clone()) - .dismiss_button("Cancel"), - ) - .on_event(Key::Enter, save_selection); - - s.add_layer(popup); + create_password_options_dialog(&category_value, &reveal_flag, &password_length, s); } }) - .button(CATALOG.gettext("Generate Password"), { - let category_value = category_value.clone(); - let password_length = password_length.clone(); - move |s| { - let category = *category_value.lock().unwrap(); - let length = *password_length.lock().unwrap(); - let new_password = - ripasso::password_generator::password_generator(length, category); - - s.call_on_name("new_password_input", |e: &mut EditView| { - e.set_content(new_password); - }); - } + .button(CATALOG.gettext("Generate Password"), move |s| { + generate_password_callback(&category_value, &password_length, s); }) .button(CATALOG.gettext("Generate Passphrase"), move |s| { - let new_password = match ripasso::passphrase_generator::passphrase_generator(6) { - Ok(words) => words.join(" "), - Err(err) => { - helpers::errorbox(s, &ripasso::pass::Error::from(err)); - return; - } - }; - s.call_on_name("new_password_input", |e: &mut EditView| { - e.set_content(new_password); - }); + generate_passphrase_callback(s); }) .button(CATALOG.gettext("Save"), move |ui: &mut Cursive| { - create_save(ui, store.clone()) + create_save(ui, &store); }) .dismiss_button(CATALOG.gettext("Cancel")); @@ -822,14 +850,14 @@ fn create(ui: &mut Cursive, store: PasswordStoreType) { }) .on_event(Key::Enter, move |ui: &mut Cursive| { if ui.screen_mut().len() == 1 { - create_save(ui, store2.clone()); + create_save(ui, &store2); } }); ui.add_layer(ev); } -fn delete_recipient(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn delete_recipient(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let mut l = ui .find_name::>>("recipients") .unwrap(); @@ -857,15 +885,16 @@ fn delete_recipient(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { } } -fn delete_recipient_verification(ui: &mut Cursive, store: PasswordStoreType) { +fn delete_recipient_verification(ui: &mut Cursive, store: &PasswordStoreType) { + let store = store.clone(); ui.add_layer(CircularFocus::new( Dialog::around(TextView::new( CATALOG.gettext("Are you sure you want to remove this person?"), )) .button(CATALOG.gettext("Yes"), move |ui: &mut Cursive| { - let res = delete_recipient(ui, store.clone()); + let res = delete_recipient(ui, &store); if let Err(err) = res { - helpers::errorbox(ui, &err) + helpers::errorbox(ui, &err); } else { ui.pop_layer(); } @@ -874,7 +903,7 @@ fn delete_recipient_verification(ui: &mut Cursive, store: PasswordStoreType) { )); } -fn add_recipient(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) -> Result<()> { +fn add_recipient(ui: &mut Cursive, store: &PasswordStoreType, config_path: &Path) -> Result<()> { let l = &*get_value_from_input(ui, "key_id_input").unwrap(); let dir = &*get_value_from_input(ui, "dir_id_input").unwrap(); @@ -891,30 +920,27 @@ fn add_recipient(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) let dir_path = std::path::PathBuf::from(dir); let res = store.add_recipient(&recipient, &dir_path, config_path); - match res { - Err(err) => helpers::errorbox(ui, &err), - Ok(_) => { - let all_recipients_res = store.recipients_for_path(&dir_path); - match all_recipients_res { - Err(err) => helpers::errorbox(ui, &err), - Ok(recipients) => { - let (max_width_key, max_width_name) = recipients_widths(&recipients); - - let mut recipients_view = ui - .find_name::>>("recipients") - .unwrap(); - recipients_view.add_item( - render_recipient_label(&recipient, max_width_key, max_width_name), - Some((dir_path, recipient)), - ); - - ui.pop_layer(); - ui.call_on_name("status_bar", |l: &mut TextView| { - l.set_content( - CATALOG.gettext("Added team member to password store"), - ); - }); - } + if let Err(err) = res { + helpers::errorbox(ui, &err); + } else { + let all_recipients_res = store.recipients_for_path(&dir_path); + match all_recipients_res { + Err(err) => helpers::errorbox(ui, &err), + Ok(recipients) => { + let (max_width_key, max_width_name) = recipients_widths(&recipients); + + let mut recipients_view = ui + .find_name::>>("recipients") + .unwrap(); + recipients_view.add_item( + render_recipient_label(&recipient, max_width_key, max_width_name), + Some((dir_path, recipient)), + ); + + ui.pop_layer(); + ui.call_on_name("status_bar", |l: &mut TextView| { + l.set_content(CATALOG.gettext("Added team member to password store")); + }); } } } @@ -924,7 +950,7 @@ fn add_recipient(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) Ok(()) } -fn add_recipient_dialog(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) { +fn add_recipient_dialog(ui: &mut Cursive, store: &PasswordStoreType, config_path: &Path) { let mut all_fields = LinearLayout::vertical(); let mut recipient_fields = LinearLayout::horizontal(); let mut dir_fields = LinearLayout::horizontal(); @@ -954,13 +980,14 @@ fn add_recipient_dialog(ui: &mut Cursive, store: PasswordStoreType, config_path: all_fields.add_child(recipient_fields); all_fields.add_child(dir_fields); + let store = store.clone(); let config_path = config_path.to_path_buf(); let cf = CircularFocus::new( Dialog::around(all_fields) .button(CATALOG.gettext("Yes"), move |ui: &mut Cursive| { - let res = add_recipient(ui, store.clone(), &config_path); + let res = add_recipient(ui, &store, &config_path); if let Err(err) = res { - helpers::errorbox(ui, &err) + helpers::errorbox(ui, &err); } }) .dismiss_button(CATALOG.gettext("Cancel")), @@ -1008,8 +1035,8 @@ fn render_recipient_label( ) } -fn get_sub_dirs(dir: &PathBuf) -> Result> { - let mut to_visit = vec![dir.clone()]; +fn get_sub_dirs(dir: &Path) -> Result> { + let mut to_visit = vec![dir.to_path_buf()]; let mut all = vec![PathBuf::from("./")]; while let Some(d) = to_visit.pop() { for entry in std::fs::read_dir(d)? { @@ -1027,7 +1054,7 @@ fn get_sub_dirs(dir: &PathBuf) -> Result> { Ok(all) } -fn view_recipients(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) -> Result<()> { +fn view_recipients(ui: &mut Cursive, store: &PasswordStoreType, config_path: &Path) -> Result<()> { let sub_dirs = get_sub_dirs(&store.lock()?.lock()?.get_store_path()); if let Err(err) = sub_dirs { helpers::errorbox(ui, &err); @@ -1049,10 +1076,10 @@ fn view_recipients(ui: &mut Cursive, store: PasswordStoreType, config_path: &Pat path_to_recipients.insert(dir.clone(), recipients_res?); } - view_recipients_for_many_dirs(ui, store, path_to_recipients, config_path); + view_recipients_for_many_dirs(ui, store, &path_to_recipients, config_path); } std::cmp::Ordering::Equal => { - do_view_recipients_for_dir(ui, store, sub_dirs[0].clone(), config_path); + do_view_recipients_for_dir(ui, store, &sub_dirs[0], config_path); } std::cmp::Ordering::Less => { helpers::errorbox(ui, &pass::Error::Generic("no subdirectories found")); @@ -1062,7 +1089,7 @@ fn view_recipients(ui: &mut Cursive, store: PasswordStoreType, config_path: &Pat Ok(()) } -fn do_view_recipients(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) { +fn do_view_recipients(ui: &mut Cursive, store: &PasswordStoreType, config_path: &Path) { let res = view_recipients(ui, store, config_path); if let Err(err) = res { helpers::errorbox(ui, &err); @@ -1071,15 +1098,15 @@ fn do_view_recipients(ui: &mut Cursive, store: PasswordStoreType, config_path: & fn view_recipients_for_many_dirs( ui: &mut Cursive, - store: PasswordStoreType, - path_to_recipients: HashMap>, + store: &PasswordStoreType, + path_to_recipients: &HashMap>, config_path: &Path, ) { let mut recipients_view = SelectView::>::new() .h_align(cursive::align::HAlign::Left) .with_name("recipients"); - for (path, recipients) in &path_to_recipients { + for (path, recipients) in path_to_recipients { recipients_view .get_mut() .add_item(path.to_string_lossy(), None); @@ -1087,7 +1114,7 @@ fn view_recipients_for_many_dirs( for recipient in recipients { recipients_view.get_mut().add_item( render_recipient_label(recipient, max_width_key, max_width_name), - Some((path.to_path_buf(), recipient.clone())), + Some((path.clone(), recipient.clone())), ); } } @@ -1101,15 +1128,16 @@ fn view_recipients_for_many_dirs( .child(TextView::new(CATALOG.gettext("del: Remove"))), ); + let store = store.clone(); let store2 = store.clone(); let config_path = config_path.to_path_buf(); let recipients_event = OnEventView::new(ll) .on_event(Key::Del, move |ui: &mut Cursive| { - delete_recipient_verification(ui, store.clone()) + delete_recipient_verification(ui, &store); }) .on_event(Key::Ins, move |ui: &mut Cursive| { - add_recipient_dialog(ui, store2.clone(), &config_path) + add_recipient_dialog(ui, &store2, &config_path); }) .on_event(Key::Esc, |s| { s.pop_layer(); @@ -1120,8 +1148,8 @@ fn view_recipients_for_many_dirs( fn do_view_recipients_for_dir( ui: &mut Cursive, - store: PasswordStoreType, - dir: PathBuf, + store: &PasswordStoreType, + dir: &Path, config_path: &Path, ) { let res = view_recipients_for_dir(ui, store, dir, config_path); @@ -1132,11 +1160,11 @@ fn do_view_recipients_for_dir( fn view_recipients_for_dir( ui: &mut Cursive, - store: PasswordStoreType, - dir: PathBuf, + store: &PasswordStoreType, + dir: &Path, config_path: &Path, ) -> Result<()> { - let path = store.lock()?.lock()?.get_store_path().join(dir.clone()); + let path = store.lock()?.lock()?.get_store_path().join(dir); let recipients_res = store.lock()?.lock()?.recipients_for_path(&path); if let Err(err) = recipients_res { @@ -1153,7 +1181,7 @@ fn view_recipients_for_dir( for recipient in recipients { recipients_view.get_mut().add_item( render_recipient_label(&recipient, max_width_key, max_width_name), - Some((dir.clone(), recipient)), + Some((dir.to_path_buf(), recipient)), ); } @@ -1167,15 +1195,16 @@ fn view_recipients_for_dir( .child(TextView::new(CATALOG.gettext("del: Remove"))), ); + let store = store.clone(); let store2 = store.clone(); let config_path = config_path.to_path_buf(); let recipients_event = OnEventView::new(ll) .on_event(Key::Del, move |ui: &mut Cursive| { - delete_recipient_verification(ui, store.clone()) + delete_recipient_verification(ui, &store); }) .on_event(Key::Ins, move |ui: &mut Cursive| { - add_recipient_dialog(ui, store2.clone(), &config_path) + add_recipient_dialog(ui, &store2, &config_path); }) .on_event(Key::Esc, |s| { s.pop_layer(); @@ -1227,7 +1256,7 @@ fn search(store: &PasswordStoreType, ui: &mut Cursive, query: &str) -> Result<() .find_name::>("results") .unwrap(); - let r = pass::search(&*store.lock()?.lock()?, &String::from(query)); + let r = pass::search(&*store.lock()?.lock()?, query); l.clear(); for p in &r { @@ -1249,18 +1278,18 @@ fn help() { println!("{}", CATALOG.gettext("A password manager that uses the file format of the standard unix password manager 'pass', implemented in Rust. Ripasso reads $HOME/.password-store/ by default, override this by setting the PASSWORD_STORE_DIR environmental variable.")); } -fn do_git_push(ui: &mut Cursive, store: PasswordStoreType) { +fn do_git_push(ui: &mut Cursive, store: &PasswordStoreType) { let res = git_push(ui, store); if let Err(err) = res { helpers::errorbox(ui, &err); } } -fn git_push(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn git_push(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let push_result = push(&*store.lock()?.lock()?); match push_result { Err(err) => helpers::errorbox(ui, &err), - Ok(_) => { + Ok(()) => { ui.call_on_name("status_bar", |l: &mut TextView| { l.set_content(CATALOG.gettext("Pushed to remote git repository")); }); @@ -1269,14 +1298,14 @@ fn git_push(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { Ok(()) } -fn do_git_pull(ui: &mut Cursive, store: PasswordStoreType) { +fn do_git_pull(ui: &mut Cursive, store: &PasswordStoreType) { let res = git_pull(ui, store); if let Err(err) = res { helpers::errorbox(ui, &err); } } -fn git_pull(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { +fn git_pull(ui: &mut Cursive, store: &PasswordStoreType) -> Result<()> { let _ = pull(&*store.lock()?.lock()?).map_err(|err| helpers::errorbox(ui, &err)); let _ = store .lock()? @@ -1290,8 +1319,7 @@ fn git_pull(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { "results", |l: &mut SelectView| -> Result<()> { l.clear(); - #[allow(clippy::significant_drop_in_scrutinee)] - for p in store.lock()?.lock()?.passwords.iter() { + for p in &store.lock()?.lock()?.passwords { l.add_item(create_label(p, col), p.clone()); } Ok(()) @@ -1304,7 +1332,7 @@ fn git_pull(ui: &mut Cursive, store: PasswordStoreType) -> Result<()> { Ok(()) } -fn do_gpg_import(ui: &mut Cursive, store: PasswordStoreType, config_path: &Path) -> Result<()> { +fn do_gpg_import(ui: &mut Cursive, store: &PasswordStoreType, config_path: &Path) -> Result<()> { let ta = ui.find_name::