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>> {
260 let mut result = HashSet::new();
262 let mut to_process = vec![*id];
263 let mut i = 0;
264 while i < to_process.len() {
265 let new_descendents = self
266 .children(&to_process[i])?
267 .into_iter()
268 .filter(|f| !result.contains(f))
269 .collect::<Vec<Uuid>>();
270 to_process.extend(new_descendents.iter());
272 result.extend(new_descendents.into_iter());
273 i += 1;
274 }
275 Ok(result)
276 }
277
278 pub fn descendants_using_links(&mut self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
279 let mut result = HashSet::new();
281 let mut to_process = vec![*id];
282 let mut i = 0;
283 while i < to_process.len() {
284 let new_descendents = self
285 .children_using_links(&to_process[i])?
286 .into_iter()
287 .filter(|f| !result.contains(f))
288 .collect::<Vec<Uuid>>();
289 to_process.extend(new_descendents.iter());
291 result.extend(new_descendents.into_iter());
292 i += 1;
293 }
294 Ok(result)
295 }
296
297 pub fn ancestors(&self, id: &Uuid) -> LbResult<HashSet<Uuid>> {
301 let mut result = HashSet::new();
302 let mut current_file = self.find(id)?;
303 while !current_file.is_root()
304 && self.maybe_find(current_file.parent()).is_some()
305 && !result.contains(current_file.parent())
306 {
307 result.insert(*current_file.parent());
308 current_file = self.find_parent(current_file)?;
309 }
310 Ok(result)
311 }
312
313 pub fn stage<T2: TreeLikeMut<F = T::F>>(self, staged: T2) -> LazyTree<StagedTree<T, T2>> {
314 LazyTree::<StagedTree<T, T2>> {
316 tree: StagedTree::new(self.tree, staged),
317 name: HashMap::new(),
318 implicit_deleted: HashMap::new(),
319 children: HashMap::new(),
320 linked_by: HashMap::new(),
321 }
322 }
323
324 pub fn stage_removals(self, removed: HashSet<Uuid>) -> LazyTree<StagedTree<T, Option<T::F>>> {
326 LazyTree::<StagedTree<T, Option<T::F>>> {
328 tree: StagedTree::removal(self.tree, removed),
329 name: HashMap::new(),
330 implicit_deleted: HashMap::new(),
331 children: HashMap::new(),
332 linked_by: HashMap::new(),
333 }
334 }
335
336 pub fn assert_names_decryptable(&mut self, keychain: &Keychain) -> LbResult<()> {
338 for id in self.ids() {
339 if self.name(&id, keychain).is_err() {
340 return Err(LbErrKind::Validation(ValidationFailure::NonDecryptableFileName(id)))?;
341 }
342 }
343 Ok(())
344 }
345}
346
347pub type Stage1<Base, Local> = StagedTree<Base, Local>;
348pub type LazyStaged1<Base, Local> = LazyTree<Stage1<Base, Local>>;
349pub type Stage2<Base, Local, Staged> = StagedTree<StagedTree<Base, Local>, Staged>;
350pub type LazyStage2<Base, Local, Staged> = LazyTree<Stage2<Base, Local, Staged>>;
351
352#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
353pub enum ValidationFailure {
354 Orphan(Uuid),
355
356 Cycle(HashSet<Uuid>),
358
359 PathConflict(HashSet<Uuid>),
361
362 FileNameTooLong(Uuid),
364
365 NonFolderWithChildren(Uuid),
367
368 OwnedLink(Uuid),
370
371 BrokenLink(Uuid),
373
374 DuplicateLink {
376 target: Uuid,
377 },
378
379 SharedLink {
381 link: Uuid,
382 shared_ancestor: Uuid,
383 },
384
385 FileWithDifferentOwnerParent(Uuid),
386 NonDecryptableFileName(Uuid),
387 DeletedFileUpdated(Uuid),
388}
389
390impl<T> LazyTree<T>
391where
392 T: TreeLikeMut,
393{
394 pub fn stage_and_promote<S: TreeLikeMut<F = T::F>>(&mut self, mut staged: S) -> LbResult<()> {
395 for id in staged.ids() {
396 if let Some(removed) = staged.remove(id)? {
397 self.tree.insert(removed)?;
398 }
399 }
400 self.name = HashMap::new();
402 self.implicit_deleted = HashMap::new();
403 self.children = HashMap::new();
404 self.linked_by = HashMap::new();
405 Ok(())
406 }
407
408 pub fn stage_validate_and_promote<S: TreeLikeMut<F = T::F>>(
409 &mut self, mut staged: S, owner: Owner,
410 ) -> LbResult<()> {
411 StagedTree::new(&self.tree, &mut staged)
412 .to_lazy()
413 .validate(owner)?;
414 self.stage_and_promote(staged)?;
415 Ok(())
416 }
417
418 pub fn stage_removals_and_promote(&mut self, removed: HashSet<Uuid>) -> LbResult<()> {
420 for id in removed {
421 self.tree.remove(id)?;
422 }
423 self.name = HashMap::new();
425 self.implicit_deleted = HashMap::new();
426 self.children = HashMap::new();
427 self.linked_by = HashMap::new();
428 Ok(())
429 }
430}
431
432impl<Base, Staged> LazyStaged1<Base, Staged>
433where
434 Base: TreeLikeMut,
435 Staged: TreeLikeMut<F = Base::F>,
436{
437 pub fn promote(self) -> LbResult<LazyTree<Base>> {
439 let mut staged = self.tree.staged;
440 let mut base = self.tree.base;
441 for id in staged.ids() {
442 if let Some(removed) = staged.remove(id)? {
443 base.insert(removed)?;
444 }
445 }
446 for id in self.tree.removed {
447 base.remove(id)?;
448 }
449
450 Ok(LazyTree {
451 tree: base,
452 name: HashMap::new(),
453 implicit_deleted: HashMap::new(),
454 children: HashMap::new(),
455 linked_by: HashMap::new(),
456 })
457 }
458}
459
460impl<Base, Staged> LazyStaged1<Base, Staged>
461where
462 Base: TreeLike,
463 Staged: TreeLikeMut<F = Base::F>,
464{
465 pub fn unstage(self) -> (LazyTree<Base>, Staged) {
467 (
468 LazyTree {
469 tree: self.tree.base,
470 name: HashMap::new(),
471 implicit_deleted: HashMap::new(),
472 children: HashMap::new(),
473 linked_by: HashMap::new(),
474 },
475 self.tree.staged,
476 )
477 }
478}
479
480impl<T: TreeLike> TreeLike for LazyTree<T> {
481 type F = T::F;
482
483 fn ids(&self) -> Vec<Uuid> {
484 self.tree.ids()
485 }
486
487 fn maybe_find(&self, id: &Uuid) -> Option<&Self::F> {
488 self.tree.maybe_find(id)
489 }
490}