1use crate::model::access_info::UserAccessMode;
2use crate::model::crypto::AESKey;
3use crate::model::errors::{LbErrKind, LbResult};
4use crate::model::file_like::FileLike;
5use crate::model::file_metadata::{FileType, Owner};
6use crate::model::staged::StagedTree;
7use crate::model::symkey;
8use crate::model::tree_like::{TreeLike, TreeLikeMut};
9use crate::service::keychain::Keychain;
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet};
12use uuid::Uuid;
13
14#[derive(Clone, Debug)]
15pub struct LazyTree<T: TreeLike> {
16 pub tree: T,
17 pub name: HashMap<Uuid, String>,
18 pub implicit_deleted: HashMap<Uuid, bool>,
19 pub linked_by: HashMap<Uuid, Uuid>,
20 pub children: HashMap<Uuid, HashSet<Uuid>>,
21}
22
23impl<T: TreeLike> LazyTree<T> {
24 pub fn new(tree: T) -> Self {
25 Self {
26 name: HashMap::new(),
27 implicit_deleted: HashMap::new(),
28 children: HashMap::new(),
29 linked_by: HashMap::new(),
30 tree,
31 }
32 }
33}
34
35impl<T: TreeLike> LazyTree<T> {
36 pub fn access_mode(&self, owner: Owner, id: &Uuid) -> LbResult<Option<UserAccessMode>> {
37 let mut file = self.find(id)?;
38 let mut max_access_mode = None;
39 let mut visited_ids = vec![];
40 while !visited_ids.contains(file.id()) {
41 visited_ids.push(*file.id());
42 let access_mode = file.access_mode(&owner);
43 if access_mode > max_access_mode {
44 max_access_mode = access_mode;
45 }
46 if file.parent() == file.id() {
47 break; } else if let Some(parent) = self.maybe_find(file.parent()) {
49 file = parent
50 } else {
51 break; }
53 }
54 Ok(max_access_mode)
55 }
56
57 pub fn in_pending_share(&mut self, id: &Uuid) -> LbResult<bool> {
58 let mut id = *id;
59 loop {
60 if self.find(&id)?.parent() == self.find(&id)?.id() {
61 return Ok(false); } else if let Some(link) = self.linked_by(&id)? {
63 id = link;
64 } else if self.maybe_find(self.find(&id)?.parent()).is_some() {
65 id = *self.find(&id)?.parent();
66 } else {
67 return Ok(true); }
69 }
70 }
71
72 pub fn all_children(&mut self) -> LbResult<&HashMap<Uuid, HashSet<Uuid>>> {
73 if self.children.is_empty() {
74 let mut all_children: HashMap<Uuid, HashSet<Uuid>> = HashMap::new();
75 for file in self.all_files()? {
76 if !file.is_root() {
77 let mut children = all_children.remove(file.parent()).unwrap_or_default();
78 children.insert(*file.id());
79 all_children.insert(*file.parent(), children);
80 }
81 }
82 self.children = all_children;
83 }
84
85 Ok(&self.children)
86 }
87
88 pub fn calculate_deleted(&mut self, id: &Uuid) -> LbResult<bool> {
89 let (visited_ids, deleted) = {
90 let mut file = self.find(id)?;
91 let mut visited_ids = vec![];
92 let mut deleted = false;
93
94 while !file.is_root()
95 && self.maybe_find(file.parent()).is_some()
96 && !visited_ids.contains(file.id())
97 {
98 visited_ids.push(*file.id());
99 if let Some(&implicit) = self.implicit_deleted.get(file.id()) {
100 deleted = implicit;
101 break;
102 }
103
104 if file.explicitly_deleted() {
105 deleted = true;
106 break;
107 }
108
109 file = match self.maybe_find_parent(file) {
110 Some(file) => file,
111 None => {
112 if !file.user_access_keys().is_empty() {
113 break;
114 } else {
115 return Err(LbErrKind::FileParentNonexistent)?;
116 }
117 }
118 }
119 }
120
121 (visited_ids, deleted)
122 };
123
124 for id in visited_ids {
125 self.implicit_deleted.insert(id, deleted);
126 }
127
128 Ok(deleted)
129 }
130
131 pub fn decrypt_key(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<AESKey> {
132 let mut file_id = *self.find(id)?.id();
133 let mut visited_ids = vec![];
134
135 loop {
136 if keychain.contains_aes_key(&file_id)? {
137 break;
138 }
139
140 let my_pk = keychain.get_pk()?;
141
142 let maybe_file_key = if let Some(user_access) = self
143 .find(&file_id)?
144 .user_access_keys()
145 .iter()
146 .find(|access| access.encrypted_for == my_pk)
147 {
148 Some(user_access.decrypt(keychain.get_account()?)?)
149 } else {
150 None
151 };
152 if let Some(file_key) = maybe_file_key {
153 keychain.insert_aes_key(file_id, file_key)?;
154 break;
155 }
156
157 visited_ids.push(file_id);
158 file_id = *self.find_parent(self.find(&file_id)?)?.id();
159 }
160
161 for id in visited_ids.iter().rev() {
162 let decrypted_key = {
163 let file = self.find(id)?;
164 let parent = self.find_parent(file)?;
165 let parent_key =
166 keychain
167 .get_aes_key(parent.id())?
168 .ok_or(LbErrKind::Unexpected(
169 "parent key should have been populated by prior routine".to_string(),
170 ))?;
171 let encrypted_key = file.folder_access_key();
172 symkey::decrypt(&parent_key, encrypted_key)?
173 };
174 keychain.insert_aes_key(*id, decrypted_key)?;
175 }
176
177 Ok(keychain.get_aes_key(id)?.ok_or(LbErrKind::Unexpected(
178 "parent key should have been populated by prior routine (2)".to_string(),
179 ))?)
180 }
181
182 pub fn name(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<String> {
183 if let Some(name) = self.name.get(id) {
184 return Ok(name.clone());
185 }
186 let key = self.decrypt_key(id, keychain)?;
187 let name = self.find(id)?.secret_name().to_string(&key)?;
188 self.name.insert(*id, name.clone());
189 Ok(name)
190 }
191
192 pub fn name_using_links(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<String> {
193 let id = if let Some(link) = self.linked_by(id)? { link } else { *id };
194 self.name(&id, keychain)
195 }
196
197 pub fn parent_using_links(&mut self, id: &Uuid) -> LbResult<Uuid> {
198 let id = if let Some(link) = self.linked_by(id)? { link } else { *id };
199 Ok(*self.find(&id)?.parent())
200 }
201
202 pub fn linked_by(&mut self, id: &Uuid) -> LbResult<Option<Uuid>> {
203 if self.linked_by.is_empty() {
204 for link_id in self.ids() {
205 if let FileType::Link { target } = self.find(&link_id)?.file_type() {
206 if !self.calculate_deleted(&link_id)? {
207 self.linked_by.insert(target, link_id);
208 }
209 }
210 }
211 }
212
213 Ok(self.linked_by.get(id).copied())
214 }
215
216 pub fn children(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
219 if let Some(children) = self.children.get(id) {
221 return Ok(children.clone());
222 }
223
224 let file = self.find(id)?;
226 if !file.is_folder() {
227 return Ok(HashSet::default());
228 }
229
230 self.all_children()?;
232
233 if let Some(children) = self.children.get(id) {
235 return Ok(children.clone());
236 }
237
238 Ok(HashSet::new())
239 }
240
241 pub fn children_using_links(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
243 let mut children = HashSet::new();
244 for child in self.children(id)? {
245 if let FileType::Link { target } = self.find(&child)?.file_type() {
246 if !self.calculate_deleted(&child)? {
247 children.insert(target);
248 }
249 } else {
250 children.insert(child);
251 }
252 }
253
254 Ok(children)
255 }
256
257 pub fn descendants(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
261 let mut result = HashSet::new();
263 let mut to_process = vec![*id];
264 let mut i = 0;
265 while i < to_process.len() {
266 let new_descendents = self
267 .children(&to_process[i])?
268 .into_iter()
269 .filter(|f| !result.contains(f))
270 .collect::<Vec<Uuid>>();
271 to_process.extend(new_descendents.iter());
273 result.extend(new_descendents.into_iter());
274 i += 1;
275 }
276 Ok(result)
277 }
278
279 pub fn descendants_using_links(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
280 let mut result = HashSet::new();
282 let mut to_process = vec![*id];
283 let mut i = 0;
284 while i < to_process.len() {
285 let new_descendents = self
286 .children_using_links(&to_process[i])?
287 .into_iter()
288 .filter(|f| !result.contains(f))
289 .collect::<Vec<Uuid>>();
290 to_process.extend(new_descendents.iter());
292 result.extend(new_descendents.into_iter());
293 i += 1;
294 }
295 Ok(result)
296 }
297
298 pub fn ancestors(&self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
302 let mut result = HashSet::new();
303 let mut current_file = self.find(id)?;
304 while !current_file.is_root()
305 && self.maybe_find(current_file.parent()).is_some()
306 && !result.contains(current_file.parent())
307 {
308 result.insert(*current_file.parent());
309 current_file = self.find_parent(current_file)?;
310 }
311 Ok(result)
312 }
313
314 pub fn stage<T2: TreeLikeMut<F = T::F>>(self, staged: T2) -> LazyTree<StagedTree<T, T2>> {
315 LazyTree::<StagedTree<T, T2>> {
317 tree: StagedTree::new(self.tree, staged),
318 name: HashMap::new(),
319 implicit_deleted: HashMap::new(),
320 children: HashMap::new(),
321 linked_by: HashMap::new(),
322 }
323 }
324
325 pub fn stage_removals(self, removed: HashSet<Uuid>) -> LazyTree<StagedTree<T, Option<T::F>>> {
327 LazyTree::<StagedTree<T, Option<T::F>>> {
329 tree: StagedTree::removal(self.tree, removed),
330 name: HashMap::new(),
331 implicit_deleted: HashMap::new(),
332 children: HashMap::new(),
333 linked_by: HashMap::new(),
334 }
335 }
336
337 pub fn assert_names_decryptable(&mut self, keychain: &Keychain) -> LbResult<()> {
339 for id in self.ids() {
340 if self.name(&id, keychain).is_err() {
341 return Err(LbErrKind::Validation(ValidationFailure::NonDecryptableFileName(id)))?;
342 }
343 }
344 Ok(())
345 }
346}
347
348pub type Stage1<Base, Local> = StagedTree<Base, Local>;
349pub type LazyStaged1<Base, Local> = LazyTree<Stage1<Base, Local>>;
350pub type Stage2<Base, Local, Staged> = StagedTree<StagedTree<Base, Local>, Staged>;
351pub type LazyStage2<Base, Local, Staged> = LazyTree<Stage2<Base, Local, Staged>>;
352
353#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
354pub enum ValidationFailure {
355 Orphan(Uuid),
356
357 Cycle(HashSet<Uuid>),
359
360 PathConflict(HashSet<Uuid>),
362
363 FileNameTooLong(Uuid),
365
366 NonFolderWithChildren(Uuid),
368
369 OwnedLink(Uuid),
371
372 BrokenLink(Uuid),
374
375 DuplicateLink {
377 target: Uuid,
378 },
379
380 SharedLink {
382 link: Uuid,
383 shared_ancestor: Uuid,
384 },
385
386 FileWithDifferentOwnerParent(Uuid),
387 NonDecryptableFileName(Uuid),
388 DeletedFileUpdated(Uuid),
389}
390
391impl<T> LazyTree<T>
392where
393 T: TreeLikeMut,
394{
395 pub fn stage_and_promote<S: TreeLikeMut<F = T::F>>(&mut self, mut staged: S) -> LbResult<()> {
396 for id in staged.ids() {
397 if let Some(removed) = staged.remove(id)? {
398 self.tree.insert(removed)?;
399 }
400 }
401 self.name = HashMap::new();
403 self.implicit_deleted = HashMap::new();
404 self.children = HashMap::new();
405 self.linked_by = HashMap::new();
406 Ok(())
407 }
408
409 pub fn stage_validate_and_promote<S: TreeLikeMut<F = T::F>>(
410 &mut self, mut staged: S, owner: Owner,
411 ) -> LbResult<()> {
412 StagedTree::new(&self.tree, &mut staged)
413 .to_lazy()
414 .validate(owner)?;
415 self.stage_and_promote(staged)?;
416 Ok(())
417 }
418
419 pub fn stage_removals_and_promote(&mut self, removed: HashSet<Uuid>) -> LbResult<()> {
421 for id in removed {
422 self.tree.remove(id)?;
423 }
424 self.name = HashMap::new();
426 self.implicit_deleted = HashMap::new();
427 self.children = HashMap::new();
428 self.linked_by = HashMap::new();
429 Ok(())
430 }
431}
432
433impl<Base, Staged> LazyStaged1<Base, Staged>
434where
435 Base: TreeLikeMut,
436 Staged: TreeLikeMut<F = Base::F>,
437{
438 pub fn promote(self) -> LbResult<LazyTree<Base>> {
440 let mut staged = self.tree.staged;
441 let mut base = self.tree.base;
442 for id in staged.ids() {
443 if let Some(removed) = staged.remove(id)? {
444 base.insert(removed)?;
445 }
446 }
447 for id in self.tree.removed {
448 base.remove(id)?;
449 }
450
451 Ok(LazyTree {
452 tree: base,
453 name: HashMap::new(),
454 implicit_deleted: HashMap::new(),
455 children: HashMap::new(),
456 linked_by: HashMap::new(),
457 })
458 }
459}
460
461impl<Base, Staged> LazyStaged1<Base, Staged>
462where
463 Base: TreeLike,
464 Staged: TreeLikeMut<F = Base::F>,
465{
466 pub fn unstage(self) -> (LazyTree<Base>, Staged) {
468 (
469 LazyTree {
470 tree: self.tree.base,
471 name: HashMap::new(),
472 implicit_deleted: HashMap::new(),
473 children: HashMap::new(),
474 linked_by: HashMap::new(),
475 },
476 self.tree.staged,
477 )
478 }
479}
480
481impl<T: TreeLike> TreeLike for LazyTree<T> {
482 type F = T::F;
483
484 fn ids(&self) -> Vec<Uuid> {
485 self.tree.ids()
486 }
487
488 fn maybe_find(&self, id: &Uuid) -> Option<&Self::F> {
489 self.tree.maybe_find(id)
490 }
491}