#[cfg(feature = "client_base")]
pub mod client;
#[cfg(feature = "server")]
pub mod server;
use std::path::PathBuf;
use tokio::
{
fs::File,
io::AsyncReadExt,
net::tcp::OwnedWriteHalf,
};
use wincode::{ SchemaRead, SchemaWrite };
#[cfg(feature = "server")]
use why2::stream::RexStream;
use crate::
{
consts::{ self, Streams },
crypto::RexPacketStream,
network::
{
self,
EncryptionMode,
SequencedPacket,
},
};
#[cfg(feature = "server")]
use crate::
{
crypto,
network::server as chat_server,
};
#[derive(SchemaWrite, SchemaRead, Clone)]
pub enum FilePacketCode
{
Data { data: Vec<u8> },
Metadata
{
size: u64, filename: String, hash: [u8; 32], },
}
#[derive(SchemaWrite, SchemaRead, Clone)]
pub struct FilePacket {
pub uid: u64, pub code: FilePacketCode, pub seq: usize, }
impl SequencedPacket for FilePacket
{
fn seq(&self) -> usize { self.seq }
fn set_seq(&mut self, seq: usize) { self.seq = seq; }
}
pub async fn send_file (
path: PathBuf,
mut write_stream: OwnedWriteHalf,
uid: u64,
rex_stream: &mut RexPacketStream,
mut seq: Option<&mut usize>,
#[cfg(feature = "server")] disk_stream: &mut RexStream,
)
{
let mut file = File::open(path).await.expect("Cannot open file for upload");
let mut buffer = vec![0; consts::UPLOAD_CHUNK_SIZE];
loop
{
match file.read(&mut buffer).await
{
Ok(0) => break, Ok(bytes) =>
{
let plaintext =
{
#[cfg(feature = "server")]
{
let input_i64 = crypto::bytes_to_i64(&buffer[..bytes]);
let mut decrypted_i64 = disk_stream.update(&input_i64).expect("Disk stream decryption failed");
decrypted_i64.extend(disk_stream.finalize().expect("Disk stream finalize failed"));
let mut out = crypto::i64_to_bytes(&decrypted_i64);
out.truncate(bytes);
out
}
#[cfg(feature = "client_base")]
{
buffer[..bytes].to_vec()
}
};
network::send_tcp(&mut write_stream, FilePacket
{
uid,
code: FilePacketCode::Data { data: plaintext },
seq: 0,
}, EncryptionMode::Stream(rex_stream), seq.as_deref_mut()).await;
},
Err(_) => {}, }
}
}
pub async fn receive_file
(
streams: &mut Streams<'_>,
rex_stream: &mut RexPacketStream,
seq: &mut usize
) -> Option<(u64, FilePacketCode)>
{
let read = network::read_tcp
(
streams,
EncryptionMode::Stream(rex_stream),
#[cfg(feature = "server")] true,
).await?;
match wincode::deserialize::<FilePacket>(&read.data)
{
Ok(packet) =>
{
if packet.seq > *seq || *seq == 0 {
*seq = packet.seq;
} else { return None; }
Some((packet.uid, packet.code))
},
Err(_) =>
{
#[cfg(feature = "server")]
chat_server::remove_connection(&read.peer_addr, false, Some("packet")).await;
None
}
}
}