feat: scope server pins by port, default port inherits (agreed with bunshin)

This commit is contained in:
randogoth 2026-09-30 13:46:05 +03:00
parent 96a558da16
commit 13c624f2b0
7 changed files with 149 additions and 48 deletions

View file

@ -249,11 +249,21 @@ pub fn import(store: &Store, master: &[u8; KEY_LEN], text: &str) -> Result<Impor
for (host, key) in &payload.servers {
let key: Option<[u8; KEY_LEN]> = unb32(key).ok().and_then(|k| k.try_into().ok());
// A backup pin key is a bare host (the default port) or host:port,
// scoping exactly as the live store does; the port splits from the
// right, so a host without a valid port suffix stays whole.
let (host, port) = match host.rsplit_once(':') {
Some((h, p)) => match p.parse::<u16>() {
Ok(port) => (h.to_string(), port),
Err(_) => (host.clone(), crate::address::DEFAULT_PORT),
},
None => (host.clone(), crate::address::DEFAULT_PORT),
};
match key {
None => summary.malformed += 1,
Some(key) => match store.server_pin(host)? {
Some(key) => match store.server_pin(&host, port)? {
None => {
store.pin_server(host, &key)?;
store.pin_server(&host, port, &key)?;
summary.pins_added += 1;
}
Some(existing) if existing != key => summary.pins_conflicted += 1,
@ -337,7 +347,7 @@ mod tests {
fn seeded_store() -> (Store, [u8; KEY_LEN]) {
let store = Store::open_in_memory().unwrap();
let master = [7u8; KEY_LEN];
store.pin_server("example.org", &[1u8; KEY_LEN]).unwrap();
store.pin_server("example.org", crate::address::DEFAULT_PORT, &[1u8; KEY_LEN]).unwrap();
store
.save_contact("alice@example.org", &[2u8; KEY_LEN], true)
.unwrap();
@ -366,7 +376,7 @@ mod tests {
assert_eq!(summary.contacts_added, 1);
assert_eq!(summary.malformed, 0);
assert_eq!(fresh.server_pin("example.org").unwrap(), Some([1u8; KEY_LEN]));
assert_eq!(fresh.server_pin("example.org", crate::address::DEFAULT_PORT).unwrap(), Some([1u8; KEY_LEN]));
let (key, verified) = fresh.contact("alice@example.org").unwrap().unwrap();
assert_eq!(key, [2u8; KEY_LEN]);
assert!(verified);
@ -395,13 +405,13 @@ mod tests {
let file = export(&store, &master).unwrap();
let mine = Store::open_in_memory().unwrap();
mine.pin_server("example.org", &[6u8; KEY_LEN]).unwrap();
mine.pin_server("example.org", crate::address::DEFAULT_PORT, &[6u8; KEY_LEN]).unwrap();
mine.save_contact("alice@example.org", &[7u8; KEY_LEN], false)
.unwrap();
let summary = import(&mine, &master, &file).unwrap();
assert_eq!(summary.pins_conflicted, 1);
assert_eq!(summary.contacts_conflicted, 1);
assert_eq!(mine.server_pin("example.org").unwrap(), Some([6u8; KEY_LEN]));
assert_eq!(mine.server_pin("example.org", crate::address::DEFAULT_PORT).unwrap(), Some([6u8; KEY_LEN]));
let (key, _) = mine.contact("alice@example.org").unwrap().unwrap();
assert_eq!(key, [7u8; KEY_LEN]);
}