use crate::message::{Message, MessageHandler};
use crate::peer::PeerMessage;
use crate::utils::zlib::{compress_stored, deflate};
use std::sync::mpsc::Sender;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SharedDirectory {
pub name: String,
pub files: Vec<(String, u64)>,
}
pub struct SharedFileListResponseHandler;
impl MessageHandler<PeerMessage> for SharedFileListResponseHandler {
fn get_code(&self) -> u8 {
5
}
fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) {
let directories = parse_shared_file_list(message);
let _ = sender.send(PeerMessage::ShareListReceived(directories));
}
}
#[must_use]
pub fn build_shared_file_list(dirs: &[SharedDirectory]) -> Message {
let mut payload = Message::new();
payload.write_int32(dirs.len() as u32);
for dir in dirs {
payload
.write_string(&dir.name)
.write_int32(dir.files.len() as u32);
for (name, size) in &dir.files {
payload
.write_int8(1)
.write_string(name)
.write_int64(*size)
.write_string("") .write_int32(0); }
}
payload.write_int32(0); payload.write_int32(0);
let compressed = compress_stored(&payload.get_data());
Message::new()
.write_int32(5)
.write_raw_bytes(compressed)
.clone()
}
#[must_use]
pub fn parse_shared_file_list(message: &mut Message) -> Vec<SharedDirectory> {
let pointer = message.get_pointer();
let size = message.get_size();
let compressed = message.get_slice(pointer, size);
let Ok(data) = deflate(&compressed) else {
return Vec::new();
};
let mut body = Message::new_with_data(data);
let dir_count = body.read_int32();
let mut dirs = Vec::new();
for _ in 0..dir_count {
if body.get_pointer() >= body.get_size() {
break;
}
let name = body.read_string();
let file_count = body.read_int32();
let mut files = Vec::new();
for _ in 0..file_count {
if body.get_pointer() >= body.get_size() {
break;
}
body.read_int8(); let filename = body.read_string();
let file_size = body.read_int64();
body.read_string(); let attr_count = body.read_int32();
for _ in 0..attr_count {
if body.get_pointer() + 8 > body.get_size() {
break;
}
body.read_int32();
body.read_int32();
}
files.push((filename, file_size));
}
dirs.push(SharedDirectory { name, files });
}
dirs
}
#[test]
fn hostile_dir_count_does_not_hang() {
let compressed =
crate::utils::zlib::compress_stored(&u32::MAX.to_le_bytes());
let mut message = crate::message::framed(|m| {
m.write_raw_bytes(compressed);
});
assert!(parse_shared_file_list(&mut message).is_empty());
}
#[test]
fn shared_file_list_roundtrips() {
let dirs = vec![
SharedDirectory {
name: "music\\album".to_string(),
files: vec![
("song one.flac".to_string(), 123),
("song two.flac".to_string(), 456),
],
},
SharedDirectory {
name: "music".to_string(),
files: vec![("top.mp3".to_string(), 789)],
},
];
let message = build_shared_file_list(&dirs);
let mut decoded = Message::new_with_data(message.get_buffer());
decoded.set_pointer(8);
assert_eq!(parse_shared_file_list(&mut decoded), dirs);
}