mirror of
https://github.com/veeso/termscp.git
synced 2026-09-28 15:01:19 -07:00
on_sent and on_recv methods for file transfer to finalize streams
This commit is contained in:
Generated
+1
-1
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user