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