diff --git a/data/style.css b/data/style.css index 91ff09e..b71275f 100644 --- a/data/style.css +++ b/data/style.css @@ -21,6 +21,9 @@ button.tiny { padding-right:10px; padding-bottom:0px; } +button.tiny:checked{ + color: white; +} .monospace{ font-family: "source code pro"; color: rgb(200,200,200); diff --git a/src/model/g_site.rs b/src/model/g_site.rs index 70caa49..4e6d12d 100644 --- a/src/model/g_site.rs +++ b/src/model/g_site.rs @@ -117,6 +117,20 @@ impl GSite { let self_ = imp::GSite::from_instance(&self); *self_.siteDescriptor.borrow().siteName.borrow_mut() = name.to_owned(); } + pub fn set_descriptor_version(&self, version: spectre::AlgorithmVersion){ + self.update_descriptor(SiteDescriptor{ + siteName: RefCell::new(self.descriptor().siteName.borrow().to_owned()), + resultType: self.descriptor().resultType, + algorithmVersion: version + }); + } + pub fn set_descriptor_type(&self, p_type: spectre::ResultType){ + self.update_descriptor(SiteDescriptor{ + siteName: RefCell::new(self.descriptor().siteName.borrow().to_owned()), + resultType: p_type, + algorithmVersion: self.descriptor().algorithmVersion + }); + } pub fn descriptor_name(&self) -> String { self.descriptor().siteName.borrow().clone() } @@ -149,12 +163,21 @@ impl GSite { pub fn is_search(&self) -> bool { let is_search = *imp::GSite::from_instance(&self).isSearch.borrow(); is_search - // *self_. } pub fn set_site(&self, new_site : &spectre::Site){ let self_ = imp::GSite::from_instance(&self); self_.site.replace(Some(*new_site)); } + pub fn get_password(&self, key : spectre::UserKey) -> String { + let d = self.descriptor(); + if d.siteName.borrow().len() > 0{ + println!("Generated pwd with version: V{:?}",d.algorithmVersion as i32); + let res = spectre::site_result(&d.siteName.borrow(), key, d.resultType, d.algorithmVersion); + res + }else{ + String::from("") + } + } } diff --git a/src/spectre/mod.rs b/src/spectre/mod.rs index 814be5c..15444f3 100644 --- a/src/spectre/mod.rs +++ b/src/spectre/mod.rs @@ -5,7 +5,7 @@ use std::fmt::Debug; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; -extern crate num; +pub extern crate num; pub type UserKey = spectrebind::SpectreUserKey; impl Debug for UserKey{ @@ -24,7 +24,7 @@ impl Default for UserKey{ } } #[repr(u32)] -#[derive(FromPrimitive, Clone, Copy)] +#[derive(FromPrimitive, Clone, Copy, PartialEq)] pub enum AlgorithmVersion { /** V0 did math with chars whose signedness was platform-dependent. */ V0 = spectrebind::SpectreAlgorithmV0, @@ -39,7 +39,7 @@ pub const AlgorithmVersionDefault: AlgorithmVersion = AlgorithmVersion::V3; pub const AlgorithmVersionLatest: AlgorithmVersion = AlgorithmVersion::V3; #[repr(u32)] -#[derive(FromPrimitive, Clone, Copy)] +#[derive(FromPrimitive, Clone, Copy, PartialEq)] pub enum ResultType { /** 16: pg^VMAUBk5x3p%HP%i4= */ TemplateMaximum = spectrebind::SpectreResultTemplateMaximum, @@ -65,6 +65,50 @@ pub enum ResultType { /** 4160: Derive a unique binary key. */ DeriveKey = spectrebind::SpectreResultDeriveKey, } +impl ResultType { + pub fn iterable() -> Vec { + vec![ResultType::TemplateMaximum, + ResultType::TemplateLong, + ResultType::TemplateMedium, + ResultType::TemplateShort, + ResultType::TemplateBasic, + ResultType::TemplatePIN, + ResultType::TemplateName, + ResultType::TemplatePhrase] + } +} +impl std::str::FromStr for ResultType { + type Err = std::string::ParseError; + fn from_str(s: &str) -> Result { + + match s { + "Maximum" => return Ok(ResultType::TemplateMaximum), + "Long" => return Ok(ResultType::TemplateLong), + "Medium" => return Ok(ResultType::TemplateMedium), + "Short" => return Ok(ResultType::TemplateShort), + "Basic" => return Ok(ResultType::TemplateBasic), + "PIN" => return Ok(ResultType::TemplatePIN), + "Name" => return Ok(ResultType::TemplateName), + "Phrase" => return Ok(ResultType::TemplatePhrase), + default => return Ok(ResultTypeDefault) + } + } +} +impl std::string::ToString for ResultType { + fn to_string(&self) -> String { + match self { + ResultType::TemplateMaximum => return "Maximum".to_owned(), + ResultType::TemplateLong => return "Long".to_owned(), + ResultType::TemplateMedium => return "Medium".to_owned(), + ResultType::TemplateShort => return "Short".to_owned(), + ResultType::TemplateBasic => return "Basic".to_owned(), + ResultType::TemplatePIN => return "PIN".to_owned(), + ResultType::TemplateName => return "Name".to_owned(), + ResultType::TemplatePhrase => return "Phrase".to_owned(), + default => return "".to_owned() + } + } +} pub const ResultTypeDefault: ResultType = ResultType::TemplateLong; pub fn name_for_format(format: u32) -> String { @@ -263,6 +307,9 @@ impl Site { Err(_) => panic!("SystemTime before UNIX EPOCH!"), } } + pub fn last_used(&self) -> i64 { + self.lastUsed as i64 + } pub fn get_algorithm(&self) -> AlgorithmVersion { num::FromPrimitive::from_u32(self.loginType as u32).unwrap() } @@ -368,7 +415,6 @@ impl User { pub fn has_site(&self, site_name: &String) -> bool { for s in self.get_sites() { unsafe { - println!("{:?}",s.as_ref().unwrap().get_name()); if (*s).get_name() == site_name.clone() { return true; } diff --git a/src/ui/password_list_box.rs b/src/ui/password_list_box.rs index e2fc2f3..714ffd4 100644 --- a/src/ui/password_list_box.rs +++ b/src/ui/password_list_box.rs @@ -121,7 +121,9 @@ mod imp { copy_button.set_size_request(120, -1); copy_button.add_css_class("suggested-action"); copy_button.connect_clicked(glib::clone!(@weak obj, @weak copy_button => move |_| { - crate::ui::password_window::helper::copy_to_clipboard_with_notification(©_button, &obj.get_password()); + let self_ = PasswordListBox::from_instance(&obj); + crate::ui::password_window::helper::copy_to_clipboard_with_notification(©_button, &self_.site.borrow().as_ref().unwrap().get_password(*self_.user_key.borrow().as_ref().unwrap())); + })); hbox_bottom.append(©_button); @@ -182,7 +184,6 @@ impl PasswordListBox { // } pub fn set_site(&self, site: &GSite) { let self_ = imp::PasswordListBox::from_instance(&self); - // self_.password_label.borrow().as_ref().unwrap().set_text(spectre::site_result(name, user_key: UserKey, result_type: ResultType, algorithm_version: AlgorithmVersion)); self_.site_label.borrow().as_ref().unwrap().set_text(&site.name()); *self_.site.borrow_mut() = Some(site.clone()); self_ @@ -190,19 +191,25 @@ impl PasswordListBox { .borrow() .as_ref() .unwrap() - .set_text(&self.get_password()); + .set_text(&self_.site.borrow().as_ref().unwrap().get_password(*self_.user_key.borrow().as_ref().unwrap())); } - pub fn get_password(&self) -> String { - let self_ = imp::PasswordListBox::from_instance(&self); + // pub fn get_password(&self) -> String { + // let self_ = imp::PasswordListBox::from_instance(&self); - // TODO remove hardcoded password_type - let password_type: spectre::ResultType = spectre::ResultType::TemplateLong; - spectre::site_result( - &self_.site.borrow().as_ref().unwrap().name(), - *self_.user_key.borrow().as_ref().unwrap(), - password_type, - spectre::AlgorithmVersionDefault, - ) - } + // // TODO remove hardcoded password_type + // let password_type: spectre::ResultType = spectre::ResultType::TemplateLong; + // let site_name = self_.site.borrow().as_ref().unwrap().name(); + // if site_name.len() > 0{ + // spectre::site_result( + // &self_.site.borrow().as_ref().unwrap().name(), + // *self_.user_key.borrow().as_ref().unwrap(), + // password_type, + // spectre::AlgorithmVersionDefault, + // ) + // }else{ + // String::from("") + // } + + // } } diff --git a/src/ui/password_search_box.rs b/src/ui/password_search_box.rs index 2fdd3a9..ca47121 100644 --- a/src/ui/password_search_box.rs +++ b/src/ui/password_search_box.rs @@ -1,5 +1,5 @@ -use crate::spectre; use crate::model::g_site::*; +use crate::spectre; use glib::subclass::Signal; use gtk::glib; use gtk::prelude::*; @@ -8,8 +8,10 @@ use once_cell::sync::Lazy; use std::cell::{RefCell, RefMut}; use std::env; use std::rc::Rc; +use std::str::FromStr; mod imp { use super::*; + use crate::spectre::num::FromPrimitive; // use gtk::subclass::prelude::*; #[derive(Debug, Default)] @@ -24,6 +26,11 @@ mod imp { pub password_show_button: RefCell>, pub hbox_bottom: RefCell>, pub hbox_top: RefCell>, + pub type_combo_box: RefCell>, + // pub version: RefCell + // pub v1_button: RefCell>, + // pub v2_button: RefCell>, + // pub v3_button: RefCell>, } #[glib::object_subclass] @@ -79,16 +86,38 @@ mod imp { let hbox_linked = gtk::Box::new(gtk::Orientation::Horizontal, 0); hbox_linked.add_css_class("linked"); - let password_short_button = gtk::Button::with_label("short"); - password_short_button.add_css_class("tiny"); - let password_normal_button = gtk::Button::with_label("normal"); - password_normal_button.add_css_class("tiny"); - let password_long_button = gtk::Button::with_label("long"); - password_long_button.add_css_class("tiny"); - hbox_linked.append(&password_normal_button); - hbox_linked.append(&password_short_button); - hbox_linked.append(&password_long_button); + hbox_linked.set_valign(Align::Center); + let mut last_button: Option = None; + for i in 0..4 { + let button = gtk::ToggleButton::with_label(&format!("V{}", i)); + button.add_css_class("tiny"); + if let Some(b) = last_button { + button.set_group(Some(&b)); + } + hbox_linked.append(&button); + if spectre::AlgorithmVersion::from_i32(i).unwrap() == spectre::AlgorithmVersionDefault { + button.set_active(true); + } + button.connect_active_notify(glib::clone!(@weak obj => move |button| { + let self_ = PasswordSearchBox::from_instance(&obj); + if button.is_active(){ + if let Some(s) = self_.site.borrow().as_ref() { + let v = spectre::AlgorithmVersion::from_i32(i).unwrap(); + s.set_descriptor_version(v) + } + } + })); + last_button = Some(button); + } hbox_bottom_left_bottom.append(&hbox_linked); + let type_combo_box = gtk::ComboBoxText::new(); + for res_type in spectre::ResultType::iterable(){ + type_combo_box.append(Some(&res_type.to_string()), &res_type.to_string()); + } + let a_id = spectre::ResultTypeDefault.to_string(); + // println!("{}", a_id); + type_combo_box.set_active_id(Some(&a_id)); + hbox_bottom_left_bottom.append(&type_combo_box); let password_show_button = gtk::ToggleButton::with_label("Hidden"); password_show_button.set_halign(gtk::Align::End); password_show_button.add_css_class("tiny"); @@ -111,6 +140,7 @@ mod imp { *self.create_copy_button.borrow_mut() = Some(create_copy_button); *self.password_label.borrow_mut() = Some(password_label); *self.password_show_button.borrow_mut() = Some(password_show_button); + *self.type_combo_box.borrow_mut() = Some(type_combo_box); } fn dispose(&self, _obj: &Self::Type) { @@ -122,12 +152,13 @@ mod imp { child.unparent(); } } - fn signals() -> &'static [Signal] { static SIGNALS: Lazy> = Lazy::new(|| { vec![ Signal::builder("search-changed", &[GSite::static_type().into()], <()>::static_type().into()).build(), Signal::builder("copy-create-activated", &[GSite::static_type().into()], <()>::static_type().into()).build(), + // Signal::builder("version-changed", &[GSite::static_type().into()], <()>::static_type().into()).build(), + // Signal::builder("type-changed", &[GSite::static_type().into()], <()>::static_type().into()).build(), ] }); SIGNALS.as_ref() @@ -180,7 +211,7 @@ impl PasswordSearchBox { } } } - fn calculate_copy_button_mode(&self, ) -> CopyButtonMode { + fn calculate_copy_button_mode(&self) -> CopyButtonMode { let self_ = imp::PasswordSearchBox::from_instance(&self); let user = self_.user.clone(); let entry = self_.site_entry.clone(); @@ -196,13 +227,18 @@ impl PasswordSearchBox { let self_ = imp::PasswordSearchBox::from_instance(&self); let self_clone = self.clone(); let user = self_.user.clone(); + let create_copy_button = self_.create_copy_button.borrow().as_ref().unwrap().clone(); self_.site_entry.borrow().as_ref().unwrap().connect_changed(move |entry| { let self_ = imp::PasswordSearchBox::from_instance(&self_clone); + self_.site.borrow().as_ref().unwrap().set_descriptor_name(&entry.text().to_string()); self_clone.update_password_label(); self_clone.set_copy_button_mode(&self_clone.calculate_copy_button_mode()); - self_.site.borrow().as_ref().unwrap().set_descriptor_name(&entry.text().to_string()); - self_clone.emit_by_name("search-changed", &[&self_.site.borrow().as_ref().unwrap()]).unwrap(); + let site_clone = self_.site.clone(); + { + println!("{:?}", self_.site.try_borrow_mut()); + } + self_clone.emit_by_name("search-changed", &[&site_clone.borrow().as_ref().unwrap()]).unwrap(); }); let self_clone = self.clone(); @@ -215,52 +251,70 @@ impl PasswordSearchBox { let entry = self_.site_entry.borrow().as_ref().unwrap().clone(); // let self_site = self_.site.borrow().as_ref().unwrap().clone(); create_copy_button.connect_clicked(glib::clone!(@weak self as self_clone => move |_| { - // button.clipboard().set_text(&self_clone.get_password_for_label()) let self_ = imp::PasswordSearchBox::from_instance(&self_clone); - self_clone.emit_by_name("copy-create-activated", &[&self_.site.borrow().as_ref().unwrap()]).unwrap(); })); // let self_site = self_.site.borrow().as_ref().unwrap().clone(); let site_entry = self_.site_entry.borrow().as_ref().unwrap().clone(); site_entry.connect_activate(glib::clone!(@weak self as self_clone => move |entry|{ let self_ = imp::PasswordSearchBox::from_instance(&self_clone); - self_clone.emit_by_name("copy-create-activated", &[&self_.site.borrow().as_ref().unwrap()]).unwrap(); + let site_clone = self_.site.clone(); + self_clone.emit_by_name("copy-create-activated", &[&site_clone.borrow().as_ref().unwrap()]).unwrap(); + })); + let type_combo_box = self_.type_combo_box.borrow().as_ref().unwrap().clone(); + type_combo_box.connect_changed(glib::clone!(@weak self as self_clone => move |combo_box| { + let self_ = imp::PasswordSearchBox::from_instance(&self_clone); + self_.site.borrow().as_ref().unwrap().set_descriptor_type(spectre::ResultType::from_str(&combo_box.active_id().unwrap()).ok().unwrap()); + self_clone.update_password_label(); })); // button.connect_clicked(clone!(@weak self as tag => move |_btn| { - // })); } pub fn set_site(&self, site: &GSite) { let self_ = imp::PasswordSearchBox::from_instance(&self); + { + let res = self_.site.try_borrow_mut(); + println!("{:?}", res); + } *self_.site.borrow_mut() = Some(site.clone()); - self_.site_entry.borrow().as_ref().unwrap().set_text(&site.descriptor_name()); + let new_site_name = site.descriptor_name(); + let current_site_name = self_.site_entry.borrow().as_ref().unwrap().text(); + println!("current: {:?}", new_site_name); + println!("new: {:?}", current_site_name); + if current_site_name != new_site_name { + self_.site_entry.borrow().as_ref().unwrap().set_text(&new_site_name); + } self.update_password_label(); } fn update_password_label(&self) { let self_ = imp::PasswordSearchBox::from_instance(&self); let self_clone = self.clone(); let password_label = self_.password_label.borrow().as_ref().unwrap().clone(); - password_label.set_text(self_clone.get_password_for_label().as_str()); - } - pub fn get_password_for_label(&self) -> String { - let self_ = imp::PasswordSearchBox::from_instance(&self); - + + // Get Password for label // TODO remove hardcoded password_type - let password_type: spectre::ResultType = spectre::ResultType::TemplateLong; + // let password_type: spectre::ResultType = spectre::ResultType::TemplateLong; let password_show_button = self_.password_show_button.borrow().as_ref().unwrap().clone(); if password_show_button.is_active() { - return String::from("Hidden"); - } - let site_entry = self_.site_entry.borrow().as_ref().unwrap().clone(); - if site_entry.text().len() > 0 { - spectre::site_result( - site_entry.text().as_str(), - *self_.user_key.borrow().as_ref().unwrap(), - password_type, - spectre::AlgorithmVersionDefault, - ) - } else { - String::from("") + password_label.set_text("Hidden"); + return; } + // let site_entry = self_.site_entry.borrow().as_ref(); + // if site_entry.unwrap().text().len() > 0{ + password_label.set_text(&self_.site.borrow().as_ref().unwrap().get_password(*self_.user_key.borrow().as_ref().unwrap())) + // } + + // let pwd = if site_entry.text().len() > 0 { + // spectre::site_result( + // site_entry.text().as_str(), + // *self_.user_key.borrow().as_ref().unwrap(), + // password_type, + // spectre::AlgorithmVersionDefault, + // ) + // } else { + // String::from("") + // }; + + } } diff --git a/src/ui/password_window/imp.rs b/src/ui/password_window/imp.rs index 31828db..b94b733 100644 --- a/src/ui/password_window/imp.rs +++ b/src/ui/password_window/imp.rs @@ -7,7 +7,7 @@ pub struct PasswordWindow { // pub string_store: gtk::FilterListModel, pub filter_store: gtk::FilterListModel, pub list_view: gtk::ListView, - pub entry_site_name: Option, + pub entry_site: GSite, // pub signal_search_changed: Rc>>, // pub signal_copy_create_activated: Rc>>, } @@ -20,29 +20,28 @@ impl ObjectSubclass for PasswordWindow { fn new() -> Self { Self { - // string_store: gtk::StringList::new(&[]), filter_store: { - let stringx = gtk::PropertyExpression::new(gtk::StringObject::static_type(), gtk::NONE_EXPRESSION, "string"); - let filter = gtk::StringFilter::new(Some(&stringx)); - // filter.set_search(Some("")); - filter.set_match_mode(gtk::StringFilterMatchMode::Substring); - // let filter = gtk::StringFilterBuilder::new() - // .match_mode(gtk::StringFilterMatchMode::Substring) - // .expression(&stringx) - // .search("te") - // .build(); + let custom_sorter = gtk::CustomSorter::new(|a,b| { + let a_site = a.clone().downcast::().ok().unwrap(); + let b_site = b.clone().downcast::().ok().unwrap(); + if a_site.is_search() {return gtk::Ordering::Smaller;} + if b_site.is_search() {return gtk::Ordering::Larger;} + match a_site.site().unwrap().last_used() > b_site.site().unwrap().last_used() { + true => gtk::Ordering::Smaller, + false => gtk::Ordering::Larger + } + }); use gtk::gio; use crate::model::g_site::GSite; let custom_filter = gtk::CustomFilter::new(|_| true); let site_store = gio::ListStore::new(GSite::static_type()); - // site_store.append(GSite(spectre::Site)); - // gtk::FilterListModel::new(Some(>k::StringList::new(&[])), Some(&custom_filter)) - gtk::FilterListModel::new(Some(&site_store), Some(&custom_filter)) + let sort_site_store = gtk::SortListModel::new(Some(&site_store), Some(&custom_sorter)); + gtk::FilterListModel::new(Some(&sort_site_store), Some(&custom_filter)) }, list_view: gtk::ListView::new(Option::<>k::NoSelection>::None, Option::<>k::SignalListItemFactory>::None), - entry_site_name: Option::::None, + entry_site: GSite::new_search(), user: Rc::new(RefCell::new(None)), user_key: Rc::new(RefCell::new(None)), // signal_search_changed: Rc::new(RefCell::new(None)), diff --git a/src/ui/password_window/mod.rs b/src/ui/password_window/mod.rs index 74c7919..42058a8 100644 --- a/src/ui/password_window/mod.rs +++ b/src/ui/password_window/mod.rs @@ -23,7 +23,7 @@ pub mod helper { widget.clipboard().set_text(text); let app = widget.root().unwrap().downcast::().ok().unwrap().application().unwrap(); let noti = gtk::gio::Notification::new("Password copied!"); - noti.set_body(Some("It can be pasted anywhere using ctrl+v.")); + noti.set_body(Some("It can be pasted anywhere using Ctrl+V.")); app.send_notification(Some("copy-notification"), ¬i); } } @@ -115,7 +115,7 @@ impl PasswordWindow { // let store = self_.string_store.model().unwrap().downcast::().ok().unwrap(); let store = self.get_store(); // store.append("___search"); - store.append(&GSite::new_search()); + store.append(&self_.entry_site); for site in site_list.iter().rev() { unsafe { let site_name: String = (**site).get_name(); @@ -133,7 +133,7 @@ impl PasswordWindow { fn get_store(&self) -> gtk::gio::ListStore { let self_ = &imp::PasswordWindow::from_instance(self); let store = &self_.filter_store; - store.model().unwrap().downcast::().unwrap() + store.model().unwrap().downcast::().unwrap().model().unwrap().downcast::().unwrap() } pub fn filter_site_list(&self, filter_str: &str) { @@ -146,7 +146,6 @@ impl PasswordWindow { let s = g_site.site().unwrap().get_name(); s.contains(&f_str) }); - println!("filtering with: {}", filter); } pub fn activate_copy_or_create(&self, site: &GSite) { let self_ = &imp::PasswordWindow::from_instance(self); @@ -155,7 +154,7 @@ impl PasswordWindow { let site_des = site.descriptor(); if usr.borrow().as_ref().unwrap().has_site(&site_des.siteName.borrow()) { - let pwd = spectre::site_result(&site_des.siteName.borrow(), *key.borrow().as_ref().unwrap(), site_des.resultType, site_des.algorithmVersion); + let pwd = site.get_password(*key.borrow().as_ref().unwrap()); helper::copy_to_clipboard_with_notification(&self_.list_view, &pwd); self.hide(); } else {