authenticator/models/
providers.rs1use anyhow::Result;
2use gtk::{gio, glib, prelude::*, subclass::prelude::*};
3
4use super::{Account, Algorithm, Method, OTP, Provider, ProviderPatch};
5
6mod imp {
7 use std::cell::{Cell, RefCell};
8
9 use super::*;
10
11 #[derive(Default)]
12 pub struct ProvidersModel(pub RefCell<Vec<Provider>>, pub Cell<bool>);
13
14 #[glib::object_subclass]
15 impl ObjectSubclass for ProvidersModel {
16 const NAME: &'static str = "ProvidersModel";
17 type Type = super::ProvidersModel;
18 type Interfaces = (gio::ListModel,);
19 }
20 impl ObjectImpl for ProvidersModel {}
21 impl ListModelImpl for ProvidersModel {
22 fn item_type(&self) -> glib::Type {
23 Provider::static_type()
24 }
25 fn n_items(&self) -> u32 {
26 self.0.borrow().len() as u32
27 }
28 fn item(&self, position: u32) -> Option<glib::Object> {
29 self.0
30 .borrow()
31 .get(position as usize)
32 .map(|o| o.clone().upcast::<glib::Object>())
33 }
34 }
35}
36
37glib::wrapper! {
38 pub struct ProvidersModel(ObjectSubclass<imp::ProvidersModel>)
39 @implements gio::ListModel;
40}
41
42impl ProvidersModel {
43 #[allow(clippy::too_many_arguments)]
44 pub fn find_or_create(
45 &self,
46 name: &str,
47 period: Option<u32>,
48 method: Method,
49 website: Option<String>,
50 algorithm: Algorithm,
51 digits: Option<u32>,
52 default_counter: Option<u32>,
53 help_url: Option<String>,
54 image_uri: Option<String>,
55 ) -> Result<Provider> {
56 let provider = match self.find_by_name(name) {
57 Some(p) => {
58 p.update(&ProviderPatch {
62 name: name.to_owned(),
63 website,
64 help_url,
65 image_uri,
66 period: period.unwrap_or_else(|| p.period()) as i32,
67 digits: digits.unwrap_or_else(|| p.digits()) as i32,
68 default_counter: default_counter.unwrap_or_else(|| p.default_counter()) as i32,
69 algorithm: algorithm.to_string(),
70 method: method.to_string(),
71 is_backup_restore: true,
72 })?;
73 p
74 }
75 None => {
76 let p = Provider::create(
77 name,
78 period.unwrap_or(OTP::DEFAULT_PERIOD),
79 algorithm,
80 website,
81 method,
82 digits.unwrap_or(OTP::DEFAULT_DIGITS),
83 default_counter.unwrap_or(OTP::DEFAULT_COUNTER),
84 help_url,
85 image_uri,
86 )?;
87 self.append(&p);
88 p
89 }
90 };
91 Ok(provider)
92 }
93
94 fn find_by_name(&self, name: &str) -> Option<Provider> {
95 for pos in 0..self.n_items() {
96 let provider = self.item(pos).and_downcast::<Provider>().unwrap();
97 if provider.name() == name {
98 return Some(provider);
99 }
100 }
101 None
102 }
103
104 pub fn find_by_id(&self, id: u32) -> Option<Provider> {
105 for pos in 0..self.n_items() {
106 let provider = self.item(pos).and_downcast::<Provider>().unwrap();
107 if provider.id() == id {
108 return Some(provider);
109 }
110 }
111 None
112 }
113
114 pub fn has_providers(&self) -> bool {
115 let mut found = false;
116 for pos in 0..self.n_items() {
117 let provider = self.item(pos).and_downcast::<Provider>().unwrap();
118 if provider.has_accounts() {
119 found = true;
120 break;
121 }
122 }
123 found
124 }
125
126 #[allow(deprecated)]
127 pub fn completion_model(&self) -> gtk::ListStore {
128 let store = gtk::ListStore::new(&[u32::static_type(), String::static_type()]);
129 for pos in 0..self.n_items() {
130 let obj = self.item(pos).unwrap();
131 let provider = obj.downcast_ref::<Provider>().unwrap();
132 store.set(
133 &store.append(),
134 &[(0, &provider.id()), (1, &provider.name())],
135 );
136 }
137 store
138 }
139
140 pub fn append(&self, provider: &Provider) {
141 let pos = {
142 let mut data = self.imp().0.borrow_mut();
143 data.push(provider.clone());
144 (data.len() - 1) as u32
145 };
146 self.items_changed(pos, 0, 1);
147 }
148
149 fn splice(&self, providers: &[Provider]) {
150 let len = providers.len();
151 let pos = {
152 let mut data = self.imp().0.borrow_mut();
153 let pos = data.len();
154 data.extend_from_slice(providers);
155 pos as u32
156 };
157 self.items_changed(pos, 0, len as u32);
158 }
159
160 pub fn delete_provider(&self, provider: &Provider) {
161 let mut provider_pos = None;
162 for pos in 0..self.n_items() {
163 let p = self.item(pos).and_downcast::<Provider>().unwrap();
164 if p.id() == provider.id() {
165 provider_pos = Some(pos);
166 break;
167 }
168 }
169 if let Some(pos) = provider_pos {
170 {
171 let mut data = self.imp().0.borrow_mut();
172 data.remove(pos as usize);
173 }
174 self.items_changed(pos, 1, 0);
175 }
176 }
177
178 pub fn add_account(&self, account: &Account, provider: &Provider) {
179 let mut found = false;
180 for pos in 0..self.n_items() {
181 let obj = self.item(pos).unwrap();
182 let p = obj.downcast_ref::<Provider>().unwrap();
183 if p.id() == provider.id() {
184 found = true;
185 p.add_account(account);
186 break;
187 }
188 }
189 if !found {
190 provider.add_account(account);
191 self.append(provider);
192 }
193 }
194
195 pub fn find_accounts(&self, terms: &[String]) -> Vec<Account> {
196 let mut results = vec![];
197
198 for pos in 0..self.n_items() {
199 let obj = self.item(pos).unwrap();
200 let provider = obj.downcast_ref::<Provider>().unwrap();
201 let accounts = provider.find_accounts(terms);
202 results.extend(accounts);
203 }
204 results
205 }
206
207 pub fn is_loaded(&self) -> bool {
209 self.imp().1.get()
210 }
211
212 pub fn load(&self) {
213 if self.is_loaded() {
214 return;
215 }
216 tracing::info!("Loading providers");
217 let providers = Provider::load()
219 .expect("Failed to load providers from the database")
220 .collect::<Vec<_>>();
221 self.splice(&providers);
222 self.imp().1.set(true);
223 }
224}
225
226impl Default for ProvidersModel {
227 fn default() -> Self {
228 glib::Object::new()
229 }
230}