1use crate::model::access_info::UserAccessMode;
2use crate::model::api::METADATA_FEE;
3use crate::model::crypto::AESKey;
4use crate::model::errors::{LbErrKind, LbResult};
5use crate::model::file_like::FileLike;
6use crate::model::file_metadata::{FileType, Owner};
7use crate::model::staged::StagedTree;
8use crate::model::symkey;
9use crate::model::tree_like::{TreeLike, TreeLikeMut};
10use crate::service::keychain::Keychain;
11use db_rs::hasher::UuidIdentityHasherBuilder;
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, HashSet};
14use uuid::Uuid;
15
16#[derive(Clone, Debug)]
17pub struct LazyTree<T: TreeLike> {
18 pub tree: T,
19 pub name: HashMap<Uuid, String, UuidIdentityHasherBuilder>,
20 pub implicit_deleted: HashMap<Uuid, bool, UuidIdentityHasherBuilder>,
21 pub linked_by: HashMap<Uuid, Uuid, UuidIdentityHasherBuilder>,
22 pub children: HashMap<Uuid, Vec<Uuid>, UuidIdentityHasherBuilder>,
23}
24
25impl<T: TreeLike> LazyTree<T> {
26 pub fn new(tree: T) -> Self {
27 Self {
28 name: uuid_map(),
29 implicit_deleted: uuid_map(),
30 children: uuid_map(),
31 linked_by: uuid_map(),
32 tree,
33 }
34 }
35}
36
37impl<T: TreeLike> LazyTree<T> {
38 pub fn access_mode(&self, owner: Owner, id: &Uuid) -> LbResult<Option<UserAccessMode>> {
39 let mut file = self.find(id)?;
40 let mut max_access_mode = None;
41 let mut visited_ids = vec![];
42 while !visited_ids.contains(file.id()) {
43 visited_ids.push(*file.id());
44 let access_mode = file.access_mode(&owner);
45 if access_mode > max_access_mode {
46 max_access_mode = access_mode;
47 }
48 if file.parent() == file.id() {
49 break; } else if let Some(parent) = self.maybe_find(file.parent()) {
51 file = parent
52 } else {
53 break; }
55 }
56 Ok(max_access_mode)
57 }
58
59 pub fn in_pending_share(&mut self, id: &Uuid) -> LbResult<bool> {
60 let mut id = *id;
61 loop {
62 if self.find(&id)?.parent() == self.find(&id)?.id() {
63 return Ok(false); } else if let Some(link) = self.linked_by(&id)? {
65 id = link;
66 } else if self.maybe_find(self.find(&id)?.parent()).is_some() {
67 id = *self.find(&id)?.parent();
68 } else {
69 return Ok(true); }
71 }
72 }
73
74 pub fn all_children(
75 &mut self,
76 ) -> LbResult<&HashMap<Uuid, Vec<Uuid>, UuidIdentityHasherBuilder>> {
77 if self.children.is_empty() {
78 let mut all_children = uuid_map();
79 for file in self.all_files()? {
80 if !file.is_root() {
81 let mut children: Vec<Uuid> =
82 all_children.remove(file.parent()).unwrap_or_default();
83 children.push(*file.id());
84 all_children.insert(*file.parent(), children);
85 }
86 }
87 self.children = all_children;
88 }
89
90 Ok(&self.children)
91 }
92
93 pub fn calculate_deleted(&mut self, id: &Uuid) -> LbResult<bool> {
94 let (visited_ids, deleted) = {
95 let mut file = self.find(id)?;
96 let mut visited_ids = vec![];
97 let mut deleted = false;
98
99 while !file.is_root()
100 && self.maybe_find(file.parent()).is_some()
101 && !visited_ids.contains(file.id())
102 {
103 visited_ids.push(*file.id());
104 if let Some(&implicit) = self.implicit_deleted.get(file.id()) {
105 deleted = implicit;
106 break;
107 }
108
109 if file.explicitly_deleted() {
110 deleted = true;
111 break;
112 }
113
114 file = match self.maybe_find_parent(file) {
115 Some(file) => file,
116 None => {
117 if !file.user_access_keys().is_empty() {
118 break;
119 } else {
120 return Err(LbErrKind::FileParentNonexistent)?;
121 }
122 }
123 }
124 }
125
126 (visited_ids, deleted)
127 };
128
129 for id in visited_ids {
130 self.implicit_deleted.insert(id, deleted);
131 }
132
133 Ok(deleted)
134 }
135
136 pub fn calculate_usage(&mut self, owner: Owner) -> LbResult<u64> {
140 let mut total: u64 = 0;
141 for id in self.ids() {
142 let file_owner = self.find(&id)?.owner();
144 if file_owner != owner {
145 continue;
146 }
147 if self.calculate_deleted(&id)? {
149 continue;
150 }
151 let file_size = self.find(&id)?.doc_size().unwrap_or(0) as u64;
152 total += file_size + METADATA_FEE;
153 }
154 Ok(total)
155 }
156
157 pub fn decrypt_key(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<AESKey> {
158 let mut file_id = *self.find(id)?.id();
159 let mut visited_ids = vec![];
160
161 loop {
162 if keychain.contains_aes_key(&file_id)? {
163 break;
164 }
165
166 let my_pk = keychain.get_pk()?;
167
168 let maybe_file_key = if let Some(user_access) = self
169 .find(&file_id)?
170 .user_access_keys()
171 .iter()
172 .find(|access| access.encrypted_for == my_pk)
173 {
174 Some(user_access.decrypt(keychain.get_account()?)?)
175 } else {
176 None
177 };
178 if let Some(file_key) = maybe_file_key {
179 keychain.insert_aes_key(file_id, file_key)?;
180 break;
181 }
182
183 visited_ids.push(file_id);
184 file_id = *self.find_parent(self.find(&file_id)?)?.id();
185 }
186
187 for id in visited_ids.iter().rev() {
188 let decrypted_key = {
189 let file = self.find(id)?;
190 let parent = self.find_parent(file)?;
191 let parent_key =
192 keychain
193 .get_aes_key(parent.id())?
194 .ok_or(LbErrKind::Unexpected(
195 "parent key should have been populated by prior routine".to_string(),
196 ))?;
197 let encrypted_key = file.folder_access_key();
198 symkey::decrypt(&parent_key, encrypted_key)?
199 };
200 keychain.insert_aes_key(*id, decrypted_key)?;
201 }
202
203 Ok(keychain.get_aes_key(id)?.ok_or(LbErrKind::Unexpected(
204 "parent key should have been populated by prior routine (2)".to_string(),
205 ))?)
206 }
207
208 pub fn name(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<String> {
209 if let Some(name) = self.name.get(id) {
210 return Ok(name.clone());
211 }
212 let key = self.decrypt_key(id, keychain)?;
213 let name = self.find(id)?.secret_name().to_string(&key)?;
214 self.name.insert(*id, name.clone());
215 Ok(name)
216 }
217
218 pub fn name_using_links(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<String> {
219 let id = if let Some(link) = self.linked_by(id)? { link } else { *id };
220 self.name(&id, keychain)
221 }
222
223 pub fn parent_using_links(&mut self, id: &Uuid) -> LbResult<Uuid> {
224 let id = if let Some(link) = self.linked_by(id)? { link } else { *id };
225 Ok(*self.find(&id)?.parent())
226 }
227
228 pub fn linked_by(&mut self, id: &Uuid) -> LbResult<Option<Uuid>> {
229 if self.linked_by.is_empty() {
230 for link_id in self.ids() {
231 if let FileType::Link { target } = self.find(&link_id)?.file_type() {
232 if !self.calculate_deleted(&link_id)? {
233 self.linked_by.insert(target, link_id);
234 }
235 }
236 }
237 }
238
239 Ok(self.linked_by.get(id).copied())
240 }
241
242 pub fn children(&mut self, id: &Uuid) -> LbResult<Vec<Uuid>> {
245 if let Some(children) = self.children.get(id) {
247 return Ok(children.clone());
248 }
249
250 let file = self.find(id)?;
252 if !file.is_folder() {
253 return Ok(vec![]);
254 }
255
256 self.all_children()?;
258
259 if let Some(children) = self.children.get(id) {
261 return Ok(children.clone());
262 }
263
264 Ok(vec![])
265 }
266
267 pub fn children_using_links(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
270 let mut children = HashSet::new();
271 for child in self.children(id)? {
272 if let FileType::Link { target } = self.find(&child)?.file_type() {
273 if !self.calculate_deleted(&child)? {
274 children.insert(target);
275 }
276 } else {
277 children.insert(child);
278 }
279 }
280
281 Ok(children)
282 }
283
284 pub fn descendants(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
288 let mut result = HashSet::new();
290 let mut to_process = vec![*id];
291 let mut i = 0;
292 while i < to_process.len() {
293 let new_descendents = self
294 .children(&to_process[i])?
295 .into_iter()
296 .filter(|f| !result.contains(f))
297 .collect::<Vec<Uuid>>();
298 to_process.extend(new_descendents.iter());
300 result.extend(new_descendents.into_iter());
301 i += 1;
302 }
303 Ok(result)
304 }
305
306 pub fn descendants_using_links(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
307 let mut result = HashSet::new();
309 let mut to_process = vec![*id];
310 let mut i = 0;
311 while i < to_process.len() {
312 let new_descendents = self
313 .children_using_links(&to_process[i])?
314 .into_iter()
315 .filter(|f| !result.contains(f))
316 .collect::<Vec<Uuid>>();
317 to_process.extend(new_descendents.iter());
319 result.extend(new_descendents.into_iter());
320 i += 1;
321 }
322 Ok(result)
323 }
324
325 pub fn ancestors(&self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
329 let mut result = HashSet::new();
330 let mut current_file = self.find(id)?;
331 while !current_file.is_root()
332 && self.maybe_find(current_file.parent()).is_some()
333 && !result.contains(current_file.parent())
334 {
335 result.insert(*current_file.parent());
336 current_file = self.find_parent(current_file)?;
337 }
338 Ok(result)
339 }
340
341 pub fn pending_roots(&mut self, keychain: &Keychain) -> LbResult<Vec<Uuid>> {
342 let mut result = Vec::new();
343 let owner = Owner(keychain.get_pk()?);
344 for id in self.ids() {
345 if self.find(&id)?.owner() == owner {
347 continue;
348 }
349
350 if self.find(&id)?.access_mode(&owner).is_none() {
352 continue;
353 }
354
355 if self.calculate_deleted(&id)? {
357 continue;
358 }
359
360 if self.linked_by(&id)?.is_some() {
362 continue;
363 }
364
365 result.push(id);
366 }
367
368 Ok(result)
369 }
370
371 pub fn non_deleted_pending_files(&mut self, keychain: &Keychain) -> LbResult<Vec<Uuid>> {
372 let roots = self.pending_roots(keychain)?;
373 let mut result = vec![];
374
375 for id in roots {
376 result.push(id);
377 let descendants = self.descendants(&id)?;
378
379 for id in descendants {
380 if !self.calculate_deleted(&id)? {
381 result.push(id);
382 }
383 }
384 }
385
386 Ok(result)
387 }
388
389 pub fn stage<T2: TreeLikeMut<F = T::F>>(self, staged: T2) -> LazyTree<StagedTree<T, T2>> {
390 LazyTree::<StagedTree<T, T2>> {
392 tree: StagedTree::new(self.tree, staged),
393 name: uuid_map(),
394 implicit_deleted: uuid_map(),
395 children: uuid_map(),
396 linked_by: uuid_map(),
397 }
398 }
399
400 pub fn assert_names_decryptable(&mut self, keychain: &Keychain) -> LbResult<()> {
414 for id in self.ids() {
415 if self.name(&id, keychain).is_err() {
416 return Err(LbErrKind::Validation(ValidationFailure::NonDecryptableFileName(id)))?;
417 }
418 }
419 Ok(())
420 }
421}
422
423pub type Stage1<Base, Local> = StagedTree<Base, Local>;
424pub type LazyStaged1<Base, Local> = LazyTree<Stage1<Base, Local>>;
425pub type Stage2<Base, Local, Staged> = StagedTree<StagedTree<Base, Local>, Staged>;
426pub type LazyStage2<Base, Local, Staged> = LazyTree<Stage2<Base, Local, Staged>>;
427
428#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
429pub enum ValidationFailure {
430 Orphan(Uuid),
431
432 Cycle(HashSet<Uuid>),
434
435 PathConflict(HashSet<Uuid>),
437
438 FileNameTooLong(Uuid),
440
441 NonFolderWithChildren(Uuid),
443
444 OwnedLink(Uuid),
446
447 BrokenLink(Uuid),
449
450 DuplicateLink {
452 target: Uuid,
453 },
454
455 SharedLink {
457 link: Uuid,
458 shared_ancestor: Uuid,
459 },
460
461 FileWithDifferentOwnerParent(Uuid),
462 NonDecryptableFileName(Uuid),
463 DeletedFileUpdated(Uuid),
464}
465
466impl<T> LazyTree<T>
467where
468 T: TreeLikeMut,
469{
470 pub fn stage_and_promote<S: TreeLikeMut<F = T::F>>(&mut self, mut staged: S) -> LbResult<()> {
471 for id in staged.ids() {
472 if let Some(removed) = staged.remove(id)? {
473 self.tree.insert(removed)?;
474 }
475 }
476 self.name = uuid_map();
478 self.implicit_deleted = uuid_map();
479 self.children = uuid_map();
480 self.linked_by = uuid_map();
481 Ok(())
482 }
483
484 pub fn stage_validate_and_promote<S: TreeLikeMut<F = T::F>>(
485 &mut self, mut staged: S, owner: Owner,
486 ) -> LbResult<()> {
487 StagedTree::new(&self.tree, &mut staged)
488 .to_lazy()
489 .validate(owner)?;
490 self.stage_and_promote(staged)?;
491 Ok(())
492 }
493
494 pub fn stage_removals_and_promote(&mut self, removed: HashSet<Uuid>) -> LbResult<()> {
496 for id in removed {
497 self.tree.remove(id)?;
498 }
499 self.name = uuid_map();
501 self.implicit_deleted = uuid_map();
502 self.children = uuid_map();
503 self.linked_by = uuid_map();
504 Ok(())
505 }
506}
507
508impl<Base, Staged> LazyStaged1<Base, Staged>
509where
510 Base: TreeLikeMut,
511 Staged: TreeLikeMut<F = Base::F>,
512{
513 pub fn promote(self) -> LbResult<LazyTree<Base>> {
515 let mut staged = self.tree.staged;
516 let mut base = self.tree.base;
517 for id in staged.ids() {
518 if let Some(removed) = staged.remove(id)? {
519 base.insert(removed)?;
520 }
521 }
522 for id in self.tree.removed {
523 base.remove(id)?;
524 }
525
526 Ok(LazyTree {
527 tree: base,
528 name: uuid_map(),
529 implicit_deleted: uuid_map(),
530 children: uuid_map(),
531 linked_by: uuid_map(),
532 })
533 }
534}
535
536impl<Base, Staged> LazyStaged1<Base, Staged>
537where
538 Base: TreeLike,
539 Staged: TreeLikeMut<F = Base::F>,
540{
541 pub fn unstage(self) -> (LazyTree<Base>, Staged) {
543 (
544 LazyTree {
545 tree: self.tree.base,
546 name: uuid_map(),
547 implicit_deleted: uuid_map(),
548 children: uuid_map(),
549 linked_by: uuid_map(),
550 },
551 self.tree.staged,
552 )
553 }
554}
555
556impl<T: TreeLike> TreeLike for LazyTree<T> {
557 type F = T::F;
558
559 fn ids(&self) -> Vec<Uuid> {
560 self.tree.ids()
561 }
562
563 fn maybe_find(&self, id: &Uuid) -> Option<&Self::F> {
564 self.tree.maybe_find(id)
565 }
566}
567
568fn uuid_map<V>() -> HashMap<Uuid, V, UuidIdentityHasherBuilder> {
569 HashMap::with_hasher(UuidIdentityHasherBuilder)
570}