on_sent and on_recv methods for file transfer to finalize streams

This commit is contained in:
ChristianVisintin
2020-12-04 18:25:59 +01:00
parent af6d6e0039
commit 54c02cd4e2
6 changed files with 110 additions and 74 deletions
Generated
+1 -1
View File
@@ -147,7 +147,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "ftp" name = "ftp"
version = "3.0.1" version = "3.0.1"
source = "git+https://github.com/ChristianVisintin/rust-ftp#cfe594f6e6acd6137087847357bf902207d4e477" source = "git+https://github.com/ChristianVisintin/rust-ftp#63b06ef1b21cd7bd0a44682e514144e67c071d0e"
dependencies = [ dependencies = [
"chrono", "chrono",
"lazy_static", "lazy_static",
+45 -64
View File
@@ -34,7 +34,6 @@ use crate::utils::lstime_to_systime;
// Includes // Includes
use ftp::native_tls::TlsConnector; use ftp::native_tls::TlsConnector;
use ftp::status::{CLOSING_DATA_CONNECTION, REQUESTED_FILE_ACTION_OK};
use ftp::FtpStream; use ftp::FtpStream;
use regex::Regex; use regex::Regex;
use std::io::{Read, Write}; use std::io::{Read, Write};
@@ -47,7 +46,6 @@ use std::time::SystemTime;
pub struct FtpFileTransfer { pub struct FtpFileTransfer {
stream: Option<FtpStream>, stream: Option<FtpStream>,
ftps: bool, ftps: bool,
pending_write_response: bool,
} }
impl FtpFileTransfer { impl FtpFileTransfer {
@@ -58,29 +56,9 @@ impl FtpFileTransfer {
FtpFileTransfer { FtpFileTransfer {
stream: None, stream: None,
ftps: ftps, ftps: ftps,
pending_write_response: false,
} }
} }
/// ### handle_pending_operations
///
/// Handle pending operations, such as closing write command after writing a file
fn handle_pending_operations(&mut self) -> Result<(), FileTransferError> {
if self.pending_write_response {
// Send write response ok
if let Some(stream) = &mut self.stream {
match stream.read_response_in(&[
CLOSING_DATA_CONNECTION,
REQUESTED_FILE_ACTION_OK,
]) {
Ok(_) => self.pending_write_response = false,
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::ProtocolError, format!("Could not close write: {}", err)))
}
}
}
Ok(())
}
/// ### parse_list_line /// ### parse_list_line
/// ///
/// Parse a line of LIST command output and instantiates an FsEntry from it /// Parse a line of LIST command output and instantiates an FsEntry from it
@@ -260,12 +238,18 @@ impl FileTransfer for FtpFileTransfer {
}; };
// If SSL, open secure session // If SSL, open secure session
if self.ftps { if self.ftps {
let ctx = match TlsConnector::builder().danger_accept_invalid_certs(true).danger_accept_invalid_hostnames(true).build() { let ctx = match TlsConnector::builder()
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
{
Ok(tls) => tls, Ok(tls) => tls,
Err(err) => return Err(FileTransferError::new_ex( Err(err) => {
FileTransferErrorType::SslError, return Err(FileTransferError::new_ex(
format!("{}", err), FileTransferErrorType::SslError,
)) format!("{}", err),
))
}
}; };
stream = match stream.into_secure(ctx, address.as_str()) { stream = match stream.into_secure(ctx, address.as_str()) {
Ok(s) => s, Ok(s) => s,
@@ -332,10 +316,6 @@ impl FileTransfer for FtpFileTransfer {
/// Print working directory /// Print working directory
fn pwd(&mut self) -> Result<PathBuf, FileTransferError> { fn pwd(&mut self) -> Result<PathBuf, FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.pwd() { Some(stream) => match stream.pwd() {
Ok(path) => Ok(PathBuf::from(path.as_str())), Ok(path) => Ok(PathBuf::from(path.as_str())),
@@ -355,10 +335,6 @@ impl FileTransfer for FtpFileTransfer {
/// Change working directory /// Change working directory
fn change_dir(&mut self, dir: &Path) -> Result<PathBuf, FileTransferError> { fn change_dir(&mut self, dir: &Path) -> Result<PathBuf, FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.cwd(&dir.to_string_lossy()) { Some(stream) => match stream.cwd(&dir.to_string_lossy()) {
Ok(_) => Ok(PathBuf::from(dir)), Ok(_) => Ok(PathBuf::from(dir)),
@@ -378,10 +354,6 @@ impl FileTransfer for FtpFileTransfer {
/// List directory entries /// List directory entries
fn list_dir(&mut self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError> { fn list_dir(&mut self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.list(Some(&path.to_string_lossy())) { Some(stream) => match stream.list(Some(&path.to_string_lossy())) {
Ok(entries) => { Ok(entries) => {
@@ -410,10 +382,6 @@ impl FileTransfer for FtpFileTransfer {
/// ///
/// Make directory /// Make directory
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> { fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.mkdir(&dir.to_string_lossy()) { Some(stream) => match stream.mkdir(&dir.to_string_lossy()) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@@ -432,10 +400,6 @@ impl FileTransfer for FtpFileTransfer {
/// ///
/// Remove a file or a directory /// Remove a file or a directory
fn remove(&mut self, fsentry: &FsEntry) -> Result<(), FileTransferError> { fn remove(&mut self, fsentry: &FsEntry) -> Result<(), FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
if self.stream.is_none() { if self.stream.is_none() {
return Err(FileTransferError::new( return Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession, FileTransferErrorType::UninitializedSession,
@@ -488,10 +452,6 @@ impl FileTransfer for FtpFileTransfer {
/// ///
/// Rename file or a directory /// Rename file or a directory
fn rename(&mut self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError> { fn rename(&mut self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => { Some(stream) => {
// Get name // Get name
@@ -544,17 +504,9 @@ impl FileTransfer for FtpFileTransfer {
/// Data contains the file data /// Data contains the file data
/// Returns file and its size /// Returns file and its size
fn send_file(&mut self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError> { fn send_file(&mut self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.put_with_stream(&file_name.to_string_lossy()) { Some(stream) => match stream.put_with_stream(&file_name.to_string_lossy()) {
Ok(writer) => { Ok(writer) => Ok(Box::new(writer)),
// Set pending write response
self.pending_write_response = true;
Ok(Box::new(writer))
}
Err(err) => Err(FileTransferError::new_ex( Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::FileCreateDenied, FileTransferErrorType::FileCreateDenied,
format!("{}", err), format!("{}", err),
@@ -571,10 +523,6 @@ impl FileTransfer for FtpFileTransfer {
/// Receive file from remote with provided name /// Receive file from remote with provided name
/// Returns file and its size /// Returns file and its size
fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError> { fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError> {
// Close pending operations
if let Err(err) = self.handle_pending_operations() {
return Err(err);
}
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.get(&file_name.to_string_lossy()) { Some(stream) => match stream.get(&file_name.to_string_lossy()) {
Ok(reader) => Ok(Box::new(reader)), Ok(reader) => Ok(Box::new(reader)),
@@ -588,6 +536,39 @@ impl FileTransfer for FtpFileTransfer {
)), )),
} }
} }
/// ### on_sent
///
/// Finalize send method.
/// This method must be implemented only if necessary; in case you don't need it, just return `Ok(())`
/// The purpose of this method is to finalize the connection with the peer when writing data.
/// This is necessary for some protocols such as FTP.
/// You must call this method each time you want to finalize the write of the remote file.
fn on_sent(&mut self, writable: Box<dyn Write>) -> Result<(), FileTransferError> {
match &mut self.stream {
Some(stream) => match stream.finalize_put_stream(writable) {
Ok(_) => Ok(()),
Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
},
None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
}
}
/// ### on_recv
///
/// Finalize recv method.
/// This method must be implemented only if necessary; in case you don't need it, just return `Ok(())`
/// The purpose of this method is to finalize the connection with the peer when reading data.
/// This mighe be necessary for some protocols.
/// You must call this method each time you want to finalize the read of the remote file.
fn on_recv(&mut self, _readable: Box<dyn Read>) -> Result<(), FileTransferError> {
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]
+19 -3
View File
@@ -95,9 +95,7 @@ impl FileTransferError {
impl std::fmt::Display for FileTransferError { impl std::fmt::Display for FileTransferError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let err: String = match &self.code { let err: String = match &self.code {
FileTransferErrorType::AuthenticationFailed => { FileTransferErrorType::AuthenticationFailed => String::from("Authentication failed"),
String::from("Authentication failed")
}
FileTransferErrorType::BadAddress => String::from("Bad address syntax"), FileTransferErrorType::BadAddress => String::from("Bad address syntax"),
FileTransferErrorType::ConnectionError => String::from("Connection error"), FileTransferErrorType::ConnectionError => String::from("Connection error"),
FileTransferErrorType::DirStatFailed => String::from("Could not stat directory"), FileTransferErrorType::DirStatFailed => String::from("Could not stat directory"),
@@ -199,4 +197,22 @@ pub trait FileTransfer {
/// Receive file from remote with provided name /// Receive file from remote with provided name
/// Returns file and its size /// Returns file and its size
fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError>; fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError>;
/// ### on_sent
///
/// Finalize send method.
/// This method must be implemented only if necessary; in case you don't need it, just return `Ok(())`
/// The purpose of this method is to finalize the connection with the peer when writing data.
/// This is necessary for some protocols such as FTP.
/// You must call this method each time you want to finalize the write of the remote file.
fn on_sent(&mut self, writable: Box<dyn Write>) -> Result<(), FileTransferError>;
/// ### on_recv
///
/// Finalize recv method.
/// This method must be implemented only if necessary; in case you don't need it, just return `Ok(())`
/// The purpose of this method is to finalize the connection with the peer when reading data.
/// This mighe be necessary for some protocols.
/// You must call this method each time you want to finalize the read of the remote file.
fn on_recv(&mut self, readable: Box<dyn Read>) -> Result<(), FileTransferError>;
} }
+21 -4
View File
@@ -536,6 +536,26 @@ impl FileTransfer for SftpFileTransfer {
} }
} }
} }
/// ### on_sent
///
/// Finalize send method. This method must be implemented only if necessary.
/// The purpose of this method is to finalize the connection with the peer when writing data.
/// This is necessary for some protocols such as FTP.
/// You must call this method each time you want to finalize the write of the remote file.
fn on_sent(&mut self, _writable: Box<dyn Write>) -> Result<(), FileTransferError> {
Ok(())
}
/// ### on_recv
///
/// Finalize recv method. This method must be implemented only if necessary.
/// The purpose of this method is to finalize the connection with the peer when reading data.
/// This mighe be necessary for some protocols.
/// You must call this method each time you want to finalize the read of the remote file.
fn on_recv(&mut self, _readable: Box<dyn Read>) -> Result<(), FileTransferError> {
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]
@@ -696,10 +716,7 @@ mod tests {
assert_eq!(client.wrkdir, PathBuf::from("/")); assert_eq!(client.wrkdir, PathBuf::from("/"));
// List dir // List dir
let pwd: PathBuf = client.pwd().ok().unwrap(); let pwd: PathBuf = client.pwd().ok().unwrap();
let files: Vec<FsEntry> = client let files: Vec<FsEntry> = client.list_dir(pwd.as_path()).ok().unwrap();
.list_dir(pwd.as_path())
.ok()
.unwrap();
assert_eq!(files.len(), 3); // There are 3 files assert_eq!(files.len(), 3); // There are 3 files
// Disconnect // Disconnect
assert!(client.disconnect().is_ok()); assert!(client.disconnect().is_ok());
@@ -175,7 +175,6 @@ enum FileExplorerTab {
/// ## LogLevel /// ## LogLevel
/// ///
/// Log level type /// Log level type
#[allow(dead_code)]
enum LogLevel { enum LogLevel {
Error, Error,
Warn, Warn,
@@ -41,7 +41,14 @@ impl FileTransferActivity {
Ok(welcome) => { Ok(welcome) => {
if let Some(banner) = welcome { if let Some(banner) = welcome {
// Log welcome // Log welcome
self.log(LogLevel::Info, format!("Established connection with '{}': \"{}\"", self.params.address, banner).as_ref()); self.log(
LogLevel::Info,
format!(
"Established connection with '{}': \"{}\"",
self.params.address, banner
)
.as_ref(),
);
} }
// Set state to explorer // Set state to explorer
self.input_mode = InputMode::Explorer; self.input_mode = InputMode::Explorer;
@@ -174,6 +181,14 @@ impl FileTransferActivity {
last_progress_val = self.transfer_progress; last_progress_val = self.transfer_progress;
} }
} }
// Finalize stream
if let Err(err) = self.client.on_sent(rhnd) {
self.log(
LogLevel::Warn,
format!("Could not finalize remote stream: \"{}\"", err)
.as_str(),
);
}
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!( format!(
@@ -352,6 +367,14 @@ impl FileTransferActivity {
last_progress_val = self.transfer_progress; last_progress_val = self.transfer_progress;
} }
} }
// Finalize stream
if let Err(err) = self.client.on_recv(rhnd) {
self.log(
LogLevel::Warn,
format!("Could not finalize remote stream: \"{}\"", err)
.as_str(),
);
}
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,