1use crate::prelude::*;
2
3use std::io::{Read as _, Write as _};
4
5use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
6
7#[derive(
8 serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
9)]
10pub struct Entry {
11 pub id: String,
12 pub org_id: Option<String>,
13 pub folder: Option<String>,
14 pub folder_id: Option<String>,
15 pub name: String,
16 pub data: EntryData,
17 pub fields: Vec<Field>,
18 pub notes: Option<String>,
19 pub history: Vec<HistoryEntry>,
20 pub key: Option<String>,
21 pub master_password_reprompt: crate::api::CipherRepromptType,
22}
23
24impl Entry {
25 pub fn master_password_reprompt(&self) -> bool {
26 self.master_password_reprompt != crate::api::CipherRepromptType::None
27 }
28}
29
30#[derive(serde::Serialize, Debug, Clone, Eq, PartialEq)]
31pub struct Uri {
32 pub uri: String,
33 pub match_type: Option<crate::api::UriMatchType>,
34}
35
36impl<'de> serde::Deserialize<'de> for Uri {
38 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
39 where
40 D: serde::Deserializer<'de>,
41 {
42 struct StringOrUri;
43 impl<'de> serde::de::Visitor<'de> for StringOrUri {
44 type Value = Uri;
45
46 fn expecting(
47 &self,
48 formatter: &mut std::fmt::Formatter,
49 ) -> std::fmt::Result {
50 formatter.write_str("uri")
51 }
52
53 fn visit_str<E>(
54 self,
55 value: &str,
56 ) -> std::result::Result<Self::Value, E>
57 where
58 E: serde::de::Error,
59 {
60 Ok(Uri {
61 uri: value.to_string(),
62 match_type: None,
63 })
64 }
65
66 fn visit_map<M>(
67 self,
68 mut map: M,
69 ) -> std::result::Result<Self::Value, M::Error>
70 where
71 M: serde::de::MapAccess<'de>,
72 {
73 let mut uri = None;
74 let mut match_type = None;
75 while let Some(key) = map.next_key()? {
76 match key {
77 "uri" => {
78 if uri.is_some() {
79 return Err(
80 serde::de::Error::duplicate_field("uri"),
81 );
82 }
83 uri = Some(map.next_value()?);
84 }
85 "match_type" => {
86 if match_type.is_some() {
87 return Err(
88 serde::de::Error::duplicate_field(
89 "match_type",
90 ),
91 );
92 }
93 match_type = map.next_value()?;
94 }
95 _ => {
96 return Err(serde::de::Error::unknown_field(
97 key,
98 &["uri", "match_type"],
99 ))
100 }
101 }
102 }
103
104 uri.map_or_else(
105 || Err(serde::de::Error::missing_field("uri")),
106 |uri| Ok(Self::Value { uri, match_type }),
107 )
108 }
109 }
110
111 deserializer.deserialize_any(StringOrUri)
112 }
113}
114
115#[derive(
116 serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
117)]
118pub enum EntryData {
119 Login {
120 username: Option<String>,
121 password: Option<String>,
122 totp: Option<String>,
123 uris: Vec<Uri>,
124 },
125 Card {
126 cardholder_name: Option<String>,
127 number: Option<String>,
128 brand: Option<String>,
129 exp_month: Option<String>,
130 exp_year: Option<String>,
131 code: Option<String>,
132 },
133 Identity {
134 title: Option<String>,
135 first_name: Option<String>,
136 middle_name: Option<String>,
137 last_name: Option<String>,
138 address1: Option<String>,
139 address2: Option<String>,
140 address3: Option<String>,
141 city: Option<String>,
142 state: Option<String>,
143 postal_code: Option<String>,
144 country: Option<String>,
145 phone: Option<String>,
146 email: Option<String>,
147 ssn: Option<String>,
148 license_number: Option<String>,
149 passport_number: Option<String>,
150 username: Option<String>,
151 },
152 SecureNote,
153 SshKey {
154 private_key: Option<String>,
155 public_key: Option<String>,
156 fingerprint: Option<String>,
157 },
158}
159
160#[derive(
161 serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
162)]
163pub struct Field {
164 pub ty: Option<crate::api::FieldType>,
165 pub name: Option<String>,
166 pub value: Option<String>,
167 pub linked_id: Option<crate::api::LinkedIdType>,
168}
169
170#[derive(
171 serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
172)]
173pub struct HistoryEntry {
174 pub last_used_date: String,
175 pub password: String,
176}
177
178#[derive(serde::Serialize, serde::Deserialize, Default, Debug)]
179pub struct Db {
180 pub access_token: Option<String>,
181 pub refresh_token: Option<String>,
182
183 pub kdf: Option<crate::api::KdfType>,
184 pub iterations: Option<u32>,
185 pub memory: Option<u32>,
186 pub parallelism: Option<u32>,
187 pub protected_key: Option<String>,
188 pub protected_private_key: Option<String>,
189 pub protected_org_keys: std::collections::HashMap<String, String>,
190
191 pub entries: Vec<Entry>,
192}
193
194impl Db {
195 pub fn new() -> Self {
196 Self::default()
197 }
198
199 pub fn load(server: &str, email: &str) -> Result<Self> {
200 let file = crate::dirs::db_file(server, email);
201 let mut fh =
202 std::fs::File::open(&file).map_err(|source| Error::LoadDb {
203 source,
204 file: file.clone(),
205 })?;
206 let mut json = String::new();
207 fh.read_to_string(&mut json)
208 .map_err(|source| Error::LoadDb {
209 source,
210 file: file.clone(),
211 })?;
212 let slf: Self = serde_json::from_str(&json)
213 .map_err(|source| Error::LoadDbJson { source, file })?;
214 Ok(slf)
215 }
216
217 pub async fn load_async(server: &str, email: &str) -> Result<Self> {
218 let file = crate::dirs::db_file(server, email);
219 let mut fh =
220 tokio::fs::File::open(&file).await.map_err(|source| {
221 Error::LoadDbAsync {
222 source,
223 file: file.clone(),
224 }
225 })?;
226 let mut json = String::new();
227 fh.read_to_string(&mut json).await.map_err(|source| {
228 Error::LoadDbAsync {
229 source,
230 file: file.clone(),
231 }
232 })?;
233 let slf: Self = serde_json::from_str(&json)
234 .map_err(|source| Error::LoadDbJson { source, file })?;
235 Ok(slf)
236 }
237
238 pub fn save(&self, server: &str, email: &str) -> Result<()> {
240 use std::os::unix::fs::{OpenOptionsExt as _, PermissionsExt as _};
241 let file = crate::dirs::db_file(server, email);
242 std::fs::create_dir_all(file.parent().unwrap()).map_err(
245 |source| Error::SaveDb {
246 source,
247 file: file.clone(),
248 },
249 )?;
250 let mut fh = std::fs::OpenOptions::new()
251 .write(true)
252 .create(true)
253 .truncate(true)
254 .mode(0o600)
255 .open(&file)
256 .map_err(|source| Error::SaveDb {
257 source,
258 file: file.clone(),
259 })?;
260 fh.set_permissions(std::fs::Permissions::from_mode(0o600))
261 .map_err(|source| Error::SaveDb {
262 source,
263 file: file.clone(),
264 })?;
265 fh.write_all(
266 serde_json::to_string(self)
267 .map_err(|source| Error::SaveDbJson {
268 source,
269 file: file.clone(),
270 })?
271 .as_bytes(),
272 )
273 .map_err(|source| Error::SaveDb { source, file })?;
274 Ok(())
275 }
276
277 pub async fn save_async(&self, server: &str, email: &str) -> Result<()> {
279 use std::os::unix::fs::PermissionsExt as _;
280 let file = crate::dirs::db_file(server, email);
281 tokio::fs::create_dir_all(file.parent().unwrap())
284 .await
285 .map_err(|source| Error::SaveDbAsync {
286 source,
287 file: file.clone(),
288 })?;
289 let mut fh = tokio::fs::OpenOptions::new()
290 .write(true)
291 .create(true)
292 .truncate(true)
293 .mode(0o600)
294 .open(&file)
295 .await
296 .map_err(|source| Error::SaveDbAsync {
297 source,
298 file: file.clone(),
299 })?;
300 fh.set_permissions(std::fs::Permissions::from_mode(0o600))
301 .await
302 .map_err(|source| Error::SaveDbAsync {
303 source,
304 file: file.clone(),
305 })?;
306 fh.write_all(
307 serde_json::to_string(self)
308 .map_err(|source| Error::SaveDbJson {
309 source,
310 file: file.clone(),
311 })?
312 .as_bytes(),
313 )
314 .await
315 .map_err(|source| Error::SaveDbAsync { source, file })?;
316 Ok(())
317 }
318
319 pub fn remove(server: &str, email: &str) -> Result<()> {
320 let file = crate::dirs::db_file(server, email);
321 let res = std::fs::remove_file(&file);
322 if let Err(e) = &res {
323 if e.kind() == std::io::ErrorKind::NotFound {
324 return Ok(());
325 }
326 }
327 res.map_err(|source| Error::RemoveDb { source, file })?;
328 Ok(())
329 }
330
331 pub fn needs_login(&self) -> bool {
332 self.access_token.is_none()
333 || self.refresh_token.is_none()
334 || self.iterations.is_none()
335 || self.kdf.is_none()
336 || self.protected_key.is_none()
337 }
338}