1use chrono::{NaiveDateTime, Timelike};
64use std::borrow::Cow;
65use std::ops::{Index, IndexMut};
66use uuid::Uuid;
67
68#[doc(hidden)]
69pub fn doc_sample_db() -> Database {
70 let mut database = Database::default();
71
72 let mut root_entry = Entry::default();
73 root_entry.set_title("Foo");
74 root_entry.set_url("http://example.com");
75 root_entry.set_password("password1");
76
77 database.add_entry(root_entry);
78
79 let child_group = Group::new("Child Group");
80 database.add_group(child_group);
81
82 let mut child_entry = Entry::default();
83 child_entry.set_title("Bar");
84 child_entry.set_url("http://example.com");
85 child_entry.set_password("password2");
86 database
87 .find_group_mut(|g: &Group| g.name == "Child Group")
88 .unwrap()
89 .add_entry(child_entry);
90
91 database
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
96pub(crate) enum Value {
97 Protected(String),
99 Standard(String),
101 Empty,
103 ProtectEmpty,
105}
106
107impl Default for Value {
108 fn default() -> Value {
109 Value::Empty
110 }
111}
112
113#[derive(Debug, Default, Clone, PartialEq, Eq)]
114pub struct Field {
116 pub(crate) key: String,
118 pub(crate) value: Value,
120}
121
122impl Field {
123 pub fn new(key: &str, value: &str) -> Field {
125 Field {
126 key: key.to_string(),
127 value: Value::Standard(value.to_string()),
128 }
129 }
130
131 pub fn new_protected(key: &str, value: &str) -> Field {
133 Field {
134 key: key.to_string(),
135 value: Value::Protected(value.to_string()),
136 }
137 }
138
139 pub fn key(&self) -> &str {
141 &self.key
142 }
143
144 pub fn set_key(&mut self, new_key: &str) {
146 self.key = new_key.to_string();
147 }
148
149 pub fn value(&self) -> Option<&str> {
151 match self.value {
152 Value::Protected(ref s) => Some(s),
153 Value::Standard(ref s) => Some(s),
154 _ => None,
155 }
156 }
157
158 pub fn set_value(&mut self, value: &str) {
160 if self.protected() {
161 self.value = Value::Protected(value.to_string());
162 } else {
163 self.value = Value::Standard(value.to_string());
164 }
165 }
166
167 pub fn clear(&mut self) {
169 if self.protected() {
170 self.value = Value::ProtectEmpty;
171 } else {
172 self.value = Value::Empty;
173 }
174 }
175
176 pub fn protected(&self) -> bool {
181 matches!(self.value, Value::Protected(_))
182 }
183
184 pub fn set_protected(&mut self, protected: bool) {
189 let existing_value = std::mem::take(&mut self.value);
190 self.value = match (protected, existing_value) {
191 (true, Value::Standard(s)) => Value::Protected(s),
192 (false, Value::Protected(s)) => Value::Standard(s),
193 (true, Value::Empty) => Value::ProtectEmpty,
194 (false, Value::ProtectEmpty) => Value::Empty,
195 (_, v) => v,
196 }
197 }
198}
199
200#[derive(Default, Debug, Clone, PartialEq, Eq)]
202pub struct History {
203 entries: Vec<Entry>,
204}
205
206impl History {
207 pub fn get(&self, index: usize) -> Option<&Entry> {
209 self.entries.get(index)
210 }
211
212 pub fn get_mut(&mut self, index: usize) -> Option<&mut Entry> {
214 self.entries.get_mut(index)
215 }
216
217 pub fn push(&mut self, entry: Entry) {
219 self.entries.push(entry);
220 }
221
222 pub fn len(&self) -> usize {
224 self.entries.len()
225 }
226
227 pub fn is_empty(&self) -> bool {
229 self.len() == 0
230 }
231
232 pub fn remove(&mut self, idx: usize) -> Entry {
234 self.entries.remove(idx)
235 }
236
237 pub fn entries(&self) -> impl Iterator<Item = &Entry> {
239 self.entries.iter()
240 }
241
242 pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut Entry> {
244 self.entries.iter_mut()
245 }
246}
247
248impl Index<usize> for History {
249 type Output = Entry;
250 fn index(&self, index: usize) -> &Self::Output {
251 self.get(index).unwrap()
252 }
253}
254
255impl IndexMut<usize> for History {
256 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
257 self.get_mut(index).unwrap()
258 }
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct Entry {
264 uuid: Uuid,
266 fields: Vec<Field>,
268 pub(crate) history: History,
270 pub(crate) times: Times,
272}
273
274impl Entry {
275 pub fn add_field(&mut self, field: Field) {
277 self.fields.push(field);
278 }
279
280 pub fn remove_field(&mut self, key: &str) {
284 let mut matching_field_indices: Vec<_> = self
285 .fields
286 .iter()
287 .enumerate()
288 .filter_map(|(idx, field)| if field.key == key { Some(idx) } else { None })
289 .collect();
290 matching_field_indices.sort();
291 matching_field_indices.reverse();
292 for index in matching_field_indices {
293 self.fields.remove(index);
294 }
295 }
296
297 pub fn new_version(&mut self) {
299 let mut new_entry = self.clone();
300 new_entry.history = History::default();
301 self.history.push(new_entry);
302 }
303
304 pub fn fields(&self) -> impl Iterator<Item = &Field> {
306 self.fields.iter()
307 }
308
309 pub fn fields_mut(&mut self) -> impl Iterator<Item = &mut Field> {
311 self.fields.iter_mut()
312 }
313
314 pub fn history(&self) -> &History {
316 &self.history
317 }
318
319 pub fn history_mut(&mut self) -> &mut History {
321 &mut self.history
322 }
323
324 pub fn find(&self, key: &str) -> Option<&Field> {
326 self.fields.iter().find(|i| i.key.as_str() == key)
327 }
328
329 pub fn find_mut(&mut self, key: &str) -> Option<&mut Field> {
331 self.fields.iter_mut().find(|i| i.key.as_str() == key)
332 }
333
334 pub fn times(&self) -> &Times {
336 &self.times
337 }
338
339 pub fn times_mut(&mut self) -> &mut Times {
341 &mut self.times
342 }
343
344 fn find_string_value(&self, key: &str) -> Option<&str> {
345 self.find(key).and_then(|f| f.value())
346 }
347
348 pub fn uuid(&self) -> Uuid {
350 self.uuid
351 }
352
353 pub fn set_uuid(&mut self, uuid: Uuid) {
355 self.uuid = uuid;
356 }
357
358 pub fn title(&self) -> Option<&str> {
360 self.find_string_value("Title")
361 }
362
363 pub fn set_title<S: ToString>(&mut self, title: S) {
365 let title = title.to_string();
366 match self.find_mut("Title") {
367 Some(f) => f.value = Value::Standard(title),
368 None => self.fields.push(Field::new("Title", &title)),
369 }
370 }
371
372 pub fn username(&self) -> Option<&str> {
374 self.find_string_value("UserName")
375 }
376
377 pub fn set_username<S: ToString>(&mut self, username: S) {
379 let username = username.to_string();
380 match self.find_mut("UserName") {
381 Some(f) => f.value = Value::Standard(username),
382 None => self.fields.push(Field::new("UserName", &username)),
383 }
384 }
385
386 pub fn url(&self) -> Option<&str> {
388 self.find_string_value("URL")
389 }
390
391 pub fn set_url<S: ToString>(&mut self, url: S) {
393 let url = url.to_string();
394 match self.find_mut("URL") {
395 Some(f) => f.value = Value::Standard(url),
396 None => self.fields.push(Field::new("URL", &url)),
397 }
398 }
399
400 pub fn otp(&self) -> Option<Otp> {
402 self.find_string_value("otp").map(|url| Otp {
403 url: Cow::Borrowed(url),
404 })
405 }
406
407 pub fn set_otp(&mut self, otp: Otp) {
409 match self.find_mut("otp") {
410 Some(f) => f.value = Value::Protected(otp.url.to_string()),
411 None => self
412 .fields
413 .push(Field::new_protected("otp", otp.url.as_ref())),
414 }
415 }
416
417 pub fn password(&self) -> Option<&str> {
419 self.find_string_value("Password")
420 }
421
422 pub fn set_password<S: ToString>(&mut self, password: S) {
424 let password = password.to_string();
425 match self.find_mut("Password") {
426 Some(f) => f.value = Value::Protected(password),
427 None => self
428 .fields
429 .push(Field::new_protected("Password", &password)),
430 }
431 }
432}
433
434impl Default for Entry {
435 fn default() -> Entry {
436 Entry {
437 uuid: Uuid::new_v4(),
438 fields: Vec::new(),
439 history: History::default(),
440 times: Times::default(),
441 }
442 }
443}
444
445#[derive(Debug, Clone, PartialEq, Eq)]
446pub struct Group {
448 uuid: Uuid,
450 name: String,
452 entries: Vec<Entry>,
454 groups: Vec<Group>,
456 pub(crate) times: Times,
458}
459
460impl Group {
461 pub fn new<S: ToString>(name: S) -> Group {
463 Group {
464 uuid: Uuid::new_v4(),
465 name: name.to_string(),
466 entries: Vec::new(),
467 groups: Vec::new(),
468 times: Times::default(),
469 }
470 }
471
472 pub fn uuid(&self) -> Uuid {
474 self.uuid
475 }
476
477 pub fn set_uuid(&mut self, uuid: Uuid) {
479 self.uuid = uuid
480 }
481
482 pub fn name(&self) -> &str {
484 &self.name
485 }
486
487 pub fn set_name<S: ToString>(&mut self, name: S) {
489 self.name = name.to_string();
490 }
491
492 pub fn add_entry(&mut self, entry: Entry) {
494 self.entries.push(entry);
495 }
496
497 pub fn remove_entry(&mut self, uuid: Uuid) -> Option<Entry> {
502 let index = self
503 .entries
504 .iter()
505 .enumerate()
506 .find(|(_, entry)| entry.uuid() == uuid)
507 .map(|(index, _)| index);
508
509 if let Some(index) = index {
510 Some(self.entries.remove(index))
511 } else {
512 None
513 }
514 }
515
516 pub fn add_group(&mut self, group: Group) {
518 self.groups.push(group);
519 }
520
521 pub fn remove_group(&mut self, uuid: Uuid) -> Option<Group> {
526 let index = self
527 .groups
528 .iter()
529 .enumerate()
530 .find(|(_, group)| group.uuid() == uuid)
531 .map(|(index, _)| index);
532
533 if let Some(index) = index {
534 Some(self.groups.remove(index))
535 } else {
536 None
537 }
538 }
539
540 pub fn groups(&self) -> impl Iterator<Item = &Group> {
542 self.groups.iter()
543 }
544
545 pub fn groups_mut(&mut self) -> impl Iterator<Item = &mut Group> {
547 self.groups.iter_mut()
548 }
549
550 pub fn group_count(&self) -> usize {
552 self.groups.len()
553 }
554
555 pub fn entry_count(&self) -> usize {
557 self.entries.len()
558 }
559
560 pub fn entries(&self) -> impl Iterator<Item = &Entry> {
562 self.entries.iter()
563 }
564
565 pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut Entry> {
567 self.entries.iter_mut()
568 }
569
570 pub fn recursive_entries<'a>(&'a self) -> Box<dyn Iterator<Item = &Entry> + 'a> {
572 Box::new(
573 self.groups
574 .iter()
575 .flat_map(|c| c.recursive_entries())
576 .chain(self.entries.iter()),
577 )
578 }
579
580 pub fn recursive_entries_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &mut Entry> + 'a> {
582 Box::new(
583 self.groups
584 .iter_mut()
585 .flat_map(|c| c.recursive_entries_mut())
586 .chain(self.entries.iter_mut()),
587 )
588 }
589
590 pub fn recursive_groups<'a>(&'a self) -> Box<dyn Iterator<Item = &Group> + 'a> {
592 Box::new(
593 self.groups
594 .iter()
595 .flat_map(|g| g.recursive_groups())
596 .chain(self.groups.iter()),
597 )
598 }
599
600 pub fn find_group<F: FnMut(&Group) -> bool>(&self, mut f: F) -> Option<&Group> {
602 self.find_group_internal(&mut f)
603 }
604
605 fn find_group_internal<F: FnMut(&Group) -> bool>(&self, f: &mut F) -> Option<&Group> {
606 for group in self.groups() {
607 if f(group) {
608 return Some(group);
609 } else if let Some(g) = group.find_group_internal(f) {
610 return Some(g);
611 }
612 }
613 None
614 }
615
616 pub fn find_group_mut<F: FnMut(&Group) -> bool>(&mut self, mut f: F) -> Option<&mut Group> {
618 self.find_group_mut_internal(&mut f)
619 }
620
621 fn find_group_mut_internal<F: FnMut(&Group) -> bool>(
622 &mut self,
623 f: &mut F,
624 ) -> Option<&mut Group> {
625 for group in self.groups_mut() {
626 if f(group) {
627 return Some(group);
628 } else if let Some(g) = group.find_group_mut_internal(f) {
629 return Some(g);
630 }
631 }
632 None
633 }
634
635 pub fn find_entry<F: FnMut(&Entry) -> bool>(&self, mut f: F) -> Option<&Entry> {
637 self.find_entry_internal(&mut f)
638 }
639
640 fn find_entry_internal<F: FnMut(&Entry) -> bool>(&self, f: &mut F) -> Option<&Entry> {
641 for entry in self.entries() {
642 if f(entry) {
643 return Some(entry);
644 }
645 }
646 for group in self.groups() {
647 if let Some(e) = group.find_entry_internal(f) {
648 return Some(e);
649 }
650 }
651 None
652 }
653
654 pub fn find_entry_mut<F: FnMut(&Entry) -> bool>(&mut self, mut f: F) -> Option<&mut Entry> {
656 self.find_entry_mut_internal(&mut f)
657 }
658
659 fn find_entry_mut_internal<F: FnMut(&Entry) -> bool>(
660 &mut self,
661 f: &mut F,
662 ) -> Option<&mut Entry> {
663 let found_in_entries = self
664 .entries()
665 .enumerate()
666 .find(|(_, e)| f(e))
667 .map(|(idx, _)| idx);
668
669 if let Some(idx) = found_in_entries {
670 return Some(&mut self.entries[idx]);
671 } else {
672 for group in self.groups_mut() {
673 if let Some(e) = group.find_entry_mut_internal(f) {
674 return Some(e);
675 }
676 }
677 }
678 None
679 }
680
681 pub fn times(&self) -> &Times {
683 &self.times
684 }
685
686 pub fn times_mut(&mut self) -> &mut Times {
688 &mut self.times
689 }
690}
691
692impl Default for Group {
693 fn default() -> Group {
694 Group {
695 uuid: Uuid::new_v4(),
696 name: String::new(),
697 entries: Vec::new(),
698 groups: Vec::new(),
699 times: Times::default(),
700 }
701 }
702}
703
704#[derive(Debug, Default, Clone, PartialEq, Eq)]
705pub struct MemoryProtection {
707 pub protect_title: bool,
709 pub protect_user_name: bool,
711 pub protect_password: bool,
713 pub protect_url: bool,
715 pub protect_notes: bool,
717}
718
719#[derive(Debug, Default, Clone, PartialEq, Eq)]
720pub struct Meta {
722 pub generator: String,
724 pub database_name: String,
726 pub database_description: String,
728 pub custom_data: Vec<Field>,
730 pub memory_protection: MemoryProtection,
732}
733
734#[derive(Debug, Clone, PartialEq, Eq)]
735pub struct Times {
737 pub last_modification_time: NaiveDateTime,
739 pub creation_time: NaiveDateTime,
741 pub last_access_time: NaiveDateTime,
743 pub expiry_time: NaiveDateTime,
745 pub location_changed: NaiveDateTime,
747 pub expires: bool,
749 pub usage_count: u32,
751}
752
753impl Default for Times {
754 fn default() -> Times {
755 let now = chrono::Local::now()
756 .naive_local()
757 .with_nanosecond(0)
758 .unwrap();
759 Times {
760 expires: false,
761 usage_count: 0,
762 last_modification_time: now,
763 creation_time: now,
764 last_access_time: now,
765 expiry_time: now,
766 location_changed: now,
767 }
768 }
769}
770
771#[derive(Debug, Clone, PartialEq, Eq)]
772pub struct Database {
776 pub(crate) meta: Meta,
778 pub(crate) groups: Vec<Group>,
780}
781
782impl Default for Database {
783 fn default() -> Self {
784 let root = Group::new("Root");
785 Database {
786 meta: Meta::default(),
787 groups: vec![root],
788 }
789 }
790}
791
792impl Database {
793 pub fn meta(&self) -> &Meta {
795 &self.meta
796 }
797
798 pub fn meta_mut(&mut self) -> &mut Meta {
800 &mut self.meta
801 }
802
803 pub fn name(&self) -> &str {
805 &self.meta.database_name
806 }
807
808 pub fn set_name<S: ToString>(&mut self, name: S) {
810 self.meta.database_name = name.to_string();
811 }
812
813 pub fn description(&self) -> &str {
815 &self.meta.database_description
816 }
817
818 pub fn set_description<S: ToString>(&mut self, desc: S) {
820 self.meta.database_description = desc.to_string();
821 }
822
823 pub fn add_entry(&mut self, entry: Entry) {
825 self.groups[0].entries.push(entry);
826 }
827
828 pub fn add_group(&mut self, entry: Group) {
830 self.groups[0].groups.push(entry);
831 }
832
833 pub fn replace_root(&mut self, group: Group) {
835 self.groups = vec![group];
836 }
837
838 pub fn find_group<F: FnMut(&Group) -> bool>(&self, f: F) -> Option<&Group> {
840 self.root().find_group(f)
841 }
842
843 pub fn find_group_mut<F: FnMut(&Group) -> bool>(&mut self, f: F) -> Option<&mut Group> {
845 self.root_mut().find_group_mut(f)
846 }
847
848 pub fn find_entry<F: FnMut(&Entry) -> bool>(&self, f: F) -> Option<&Entry> {
850 self.root().find_entry(f)
851 }
852
853 pub fn find_entry_mut<F: FnMut(&Entry) -> bool>(&mut self, f: F) -> Option<&mut Entry> {
855 self.root_mut().find_entry_mut(f)
856 }
857
858 pub fn root(&self) -> &Group {
860 &self.groups[0]
861 }
862
863 pub fn root_mut(&mut self) -> &mut Group {
865 &mut self.groups[0]
866 }
867}
868
869pub struct Otp<'a> {
871 url: Cow<'a, str>,
872}
873
874impl<'a> Otp<'a> {
875 pub fn new<S: ToString>(secret: S, period: u32, digits: u32) -> Otp<'static> {
877 let url = format!(
878 "otpauth://totp/kdbxrs:kdbxrs?secret={}&period={}&digits={}",
879 secret.to_string(),
880 period,
881 digits
882 );
883 Otp {
884 url: Cow::Owned(url),
885 }
886 }
887
888 fn find_url_param(&self, key: &str) -> Option<&str> {
889 let mut parts = self.url.split('?');
890 let _path = parts.next()?;
891 let params = parts.next()?;
892 let params = params.split('&');
893
894 for param in params {
895 let mut param_parts = param.split('=');
896 let pkey = param_parts.next()?;
897 if pkey == key {
898 return param_parts.next();
899 }
900 }
901 None
902 }
903
904 pub fn secret(&self) -> Option<&str> {
906 self.find_url_param("secret")
907 }
908
909 pub fn period(&self) -> Option<u32> {
911 self.find_url_param("secret").and_then(|p| p.parse().ok())
912 }
913
914 pub fn digits(&self) -> Option<u32> {
916 self.find_url_param("digits").and_then(|p| p.parse().ok())
917 }
918}