From 54c02cd4e28390e534dc7ba627049a88b5e8bad1 Mon Sep 17 00:00:00 2001 From: ChristianVisintin Date: Fri, 4 Dec 2020 18:25:59 +0100 Subject: [PATCH] on_sent and on_recv methods for file transfer to finalize streams --- Cargo.lock | 2 +- src/filetransfer/ftp_transfer.rs | 109 ++++++++---------- src/filetransfer/mod.rs | 22 +++- src/filetransfer/sftp_transfer.rs | 25 +++- .../activities/filetransfer_activity/mod.rs | 1 - .../filetransfer_activity/session.rs | 25 +++- 6 files changed, 110 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d87483..321c275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,7 +147,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "ftp" version = "3.0.1" -source = "git+https://github.com/ChristianVisintin/rust-ftp#cfe594f6e6acd6137087847357bf902207d4e477" +source = "git+https://github.com/ChristianVisintin/rust-ftp#63b06ef1b21cd7bd0a44682e514144e67c071d0e" dependencies = [ "chrono", "lazy_static", diff --git a/src/filetransfer/ftp_transfer.rs b/src/filetransfer/ftp_transfer.rs index 2c13aa1..c533eba 100644 --- a/src/filetransfer/ftp_transfer.rs +++ b/src/filetransfer/ftp_transfer.rs @@ -34,7 +34,6 @@ use crate::utils::lstime_to_systime; // Includes use ftp::native_tls::TlsConnector; -use ftp::status::{CLOSING_DATA_CONNECTION, REQUESTED_FILE_ACTION_OK}; use ftp::FtpStream; use regex::Regex; use std::io::{Read, Write}; @@ -47,7 +46,6 @@ use std::time::SystemTime; pub struct FtpFileTransfer { stream: Option, ftps: bool, - pending_write_response: bool, } impl FtpFileTransfer { @@ -58,29 +56,9 @@ impl FtpFileTransfer { FtpFileTransfer { stream: None, 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 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 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, - Err(err) => return Err(FileTransferError::new_ex( - FileTransferErrorType::SslError, - format!("{}", err), - )) + Err(err) => { + return Err(FileTransferError::new_ex( + FileTransferErrorType::SslError, + format!("{}", err), + )) + } }; stream = match stream.into_secure(ctx, address.as_str()) { Ok(s) => s, @@ -332,10 +316,6 @@ impl FileTransfer for FtpFileTransfer { /// Print working directory fn pwd(&mut self) -> Result { - // Close pending operations - if let Err(err) = self.handle_pending_operations() { - return Err(err); - } match &mut self.stream { Some(stream) => match stream.pwd() { Ok(path) => Ok(PathBuf::from(path.as_str())), @@ -355,10 +335,6 @@ impl FileTransfer for FtpFileTransfer { /// Change working directory fn change_dir(&mut self, dir: &Path) -> Result { - // Close pending operations - if let Err(err) = self.handle_pending_operations() { - return Err(err); - } match &mut self.stream { Some(stream) => match stream.cwd(&dir.to_string_lossy()) { Ok(_) => Ok(PathBuf::from(dir)), @@ -378,10 +354,6 @@ impl FileTransfer for FtpFileTransfer { /// List directory entries fn list_dir(&mut self, path: &Path) -> Result, FileTransferError> { - // Close pending operations - if let Err(err) = self.handle_pending_operations() { - return Err(err); - } match &mut self.stream { Some(stream) => match stream.list(Some(&path.to_string_lossy())) { Ok(entries) => { @@ -410,10 +382,6 @@ impl FileTransfer for FtpFileTransfer { /// /// Make directory 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 { Some(stream) => match stream.mkdir(&dir.to_string_lossy()) { Ok(_) => Ok(()), @@ -432,10 +400,6 @@ impl FileTransfer for FtpFileTransfer { /// /// Remove a file or a directory 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() { return Err(FileTransferError::new( FileTransferErrorType::UninitializedSession, @@ -488,10 +452,6 @@ impl FileTransfer for FtpFileTransfer { /// /// Rename file or a directory 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 { Some(stream) => { // Get name @@ -544,17 +504,9 @@ impl FileTransfer for FtpFileTransfer { /// Data contains the file data /// Returns file and its size fn send_file(&mut self, file_name: &Path) -> Result, FileTransferError> { - // Close pending operations - if let Err(err) = self.handle_pending_operations() { - return Err(err); - } match &mut self.stream { Some(stream) => match stream.put_with_stream(&file_name.to_string_lossy()) { - Ok(writer) => { - // Set pending write response - self.pending_write_response = true; - Ok(Box::new(writer)) - } + Ok(writer) => Ok(Box::new(writer)), Err(err) => Err(FileTransferError::new_ex( FileTransferErrorType::FileCreateDenied, format!("{}", err), @@ -571,10 +523,6 @@ impl FileTransfer for FtpFileTransfer { /// Receive file from remote with provided name /// Returns file and its size fn recv_file(&mut self, file_name: &Path) -> Result, FileTransferError> { - // Close pending operations - if let Err(err) = self.handle_pending_operations() { - return Err(err); - } match &mut self.stream { Some(stream) => match stream.get(&file_name.to_string_lossy()) { 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) -> 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) -> Result<(), FileTransferError> { + Ok(()) + } } #[cfg(test)] diff --git a/src/filetransfer/mod.rs b/src/filetransfer/mod.rs index 8b55dc2..b25366c 100644 --- a/src/filetransfer/mod.rs +++ b/src/filetransfer/mod.rs @@ -95,9 +95,7 @@ impl FileTransferError { impl std::fmt::Display for FileTransferError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let err: String = match &self.code { - FileTransferErrorType::AuthenticationFailed => { - String::from("Authentication failed") - } + FileTransferErrorType::AuthenticationFailed => String::from("Authentication failed"), FileTransferErrorType::BadAddress => String::from("Bad address syntax"), FileTransferErrorType::ConnectionError => String::from("Connection error"), FileTransferErrorType::DirStatFailed => String::from("Could not stat directory"), @@ -199,4 +197,22 @@ pub trait FileTransfer { /// Receive file from remote with provided name /// Returns file and its size fn recv_file(&mut self, file_name: &Path) -> Result, 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) -> 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) -> Result<(), FileTransferError>; } diff --git a/src/filetransfer/sftp_transfer.rs b/src/filetransfer/sftp_transfer.rs index b62627b..764e2fa 100644 --- a/src/filetransfer/sftp_transfer.rs +++ b/src/filetransfer/sftp_transfer.rs @@ -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) -> 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) -> Result<(), FileTransferError> { + Ok(()) + } } #[cfg(test)] @@ -696,10 +716,7 @@ mod tests { assert_eq!(client.wrkdir, PathBuf::from("/")); // List dir let pwd: PathBuf = client.pwd().ok().unwrap(); - let files: Vec = client - .list_dir(pwd.as_path()) - .ok() - .unwrap(); + let files: Vec = client.list_dir(pwd.as_path()).ok().unwrap(); assert_eq!(files.len(), 3); // There are 3 files // Disconnect assert!(client.disconnect().is_ok()); diff --git a/src/ui/activities/filetransfer_activity/mod.rs b/src/ui/activities/filetransfer_activity/mod.rs index 89f4aa5..addf545 100644 --- a/src/ui/activities/filetransfer_activity/mod.rs +++ b/src/ui/activities/filetransfer_activity/mod.rs @@ -175,7 +175,6 @@ enum FileExplorerTab { /// ## LogLevel /// /// Log level type -#[allow(dead_code)] enum LogLevel { Error, Warn, diff --git a/src/ui/activities/filetransfer_activity/session.rs b/src/ui/activities/filetransfer_activity/session.rs index 78937bb..be9494b 100644 --- a/src/ui/activities/filetransfer_activity/session.rs +++ b/src/ui/activities/filetransfer_activity/session.rs @@ -41,7 +41,14 @@ impl FileTransferActivity { Ok(welcome) => { if let Some(banner) = 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 self.input_mode = InputMode::Explorer; @@ -174,6 +181,14 @@ impl FileTransferActivity { 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( LogLevel::Info, format!( @@ -352,6 +367,14 @@ impl FileTransferActivity { 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 self.log( LogLevel::Info,