use std::
{
io::Write,
ffi::OsStr,
net::TcpStream,
sync::LazyLock,
fs::{ self, File },
path::{ Path, PathBuf },
};
use dashmap::DashMap;
use sha2::{ Sha256, Digest };
use crate::
{
misc,
consts::{ self, Streams },
network::
{
self,
server::{ self, AvailableFile },
MessagePacket,
MessageCode,
},
};
struct FileTransferGuard
{
id: usize,
uid: u64,
}
impl Drop for FileTransferGuard
{
fn drop(&mut self)
{
if let Some(conn) = server::CONNECTIONS.iter().find(|c| c.id() == Some(&self.id))
{
conn.remove_file_stream(self.uid);
if ACTIVE_FILESHARES.remove(&self.uid).is_some() && let Some(uname) = conn.username()
{
let temp_dir = misc::get_upload_dir(uname);
let junk_file = temp_dir.join(self.uid.to_string());
let _ = fs::remove_file(&junk_file);
log::error!("Upload failed: {}", conn.peer_addr());
}
}
}
}
pub struct ActiveFileshare {
pub file: File, pub size: u64, pub current_size: u64, pub hash: [u8; 32], pub hasher: Sha256, pub filename: String, #[cfg(feature = "server")] pub client_id: usize, }
pub static ACTIVE_FILESHARES: LazyLock<DashMap<u64, ActiveFileshare>> = LazyLock::new(|| DashMap::new());
pub fn download(id: usize, streams: &mut Streams, uid: u64)
{
let (keys, username, peer_addr) =
{
let conn = server::CONNECTIONS.iter()
.find(|e| e.value().id() == Some(&id));
match conn
{
Some(c) =>
{
let keys = match c.keys()
{
Some(k) => k.clone(),
None => return
};
let username = match c.username()
{
Some(u) => u.clone(),
None => return
};
c.add_file_stream(uid, streams.0.try_clone().unwrap());
(keys, username, c.peer_addr().clone())
},
None => return
}
};
let _guard = FileTransferGuard
{
id,
uid,
};
let mut seq = 0usize;
loop
{
let read = match network::receive(streams, Some(&keys), Some(&mut seq))
{
Some(r) => r,
None => return
};
if let Some(file) = read.file {
if let Some(mut active) = ACTIVE_FILESHARES.get_mut(&file.uid) &&
let Some(chunk_data) = file.data && active.client_id == id
{
if chunk_data.len() <= consts::UPLOAD_CHUNK_SIZE && active.file.write_all(&chunk_data).is_ok() {
active.current_size += chunk_data.len() as u64;
if active.current_size > active.size { return; }
active.hasher.update(&chunk_data);
if active.current_size == active.size {
let valid: bool;
let temp_dir = misc::get_upload_dir(&username);
let current_path = temp_dir.join(file.uid.to_string());
let mut new_filename = None;
let mut final_path = None;
let mut insert = false;
let final_hash: [u8; 32] = active.hasher.clone().finalize().into();
if active.hash == final_hash
{
let filename = Path::new(&active.filename) .file_name()
.unwrap_or(OsStr::new("unnamed_file"));
let new_path = temp_dir.join(filename);
insert = !new_path.is_file();
valid = fs::rename(¤t_path, &new_path).is_ok();
new_filename = Some(filename);
final_path = Some(new_path);
} else { valid = false; }
if !valid { return; }
log::info!("Upload done: {peer_addr}");
let filename = new_filename.and_then(|f| f.to_str()).unwrap_or("unnamed_file").to_owned();
server::send_to_all(MessagePacket
{
text: Some(filename.clone()),
username: Some(username.clone()),
code: Some(MessageCode::Uploaded),
..Default::default()
});
if insert
{
server::AVAILABLE_FILES.get_mut(username.as_str()).unwrap().push(AvailableFile
{
hash: final_hash,
path: final_path.unwrap(),
filename,
size: active.current_size,
});
}
drop(active);
ACTIVE_FILESHARES.remove(&file.uid);
return;
}
}
}
}
}
}
pub fn upload(id: usize, stream: TcpStream, path: PathBuf, uid: u64)
{
let (keys, peer_addr) =
{
let conn = server::CONNECTIONS.iter()
.find(|e| e.value().id() == Some(&id));
match conn
{
Some(c) =>
{
let keys = match c.keys()
{
Some(k) => k.clone(),
None => return
};
c.add_file_stream(uid, stream.try_clone().unwrap());
(keys, c.peer_addr().clone())
},
None => return
}
};
let _guard = FileTransferGuard
{
id,
uid,
};
network::send_file(path, stream, uid, MessageCode::Download, Some(&keys));
log::info!("Download done: {peer_addr}");
}