kpdb/types/
entry.rs

1// Copyright (c) 2016-2017 Martijn Rijkeboer <[email protected]>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use chrono::{DateTime, Utc};
10use common;
11use std::collections::HashMap;
12use std::str;
13use super::association::Association;
14use super::binary_key::BinaryKey;
15use super::binary_value::BinaryValue;
16use super::color::Color;
17use super::custom_icon_uuid::CustomIconUuid;
18use super::entry_uuid::EntryUuid;
19use super::icon::Icon;
20use super::obfuscation::Obfuscation;
21use super::string_key::StringKey;
22use super::string_value::StringValue;
23use super::strings_map::StringsMap;
24use super::times::Times;
25
26/// An entry in the database.
27#[derive(Clone, Debug, PartialEq)]
28pub struct Entry {
29    /// Auto-type associations.
30    pub associations: Vec<Association>,
31
32    /// Default auto-type sequence.
33    pub auto_type_def_sequence: String,
34
35    /// Whether auto-type is enabled.
36    pub auto_type_enabled: bool,
37
38    /// The type of obfuscation to use for auto-typing.
39    pub auto_type_obfuscation: Obfuscation,
40
41    /// The background color.
42    pub background_color: Option<Color>,
43
44    /// Map with binaries.
45    pub binaries: HashMap<BinaryKey, BinaryValue>,
46
47    /// The date and time this entry was created.
48    pub creation_time: DateTime<Utc>,
49
50    /// The identifier of this entry's custom icon if any.
51    pub custom_icon_uuid: Option<CustomIconUuid>,
52
53    /// Whether this entry expires.
54    pub expires: bool,
55
56    /// The date and time this entry will expire if expires is true.
57    pub expiry_time: DateTime<Utc>,
58
59    /// The foreground color.
60    pub foreground_color: Option<Color>,
61
62    /// This entry's history.
63    pub history: Vec<Entry>,
64
65    /// This entry's icon.
66    pub icon: Icon,
67
68    /// The date and time this entry was last accessed.
69    pub last_accessed: DateTime<Utc>,
70
71    /// The date and time this entry was last modified.
72    pub last_modified: DateTime<Utc>,
73
74    /// The date and time the location of this entry was changed.
75    pub location_changed: DateTime<Utc>,
76
77    /// Override URL.
78    pub override_url: String,
79
80    /// Map with strings.
81    pub strings: StringsMap,
82
83    /// The tags associated with this entry.
84    pub tags: String,
85
86    /// The usage count of this entry.
87    pub usage_count: i32,
88
89    /// The identifier of this entry.
90    pub uuid: EntryUuid,
91}
92
93impl Entry {
94    /// Create a new entry.
95    pub fn new() -> Entry {
96        let mut entry = Entry::default();
97        entry.uuid = EntryUuid::new_random();
98        entry
99    }
100
101    /// Gets the notes string if any.
102    pub fn notes(&self) -> Option<&str> {
103        self.other(StringKey::Notes)
104    }
105
106    /// Gets an other string if any.
107    pub fn other(&self, key: StringKey) -> Option<&str> {
108        match self.strings.get(&key) {
109            Some(&StringValue::Plain(ref string)) => Some(string),
110            Some(&StringValue::Protected(ref secstr)) => str::from_utf8(secstr.unsecure()).ok(),
111            None => None,
112        }
113    }
114
115    /// Gets the password string if any.
116    pub fn password(&self) -> Option<&str> {
117        self.other(StringKey::Password)
118    }
119
120    /// Sets the notes string value.
121    pub fn set_notes<S: Into<String>>(&mut self, val: S) {
122        self.strings.insert(
123            StringKey::Notes,
124            StringValue::new(
125                val,
126                common::PROTECT_NOTES_DEFAULT,
127            ),
128        );
129    }
130
131    /// Sets an other string value.
132    pub fn set_other<S: Into<String>>(&mut self, key: StringKey, val: S) {
133        self.strings.insert(key, StringValue::new(val, false));
134    }
135
136    /// Sets the password string value.
137    pub fn set_password<S: Into<String>>(&mut self, val: S) {
138        self.strings.insert(
139            StringKey::Password,
140            StringValue::new(
141                val,
142                common::PROTECT_PASSWORD_DEFAULT,
143            ),
144        );
145    }
146
147    /// Sets the title string value.
148    pub fn set_title<S: Into<String>>(&mut self, val: S) {
149        self.strings.insert(
150            StringKey::Title,
151            StringValue::new(
152                val,
153                common::PROTECT_TITLE_DEFAULT,
154            ),
155        );
156    }
157
158    /// Sets the url string value.
159    pub fn set_url<S: Into<String>>(&mut self, val: S) {
160        self.strings.insert(
161            StringKey::Url,
162            StringValue::new(
163                val,
164                common::PROTECT_URL_DEFAULT,
165            ),
166        );
167    }
168
169    /// Sets the username string value.
170    pub fn set_username<S: Into<String>>(&mut self, val: S) {
171        self.strings.insert(
172            StringKey::Username,
173            StringValue::new(
174                val,
175                common::PROTECT_USERNAME_DEFAULT,
176            ),
177        );
178    }
179
180    /// Gets the title string if any.
181    pub fn title(&self) -> Option<&str> {
182        self.other(StringKey::Title)
183    }
184
185    /// Gets the url string if any.
186    pub fn url(&self) -> Option<&str> {
187        self.other(StringKey::Url)
188    }
189
190    /// Gets the username string if any.
191    pub fn username(&self) -> Option<&str> {
192        self.other(StringKey::Username)
193    }
194}
195
196impl Default for Entry {
197    fn default() -> Entry {
198        let now = Utc::now();
199        Entry {
200            associations: Vec::new(),
201            auto_type_def_sequence: String::new(),
202            auto_type_enabled: true,
203            auto_type_obfuscation: Obfuscation::None,
204            background_color: None,
205            binaries: HashMap::new(),
206            creation_time: now,
207            custom_icon_uuid: None,
208            expires: false,
209            expiry_time: now,
210            foreground_color: None,
211            history: Vec::new(),
212            icon: Icon::Key,
213            last_accessed: now,
214            last_modified: now,
215            location_changed: now,
216            override_url: String::new(),
217            strings: StringsMap::new(),
218            tags: String::new(),
219            usage_count: 0,
220            uuid: EntryUuid::nil(),
221        }
222    }
223}
224
225impl Times for Entry {
226    fn creation_time(&self) -> DateTime<Utc> {
227        self.creation_time
228    }
229
230    fn expires(&self) -> bool {
231        self.expires
232    }
233
234    fn expiry_time(&self) -> DateTime<Utc> {
235        self.expiry_time
236    }
237
238    fn last_accessed(&self) -> DateTime<Utc> {
239        self.last_accessed
240    }
241
242    fn last_modified(&self) -> DateTime<Utc> {
243        self.last_modified
244    }
245
246    fn location_changed(&self) -> DateTime<Utc> {
247        self.location_changed
248    }
249
250    fn usage_count(&self) -> i32 {
251        self.usage_count
252    }
253
254    fn set_creation_time(&mut self, val: DateTime<Utc>) {
255        self.creation_time = val;
256    }
257
258    fn set_expires(&mut self, val: bool) {
259        self.expires = val;
260    }
261
262    fn set_expiry_time(&mut self, val: DateTime<Utc>) {
263        self.expiry_time = val;
264    }
265
266    fn set_last_accessed(&mut self, val: DateTime<Utc>) {
267        self.last_accessed = val;
268    }
269
270    fn set_last_modified(&mut self, val: DateTime<Utc>) {
271        self.last_modified = val;
272    }
273
274    fn set_location_changed(&mut self, val: DateTime<Utc>) {
275        self.location_changed = val;
276    }
277
278    fn set_usage_count(&mut self, val: i32) {
279        self.usage_count = val;
280    }
281}
282
283#[cfg(test)]
284mod tests {
285
286    use chrono::Utc;
287    use std::collections::HashMap;
288    use super::*;
289    use types::EntryUuid;
290    use types::Icon;
291    use types::Obfuscation;
292    use types::StringKey;
293    use types::StringsMap;
294    use utils::test::approx_equal_datetime;
295
296    #[test]
297    fn test_new_returns_correct_instance() {
298        let now = Utc::now();
299        let entry = Entry::new();
300        assert_eq!(entry.associations, Vec::new());
301        assert_eq!(entry.auto_type_def_sequence, "");
302        assert_eq!(entry.auto_type_enabled, true);
303        assert_eq!(entry.auto_type_obfuscation, Obfuscation::None);
304        assert_eq!(entry.background_color, None);
305        assert_eq!(entry.binaries, HashMap::new());
306        assert!(approx_equal_datetime(entry.creation_time, now));
307        assert_eq!(entry.custom_icon_uuid, None);
308        assert_eq!(entry.expires, false);
309        assert!(approx_equal_datetime(entry.expiry_time, now));
310        assert_eq!(entry.foreground_color, None);
311        assert_eq!(entry.history, Vec::new());
312        assert_eq!(entry.icon, Icon::Key);
313        assert!(approx_equal_datetime(entry.last_accessed, now));
314        assert!(approx_equal_datetime(entry.last_modified, now));
315        assert!(approx_equal_datetime(entry.location_changed, now));
316        assert_eq!(entry.override_url, "");
317        assert_eq!(entry.strings, StringsMap::new());
318        assert_eq!(entry.tags, "");
319        assert_eq!(entry.usage_count, 0);
320        assert!(entry.uuid != EntryUuid::nil());
321    }
322
323    #[test]
324    fn test_notes_returns_none_on_default_entry() {
325        let entry = Entry::default();
326        assert_eq!(entry.notes(), None);
327    }
328
329    #[test]
330    fn test_other_returns_none_on_default_entry() {
331        let entry = Entry::default();
332        let key = StringKey::from_string("other");
333        assert_eq!(entry.other(key), None);
334    }
335
336    #[test]
337    fn test_password_returns_none_on_default_entry() {
338        let entry = Entry::default();
339        assert_eq!(entry.password(), None);
340    }
341
342    #[test]
343    fn test_set_notes_sets_notes() {
344        let mut entry = Entry::default();
345        entry.set_notes("test");
346        assert_eq!(entry.notes(), Some("test"));
347    }
348
349    #[test]
350    fn test_set_other_sets_other() {
351        let mut entry = Entry::default();
352        let key = StringKey::from_string("other");
353        entry.set_other(key.clone(), "test");
354        assert_eq!(entry.other(key), Some("test"));
355    }
356
357    #[test]
358    fn test_set_password_sets_password() {
359        let mut entry = Entry::default();
360        entry.set_password("test");
361        assert_eq!(entry.password(), Some("test"));
362    }
363
364    #[test]
365    fn test_set_title_sets_title() {
366        let mut entry = Entry::default();
367        entry.set_title("test");
368        assert_eq!(entry.title(), Some("test"));
369    }
370
371    #[test]
372    fn test_set_url_sets_url() {
373        let mut entry = Entry::default();
374        entry.set_url("test");
375        assert_eq!(entry.url(), Some("test"));
376    }
377
378    #[test]
379    fn test_set_username_sets_username() {
380        let mut entry = Entry::default();
381        entry.set_username("test");
382        assert_eq!(entry.username(), Some("test"));
383    }
384
385    #[test]
386    fn test_title_returns_none_on_default_entry() {
387        let entry = Entry::default();
388        assert_eq!(entry.title(), None);
389    }
390
391    #[test]
392    fn test_url_returns_none_on_default_entry() {
393        let entry = Entry::default();
394        assert_eq!(entry.url(), None);
395    }
396
397    #[test]
398    fn test_username_returns_none_on_default_entry() {
399        let entry = Entry::default();
400        assert_eq!(entry.username(), None);
401    }
402
403    #[test]
404    fn test_default_returns_correct_instance() {
405        let now = Utc::now();
406        let entry = Entry::default();
407        assert_eq!(entry.associations, Vec::new());
408        assert_eq!(entry.auto_type_def_sequence, "");
409        assert_eq!(entry.auto_type_enabled, true);
410        assert_eq!(entry.auto_type_obfuscation, Obfuscation::None);
411        assert_eq!(entry.background_color, None);
412        assert_eq!(entry.binaries, HashMap::new());
413        assert!(approx_equal_datetime(entry.creation_time, now));
414        assert_eq!(entry.custom_icon_uuid, None);
415        assert_eq!(entry.expires, false);
416        assert!(approx_equal_datetime(entry.expiry_time, now));
417        assert_eq!(entry.foreground_color, None);
418        assert_eq!(entry.history, Vec::new());
419        assert_eq!(entry.icon, Icon::Key);
420        assert!(approx_equal_datetime(entry.last_accessed, now));
421        assert!(approx_equal_datetime(entry.last_modified, now));
422        assert!(approx_equal_datetime(entry.location_changed, now));
423        assert_eq!(entry.override_url, "");
424        assert_eq!(entry.strings, StringsMap::new());
425        assert_eq!(entry.tags, "");
426        assert_eq!(entry.usage_count, 0);
427        assert_eq!(entry.uuid, EntryUuid::nil());
428    }
429}