feat(peer): ask a peer what it says about itself
The client answered a UserInfoRequest but could never send one, so a user's profile — description, upload slots, queue length — was something this client served and never saw. The reply (peer code 16) is now parsed too, with the tolerances other clients carry: a picture is skipped rather than kept, a length that overruns the message is clamped, and a message that stops early (older clients, and Museek+ writing a boolean as an integer) still yields every field before it.
This commit is contained in:
@@ -340,6 +340,11 @@ pub enum ClientOperation {
|
||||
token: u32,
|
||||
folder: String,
|
||||
},
|
||||
/// A peer answered our request for what it says about itself.
|
||||
PeerInfoReceived {
|
||||
username: String,
|
||||
info: crate::message::peer::PeerInfo,
|
||||
},
|
||||
/// A peer answered our request for one folder of their shares.
|
||||
FolderContents {
|
||||
username: String,
|
||||
@@ -494,6 +499,8 @@ pub struct ClientContext {
|
||||
browse_results: HashMap<String, Vec<SharedDirectory>>,
|
||||
/// One-folder listings received from peers, keyed by peer and folder.
|
||||
folder_contents: HashMap<(String, String), Vec<SharedDirectory>>,
|
||||
/// What peers said about themselves (peer code 16), keyed by peer.
|
||||
peer_infos: HashMap<String, crate::message::peer::PeerInfo>,
|
||||
pending_browses: HashMap<String, Instant>,
|
||||
/// Latest snapshot of the public chat-room list (from `RoomList`, code 64).
|
||||
room_list: Vec<RoomInfo>,
|
||||
@@ -651,6 +658,7 @@ impl ClientContext {
|
||||
pending_serves: HashMap::new(),
|
||||
browse_results: HashMap::new(),
|
||||
folder_contents: HashMap::new(),
|
||||
peer_infos: HashMap::new(),
|
||||
pending_browses: HashMap::new(),
|
||||
room_list: Vec::new(),
|
||||
room_events: Vec::new(),
|
||||
@@ -863,6 +871,30 @@ impl ClientContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Record what `username` says about itself.
|
||||
pub fn store_peer_info(
|
||||
&mut self,
|
||||
username: String,
|
||||
info: crate::message::peer::PeerInfo,
|
||||
) {
|
||||
self.peer_infos.insert(username, info);
|
||||
}
|
||||
|
||||
/// What `username` last said about itself, if anything.
|
||||
#[must_use]
|
||||
pub fn peer_info(
|
||||
&self,
|
||||
username: &str,
|
||||
) -> Option<crate::message::peer::PeerInfo> {
|
||||
self.peer_infos.get(username).cloned()
|
||||
}
|
||||
|
||||
/// Drop what we hold about `username`, so a poll after a fresh request
|
||||
/// cannot report the previous answer.
|
||||
pub fn invalidate_peer_info(&mut self, username: &str) {
|
||||
self.peer_infos.remove(username);
|
||||
}
|
||||
|
||||
/// Record one folder's listing from `username`.
|
||||
pub fn store_folder_contents(
|
||||
&mut self,
|
||||
|
||||
@@ -919,6 +919,11 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
ClientOperation::PeerInfoReceived { username, info } => {
|
||||
if let Ok(mut ctx) = client_context.write_safe() {
|
||||
ctx.store_peer_info(username, info);
|
||||
}
|
||||
}
|
||||
ClientOperation::FolderContents {
|
||||
username,
|
||||
token,
|
||||
|
||||
@@ -166,6 +166,34 @@ impl Client {
|
||||
))
|
||||
}
|
||||
|
||||
/// Ask `username` what it says about itself (peer code 15): its
|
||||
/// description, upload slots and queue length — a user's profile as other
|
||||
/// clients show it.
|
||||
///
|
||||
/// Any previous answer is dropped first, so [`Client::peer_info`] reports
|
||||
/// the reply to this request rather than a stale one.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`crate::SoulseekRs::NotConnected`] when there is no server connection,
|
||||
/// or [`crate::SoulseekRs::LockPoisoned`] if the client state is poisoned.
|
||||
pub fn request_peer_info(&self, username: &str) -> Result<()> {
|
||||
self.context.write_safe()?.invalidate_peer_info(username);
|
||||
let request = crate::message::peer::build_user_info_request();
|
||||
self.send_to_peer_or_queue(username, request)
|
||||
}
|
||||
|
||||
/// What `username` last said about itself.
|
||||
#[must_use]
|
||||
pub fn peer_info(
|
||||
&self,
|
||||
username: &str,
|
||||
) -> Option<crate::message::peer::PeerInfo> {
|
||||
self.context
|
||||
.read_safe()
|
||||
.ok()
|
||||
.and_then(|ctx| ctx.peer_info(username))
|
||||
}
|
||||
|
||||
/// Ask `username` for the contents of one folder of their shares (peer
|
||||
/// code 36) — what "download folder" in other clients asks for, without
|
||||
/// pulling their whole listing.
|
||||
|
||||
Reference in New Issue
Block a user