Skip to main content

koan_core/remote/
connections.rs

1//! Who is connected to this device, and whom it is connected to: the link to
2//! the server, the account's devices that can control this one through it,
3//! and connections on the local network either way. What Settings lists, so
4//! that a device using more power than its time on screen explains can be
5//! seen to have been driven from elsewhere, and stopped.
6
7use crate::config::Config;
8use crate::remote::proof::Peer;
9use crate::remote::{devices, nearby};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Via {
13    Server,
14    Network,
15}
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct Connection {
19    /// What `nearby::end` takes: a connection on the network to this device.
20    /// The account's devices are ended by revoking their keys on the server.
21    pub key: Option<u64>,
22    pub via: Via,
23    /// The other end can control this device.
24    pub inbound: bool,
25    /// The other device's id, when it has said.
26    pub id: Option<String>,
27    /// The device's name, or the server's or a stranger's address.
28    pub name: String,
29    /// Where on the network it is.
30    pub addr: Option<String>,
31    /// Who it is: one of the account's devices, one shared with it, or
32    /// (`None`) a device that proved nothing.
33    pub peer: Option<Peer>,
34    /// Unix seconds. `None` where this device cannot know.
35    pub since: Option<i64>,
36}
37
38/// A device disconnected from Settings and held off: its address, and its
39/// name when it proved who it is.
40#[derive(Debug, Clone, PartialEq)]
41pub struct Held {
42    pub addr: String,
43    pub name: Option<String>,
44}
45
46pub fn held() -> Vec<Held> {
47    nearby::held()
48        .into_iter()
49        .map(|(addr, id)| Held {
50            name: id.as_deref().and_then(devices::name_of),
51            addr,
52        })
53        .collect()
54}
55
56pub fn list() -> Vec<Connection> {
57    let mut out = Vec::new();
58    if let Some(since) = devices::linked_since() {
59        out.push(Connection {
60            key: None,
61            via: Via::Server,
62            inbound: false,
63            id: None,
64            name: server_name(&Config::cached().remote.url),
65            addr: None,
66            peer: None,
67            since: Some(since),
68        });
69        out.extend(
70            devices::linked_controllers()
71                .into_iter()
72                .map(|d| Connection {
73                    key: None,
74                    via: Via::Server,
75                    inbound: true,
76                    id: Some(d.id),
77                    name: d.name,
78                    addr: None,
79                    peer: Some(Peer::Own),
80                    since: None,
81                }),
82        );
83    }
84    out.extend(nearby::sessions().into_iter().map(|s| {
85        Connection {
86            key: s.key,
87            via: Via::Network,
88            inbound: s.inbound,
89            // A device's id is its own word until it proves it: one that has
90            // not is named by its address, never by the device it claims to be.
91            name: s
92                .id
93                .as_deref()
94                .filter(|_| s.proven.is_some())
95                .and_then(devices::name_of)
96                .unwrap_or_else(|| s.addr.clone()),
97            id: s.id,
98            addr: Some(s.addr),
99            peer: s.proven,
100            since: Some(s.since),
101        }
102    }));
103    out
104}
105
106/// The server as a person would name it: its host.
107fn server_name(url: &str) -> String {
108    let rest = url.split_once("://").map_or(url, |(_, r)| r);
109    rest.split(['/', '?']).next().unwrap_or(rest).to_string()
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn a_server_is_named_by_its_host() {
118        assert_eq!(
119            server_name("https://music.example.com/"),
120            "music.example.com"
121        );
122        assert_eq!(server_name("http://10.0.0.2:4533"), "10.0.0.2:4533");
123        assert_eq!(server_name("music.example.com"), "music.example.com");
124    }
125}