forked from Keelrleek/SoulStream
community: the encrypted inbox, friend requests and shares travel sealed to a box key when the other side has published one
This commit is contained in:
1 parent
13c44f731a
commit
4378c4c0cc
14 files changed
+1387
-145
No files matched your search
@@ -28,6 +28,7 @@ use persona_state::slsk_link::normalize_username;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{Emitter, Manager, State};
|
||||
|
||||
use crate::inbox::{self, Cargo, ShareCargo};
|
||||
use crate::persona;
|
||||
use crate::pipeline::LibraryState;
|
||||
use crate::playlist_formats::{ParsedEntry, ParsedPlaylist};
|
||||
@@ -59,7 +60,14 @@ pub struct Published {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ShareCard {
|
||||
pub id: String,
|
||||
/// Empty when it arrived sealed from a persona this device has no
|
||||
/// Soulseek account for; `from_key` is then the only name.
|
||||
pub from_username: String,
|
||||
#[serde(default)]
|
||||
pub from_key: String,
|
||||
/// "pm" | "inbox" — how it arrived.
|
||||
#[serde(default = "carry_pm")]
|
||||
pub carry: String,
|
||||
pub owner_key: String,
|
||||
pub playlist_uuid: String,
|
||||
pub name: String,
|
||||
@@ -67,6 +75,10 @@ pub struct ShareCard {
|
||||
pub received_at: u64,
|
||||
}
|
||||
|
||||
fn carry_pm() -> String {
|
||||
"pm".into()
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
struct Ledger {
|
||||
#[serde(default)]
|
||||
@@ -452,6 +464,24 @@ pub async fn send(app: &tauri::AppHandle, username: &str, playlist_id: i64) -> R
|
||||
name: rec.name.clone(),
|
||||
entry_count: rec.entry_count,
|
||||
};
|
||||
// Sealed through their inbox when they have one; the Soulseek carry otherwise.
|
||||
if let Some(link) = crate::slsk_link::known_link(app, username) {
|
||||
if let Ok(their) = bytes32(&link.persona_key) {
|
||||
let cargo = Cargo::Share(ShareCargo {
|
||||
owner,
|
||||
playlist_uuid: r.playlist_uuid,
|
||||
name: r.name.clone(),
|
||||
entry_count: r.entry_count,
|
||||
});
|
||||
match inbox::send(app, &their, &cargo).await {
|
||||
Ok(()) => {
|
||||
log::info!("shared playlists: sent {:?} to {username} via inbox", rec.name);
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => log::info!("shared playlists: inbox carry failed ({e}); trying the Soulseek carry"),
|
||||
}
|
||||
}
|
||||
}
|
||||
use base64::Engine;
|
||||
let bytes = bincode::serialize(&r).map_err(|e| e.to_string())?;
|
||||
let text = format!("{SHARE_PREFIX}{}", base64::engine::general_purpose::STANDARD.encode(bytes));
|
||||
@@ -460,6 +490,38 @@ pub async fn send(app: &tauri::AppHandle, username: &str, playlist_id: i64) -> R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn keep_card(app: &tauri::AppHandle, card: ShareCard) {
|
||||
let who = if card.from_username.is_empty() { card.from_key.clone() } else { card.from_username.clone() };
|
||||
let mut ledger = load(app);
|
||||
ledger.inbox.retain(|c| c.id != card.id);
|
||||
ledger.inbox.push(card);
|
||||
if let Err(e) = save(app, &ledger) {
|
||||
log::warn!("shared playlists: could not keep the share from {who}: {e}");
|
||||
} else {
|
||||
log::info!("shared playlists: share received from {who}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Sealed cargo from inbox.rs: the envelope's signer is the sender.
|
||||
pub(crate) fn on_inbox_share(app: &tauri::AppHandle, sender: [u8; 32], s: ShareCargo) {
|
||||
let from_key = b58(&sender);
|
||||
let from_username = crate::slsk_link::username_for_key(app, &from_key).unwrap_or_default();
|
||||
keep_card(
|
||||
app,
|
||||
ShareCard {
|
||||
id: format!("{}:{}:{}", from_key, b58(&s.owner), b58(&s.playlist_uuid)),
|
||||
from_username,
|
||||
from_key,
|
||||
carry: "inbox".into(),
|
||||
owner_key: b58(&s.owner),
|
||||
playlist_uuid: b58(&s.playlist_uuid),
|
||||
name: cap(&s.name, pl::MAX_NAME_BYTES),
|
||||
entry_count: s.entry_count,
|
||||
received_at: now_secs(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// The event pump hands private messages here after the link and friend
|
||||
/// handshakes. True means it was a share reference, now a card in the Inbox.
|
||||
pub fn intercept_pm(app: &tauri::AppHandle, from: &str, message: &str) -> bool {
|
||||
@@ -467,23 +529,20 @@ pub fn intercept_pm(app: &tauri::AppHandle, from: &str, message: &str) -> bool {
|
||||
use base64::Engine;
|
||||
let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(rest.trim()) else { return true };
|
||||
let Ok(r) = bincode::deserialize::<ShareRef>(&bytes) else { return true };
|
||||
let card = ShareCard {
|
||||
id: format!("{}:{}:{}", normalize_username(from), b58(&r.owner), b58(&r.playlist_uuid)),
|
||||
from_username: from.to_string(),
|
||||
owner_key: b58(&r.owner),
|
||||
playlist_uuid: b58(&r.playlist_uuid),
|
||||
name: cap(&r.name, pl::MAX_NAME_BYTES),
|
||||
entry_count: r.entry_count,
|
||||
received_at: now_secs(),
|
||||
};
|
||||
let mut ledger = load(app);
|
||||
ledger.inbox.retain(|c| c.id != card.id);
|
||||
ledger.inbox.push(card);
|
||||
if let Err(e) = save(app, &ledger) {
|
||||
log::warn!("shared playlists: could not keep the share from {from}: {e}");
|
||||
} else {
|
||||
log::info!("shared playlists: share received from {from}");
|
||||
}
|
||||
keep_card(
|
||||
app,
|
||||
ShareCard {
|
||||
id: format!("{}:{}:{}", normalize_username(from), b58(&r.owner), b58(&r.playlist_uuid)),
|
||||
from_username: from.to_string(),
|
||||
from_key: String::new(),
|
||||
carry: "pm".into(),
|
||||
owner_key: b58(&r.owner),
|
||||
playlist_uuid: b58(&r.playlist_uuid),
|
||||
name: cap(&r.name, pl::MAX_NAME_BYTES),
|
||||
entry_count: r.entry_count,
|
||||
received_at: now_secs(),
|
||||
},
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user