lockbook_shared/
server_ops.rs

1use crate::clock::get_time;
2use crate::file_like::FileLike;
3use crate::file_metadata::FileDiff;
4use crate::lazy::{LazyStaged1, LazyTree};
5use crate::server_file::{IntoServerFile, ServerFile};
6
7use crate::server_tree::ServerTree;
8use crate::signed_file::SignedFile;
9use crate::tree_like::TreeLike;
10use crate::{SharedErrorKind, SharedResult};
11
12type LazyServerStaged1<'a> = LazyStaged1<ServerTree<'a>, Vec<ServerFile>>;
13
14impl<'a> LazyTree<ServerTree<'a>> {
15    /// Validates a diff prior to staging it. Performs individual validations, then validations that
16    /// require a tree
17    pub fn stage_diff(
18        mut self, changes: Vec<FileDiff<SignedFile>>,
19    ) -> SharedResult<LazyServerStaged1<'a>> {
20        self.tree.ids.extend(changes.iter().map(|diff| *diff.id()));
21
22        // Check new.id == old.id
23        for change in &changes {
24            if let Some(old) = &change.old {
25                if old.id() != change.new.id() {
26                    return Err(SharedErrorKind::DiffMalformed.into());
27                }
28            }
29        }
30
31        // Check for changes to digest
32        for change in &changes {
33            match &change.old {
34                Some(old) => {
35                    if old.timestamped_value.value.document_hmac
36                        != change.new.timestamped_value.value.document_hmac
37                    {
38                        return Err(SharedErrorKind::HmacModificationInvalid.into());
39                    }
40                }
41                None => {
42                    if change.new.timestamped_value.value.document_hmac.is_some() {
43                        return Err(SharedErrorKind::HmacModificationInvalid.into());
44                    }
45                }
46            }
47        }
48
49        // Check for race conditions
50        for change in &changes {
51            match &change.old {
52                Some(old) => {
53                    let current = &self
54                        .maybe_find(old.id())
55                        .ok_or(SharedErrorKind::OldFileNotFound)?
56                        .file;
57                    if current != old {
58                        return Err(SharedErrorKind::OldVersionIncorrect.into());
59                    }
60                }
61                None => {
62                    if self.maybe_find(change.new.id()).is_some() {
63                        return Err(SharedErrorKind::OldVersionRequired.into());
64                    }
65                }
66            }
67        }
68
69        let now = get_time().0 as u64;
70        let changes = changes
71            .into_iter()
72            .map(|change| change.new.add_time(now))
73            .collect();
74
75        Ok(self.stage(changes))
76    }
77}