use std::
{
sync::Arc,
path::Path,
};
use tokio::
{
io::AsyncWriteExt,
fs::{ self, File },
sync::
{
Mutex,
mpsc::Sender,
},
};
use sha2::{ Sha256, Digest };
use crate::
{
config,
options,
crypto as chat_crypto,
network::
{
self,
EncryptionMode,
client::{ self, ClientEvent },
file::
{
self,
FilePacket,
FilePacketCode,
},
},
};
pub async fn upload(token: [u8; 32], uid: u64, file_hash: [u8; 32], tx: Sender<ClientEvent>)
{
let (_read_stream, mut write_stream) = client::connect(options::get_server_address()).await.expect("File connection failed");
write_stream.write_all(&token).await.unwrap();
let path = client::ACTIVE_UPLOADS.lock().unwrap().remove(&file_hash).unwrap(); let filename = path.clone().file_name().and_then(|n| n.to_str()
.map(|s| s.to_string())).unwrap_or_else(|| String::from("Unknown"));
let size = fs::metadata(&path).await.unwrap().len();
tx.send(ClientEvent::Upload(filename.clone())).await.unwrap();
let mut seq = 0usize;
let mut rex_stream = chat_crypto::init_rex_stream(options::get_keys().as_ref().unwrap(), &token).unwrap();
network::send_tcp(&mut write_stream, FilePacket
{
uid,
code: FilePacketCode::Metadata
{
size,
filename,
hash: file_hash,
},
seq: 0,
}, EncryptionMode::Stream(&mut rex_stream), Some(&mut seq)).await;
file::send_file(path, write_stream, uid, &mut rex_stream, Some(&mut seq)).await;
}
pub async fn download(token: [u8; 32], tx: Sender<ClientEvent>)
{
let (mut read_stream, mut write_stream) = client::connect(options::get_server_address()).await.expect("File connection failed");
write_stream.write_all(&token).await.unwrap();
let mut streams = (&mut read_stream, Arc::new(Mutex::new(write_stream)));
let mut seq = 0usize;
let mut rex_stream = chat_crypto::init_rex_stream(options::get_keys().as_ref().unwrap(), &token).unwrap();
let (size, filename, hash) = match file::receive_file(&mut streams, &mut rex_stream, &mut seq).await
{
Some((_, FilePacketCode::Metadata { size, filename, hash })) => (size, filename, hash),
_ => return,
};
let download_dir = config::read_config::<String>("download_directory")
.replace("{HOME}", dirs::home_dir().expect("Could not determine home directory")
.to_str().expect("Invalid home directory"));
let filename = Path::new(&filename)
.file_name()
.and_then(|f| f.to_str())
.unwrap_or("unnamed_file")
.to_string();
fs::create_dir_all(&download_dir).await.expect("Creating download directory failed");
tx.send(ClientEvent::Download(filename.clone())).await.unwrap();
let mut current_size = 0u64;
let mut file = File::create(Path::new(&download_dir).join(&filename)).await.expect("Creating download file failed");
let mut hasher = Sha256::new();
loop
{
let data = match file::receive_file(&mut streams, &mut rex_stream, &mut seq).await
{
Some((_, FilePacketCode::Data { data })) => data,
_ => return
};
if file.write_all(&data).await.is_ok()
{
current_size += data.len() as u64;
hasher.update(&data);
if current_size == size
{
let final_hash: [u8; 32] = hasher.clone().finalize().into();
file.flush().await.ok();
tx.send(if hash == final_hash
{
ClientEvent::Downloaded(filename)
} else
{
ClientEvent::DownloadFailed(filename)
}).await.unwrap();
return;
}
}
}
}