138 lines
3.9 KiB
Rust
138 lines
3.9 KiB
Rust
// Copyright (c) 2019 Reyk Floeter <contact@reykfloeter.com>
|
|
//
|
|
// Permission to use, copy, modify, and distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
use crate::{server::read_line, Config};
|
|
use std::{
|
|
io::{Error, ErrorKind, Result},
|
|
os::unix::fs::MetadataExt,
|
|
path::PathBuf,
|
|
};
|
|
use tokio::{
|
|
fs::{remove_file, File},
|
|
io::{copy, split, BufReader, BufWriter},
|
|
prelude::*,
|
|
};
|
|
use tokio_libtls::prelude::*;
|
|
|
|
pub(crate) async fn run(config: Config, filename: PathBuf) -> Result<()> {
|
|
let (cert, key, ca) = config.load_keys()?;
|
|
let mut options = config.load_client_options();
|
|
|
|
let tls_config = TlsConfigBuilder::new()
|
|
.ca_file(ca)
|
|
.keypair_file(cert, key, None)
|
|
.build()
|
|
.unwrap();
|
|
let addr = config.address.unwrap();
|
|
let tls = AsyncTls::connect(&addr.to_string(), &tls_config, options.build())
|
|
.await
|
|
.unwrap();
|
|
let peer_addr: String = addr.to_string();
|
|
|
|
let (reader, writer) = split(tls);
|
|
let mut reader = BufReader::new(reader);
|
|
let mut writer = BufWriter::new(writer);
|
|
|
|
// Send the filename
|
|
let name = match filename
|
|
.file_name()
|
|
.ok_or_else(|| {
|
|
debug!("{} failed: file name ({})", peer_addr, filename.display());
|
|
Error::new(ErrorKind::Other, "file")
|
|
})?
|
|
.to_str()
|
|
{
|
|
Some(name) => name,
|
|
None => {
|
|
debug!(
|
|
"{} failed: filename format ({})",
|
|
peer_addr,
|
|
filename.display()
|
|
);
|
|
return Err(Error::new(ErrorKind::Other, "file format"));
|
|
}
|
|
};
|
|
let _ = writer.write_all(format!("{}\n", name).as_bytes()).await;
|
|
|
|
// Send the file size
|
|
let file_size = filename.metadata()?.size();
|
|
let _ = writer
|
|
.write_all(format!("{}\n", file_size).as_bytes())
|
|
.await;
|
|
|
|
debug!(
|
|
"{} status: sending {} ({} bytes)",
|
|
peer_addr,
|
|
filename.display(),
|
|
file_size
|
|
);
|
|
|
|
// Send the actual file
|
|
let file = match File::open(&filename).await {
|
|
Ok(f) => f,
|
|
Err(err) => {
|
|
debug!(
|
|
"{} failed {}: file ({})",
|
|
peer_addr,
|
|
filename.display(),
|
|
err
|
|
);
|
|
return Err(err);
|
|
}
|
|
};
|
|
|
|
let mut file_reader = file.take(file_size);
|
|
let copied = match copy(&mut file_reader, &mut writer).await {
|
|
Ok(s) => s,
|
|
Err(err) => {
|
|
debug!("{} failed: I/O ({:?})", peer_addr, err);
|
|
return Err(err);
|
|
}
|
|
};
|
|
|
|
if copied != file_size {
|
|
drop(file_reader);
|
|
let _ = remove_file(&filename).await;
|
|
warn!(
|
|
"{} failed: {} ({}/{} bytes)",
|
|
peer_addr,
|
|
filename.display(),
|
|
copied,
|
|
file_size
|
|
);
|
|
} else {
|
|
info!(
|
|
"{} success: {} ({} bytes)",
|
|
peer_addr,
|
|
filename.display(),
|
|
copied
|
|
);
|
|
}
|
|
|
|
// Read the server result
|
|
match read_line(&peer_addr, &mut reader).await {
|
|
Ok(s) if s.starts_with("success") => s,
|
|
Ok(s) => {
|
|
debug!("server: {}", s);
|
|
return Err(Error::new(ErrorKind::Other, s));
|
|
}
|
|
Err(err) => {
|
|
debug!("{}", err);
|
|
return Err(err);
|
|
}
|
|
};
|
|
|
|
Ok(())
|
|
}
|