Skip to main content

lockbook_server_lib/
file_service.rs

1use crate::ServerError;
2use crate::ServerError::ClientError;
3use crate::billing::app_store_client::AppStoreClient;
4use crate::billing::google_play_client::GooglePlayClient;
5use crate::billing::stripe_client::StripeClient;
6use crate::defense::SERVER_BANDWIDTH_CAP;
7use crate::document_service::DocumentService;
8use crate::schema::ServerDb;
9
10use crate::{RequestContext, ServerState};
11use db_rs::Db;
12use lb_rs::model::api::{UpsertError, *};
13use lb_rs::model::clock::get_time;
14use lb_rs::model::crypto::Timestamped;
15use lb_rs::model::errors::{LbErrKind, LbResult};
16use lb_rs::model::file_like::FileLike;
17use lb_rs::model::file_metadata::{Diff, FileDiff, FileMetadata, Owner};
18use lb_rs::model::meta::Meta;
19use lb_rs::model::server_meta::{IntoServerMeta, ServerMeta};
20use lb_rs::model::server_tree::ServerTree;
21use lb_rs::model::signed_file::SignedFile;
22use lb_rs::model::tree_like::TreeLike;
23use std::collections::{HashMap, HashSet};
24use std::hash::Hash;
25use std::ops::DerefMut;
26use tracing::{debug, error, warn};
27
28impl<S, A, G, D> ServerState<S, A, G, D>
29where
30    S: StripeClient,
31    A: AppStoreClient,
32    G: GooglePlayClient,
33    D: DocumentService,
34{
35    pub async fn upsert_file_metadata(
36        &self, context: RequestContext<UpsertRequest>,
37    ) -> Result<(), ServerError<UpsertError>> {
38        let request = context.request;
39        let req_owner = Owner(context.public_key);
40
41        let mut new_deleted = vec![];
42        {
43            let mut prior_deleted = HashSet::new();
44            let mut current_deleted = HashSet::new();
45
46            let mut lock = self.index_db.lock().await;
47            let db = lock.deref_mut();
48            let tx = db.begin_transaction()?;
49
50            let usage_cap =
51                Self::get_cap(db, &context.public_key).map_err(|err| internal!("{:?}", err))?;
52
53            let mut tree = ServerTree::new(
54                req_owner,
55                &mut db.owned_files,
56                &mut db.shared_files,
57                &mut db.file_children,
58                &mut db.metas,
59            )?
60            .to_lazy();
61
62            let old_usage = Self::get_usage_helper(&mut tree)
63                .map_err(|err| internal!("{:?}", err))?
64                .iter()
65                .map(|f| f.size_bytes)
66                .sum::<u64>();
67
68            for id in tree.ids() {
69                if tree.calculate_deleted(&id)? {
70                    prior_deleted.insert(id);
71                }
72            }
73
74            let mut tree = tree.stage_diff(request.updates.clone())?;
75            for id in tree.ids() {
76                if tree.calculate_deleted(&id)? {
77                    current_deleted.insert(id);
78                }
79            }
80
81            tree.validate(req_owner)?;
82
83            let new_usage = Self::get_usage_helper(&mut tree)
84                .map_err(|err| internal!("{:?}", err))?
85                .iter()
86                .map(|f| f.size_bytes)
87                .sum::<u64>();
88
89            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on upsert");
90
91            if new_usage > usage_cap && new_usage >= old_usage {
92                warn!("usage over cap");
93                return Err(ClientError(UpsertError::UsageIsOverDataCap));
94            }
95
96            let tree = tree.promote()?;
97
98            for id in tree.ids() {
99                if tree.find(&id)?.is_document()
100                    && current_deleted.contains(&id)
101                    && !prior_deleted.contains(&id)
102                {
103                    let meta = tree.find(&id)?;
104                    if let Some(hmac) = meta.file.timestamped_value.value.document_hmac().copied() {
105                        new_deleted.push((*meta.id(), hmac));
106                    }
107                }
108            }
109
110            let all_files: Vec<ServerMeta> = tree.all_files()?.into_iter().cloned().collect();
111            for meta in all_files {
112                let id = meta.id();
113                if current_deleted.contains(id) && !prior_deleted.contains(id) {
114                    for user_access_info in meta.user_access_keys() {
115                        db.shared_files
116                            .remove(&Owner(user_access_info.encrypted_for), id)?;
117                    }
118                }
119            }
120
121            db.last_seen.insert(req_owner, get_time().0 as u64)?;
122
123            tx.drop_safely()?;
124        }
125
126        for (id, hmac) in new_deleted {
127            self.document_service.delete(&id, &hmac).await?;
128            let hmac = base64::encode_config(hmac, base64::URL_SAFE);
129            debug!(?id, ?hmac, "Deleted document contents");
130        }
131        Ok(())
132    }
133
134    pub async fn upsert_file_metadata_v2(
135        &self, context: RequestContext<UpsertRequestV2>,
136    ) -> Result<(), ServerError<UpsertError>> {
137        let request = context.request;
138        let req_owner = Owner(context.public_key);
139
140        {
141            let mut prior_deleted = HashSet::new();
142            let mut current_deleted = HashSet::new();
143
144            let mut lock = self.index_db.lock().await;
145            let db = lock.deref_mut();
146            let tx = db.begin_transaction()?;
147
148            let usage_cap =
149                Self::get_cap(db, &context.public_key).map_err(|err| internal!("{:?}", err))?;
150
151            let mut tree = ServerTree::new(
152                req_owner,
153                &mut db.owned_files,
154                &mut db.shared_files,
155                &mut db.file_children,
156                &mut db.metas,
157            )?
158            .to_lazy();
159
160            let old_usage = Self::get_usage_helper(&mut tree)
161                .map_err(|err| internal!("{:?}", err))?
162                .iter()
163                .map(|f| f.size_bytes)
164                .sum::<u64>();
165
166            for id in tree.ids() {
167                if tree.calculate_deleted(&id)? {
168                    prior_deleted.insert(id);
169                }
170            }
171
172            let mut tree = tree.stage_diff_v2(request.updates.clone())?;
173            for id in tree.ids() {
174                if tree.calculate_deleted(&id)? {
175                    current_deleted.insert(id);
176                }
177            }
178
179            tree.validate(req_owner)?;
180
181            let new_usage = Self::get_usage_helper(&mut tree)
182                .map_err(|err| internal!("{:?}", err))?
183                .iter()
184                .map(|f| f.size_bytes)
185                .sum::<u64>();
186
187            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on upsert");
188
189            if new_usage > usage_cap && new_usage >= old_usage {
190                warn!("user over cap");
191                return Err(ClientError(UpsertError::UsageIsOverDataCap));
192            }
193
194            let tree = tree.promote()?;
195
196            for id in tree.ids() {
197                if tree.find(&id)?.is_document()
198                    && current_deleted.contains(&id)
199                    && !prior_deleted.contains(&id)
200                {
201                    let meta = tree.find(&id)?;
202                    if let Some(hmac) = meta.file.timestamped_value.value.document_hmac().copied() {
203                        db.scheduled_file_cleanups
204                            .insert((*meta.id(), hmac), get_time().0)?;
205                    }
206                }
207            }
208
209            let all_files: Vec<ServerMeta> = tree.all_files()?.into_iter().cloned().collect();
210            for meta in all_files {
211                let id = meta.id();
212                if current_deleted.contains(id) && !prior_deleted.contains(id) {
213                    for user_access_info in meta.user_access_keys() {
214                        db.shared_files
215                            .remove(&Owner(user_access_info.encrypted_for), id)?;
216                    }
217                }
218            }
219
220            db.last_seen.insert(req_owner, get_time().0 as u64)?;
221
222            tx.drop_safely()?;
223        }
224
225        Ok(())
226    }
227
228    pub async fn change_doc(
229        &self, context: RequestContext<ChangeDocRequest>,
230    ) -> Result<(), ServerError<ChangeDocError>> {
231        use ChangeDocError::*;
232        let request = context.request;
233        let mut request = ChangeDocRequestV2 {
234            diff: FileDiff {
235                old: request.diff.old.map(|f| f.into()),
236                new: request.diff.new.into(),
237            },
238            new_content: request.new_content,
239        };
240
241        let owner = Owner(context.public_key);
242        let id = *request.diff.id();
243
244        // Validate Diff
245        if request.diff.diff() != vec![Diff::Hmac] {
246            return Err(ClientError(DiffMalformed));
247        }
248        let hmac = if let Some(hmac) = request.diff.new.document_hmac() {
249            base64::encode_config(hmac, base64::URL_SAFE)
250        } else {
251            return Err(ClientError(HmacMissing));
252        };
253
254        let req_pk = context.public_key;
255
256        {
257            let mut lock = self.index_db.lock().await;
258            let db = lock.deref_mut();
259            let usage_cap = Self::get_cap(db, &context.public_key)
260                .map_err(|err| internal!("{:?}", err))? as usize;
261
262            let meta = db
263                .metas
264                .get()
265                .get(request.diff.new.id())
266                .ok_or(ClientError(DocumentNotFound))?
267                .clone();
268
269            let mut tree = ServerTree::new(
270                owner,
271                &mut db.owned_files,
272                &mut db.shared_files,
273                &mut db.file_children,
274                &mut db.metas,
275            )?
276            .to_lazy();
277
278            let old_usage = Self::get_usage_helper(&mut tree)
279                .map_err(|err| internal!("{:?}", err))?
280                .iter()
281                .map(|f| f.size_bytes)
282                .sum::<u64>() as usize;
283            let old_size = *meta.file.timestamped_value.value.doc_size();
284
285            // populate sizes in request
286            if let Some(old) = &mut request.diff.old {
287                match &mut old.timestamped_value.value {
288                    Meta::V1 { doc_size, .. } => {
289                        *doc_size = old_size;
290                    }
291                }
292            }
293
294            match &mut request.diff.new.timestamped_value.value {
295                Meta::V1 { doc_size, .. } => {
296                    *doc_size = Some(request.new_content.value.len());
297                }
298            }
299
300            let new_size = request.new_content.value.len();
301
302            let new_usage = old_usage - old_size.unwrap_or_default() + new_size;
303
304            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on change doc");
305
306            if new_usage > usage_cap {
307                return Err(ClientError(UsageIsOverDataCap));
308            }
309
310            let meta_owner = meta.owner();
311
312            let direct_access = meta_owner.0 == req_pk;
313
314            if tree.maybe_find(request.diff.new.id()).is_none() {
315                return Err(ClientError(NotPermissioned));
316            }
317
318            let mut share_access = false;
319            if !direct_access {
320                for ancestor in tree
321                    .ancestors(request.diff.id())?
322                    .iter()
323                    .chain(vec![request.diff.new.id()])
324                {
325                    let meta = tree.find(ancestor)?;
326
327                    if meta
328                        .user_access_keys()
329                        .iter()
330                        .any(|access| access.encrypted_for == req_pk)
331                    {
332                        share_access = true;
333                        break;
334                    }
335                }
336            }
337
338            if !direct_access && !share_access {
339                return Err(ClientError(NotPermissioned));
340            }
341
342            let meta = &tree
343                .maybe_find(request.diff.new.id())
344                .ok_or(ClientError(DocumentNotFound))?
345                .file;
346
347            if let Some(old) = &request.diff.old {
348                if meta != old {
349                    return Err(ClientError(OldVersionIncorrect));
350                }
351            }
352
353            if tree.calculate_deleted(request.diff.new.id())? {
354                return Err(ClientError(DocumentDeleted));
355            }
356
357            // Here is where you would check if the person is out of space as a result of the new file.
358            // You could make this a transaction and check whether or not this is an increase in size or
359            // a reduction
360        }
361
362        let new_version = get_time().0 as u64;
363        let new = request.diff.new.clone().add_time(new_version);
364        self.document_service
365            .insert(
366                request.diff.new.id(),
367                request.diff.new.document_hmac().unwrap(),
368                &request.new_content,
369            )
370            .await?;
371        debug!(?id, ?hmac, "Inserted document contents");
372
373        let result = async {
374            let mut lock = self.index_db.lock().await;
375            let db = lock.deref_mut();
376            let tx = db.begin_transaction()?;
377
378            let mut tree = ServerTree::new(
379                owner,
380                &mut db.owned_files,
381                &mut db.shared_files,
382                &mut db.file_children,
383                &mut db.metas,
384            )?
385            .to_lazy();
386
387            if tree.calculate_deleted(request.diff.new.id())? {
388                return Err(ClientError(DocumentDeleted));
389            }
390
391            let meta = &tree
392                .maybe_find(request.diff.new.id())
393                .ok_or(ClientError(DocumentNotFound))?
394                .file;
395
396            if let Some(old) = &request.diff.old {
397                if meta != old {
398                    return Err(ClientError(OldVersionIncorrect));
399                }
400            }
401
402            tree.stage(vec![new]).promote()?;
403            db.last_seen.insert(owner, get_time().0 as u64)?;
404
405            tx.drop_safely()?;
406            drop(lock);
407            Ok(())
408        };
409
410        let result = result.await;
411
412        if result.is_err() {
413            // Cleanup the NEW file created if, for some reason, the tx failed
414            self.document_service
415                .delete(request.diff.new.id(), request.diff.new.document_hmac().unwrap())
416                .await?;
417            debug!(?id, ?hmac, "Cleaned up new document contents after failed metadata update");
418        }
419
420        result?;
421
422        // New
423        if let Some(hmac) = request.diff.old.unwrap().document_hmac() {
424            self.document_service
425                .delete(request.diff.new.id(), hmac)
426                .await?;
427            let old_hmac = base64::encode_config(hmac, base64::URL_SAFE);
428            debug!(
429                ?id,
430                ?old_hmac,
431                "Cleaned up old document contents after successful metadata update"
432            );
433        }
434
435        Ok(())
436    }
437
438    pub async fn change_doc_v2(
439        &self, context: RequestContext<ChangeDocRequestV2>,
440    ) -> Result<(), ServerError<ChangeDocError>> {
441        use ChangeDocError::*;
442        let owner = Owner(context.public_key);
443        let request = context.request;
444        let id = *request.diff.id();
445
446        // Validate Diff
447        if request.diff.diff() != vec![Diff::Hmac] {
448            return Err(ClientError(DiffMalformed));
449        }
450
451        match request.diff.new.timestamped_value.value.doc_size() {
452            Some(size) => {
453                if *size != request.new_content.value.len() {
454                    return Err(ClientError(NewSizeIncorrect));
455                }
456            }
457            None => {
458                // do we even want to support this?
459                if !request.new_content.value.is_empty() {
460                    return Err(ClientError(NewSizeIncorrect));
461                }
462            }
463        }
464
465        let hmac = if let Some(hmac) = request.diff.new.document_hmac() {
466            base64::encode_config(hmac, base64::URL_SAFE)
467        } else {
468            return Err(ClientError(HmacMissing));
469        };
470
471        let req_pk = context.public_key;
472
473        {
474            let mut lock = self.index_db.lock().await;
475            let db = lock.deref_mut();
476            let usage_cap = Self::get_cap(db, &context.public_key)
477                .map_err(|err| internal!("{:?}", err))? as usize;
478
479            let meta = db
480                .metas
481                .get()
482                .get(request.diff.new.id())
483                .ok_or(ClientError(DocumentNotFound))?
484                .clone();
485
486            let mut tree = ServerTree::new(
487                owner,
488                &mut db.owned_files,
489                &mut db.shared_files,
490                &mut db.file_children,
491                &mut db.metas,
492            )?
493            .to_lazy();
494
495            let old_usage = Self::get_usage_helper(&mut tree)
496                .map_err(|err| internal!("{:?}", err))?
497                .iter()
498                .map(|f| f.size_bytes)
499                .sum::<u64>() as usize;
500            let old_size = *meta.file.timestamped_value.value.doc_size();
501
502            let new_size = request.new_content.value.len();
503
504            let new_usage = old_usage - old_size.unwrap_or_default() + new_size;
505
506            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on change doc");
507
508            if new_usage > usage_cap {
509                warn!("user over cap");
510                return Err(ClientError(UsageIsOverDataCap));
511            }
512
513            let meta_owner = meta.owner();
514
515            let direct_access = meta_owner.0 == req_pk;
516
517            if tree.maybe_find(request.diff.new.id()).is_none() {
518                return Err(ClientError(NotPermissioned));
519            }
520
521            let mut share_access = false;
522            if !direct_access {
523                for ancestor in tree
524                    .ancestors(request.diff.id())?
525                    .iter()
526                    .chain(vec![request.diff.new.id()])
527                {
528                    let meta = tree.find(ancestor)?;
529
530                    if meta
531                        .user_access_keys()
532                        .iter()
533                        .any(|access| access.encrypted_for == req_pk)
534                    {
535                        share_access = true;
536                        break;
537                    }
538                }
539            }
540
541            if !direct_access && !share_access {
542                return Err(ClientError(NotPermissioned));
543            }
544
545            let meta = &tree
546                .maybe_find(request.diff.new.id())
547                .ok_or(ClientError(DocumentNotFound))?
548                .file;
549
550            if let Some(old) = &request.diff.old {
551                if meta != old {
552                    return Err(ClientError(OldVersionIncorrect));
553                }
554            }
555
556            if tree.calculate_deleted(request.diff.new.id())? {
557                return Err(ClientError(DocumentDeleted));
558            }
559
560            let id = request.diff.new.id();
561            let hmac = request.diff.new.document_hmac().unwrap();
562            db.scheduled_file_cleanups.remove(&(*id, *hmac))?;
563        }
564
565        let new_version = get_time().0 as u64;
566        let new = request.diff.new.clone().add_time(new_version);
567        self.document_service
568            .insert(
569                request.diff.new.id(),
570                request.diff.new.document_hmac().unwrap(),
571                &request.new_content,
572            )
573            .await?;
574        debug!(?id, ?hmac, "Inserted document contents");
575
576        let result = async {
577            let mut lock = self.index_db.lock().await;
578            let db = lock.deref_mut();
579            let tx = db.begin_transaction()?;
580
581            let mut tree = ServerTree::new(
582                owner,
583                &mut db.owned_files,
584                &mut db.shared_files,
585                &mut db.file_children,
586                &mut db.metas,
587            )?
588            .to_lazy();
589
590            if tree.calculate_deleted(request.diff.new.id())? {
591                return Err(ClientError(DocumentDeleted));
592            }
593
594            let meta = &tree
595                .maybe_find(request.diff.new.id())
596                .ok_or(ClientError(DocumentNotFound))?
597                .file;
598
599            if let Some(old) = &request.diff.old {
600                if meta != old {
601                    return Err(ClientError(OldVersionIncorrect));
602                }
603            }
604
605            tree.stage(vec![new]).promote()?;
606            if let Some(old) = &request
607                .diff
608                .old
609                .and_then(|old| old.document_hmac().copied())
610            {
611                db.scheduled_file_cleanups
612                    .insert((*request.diff.new.id(), *old), get_time().0)?;
613            }
614
615            tx.drop_safely()?;
616            drop(lock);
617            Ok(())
618        };
619
620        let result = result.await;
621
622        if result.is_err() {
623            // Cleanup the NEW file created if, for some reason, the tx failed
624            self.document_service
625                .delete(request.diff.new.id(), request.diff.new.document_hmac().unwrap())
626                .await?;
627            debug!(?id, ?hmac, "Cleaned up new document contents after failed metadata update");
628        }
629
630        result?;
631
632        Ok(())
633    }
634
635    pub async fn get_document(
636        &self, context: RequestContext<GetDocRequest>,
637    ) -> Result<GetDocumentResponse, ServerError<GetDocumentError>> {
638        let request = &context.request;
639        let requester = Owner(context.public_key);
640        {
641            let mut lock = self.index_db.lock().await;
642            let db = lock.deref_mut();
643            let tx = db.begin_transaction()?;
644
645            let meta_exists = db.metas.get().get(&request.id).is_some();
646
647            let mut tree = ServerTree::new(
648                requester,
649                &mut db.owned_files,
650                &mut db.shared_files,
651                &mut db.file_children,
652                &mut db.metas,
653            )?
654            .to_lazy();
655
656            if tree.maybe_find(&request.id).is_none() {
657                return Err(if meta_exists {
658                    ClientError(GetDocumentError::NotPermissioned)
659                } else {
660                    ClientError(GetDocumentError::DocumentNotFound)
661                });
662            }
663
664            if tree.calculate_deleted(&request.id)? {
665                return Err(ClientError(GetDocumentError::DocumentNotFound));
666            }
667
668            tx.drop_safely()?;
669        };
670
671        let Some(content) = self
672            .document_service
673            .maybe_get(&request.id, &request.hmac)
674            .await?
675        else {
676            return Err(ClientError(GetDocumentError::DocumentNotFound));
677        };
678
679        let mut lock = self.index_db.lock().await;
680        let db = lock.deref_mut();
681        let tx = db.begin_transaction()?;
682
683        if self.config.features.bandwidth_controls {
684            let mut server_wide = db.server_egress.get().cloned().unwrap_or_default();
685            let mut account_bandwidth = db
686                .egress_by_owner
687                .get()
688                .get(&requester)
689                .cloned()
690                .unwrap_or_default();
691            let account_bandwidth_cap = db
692                .accounts
693                .get()
694                .get(&requester)
695                .map(|account| account.billing_info.bandwidth_cap())
696                .unwrap_or_default();
697
698            let doc_size = content.value.len();
699
700            if doc_size + server_wide.current_bandwidth() > SERVER_BANDWIDTH_CAP {
701                error!("Bandwidth caps are now being enforced");
702                if doc_size + account_bandwidth.current_bandwidth() > account_bandwidth_cap {
703                    error!("User bandwidth cap exceeded");
704                    return Err(ClientError(GetDocumentError::BandwidthExceeded));
705                }
706            }
707
708            server_wide.increase_by(doc_size);
709            account_bandwidth.increase_by(doc_size);
710
711            db.server_egress.insert(server_wide)?;
712            db.egress_by_owner.insert(requester, account_bandwidth)?;
713        }
714
715        tx.drop_safely()?;
716
717        Ok(GetDocumentResponse { content })
718    }
719
720    pub async fn get_file_ids(
721        &self, context: RequestContext<GetFileIdsRequest>,
722    ) -> Result<GetFileIdsResponse, ServerError<GetFileIdsError>> {
723        let owner = Owner(context.public_key);
724        let mut db = self.index_db.lock().await;
725        let db = db.deref_mut();
726
727        Ok(GetFileIdsResponse {
728            ids: ServerTree::new(
729                owner,
730                &mut db.owned_files,
731                &mut db.shared_files,
732                &mut db.file_children,
733                &mut db.metas,
734            )?
735            .ids()
736            .into_iter()
737            .collect(),
738        })
739    }
740
741    pub async fn get_updates(
742        &self, context: RequestContext<GetUpdatesRequest>,
743    ) -> Result<GetUpdatesResponse, ServerError<GetUpdatesError>> {
744        let request = &context.request;
745        let owner = Owner(context.public_key);
746
747        let mut db = self.index_db.lock().await;
748        let db = db.deref_mut();
749        let mut tree = ServerTree::new(
750            owner,
751            &mut db.owned_files,
752            &mut db.shared_files,
753            &mut db.file_children,
754            &mut db.metas,
755        )?
756        .to_lazy();
757
758        let mut result_ids = HashSet::new();
759        for id in tree.ids() {
760            let file = tree.find(&id)?;
761            if file.version >= request.since_metadata_version {
762                result_ids.insert(id);
763                if file.owner() != owner
764                    && file
765                        .user_access_keys()
766                        .iter()
767                        .any(|k| !k.deleted && k.encrypted_for == context.public_key)
768                {
769                    result_ids.insert(id);
770                    result_ids.extend(tree.descendants(&id)?);
771                }
772            }
773        }
774
775        Ok(GetUpdatesResponse {
776            as_of_metadata_version: get_time().0 as u64,
777            file_metadata: tree
778                .all_files()?
779                .iter()
780                .filter(|meta| result_ids.contains(meta.id()))
781                .map(|meta| match meta.file.timestamped_value.value.clone() {
782                    Meta::V1 {
783                        id,
784                        file_type,
785                        parent,
786                        name,
787                        owner,
788                        is_deleted,
789                        doc_size: _,
790                        doc_hmac,
791                        user_access_keys,
792                        folder_access_key,
793                    } => SignedFile {
794                        timestamped_value: Timestamped {
795                            timestamp: meta.file.timestamped_value.timestamp,
796                            value: FileMetadata {
797                                id,
798                                file_type,
799                                parent,
800                                name,
801                                owner,
802                                is_deleted,
803                                document_hmac: doc_hmac,
804                                user_access_keys,
805                                folder_access_key,
806                            },
807                        },
808                        signature: meta.file.signature.clone(),
809                        public_key: meta.file.public_key,
810                    },
811                })
812                .collect(),
813        })
814    }
815
816    pub async fn get_updates_v2(
817        &self, context: RequestContext<GetUpdatesRequestV2>,
818    ) -> Result<GetUpdatesResponseV2, ServerError<GetUpdatesError>> {
819        let request = &context.request;
820        let owner = Owner(context.public_key);
821
822        let mut db = self.index_db.lock().await;
823        let db = db.deref_mut();
824        let mut tree = ServerTree::new(
825            owner,
826            &mut db.owned_files,
827            &mut db.shared_files,
828            &mut db.file_children,
829            &mut db.metas,
830        )?
831        .to_lazy();
832
833        let mut result_ids = HashSet::new();
834        for id in tree.ids() {
835            let file = tree.find(&id)?;
836            if file.version >= request.since_metadata_version {
837                result_ids.insert(id);
838                if file.owner() != owner
839                    && file
840                        .user_access_keys()
841                        .iter()
842                        .any(|k| !k.deleted && k.encrypted_for == context.public_key)
843                {
844                    result_ids.insert(id);
845                    result_ids.extend(tree.descendants(&id)?);
846                }
847            }
848        }
849
850        Ok(GetUpdatesResponseV2 {
851            as_of_metadata_version: get_time().0 as u64,
852            file_metadata: tree
853                .all_files()?
854                .into_iter()
855                .filter(|meta| result_ids.contains(meta.id()))
856                .map(|meta| meta.file.clone())
857                .collect(),
858        })
859    }
860
861    pub async fn admin_disappear_file(
862        &self, context: RequestContext<AdminDisappearFileRequest>,
863    ) -> Result<(), ServerError<AdminDisappearFileError>> {
864        let mut docs_to_delete = Vec::new();
865
866        {
867            let mut db = self.index_db.lock().await;
868            let db = db.deref_mut();
869            let tx = db.begin_transaction()?;
870
871            if !Self::is_admin::<AdminDisappearFileError>(
872                db,
873                &context.public_key,
874                &self.config.admin.admins,
875            )? {
876                return Err(ClientError(AdminDisappearFileError::NotPermissioned));
877            }
878
879            let owner = {
880                let meta = db
881                    .metas
882                    .get()
883                    .get(&context.request.id)
884                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
885                if meta.is_root() {
886                    return Err(ClientError(AdminDisappearFileError::RootModificationInvalid));
887                }
888                meta.owner()
889            };
890            let mut tree = ServerTree::new(
891                owner,
892                &mut db.owned_files,
893                &mut db.shared_files,
894                &mut db.file_children,
895                &mut db.metas,
896            )?
897            .to_lazy();
898
899            let metas_to_delete = {
900                let mut metas_to_delete = tree.descendants(&context.request.id)?;
901                metas_to_delete.insert(context.request.id);
902                metas_to_delete
903            };
904            for id in metas_to_delete.clone() {
905                if !tree.calculate_deleted(&id)? {
906                    let meta = tree.find(&id)?;
907                    if meta.is_document() && meta.owner() == owner {
908                        if let Some(hmac) = meta.document_hmac() {
909                            docs_to_delete.push((*meta.id(), *hmac));
910                        }
911                    }
912                }
913            }
914
915            for id in metas_to_delete {
916                let meta = db
917                    .metas
918                    .remove(&id)?
919                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
920
921                // maintain index: owned_files
922                let owner = meta.owner();
923
924                if !db.owned_files.remove(&owner, &id)? {
925                    error!(
926                        ?id,
927                        ?owner,
928                        "attempted to disappear a file, owner or id not present in owned_files"
929                    );
930                }
931
932                // maintain index: shared_files
933                for user_access_key in meta.user_access_keys() {
934                    let sharee = Owner(user_access_key.encrypted_for);
935                    if !db.shared_files.remove(&sharee, &id)? {
936                        error!(
937                            ?id,
938                            ?sharee,
939                            "attempted to disappear a file, a sharee didn't have it shared"
940                        );
941                    }
942                }
943
944                // maintain index: file_children
945                let parent = *meta.parent();
946                if !db.file_children.remove(meta.parent(), &id)? {
947                    error!(
948                        ?id,
949                        ?parent,
950                        "attempted to disappear a file, the parent didn't have it as a child"
951                    );
952                }
953            }
954
955            let username = db
956                .accounts
957                .get()
958                .get(&Owner(context.public_key))
959                .map(|account| account.username.clone())
960                .unwrap_or_else(|| "~unknown~".to_string());
961            warn!(?username, ?context.request.id, "Disappeared file");
962
963            tx.drop_safely()?;
964        }
965
966        for (id, version) in docs_to_delete {
967            self.document_service.delete(&id, &version).await?;
968        }
969
970        Ok(())
971    }
972
973    pub async fn admin_validate_account(
974        &self, context: RequestContext<AdminValidateAccountRequest>,
975    ) -> Result<AdminValidateAccount, ServerError<AdminValidateAccountError>> {
976        let request = &context.request;
977        let mut db = self.index_db.lock().await;
978        if !Self::is_admin::<AdminValidateAccountError>(
979            &db,
980            &context.public_key,
981            &self.config.admin.admins,
982        )? {
983            return Err(ClientError(AdminValidateAccountError::NotPermissioned));
984        }
985
986        let owner = *db
987            .usernames
988            .get()
989            .get(&request.username)
990            .ok_or(ClientError(AdminValidateAccountError::UserNotFound))?;
991
992        Ok(self.validate_account_helper(&mut db, owner)?)
993    }
994
995    pub fn validate_account_helper(
996        &self, db: &mut ServerDb, owner: Owner,
997    ) -> LbResult<AdminValidateAccount> {
998        let mut result = AdminValidateAccount::default();
999
1000        let mut tree = ServerTree::new(
1001            owner,
1002            &mut db.owned_files,
1003            &mut db.shared_files,
1004            &mut db.file_children,
1005            &mut db.metas,
1006        )?
1007        .to_lazy();
1008
1009        for id in tree.ids() {
1010            if !tree.calculate_deleted(&id)? {
1011                let file = tree.find(&id)?;
1012                if file.is_document() && file.document_hmac().is_some() {
1013                    if file.file.timestamped_value.value.doc_size().is_none() {
1014                        result.documents_missing_size.push(id);
1015                    }
1016
1017                    if !self
1018                        .document_service
1019                        .exists(&id, file.document_hmac().unwrap())
1020                    {
1021                        result.documents_missing_content.push(id);
1022                    }
1023                }
1024            }
1025        }
1026
1027        let validation_res = tree.stage(None).validate(owner);
1028        match validation_res {
1029            Ok(_) => {}
1030            Err(err) => match err.kind {
1031                LbErrKind::Validation(validation) => {
1032                    result.tree_validation_failures.push(validation)
1033                }
1034                _ => {
1035                    error!(?owner, ?err, "Unexpected error while validating tree")
1036                }
1037            },
1038        }
1039
1040        Ok(result)
1041    }
1042
1043    pub async fn admin_validate_server(
1044        &self, context: RequestContext<AdminValidateServerRequest>,
1045    ) -> Result<AdminValidateServer, ServerError<AdminValidateServerError>> {
1046        let mut db = self.index_db.lock().await;
1047        let db = db.deref_mut();
1048
1049        if !Self::is_admin::<AdminValidateServerError>(
1050            db,
1051            &context.public_key,
1052            &self.config.admin.admins,
1053        )? {
1054            return Err(ClientError(AdminValidateServerError::NotPermissioned));
1055        }
1056
1057        let mut result: AdminValidateServer = Default::default();
1058
1059        let mut deleted_ids = HashSet::new();
1060        for (id, meta) in db.metas.get().clone() {
1061            // todo: optimize
1062            let mut tree = ServerTree::new(
1063                meta.owner(),
1064                &mut db.owned_files,
1065                &mut db.shared_files,
1066                &mut db.file_children,
1067                &mut db.metas,
1068            )?
1069            .to_lazy();
1070            if tree.calculate_deleted(&id)? {
1071                deleted_ids.insert(id);
1072            }
1073        }
1074
1075        // validate accounts
1076        for (owner, account) in db.accounts.get().clone() {
1077            let validation = self.validate_account_helper(db, owner)?;
1078            if !validation.is_empty() {
1079                result
1080                    .users_with_validation_failures
1081                    .insert(account.username, validation);
1082            }
1083        }
1084
1085        // validate index: usernames
1086        for (username, owner) in db.usernames.get().clone() {
1087            if let Some(account) = db.accounts.get().get(&owner) {
1088                if username != account.username {
1089                    result
1090                        .usernames_mapped_to_wrong_accounts
1091                        .insert(username, account.username.clone());
1092                }
1093            } else {
1094                result
1095                    .usernames_mapped_to_nonexistent_accounts
1096                    .insert(username, owner);
1097            }
1098        }
1099        for (_, account) in db.accounts.get().clone() {
1100            if db.usernames.get().get(&account.username).is_none() {
1101                result
1102                    .usernames_unmapped_to_accounts
1103                    .insert(account.username.clone());
1104            }
1105        }
1106
1107        // validate index: owned_files
1108        for (owner, ids) in db.owned_files.get().clone() {
1109            for id in ids {
1110                if let Some(meta) = db.metas.get().get(&id) {
1111                    if meta.owner() != owner {
1112                        insert(&mut result.owners_mapped_to_unowned_files, owner, id);
1113                    }
1114                } else {
1115                    insert(&mut result.owners_mapped_to_nonexistent_files, owner, id);
1116                }
1117            }
1118        }
1119        for (id, meta) in db.metas.get().clone() {
1120            if let Some(ids) = db.owned_files.get().get(&meta.owner()) {
1121                if !ids.contains(&id) {
1122                    insert(&mut result.owners_unmapped_to_owned_files, meta.owner(), *meta.id());
1123                }
1124            } else {
1125                result.owners_unmapped.insert(meta.owner());
1126            }
1127        }
1128
1129        // validate index: shared_files
1130        for (sharee, ids) in db.shared_files.get().clone() {
1131            for id in ids {
1132                if let Some(meta) = db.metas.get().get(&id) {
1133                    if !meta.user_access_keys().iter().any(|k| {
1134                        !k.deleted && k.encrypted_for == sharee.0 && k.encrypted_by != sharee.0
1135                    }) {
1136                        insert(&mut result.sharees_mapped_to_unshared_files, sharee, id);
1137                    }
1138                } else {
1139                    insert(&mut result.sharees_mapped_to_nonexistent_files, sharee, id);
1140                }
1141                if deleted_ids.contains(&id) {
1142                    insert(&mut result.sharees_mapped_for_deleted_files, sharee, id);
1143                }
1144            }
1145        }
1146        for (id, meta) in db.metas.get().clone() {
1147            // check for implicit deletion (can't use server tree which depends on index)
1148            let mut deleted = false;
1149            let mut ancestor = meta.clone();
1150            loop {
1151                if ancestor.explicitly_deleted() {
1152                    deleted = true;
1153                    break;
1154                }
1155                if ancestor.is_root() {
1156                    break;
1157                }
1158                match db.metas.get().get(ancestor.parent()) {
1159                    Some(parent) => ancestor = parent.clone(),
1160                    None => {
1161                        error!("missing parent for file {:?}", ancestor.parent());
1162                        deleted = true;
1163                        break;
1164                    }
1165                }
1166            }
1167            if deleted {
1168                continue;
1169            }
1170
1171            for k in meta.user_access_keys() {
1172                if k.deleted {
1173                    continue;
1174                }
1175                let sharee = Owner(k.encrypted_for);
1176                if let Some(ids) = db.shared_files.get().get(&sharee) {
1177                    let self_share = k.encrypted_for == k.encrypted_by;
1178                    let indexed_share = ids.contains(&id);
1179                    if self_share && indexed_share {
1180                        insert(&mut result.sharees_mapped_for_owned_files, sharee, id);
1181                    } else if !self_share && !indexed_share {
1182                        insert(&mut result.sharees_unmapped_to_shared_files, sharee, id);
1183                    }
1184                } else {
1185                    result.sharees_unmapped.insert(meta.owner());
1186                }
1187            }
1188        }
1189
1190        // validate index: file_children
1191        for (parent_id, child_ids) in db.file_children.get().clone() {
1192            for child_id in child_ids {
1193                if let Some(meta) = db.metas.get().get(&child_id) {
1194                    if meta.parent() != &parent_id {
1195                        insert(
1196                            &mut result.files_mapped_as_parent_to_non_children,
1197                            parent_id,
1198                            child_id,
1199                        );
1200                    }
1201                } else {
1202                    insert(
1203                        &mut result.files_mapped_as_parent_to_nonexistent_children,
1204                        parent_id,
1205                        child_id,
1206                    );
1207                }
1208            }
1209        }
1210        for (id, meta) in db.metas.get().clone() {
1211            if let Some(child_ids) = db.file_children.get().get(meta.parent()) {
1212                if meta.is_root() && child_ids.contains(&id) {
1213                    result.files_mapped_as_parent_to_self.insert(id);
1214                } else if !meta.is_root() && !child_ids.contains(&id) {
1215                    insert(&mut result.files_unmapped_as_parent_to_children, *meta.parent(), id);
1216                }
1217            } else {
1218                result.files_unmapped_as_parent.insert(*meta.parent());
1219            }
1220        }
1221
1222        // validate presence of documents
1223        for (id, meta) in db.metas.get().clone() {
1224            if let Some(hmac) = meta.document_hmac() {
1225                if !deleted_ids.contains(&id) && !self.document_service.exists(&id, hmac) {
1226                    result.files_with_hmacs_and_no_contents.insert(id);
1227                }
1228            }
1229        }
1230
1231        Ok(result)
1232    }
1233
1234    pub async fn admin_file_info(
1235        &self, context: RequestContext<AdminFileInfoRequest>,
1236    ) -> Result<AdminFileInfoResponse, ServerError<AdminFileInfoError>> {
1237        let request = &context.request;
1238        let mut db = self.index_db.lock().await;
1239        let db = db.deref_mut();
1240        if !Self::is_admin::<AdminFileInfoError>(
1241            db,
1242            &context.public_key,
1243            &self.config.admin.admins,
1244        )? {
1245            return Err(ClientError(AdminFileInfoError::NotPermissioned));
1246        }
1247
1248        let file = db
1249            .metas
1250            .get()
1251            .get(&request.id)
1252            .ok_or(ClientError(AdminFileInfoError::FileNonexistent))?
1253            .clone();
1254
1255        let mut tree = ServerTree::new(
1256            file.owner(),
1257            &mut db.owned_files,
1258            &mut db.shared_files,
1259            &mut db.file_children,
1260            &mut db.metas,
1261        )?
1262        .to_lazy();
1263
1264        let ancestors = tree
1265            .ancestors(&request.id)?
1266            .into_iter()
1267            .filter_map(|id| tree.maybe_find(&id))
1268            .cloned()
1269            .collect();
1270        let descendants = tree
1271            .descendants(&request.id)?
1272            .into_iter()
1273            .filter_map(|id| tree.maybe_find(&id))
1274            .cloned()
1275            .collect();
1276
1277        Ok(AdminFileInfoResponse { file, ancestors, descendants })
1278    }
1279
1280    pub async fn admin_rebuild_index(
1281        &self, context: RequestContext<AdminRebuildIndexRequest>,
1282    ) -> Result<(), ServerError<AdminRebuildIndexError>> {
1283        let mut db = self.index_db.lock().await;
1284
1285        match context.request.index {
1286            ServerIndex::OwnedFiles => {
1287                db.owned_files.clear()?;
1288                for owner in db.accounts.get().clone().keys() {
1289                    db.owned_files.create_key(*owner)?;
1290                }
1291                for (id, file) in db.metas.get().clone() {
1292                    db.owned_files.insert(file.owner(), id)?;
1293                }
1294            }
1295            ServerIndex::SharedFiles => {
1296                db.shared_files.clear()?;
1297                for owner in db.accounts.get().clone().keys() {
1298                    db.shared_files.create_key(*owner)?;
1299                }
1300                for (id, file) in db.metas.get().clone() {
1301                    // check for implicit deletion (can't use server tree which depends on index)
1302                    let mut deleted = false;
1303                    let mut ancestor = file.clone();
1304                    loop {
1305                        if ancestor.explicitly_deleted() {
1306                            deleted = true;
1307                            break;
1308                        }
1309                        if ancestor.is_root() {
1310                            break;
1311                        }
1312                        match db.metas.get().get(ancestor.parent()) {
1313                            Some(parent) => ancestor = parent.clone(),
1314                            None => {
1315                                error!("missing parent for file {:?}", ancestor.parent());
1316                                deleted = true;
1317                                break;
1318                            }
1319                        }
1320                    }
1321
1322                    if !deleted {
1323                        for user_access_key in file.user_access_keys() {
1324                            if !user_access_key.deleted
1325                                && user_access_key.encrypted_for != user_access_key.encrypted_by
1326                            {
1327                                db.shared_files
1328                                    .insert(Owner(user_access_key.encrypted_for), id)?;
1329                            }
1330                        }
1331                    }
1332                }
1333            }
1334            ServerIndex::FileChildren => {
1335                db.file_children.clear()?;
1336                for id in db.metas.get().clone().keys() {
1337                    db.file_children.create_key(*id)?;
1338                }
1339                for (id, file) in db.metas.get().clone() {
1340                    db.file_children.insert(*file.parent(), id)?;
1341                }
1342            }
1343        }
1344        Ok(())
1345    }
1346}
1347
1348fn insert<K: Hash + Eq, V: Hash + Eq>(map: &mut HashMap<K, HashSet<V>>, k: K, v: V) {
1349    map.entry(k).or_default().insert(v);
1350}