1use crate::Lb;
2use crate::model::api::GetPublicKeyRequest;
3use crate::model::errors::{LbErr, LbErrKind, LbResult};
4use crate::model::file::{File, ShareMode};
5use crate::model::file_like::FileLike;
6use crate::model::file_metadata::Owner;
7use crate::model::tree_like::TreeLike;
8use crate::service::events::Actor;
9use libsecp256k1::PublicKey;
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet};
12use uuid::Uuid;
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17pub struct SharingContact {
18 pub username: String,
19 pub outgoing_file_count: u64,
20 pub incoming_file_count: u64,
21 pub total_file_count: u64,
23}
24
25impl Lb {
26 #[instrument(level = "debug", skip(self))]
30 pub async fn get_sharing_contacts(&self) -> LbResult<Vec<SharingContact>> {
31 let me = self.keychain.get_pk()?;
32 let tx = self.ro_tx().await;
33 let db = tx.db();
34 let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
35 let mut counts: HashMap<Owner, (u64, u64, u64)> = HashMap::new();
36 let root = db.root.as_ref().ok_or(LbErrKind::RootNonexistent)?;
37 let mut document_ids = tree.descendants_using_links(root)?;
40 document_ids.extend(tree.non_deleted_pending_files(&self.keychain)?);
41 for id in document_ids {
42 if !tree.find(&id)?.is_document() {
43 continue;
44 }
45 let mut outgoing = HashSet::new();
46 let mut incoming = HashSet::new();
47 let mut visited = HashSet::new();
48 let mut current = id;
49 let mut deleted = false;
50 while visited.insert(current) {
51 let Some(file) = tree.maybe_find(¤t) else { break };
52 if file.explicitly_deleted() {
53 deleted = true;
54 break;
55 }
56 for access in file.user_access_keys() {
57 if access.deleted || access.encrypted_by == access.encrypted_for {
58 continue;
59 }
60 if access.encrypted_by == me {
61 outgoing.insert(Owner(access.encrypted_for));
62 } else if access.encrypted_for == me {
63 incoming.insert(Owner(access.encrypted_by));
64 }
65 }
66 if file.is_root() {
67 break;
68 }
69 current = *file.parent();
70 }
71 if deleted {
72 continue;
73 }
74 for user in outgoing.union(&incoming) {
75 let count = counts.entry(*user).or_default();
76 count.0 += u64::from(outgoing.contains(user));
77 count.1 += u64::from(incoming.contains(user));
78 count.2 += 1;
79 }
80 }
81 let mut contacts: Vec<_> = counts
82 .into_iter()
83 .filter_map(|(owner, (outgoing, incoming, total))| {
84 let username = db.pub_key_lookup.get(&owner)?.clone();
85 let trimmed = username.trim();
86 if trimmed.is_empty() || trimmed.eq_ignore_ascii_case("<unknown>") {
87 return None;
88 }
89 Some(SharingContact {
90 username,
91 outgoing_file_count: outgoing,
92 incoming_file_count: incoming,
93 total_file_count: total,
94 })
95 })
96 .collect();
97 contacts.sort_by(|a, b| {
98 b.total_file_count
99 .cmp(&a.total_file_count)
100 .then_with(|| a.username.cmp(&b.username))
101 });
102 Ok(contacts)
103 }
104
105 #[instrument(level = "debug", skip(self))]
107 pub async fn share_file(&self, id: Uuid, username: &str, mode: ShareMode) -> LbResult<()> {
108 let account = self.get_account()?;
109 let username = username.to_lowercase();
110
111 let sharee = Owner(
112 self.client
113 .request(account, GetPublicKeyRequest { username: username.clone() })
114 .await
115 .map_err(LbErr::from)?
116 .key,
117 );
118
119 let mut tx = self.begin_tx().await;
120 let db = tx.db();
121
122 let mut tree = (&db.base_metadata)
123 .to_staged(&mut db.local_metadata)
124 .to_lazy();
125 db.pub_key_lookup.insert(sharee, username)?;
126
127 tree.add_share(id, sharee, mode, &self.keychain)?;
128
129 tx.end();
130
131 self.events.meta_changed(Actor::User(None));
132
133 Ok(())
134 }
135
136 #[instrument(level = "debug", skip(self))]
140 pub async fn get_pending_shares(&self) -> LbResult<Vec<File>> {
141 let tx = self.ro_tx().await;
142 let db = tx.db();
143
144 let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
145 let pending_roots = tree.pending_roots(&self.keychain)?.into_iter();
146
147 tree.decrypt_all(&self.keychain, pending_roots, &db.pub_key_lookup, false)
148 }
149
150 #[instrument(level = "debug", skip(self))]
153 pub async fn get_pending_share_files(&self) -> LbResult<Vec<File>> {
154 let tx = self.ro_tx().await;
155 let db = tx.db();
156
157 let mut tree = (&db.base_metadata).to_staged(&db.local_metadata).to_lazy();
158 let pending_files = tree.non_deleted_pending_files(&self.keychain)?.into_iter();
159
160 tree.decrypt_all(&self.keychain, pending_files, &db.pub_key_lookup, false)
161 }
162
163 #[instrument(level = "debug", skip(self))]
164 async fn delete_share(
165 &self, id: &Uuid, maybe_encrypted_for: Option<PublicKey>,
166 ) -> LbResult<()> {
167 let mut tx = self.begin_tx().await;
168 let db = tx.db();
169
170 let mut tree = (&db.base_metadata)
171 .to_staged(&mut db.local_metadata)
172 .to_lazy();
173
174 tree.delete_share(id, maybe_encrypted_for, &self.keychain)?;
175
176 tx.end();
177 self.events.meta_changed(Actor::User(None));
178
179 Ok(())
180 }
181
182 #[instrument(level = "debug", skip(self))]
183 pub async fn known_usernames(&self) -> LbResult<Vec<String>> {
184 let db = self.ro_tx().await;
185 let db = db.db();
186
187 Ok(db
188 .pub_key_lookup
189 .iter()
190 .map(|(_, value)| value)
191 .cloned()
192 .collect())
193 }
194
195 #[instrument(level = "debug", skip(self))]
201 pub async fn username_exists(&self, username: &str) -> LbResult<bool> {
202 use crate::DEFAULT_API_LOCATION;
203 use crate::model::account::Account;
204 use crate::model::errors::LbErrKind;
205
206 let username = username.trim().to_lowercase();
207 if username.is_empty() {
208 return Ok(false);
209 }
210 if let Ok(known) = self.known_usernames().await {
212 if known.iter().any(|u| u.eq_ignore_ascii_case(&username)) {
213 return Ok(true);
214 }
215 }
216
217 let owned;
219 let account = match self.get_account() {
220 Ok(a) => a,
221 Err(_) => {
222 let api =
223 std::env::var("API_URL").unwrap_or_else(|_| DEFAULT_API_LOCATION.to_string());
224 owned = Account::new(String::new(), api);
225 &owned
226 }
227 };
228
229 match self
230 .client
231 .request(account, GetPublicKeyRequest { username })
232 .await
233 {
234 Ok(_) => Ok(true),
235 Err(e) => {
236 let err = LbErr::from(e);
237 match err.kind {
238 LbErrKind::AccountNonexistent | LbErrKind::UsernameNotFound => Ok(false),
240 _ => Err(err),
241 }
242 }
243 }
244 }
245
246 #[instrument(level = "debug", skip(self))]
247 pub async fn reject_share(&self, id: &Uuid) -> Result<(), LbErr> {
248 let pk = self.keychain.get_pk()?;
249 self.delete_share(id, Some(pk)).await
250 }
251}