| 1 | use super::*; |
| 2 | |
| 3 | pub struct Host { |
| 4 | members: Arc<Members>, |
| 5 | pairing: Mutex<Option<Live>>, |
| 6 | } |
| 7 | |
| 8 | #[allow(clippy::type_complexity)] |
| 9 | struct Members { |
| 10 | me: Hello, |
| 11 | notebook: String, |
| 12 | reach: Option<Reach>, |
| 13 | relay: Option<String>, |
| 14 | sharing: Mutex<Sharing>, |
| 15 | changes: Mutex<()>, |
| 16 | pending: Mutex<BTreeMap<[u8; 16], (Arc<Hello>, Line, Weak<super::super::Shared>)>>, |
| 17 | access: Mutex<HashMap<[u8; 16], Live>>, |
| 18 | room: Mutex<Option<Live>>, |
| 19 | served: Arc<Served>, |
| 20 | events: Arc<dyn Fn() + Send + Sync>, |
| 21 | keep: Box<dyn Fn(&Sharing) -> io::Result<()> + Send + Sync>, |
| 22 | } |
| 23 | |
| 24 | impl Host { |
| 25 | /// Shares a notebook, keeping every credential change before granting or revoking access. |
| 26 | #[allow(clippy::too_many_arguments)] |
| 27 | pub fn start( |
| 28 | storage: Box<dyn Storage>, |
| 29 | me: Hello, |
| 30 | sharing: Sharing, |
| 31 | notebook: &str, |
| 32 | reach: Option<Reach>, |
| 33 | relay: Option<&str>, |
| 34 | events: impl Fn() + Send + Sync + 'static, |
| 35 | keep: impl Fn(&Sharing) -> io::Result<()> + Send + Sync + 'static, |
| 36 | ) -> io::Result<Self> { |
| 37 | keep(&sharing)?; |
| 38 | let members = Arc::new(Members { |
| 39 | me, |
| 40 | notebook: notebook.to_owned(), |
| 41 | reach, |
| 42 | relay: relay.map(str::to_owned), |
| 43 | sharing: Mutex::new(sharing), |
| 44 | changes: Mutex::default(), |
| 45 | pending: Mutex::default(), |
| 46 | access: Mutex::default(), |
| 47 | room: Mutex::default(), |
| 48 | served: Arc::new(Served { |
| 49 | storage, |
| 50 | images: Mutex::default(), |
| 51 | snapshots: Mutex::default(), |
| 52 | puts: Mutex::default(), |
| 53 | guests: Mutex::default(), |
| 54 | writers: Mutex::default(), |
| 55 | host: Mutex::default(), |
| 56 | room: Mutex::default(), |
| 57 | }), |
| 58 | events: Arc::new(events), |
| 59 | keep: Box::new(keep), |
| 60 | }); |
| 61 | let sharing = members.sharing.lock().unwrap().clone(); |
| 62 | members.presence(sharing.secret)?; |
| 63 | for device in &sharing.members { |
| 64 | members.open(device.secret)?; |
| 65 | } |
| 66 | let pairing = Mutex::new(Some(members.pair(&sharing)?)); |
| 67 | Ok(Self { members, pairing }) |
| 68 | } |
| 69 | |
| 70 | pub fn code(&self) -> Option<String> { |
| 71 | let mut pairing = self.pairing.lock().unwrap(); |
| 72 | let pairing = pairing.as_mut()?; |
| 73 | let mut sharing = self.members.sharing.lock().unwrap(); |
| 74 | if pairing.burned() { |
| 75 | let mut next = sharing.clone(); |
| 76 | next.code = super::super::code::secret().ok()?; |
| 77 | (self.members.keep)(&next).ok()?; |
| 78 | *pairing = self.members.pair(&next).ok()?; |
| 79 | *sharing = next; |
| 80 | } |
| 81 | let code = pairing.code(); |
| 82 | if let Some(code) = &code |
| 83 | && *code != sharing.code |
| 84 | { |
| 85 | let mut next = sharing.clone(); |
| 86 | next.code = code.clone(); |
| 87 | (self.members.keep)(&next).ok()?; |
| 88 | *sharing = next; |
| 89 | } |
| 90 | code |
| 91 | } |
| 92 | |
| 93 | pub fn sharing(&self) -> Sharing { |
| 94 | self.code(); |
| 95 | self.members.sharing.lock().unwrap().clone() |
| 96 | } |
| 97 | |
| 98 | pub fn relayed(&self) -> Relayed { |
| 99 | self.pairing |
| 100 | .lock() |
| 101 | .unwrap() |
| 102 | .as_ref() |
| 103 | .map_or(Relayed::Unknown, Live::relayed) |
| 104 | } |
| 105 | |
| 106 | pub fn guests(&self) -> Vec<Peer> { |
| 107 | self.members |
| 108 | .room |
| 109 | .lock() |
| 110 | .unwrap() |
| 111 | .as_ref() |
| 112 | .map(Live::peers) |
| 113 | .unwrap_or_default() |
| 114 | } |
| 115 | |
| 116 | pub fn devices(&self) -> Vec<(Device, bool)> { |
| 117 | let sharing = self.members.sharing.lock().unwrap(); |
| 118 | let access = self.members.access.lock().unwrap(); |
| 119 | sharing |
| 120 | .members |
| 121 | .iter() |
| 122 | .map(|device| { |
| 123 | let connected = access |
| 124 | .get(&device.secret) |
| 125 | .is_some_and(|live| !live.peers().is_empty()); |
| 126 | (device.clone(), connected) |
| 127 | }) |
| 128 | .collect() |
| 129 | } |
| 130 | |
| 131 | pub fn requests(&self) -> Vec<Arc<Hello>> { |
| 132 | self.members |
| 133 | .pending |
| 134 | .lock() |
| 135 | .unwrap() |
| 136 | .values() |
| 137 | .map(|(hello, _, _)| Arc::clone(hello)) |
| 138 | .collect() |
| 139 | } |
| 140 | |
| 141 | pub fn approve(&self, approve: bool) -> io::Result<()> { |
| 142 | let mut sharing = self.members.sharing.lock().unwrap(); |
| 143 | let mut next = sharing.clone(); |
| 144 | next.approve = approve; |
| 145 | (self.members.keep)(&next)?; |
| 146 | *sharing = next; |
| 147 | drop(sharing); |
| 148 | if !approve { |
| 149 | for hello in self.requests() { |
| 150 | self.allow(&hello.peer)?; |
| 151 | } |
| 152 | } |
| 153 | (self.members.events)(); |
| 154 | Ok(()) |
| 155 | } |
| 156 | |
| 157 | /// Admits the device asking as `peer`, or drops its request if it has gone. |
| 158 | pub fn allow(&self, peer: &[u8; 16]) -> io::Result<()> { |
| 159 | let request = self.members.pending.lock().unwrap().remove(peer); |
| 160 | let Some((hello, line, source)) = request else { |
| 161 | return Ok(()); |
| 162 | }; |
| 163 | let connected = || { |
| 164 | source |
| 165 | .upgrade() |
| 166 | .is_some_and(|source| source.connected(&hello)) |
| 167 | }; |
| 168 | if connected() |
| 169 | && let Err(error) = self.members.grant(&hello, &line) |
| 170 | { |
| 171 | let mut pending = self.members.pending.lock().unwrap(); |
| 172 | if connected() && !pending.contains_key(peer) { |
| 173 | pending.insert(*peer, (hello, line, source)); |
| 174 | } |
| 175 | return Err(error); |
| 176 | } |
| 177 | (self.members.events)(); |
| 178 | Ok(()) |
| 179 | } |
| 180 | |
| 181 | pub fn decline(&self, peer: &[u8; 16]) { |
| 182 | let request = self.members.pending.lock().unwrap().remove(peer); |
| 183 | if let Some((_, line, _)) = request { |
| 184 | let _ = line.send(kind::APPROVAL, &wire::Approval::Declined); |
| 185 | (self.members.events)(); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | pub fn remove(&self, secret: &[u8; 16]) -> io::Result<()> { |
| 190 | let _change = self.members.changes.lock().unwrap(); |
| 191 | let mut sharing = self.members.sharing.lock().unwrap(); |
| 192 | if !sharing |
| 193 | .members |
| 194 | .iter() |
| 195 | .any(|device| device.secret == *secret) |
| 196 | { |
| 197 | return Ok(()); |
| 198 | } |
| 199 | let mut next = sharing.clone(); |
| 200 | next.members.retain(|device| device.secret != *secret); |
| 201 | getrandom::fill(&mut next.secret) |
| 202 | .map_err(|_| io::Error::other("System random source failed"))?; |
| 203 | next.code = super::super::code::secret()?; |
| 204 | (self.members.keep)(&next)?; |
| 205 | *sharing = next.clone(); |
| 206 | drop(sharing); |
| 207 | let removed = self.members.access.lock().unwrap().remove(secret); |
| 208 | if let Some(removed) = removed { |
| 209 | for peer in removed.peers() { |
| 210 | self.members.served.forget(&peer.hello.peer, None); |
| 211 | } |
| 212 | removed.leave("removed"); |
| 213 | } |
| 214 | self.members.presence(next.secret)?; |
| 215 | *self.pairing.lock().unwrap() = Some(self.members.pair(&next)?); |
| 216 | let welcomes = { |
| 217 | let access = self.members.access.lock().unwrap(); |
| 218 | next.members |
| 219 | .iter() |
| 220 | .filter_map(|device| Some((device.secret, access.get(&device.secret)?))) |
| 221 | .flat_map(|(secret, live)| { |
| 222 | let welcome = self.members.welcome(&next, secret); |
| 223 | (live.peers().into_iter()) |
| 224 | .filter_map(|peer| live.line(&peer.hello.peer)) |
| 225 | .map(move |line| (welcome.clone(), line)) |
| 226 | }) |
| 227 | .collect::<Vec<_>>() |
| 228 | }; |
| 229 | // Browser send failures may reenter a removal that rotates the room again. |
| 230 | #[cfg(target_arch = "wasm32")] |
| 231 | drop(_change); |
| 232 | for (welcome, line) in welcomes { |
| 233 | #[cfg(target_arch = "wasm32")] |
| 234 | if self.members.sharing.lock().unwrap().secret != next.secret { |
| 235 | break; |
| 236 | } |
| 237 | let _ = line.send(kind::WELCOME, &welcome); |
| 238 | } |
| 239 | (self.members.events)(); |
| 240 | Ok(()) |
| 241 | } |
| 242 | |
| 243 | pub fn set_presence(&self, presence: Presence) { |
| 244 | if let Some(room) = &*self.members.room.lock().unwrap() { |
| 245 | room.set_presence(presence); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | pub fn on_changed(&self, listener: crate::session::Listener) { |
| 250 | *self.members.served.host.lock().unwrap() = Some(listener); |
| 251 | } |
| 252 | |
| 253 | pub fn touched(&self, paths: &[String]) { |
| 254 | for path in paths { |
| 255 | self.members.served.changed_here(path); |
| 256 | } |
| 257 | self.members.served.tell(paths); |
| 258 | } |
| 259 | |
| 260 | pub fn stop(&self) { |
| 261 | let _change = self.members.changes.lock().unwrap(); |
| 262 | drop(self.pairing.lock().unwrap().take()); |
| 263 | self.members.pending.lock().unwrap().clear(); |
| 264 | let access = std::mem::take(&mut *self.members.access.lock().unwrap()); |
| 265 | for (_, live) in access { |
| 266 | live.leave(STOPPED); |
| 267 | } |
| 268 | drop(self.members.room.lock().unwrap().take()); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | impl Members { |
| 273 | fn welcome(&self, sharing: &Sharing, secret: [u8; 16]) -> Welcome { |
| 274 | Welcome { |
| 275 | share: sharing.share, |
| 276 | secret, |
| 277 | room: sharing.secret, |
| 278 | notebook: self.notebook.clone(), |
| 279 | host: self.me.name.clone(), |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | fn presence(self: &Arc<Self>, secret: [u8; 16]) -> io::Result<()> { |
| 284 | let told = Arc::clone(&self.events); |
| 285 | let live = Live::start( |
| 286 | self.me.clone(), |
| 287 | &Room::Notebook(secret), |
| 288 | self.reach, |
| 289 | self.relay.as_deref(), |
| 290 | move |_| told(), |
| 291 | )?; |
| 292 | *self.served.room.lock().unwrap() = Some(live.sender()); |
| 293 | *self.room.lock().unwrap() = Some(live); |
| 294 | Ok(()) |
| 295 | } |
| 296 | |
| 297 | fn open(self: &Arc<Self>, secret: [u8; 16]) -> io::Result<()> { |
| 298 | let members = Arc::downgrade(self); |
| 299 | let live = Live::start_with( |
| 300 | Hello { |
| 301 | serves: Some(self.sharing.lock().unwrap().share), |
| 302 | ..self.me.clone() |
| 303 | }, |
| 304 | &Room::Notebook(secret), |
| 305 | self.reach, |
| 306 | self.relay.as_deref(), |
| 307 | move |source, event| { |
| 308 | let Some(members) = members.upgrade() else { |
| 309 | return; |
| 310 | }; |
| 311 | match event { |
| 312 | Event::Met(hello, line) => { |
| 313 | let sharing = members.sharing.lock().unwrap(); |
| 314 | let admitted = |
| 315 | if sharing.members.iter().any(|device| device.secret == secret) { |
| 316 | Ok(members |
| 317 | .served |
| 318 | .admit(source, hello, line) |
| 319 | .then(|| members.welcome(&sharing, secret))) |
| 320 | } else { |
| 321 | Err(()) |
| 322 | }; |
| 323 | // Browser send failures may reenter membership callbacks. |
| 324 | #[cfg(target_arch = "wasm32")] |
| 325 | drop(sharing); |
| 326 | match admitted { |
| 327 | Ok(Some(welcome)) => { |
| 328 | let _ = line.send(kind::WELCOME, &welcome); |
| 329 | } |
| 330 | Err(()) => line.hang_up("removed"), |
| 331 | Ok(None) => {} |
| 332 | } |
| 333 | } |
| 334 | Event::Left(hello) => members.served.forget(&hello.peer, Some((source, hello))), |
| 335 | Event::Frame { from, kind, body } |
| 336 | if wire::KNOWN.contains(&kind) && kind > 256 && kind != kind::REPLY => |
| 337 | { |
| 338 | members.served.queue(source, from, kind, body) |
| 339 | } |
| 340 | Event::Changed => (members.events)(), |
| 341 | _ => {} |
| 342 | } |
| 343 | }, |
| 344 | )?; |
| 345 | self.access.lock().unwrap().insert(secret, live); |
| 346 | Ok(()) |
| 347 | } |
| 348 | |
| 349 | fn grant(self: &Arc<Self>, hello: &Hello, line: &Line) -> io::Result<()> { |
| 350 | let _change = self.changes.lock().unwrap(); |
| 351 | if self.room.lock().unwrap().is_none() { |
| 352 | return Err(io::ErrorKind::NotConnected.into()); |
| 353 | } |
| 354 | let mut secret = [0; 16]; |
| 355 | getrandom::fill(&mut secret) |
| 356 | .map_err(|_| io::Error::other("System random source failed"))?; |
| 357 | if self.sharing.lock().unwrap().members.len() >= 64 { |
| 358 | return Err(io::ErrorKind::ResourceBusy.into()); |
| 359 | } |
| 360 | self.open(secret)?; |
| 361 | let kept = (|| -> io::Result<Welcome> { |
| 362 | let mut sharing = self.sharing.lock().unwrap(); |
| 363 | let mut next = sharing.clone(); |
| 364 | next.members.push(Device { |
| 365 | secret, |
| 366 | name: hello.name.clone(), |
| 367 | device: hello.device.clone(), |
| 368 | }); |
| 369 | (self.keep)(&next)?; |
| 370 | *sharing = next; |
| 371 | Ok(self.welcome(&sharing, secret)) |
| 372 | })(); |
| 373 | let welcome = match kept { |
| 374 | Ok(welcome) => welcome, |
| 375 | Err(error) => { |
| 376 | drop(self.access.lock().unwrap().remove(&secret)); |
| 377 | return Err(error); |
| 378 | } |
| 379 | }; |
| 380 | line.send(kind::WELCOME, &welcome)?; |
| 381 | (self.events)(); |
| 382 | Ok(()) |
| 383 | } |
| 384 | |
| 385 | fn pair(self: &Arc<Self>, sharing: &Sharing) -> io::Result<Live> { |
| 386 | let members = Arc::downgrade(self); |
| 387 | Live::start_with( |
| 388 | Hello { |
| 389 | serves: None, |
| 390 | ..self.me.clone() |
| 391 | }, |
| 392 | &Room::share(&sharing.code, &sharing.password), |
| 393 | self.reach, |
| 394 | self.relay.as_deref(), |
| 395 | move |source, event| { |
| 396 | let Some(members) = members.upgrade() else { |
| 397 | return; |
| 398 | }; |
| 399 | match event { |
| 400 | Event::Met(hello, line) => { |
| 401 | let approve = members.sharing.lock().unwrap().approve; |
| 402 | let (approval, retired) = { |
| 403 | let mut pending = members.pending.lock().unwrap(); |
| 404 | if !source.connected(hello) { |
| 405 | return; |
| 406 | } |
| 407 | if approve { |
| 408 | if pending.len() < 32 || pending.contains_key(&hello.peer) { |
| 409 | ( |
| 410 | Some(wire::Approval::Pending), |
| 411 | pending.insert( |
| 412 | hello.peer, |
| 413 | ( |
| 414 | Arc::clone(hello), |
| 415 | line.clone(), |
| 416 | Arc::downgrade(source), |
| 417 | ), |
| 418 | ), |
| 419 | ) |
| 420 | } else { |
| 421 | (Some(wire::Approval::Failed), None) |
| 422 | } |
| 423 | } else { |
| 424 | (None, None) |
| 425 | } |
| 426 | }; |
| 427 | drop(retired); |
| 428 | if let Some(approval) = approval { |
| 429 | let _ = line.send(kind::APPROVAL, &approval); |
| 430 | (members.events)(); |
| 431 | } else if let Err(error) = members.grant(hello, line) { |
| 432 | eprintln!("Live Share: could not admit a device: {error}"); |
| 433 | let _ = line.send(kind::APPROVAL, &wire::Approval::Failed); |
| 434 | } |
| 435 | } |
| 436 | Event::Left(hello) => { |
| 437 | let retired = { |
| 438 | let mut pending = members.pending.lock().unwrap(); |
| 439 | if source.gone(hello) |
| 440 | && pending |
| 441 | .get(&hello.peer) |
| 442 | .is_some_and(|(admitted, _, _)| Arc::ptr_eq(admitted, hello)) |
| 443 | { |
| 444 | pending.remove(&hello.peer) |
| 445 | } else { |
| 446 | None |
| 447 | } |
| 448 | }; |
| 449 | drop(retired); |
| 450 | (members.events)(); |
| 451 | } |
| 452 | Event::Changed => (members.events)(), |
| 453 | _ => {} |
| 454 | } |
| 455 | }, |
| 456 | ) |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | #[cfg(all(test, not(target_arch = "wasm32")))] |
| 461 | #[path = "handover_tests.rs"] |
| 462 | mod handover_tests; |