Skip to main content

keepass/db/types/
mod.rs

1pub(crate) mod attachment;
2pub(crate) mod autotype;
3pub(crate) mod color;
4pub(crate) mod custom_data;
5pub(crate) mod entry;
6pub(crate) mod group;
7pub(crate) mod history;
8pub(crate) mod icon;
9pub(crate) mod meta;
10pub(crate) mod times;
11pub(crate) mod value;
12
13use std::collections::HashMap;
14
15pub use attachment::{Attachment, AttachmentId, AttachmentMut, AttachmentRef};
16pub use autotype::{AutoType, AutoTypeAssociation, DataTransferObfuscation};
17pub use color::{Color, ParseColorError};
18pub use custom_data::{CustomDataItem, CustomDataValue};
19pub use entry::{DestinationGroupNotFoundError, Entry, EntryId, EntryMut, EntryRef, EntryTrack};
20pub use group::{
21    DuplicateEntryIdError, DuplicateGroupIdError, Group, GroupId, GroupMut, GroupRef, GroupTrack,
22    MoveGroupError,
23};
24pub use history::History;
25pub use icon::{CustomIcon, CustomIconId, CustomIconMut, CustomIconNotFoundError, CustomIconRef, Icon};
26pub use meta::{MemoryProtection, Meta};
27pub use times::Times;
28pub use value::Value;
29
30use crate::config::DatabaseConfig;
31
32use chrono::NaiveDateTime;
33use uuid::Uuid;
34
35/// A decrypted KeePass database
36#[derive(Debug, Clone, PartialEq, Eq)]
37#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
38pub struct Database {
39    /// Configuration settings of the database such as encryption and compression algorithms
40    pub config: DatabaseConfig,
41
42    /// Metadata of the KeePass database
43    pub meta: Meta,
44
45    /// Root node of the KeePass database
46    pub(crate) root: GroupId,
47
48    /// All attachments in the database, stored in a flat HashMap
49    pub(crate) attachments: HashMap<AttachmentId, Attachment>,
50
51    /// All custom icons in the database, stored in a flat HashMap
52    pub(crate) custom_icons: HashMap<CustomIconId, CustomIcon>,
53
54    /// All entries in the database, stored in a flat HashMap
55    pub(crate) entries: HashMap<EntryId, Entry>,
56
57    /// All groups in the database, stored in a flat HashMap
58    pub(crate) groups: HashMap<GroupId, Group>,
59
60    /// References to previously-deleted objects and their deletion times.
61    pub deleted_objects: HashMap<Uuid, Option<NaiveDateTime>>,
62}
63
64impl Database {
65    /// Create a new database with a single root group and no entries, groups, or attachments.
66    ///
67    /// The root group will be assigned a new random UUID.
68    #[allow(clippy::new_without_default)]
69    pub fn new() -> Self {
70        Self::new_with_root_id(GroupId::new())
71    }
72
73    /// Create a new database with the given configuration and a single root group.
74    ///
75    /// The root group will be assigned a new random UUID.
76    pub fn with_config(config: DatabaseConfig) -> Self {
77        Self::with_data(config, GroupId::new())
78    }
79
80    /// Create a new database with the given group UUID
81    pub fn new_with_root_id(root_id: GroupId) -> Self {
82        let root = Group::with_id(root_id, None);
83
84        let mut groups = HashMap::new();
85        groups.insert(root_id, root);
86
87        Database {
88            config: DatabaseConfig::default(),
89            meta: Meta::default(),
90            root: root_id,
91            attachments: HashMap::new(),
92            custom_icons: HashMap::new(),
93            entries: HashMap::new(),
94            groups,
95            deleted_objects: HashMap::new(),
96        }
97    }
98
99    pub(crate) fn with_data(config: DatabaseConfig, root_id: GroupId) -> Self {
100        let root = Group::with_id(root_id, None);
101
102        let mut groups = HashMap::new();
103        groups.insert(root_id, root);
104
105        Database {
106            config,
107            meta: Meta::default(),
108            root: root_id,
109            attachments: HashMap::new(),
110            custom_icons: HashMap::new(),
111            entries: HashMap::new(),
112            groups,
113            deleted_objects: HashMap::new(),
114        }
115    }
116
117    /// Get an immutable reference to the root group of the database.
118    pub fn root(&self) -> GroupRef<'_> {
119        GroupRef::new(self, self.root)
120    }
121
122    /// Get a mutable reference to the root group of the database.
123    pub fn root_mut(&mut self) -> GroupMut<'_> {
124        GroupMut::new(self, self.root)
125    }
126
127    /// Get an immutable reference to the recycle bin group, if it exists
128    pub fn recycle_bin(&self) -> Option<GroupRef<'_>> {
129        let recyclebin_id = self.meta.recyclebin_uuid.map(GroupId::from_uuid)?;
130        self.group(recyclebin_id)
131    }
132
133    /// Get a mutable reference to the recycle bin group, if it exists
134    pub fn recycle_bin_mut(&mut self) -> Option<GroupMut<'_>> {
135        let recyclebin_id = self.meta.recyclebin_uuid.map(GroupId::from_uuid)?;
136        self.group_mut(recyclebin_id)
137    }
138
139    /// Get the number of attachments in the database
140    pub fn num_attachments(&self) -> usize {
141        self.attachments.len()
142    }
143
144    /// Get the number of custom icons in the database
145    pub fn num_custom_icons(&self) -> usize {
146        self.custom_icons.len()
147    }
148
149    /// Get the number of entries in the database
150    pub fn num_entries(&self) -> usize {
151        self.entries.len()
152    }
153
154    /// Get the number of groups in the database, including the root group and the recycle bin (if it exists)
155    pub fn num_groups(&self) -> usize {
156        self.groups.len()
157    }
158
159    /// Iterate over all attachments with immutable access.
160    pub fn iter_all_attachments(&self) -> impl Iterator<Item = AttachmentRef<'_>> + '_ {
161        self.attachments
162            .keys()
163            .map(move |id| AttachmentRef::new(self, *id))
164    }
165
166    /// Iterate over all attachments with mutable access. The provided closure is
167    /// called for each `AttachmentMut` and borrows are limited to the closure body.
168    pub fn foreach_attachment_mut<F>(&mut self, mut f: F)
169    where
170        F: FnMut(AttachmentMut<'_>),
171    {
172        let ids: Vec<AttachmentId> = self.attachments.keys().copied().collect();
173        for id in ids {
174            f(AttachmentMut::new(self, id));
175        }
176    }
177
178    /// Iterate over all entries with immutable access.
179    pub fn iter_all_entries(&self) -> impl Iterator<Item = EntryRef<'_>> + '_ {
180        self.entries.keys().map(move |id| EntryRef::new(self, *id))
181    }
182
183    /// Iterate over all entries with mutable access. The provided closure is
184    /// called for each `EntryMut` and borrows are limited to the closure body.
185    pub fn foreach_entry_mut<F>(&mut self, mut f: F)
186    where
187        F: FnMut(EntryMut<'_>),
188    {
189        let ids: Vec<EntryId> = self.entries.keys().copied().collect();
190        for id in ids {
191            f(EntryMut::new(self, id));
192        }
193    }
194
195    /// Iterate over all custom icons with immutable access.
196    pub fn iter_all_custom_icons(&self) -> impl Iterator<Item = CustomIconRef<'_>> + '_ {
197        self.custom_icons
198            .keys()
199            .map(move |id| CustomIconRef::new(self, *id))
200    }
201
202    /// Iterate over all custom icons with mutable access. The provided closure is
203    /// called for each `CustomIconMut` and borrows are limited to the closure body.
204    pub fn foreach_custom_icon_mut<F>(&mut self, mut f: F)
205    where
206        F: FnMut(CustomIconMut<'_>),
207    {
208        let ids: Vec<CustomIconId> = self.custom_icons.keys().copied().collect();
209        for id in ids {
210            f(CustomIconMut::new(self, id));
211        }
212    }
213
214    /// Iterate over all groups with immutable access. This includes the root group and the recycle
215    /// bin (if it exists).
216    pub fn iter_all_groups(&self) -> impl Iterator<Item = GroupRef<'_>> + '_ {
217        self.groups.keys().map(move |id| GroupRef::new(self, *id))
218    }
219
220    /// Iterate over all groups with mutable access. The provided closure is
221    /// called for each `GroupMut` and borrows are limited to the closure body.
222    pub fn foreach_group_mut<F>(&mut self, mut f: F)
223    where
224        F: FnMut(GroupMut<'_>),
225    {
226        let ids: Vec<GroupId> = self.groups.keys().copied().collect();
227        for id in ids {
228            f(GroupMut::new(self, id));
229        }
230    }
231
232    /// Get an immutable reference to the attachment with the given ID, if it exists
233    pub fn attachment(&self, id: AttachmentId) -> Option<AttachmentRef<'_>> {
234        self.attachments
235            .contains_key(&id)
236            .then(move || AttachmentRef::new(self, id))
237    }
238
239    /// Get a mutable reference to the attachment with the given ID, if it exists
240    pub fn attachment_mut(&mut self, id: AttachmentId) -> Option<AttachmentMut<'_>> {
241        self.attachments
242            .contains_key(&id)
243            .then(move || AttachmentMut::new(self, id))
244    }
245
246    /// Get an immutable reference to the custom icon with the given ID, if it exists
247    pub fn custom_icon(&self, id: CustomIconId) -> Option<CustomIconRef<'_>> {
248        self.custom_icons
249            .contains_key(&id)
250            .then(move || CustomIconRef::new(self, id))
251    }
252
253    /// Get a mutable reference to the custom icon with the given ID, if it exists
254    pub fn custom_icon_mut(&mut self, id: CustomIconId) -> Option<CustomIconMut<'_>> {
255        self.custom_icons
256            .contains_key(&id)
257            .then(move || CustomIconMut::new(self, id))
258    }
259
260    /// Get an immutable reference to the entry with the given ID, if it exists
261    pub fn entry(&self, id: EntryId) -> Option<EntryRef<'_>> {
262        self.entries
263            .contains_key(&id)
264            .then(move || EntryRef::new(self, id))
265    }
266
267    /// Get a mutable reference to the entry with the given ID, if it exists
268    pub fn entry_mut(&mut self, id: EntryId) -> Option<EntryMut<'_>> {
269        self.entries
270            .contains_key(&id)
271            .then(move || EntryMut::new(self, id))
272    }
273
274    /// Get an immutable reference to the group with the given ID, if it exists
275    pub fn group(&self, id: GroupId) -> Option<GroupRef<'_>> {
276        self.groups
277            .contains_key(&id)
278            .then(move || GroupRef::new(self, id))
279    }
280
281    /// Get a mutable reference to the group with the given ID, if it exists
282    pub fn group_mut(&mut self, id: GroupId) -> Option<GroupMut<'_>> {
283        self.groups
284            .contains_key(&id)
285            .then(move || GroupMut::new(self, id))
286    }
287}