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 pending_roots(&mut self, keychain: &Keychain) -> LbResult<Vec<Uuid>> {
315 let mut result = Vec::new();
316 let owner = Owner(keychain.get_pk()?);
317 for id in self.ids() {
318 if self.find(&id)?.owner() == owner {
320 continue;
321 }
322
323 if self.find(&id)?.access_mode(&owner).is_none() {
325 continue;
326 }
327
328 if self.calculate_deleted(&id)? {
330 continue;
331 }
332
333 if self.linked_by(&id)?.is_some() {
335 continue;
336 }
337
338 result.push(id);
339 }
340
341 Ok(result)
342 }
343
344 pub fn non_deleted_pending_files(&mut self, keychain: &Keychain) -> LbResult<Vec<Uuid>> {
345 let roots = self.pending_roots(keychain)?;
346 let mut result = vec![];
347
348 for id in roots {
349 result.push(id);
350 let descendants = self.descendants(&id)?;
351
352 for id in descendants {
353 if !self.calculate_deleted(&id)? {
354 result.push(id);
355 }
356 }
357 }
358
359 Ok(result)
360 }
361
362 pub fn stage<T2: TreeLikeMut<F = T::F>>(self, staged: T2) -> LazyTree<StagedTree<T, T2>> {
363 LazyTree::<StagedTree<T, T2>> {
365 tree: StagedTree::new(self.tree, staged),
366 name: HashMap::new(),
367 implicit_deleted: HashMap::new(),
368 children: HashMap::new(),
369 linked_by: HashMap::new(),
370 }
371 }
372
373 pub fn stage_removals(self, removed: HashSet<Uuid>) -> LazyTree<StagedTree<T, Option<T::F>>> {
375 LazyTree::<StagedTree<T, Option<T::F>>> {
377 tree: StagedTree::removal(self.tree, removed),
378 name: HashMap::new(),
379 implicit_deleted: HashMap::new(),
380 children: HashMap::new(),
381 linked_by: HashMap::new(),
382 }
383 }
384
385 pub fn assert_names_decryptable(&mut self, keychain: &Keychain) -> LbResult<()> {
387 for id in self.ids() {
388 if self.name(&id, keychain).is_err() {
389 return Err(LbErrKind::Validation(ValidationFailure::NonDecryptableFileName(id)))?;
390 }
391 }
392 Ok(())
393 }
394}
395
396pub type Stage1<Base, Local> = StagedTree<Base, Local>;
397pub type LazyStaged1<Base, Local> = LazyTree<Stage1<Base, Local>>;
398pub type Stage2<Base, Local, Staged> = StagedTree<StagedTree<Base, Local>, Staged>;
399pub type LazyStage2<Base, Local, Staged> = LazyTree<Stage2<Base, Local, Staged>>;
400
401#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
402pub enum ValidationFailure {
403 Orphan(Uuid),
404
405 Cycle(HashSet<Uuid>),
407
408 PathConflict(HashSet<Uuid>),
410
411 FileNameTooLong(Uuid),
413
414 NonFolderWithChildren(Uuid),
416
417 OwnedLink(Uuid),
419
420 BrokenLink(Uuid),
422
423 DuplicateLink {
425 target: Uuid,
426 },
427
428 SharedLink {
430 link: Uuid,
431 shared_ancestor: Uuid,
432 },
433
434 FileWithDifferentOwnerParent(Uuid),
435 NonDecryptableFileName(Uuid),
436 DeletedFileUpdated(Uuid),
437}
438
439impl<T> LazyTree<T>
440where
441 T: TreeLikeMut,
442{
443 pub fn stage_and_promote<S: TreeLikeMut<F = T::F>>(&mut self, mut staged: S) -> LbResult<()> {
444 for id in staged.ids() {
445 if let Some(removed) = staged.remove(id)? {
446 self.tree.insert(removed)?;
447 }
448 }
449 self.name = HashMap::new();
451 self.implicit_deleted = HashMap::new();
452 self.children = HashMap::new();
453 self.linked_by = HashMap::new();
454 Ok(())
455 }
456
457 pub fn stage_validate_and_promote<S: TreeLikeMut<F = T::F>>(
458 &mut self, mut staged: S, owner: Owner,
459 ) -> LbResult<()> {
460 StagedTree::new(&self.tree, &mut staged)
461 .to_lazy()
462 .validate(owner)?;
463 self.stage_and_promote(staged)?;
464 Ok(())
465 }
466
467 pub fn stage_removals_and_promote(&mut self, removed: HashSet<Uuid>) -> LbResult<()> {
469 for id in removed {
470 self.tree.remove(id)?;
471 }
472 self.name = HashMap::new();
474 self.implicit_deleted = HashMap::new();
475 self.children = HashMap::new();
476 self.linked_by = HashMap::new();
477 Ok(())
478 }
479}
480
481impl<Base, Staged> LazyStaged1<Base, Staged>
482where
483 Base: TreeLikeMut,
484 Staged: TreeLikeMut<F = Base::F>,
485{
486 pub fn promote(self) -> LbResult<LazyTree<Base>> {
488 let mut staged = self.tree.staged;
489 let mut base = self.tree.base;
490 for id in staged.ids() {
491 if let Some(removed) = staged.remove(id)? {
492 base.insert(removed)?;
493 }
494 }
495 for id in self.tree.removed {
496 base.remove(id)?;
497 }
498
499 Ok(LazyTree {
500 tree: base,
501 name: HashMap::new(),
502 implicit_deleted: HashMap::new(),
503 children: HashMap::new(),
504 linked_by: HashMap::new(),
505 })
506 }
507}
508
509impl<Base, Staged> LazyStaged1<Base, Staged>
510where
511 Base: TreeLike,
512 Staged: TreeLikeMut<F = Base::F>,
513{
514 pub fn unstage(self) -> (LazyTree<Base>, Staged) {
516 (
517 LazyTree {
518 tree: self.tree.base,
519 name: HashMap::new(),
520 implicit_deleted: HashMap::new(),
521 children: HashMap::new(),
522 linked_by: HashMap::new(),
523 },
524 self.tree.staged,
525 )
526 }
527}
528
529impl<T: TreeLike> TreeLike for LazyTree<T> {
530 type F = T::F;
531
532 fn ids(&self) -> Vec<Uuid> {
533 self.tree.ids()
534 }
535
536 fn maybe_find(&self, id: &Uuid) -> Option<&Self::F> {
537 self.tree.maybe_find(id)
538 }
539}