Filetransfer all mutable

This commit is contained in:
ChristianVisintin
2020-12-03 14:44:50 +01:00
parent 175acd9f5b
commit 7e09f5c7fa
2 changed files with 160 additions and 75 deletions

View File

@@ -54,7 +54,7 @@ pub struct FileTransferError {
/// ## FileTransferErrorType /// ## FileTransferErrorType
/// ///
/// FileTransferErrorType defines the possible errors available for a file transfer /// FileTransferErrorType defines the possible errors available for a file transfer
#[allow(dead_code)]
pub enum FileTransferErrorType { pub enum FileTransferErrorType {
AuthenticationFailed, AuthenticationFailed,
BadAddress, BadAddress,
@@ -150,7 +150,7 @@ pub trait FileTransfer {
/// ///
/// Print working directory /// Print working directory
fn pwd(&self) -> Result<PathBuf, FileTransferError>; fn pwd(&mut self) -> Result<PathBuf, FileTransferError>;
/// ### change_dir /// ### change_dir
/// ///
@@ -162,27 +162,27 @@ pub trait FileTransfer {
/// ///
/// List directory entries /// List directory entries
fn list_dir(&self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError>; fn list_dir(&mut self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError>;
/// ### mkdir /// ### mkdir
/// ///
/// Make directory /// Make directory
fn mkdir(&self, dir: &Path) -> Result<(), FileTransferError>; fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError>;
/// ### remove /// ### remove
/// ///
/// Remove a file or a directory /// Remove a file or a directory
fn remove(&self, file: &FsEntry) -> Result<(), FileTransferError>; fn remove(&mut self, file: &FsEntry) -> Result<(), FileTransferError>;
/// ### rename /// ### rename
/// ///
/// Rename file or a directory /// Rename file or a directory
fn rename(&self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError>; fn rename(&mut self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError>;
/// ### stat /// ### stat
/// ///
/// Stat file and return FsEntry /// Stat file and return FsEntry
fn stat(&self, path: &Path) -> Result<FsEntry, FileTransferError>; fn stat(&mut self, path: &Path) -> Result<FsEntry, FileTransferError>;
/// ### send_file /// ### send_file
/// ///
@@ -190,11 +190,11 @@ pub trait FileTransfer {
/// File name is referred to the name of the file as it will be saved /// File name is referred to the name of the file as it will be saved
/// Data contains the file data /// Data contains the file data
/// Returns file and its size /// Returns file and its size
fn send_file(&self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError>; fn send_file(&mut self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError>;
/// ### recv_file /// ### recv_file
/// ///
/// 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(&self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError>; fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError>;
} }

View File

@@ -69,17 +69,28 @@ impl SftpFileTransfer {
match self.sftp.as_ref().unwrap().realpath(root.as_path()) { match self.sftp.as_ref().unwrap().realpath(root.as_path()) {
Ok(p) => match self.sftp.as_ref().unwrap().stat(p.as_path()) { Ok(p) => match self.sftp.as_ref().unwrap().stat(p.as_path()) {
Ok(_) => Ok(PathBuf::from(p)), Ok(_) => Ok(PathBuf::from(p)),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::NoSuchFileOrDirectory, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
}, },
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::NoSuchFileOrDirectory, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
} }
} }
false => match self.sftp.as_ref().unwrap().realpath(p) { false => match self.sftp.as_ref().unwrap().realpath(p) {
Ok(p) => match self.sftp.as_ref().unwrap().stat(p.as_path()) { Ok(p) => match self.sftp.as_ref().unwrap().stat(p.as_path()) {
Ok(_) => Ok(PathBuf::from(p)), Ok(_) => Ok(PathBuf::from(p)),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::NoSuchFileOrDirectory, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
}, },
Err(_) => Err(FileTransferError::new(FileTransferErrorType::NoSuchFileOrDirectory)), Err(_) => Err(FileTransferError::new(
FileTransferErrorType::NoSuchFileOrDirectory,
)),
}, },
} }
} }
@@ -188,37 +199,63 @@ impl FileTransfer for SftpFileTransfer {
// Setup tcp stream // Setup tcp stream
let tcp: TcpStream = match TcpStream::connect(format!("{}:{}", address, port)) { let tcp: TcpStream = match TcpStream::connect(format!("{}:{}", address, port)) {
Ok(stream) => stream, Ok(stream) => stream,
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::BadAddress, format!("{}", err))), Err(err) => {
return Err(FileTransferError::new_ex(
FileTransferErrorType::BadAddress,
format!("{}", err),
))
}
}; };
// Create session // Create session
let mut session: Session = match Session::new() { let mut session: Session = match Session::new() {
Ok(s) => s, Ok(s) => s,
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::ConnectionError, format!("{}", err))), Err(err) => {
return Err(FileTransferError::new_ex(
FileTransferErrorType::ConnectionError,
format!("{}", err),
))
}
}; };
// Set TCP stream // Set TCP stream
session.set_tcp_stream(tcp); session.set_tcp_stream(tcp);
// Open connection // Open connection
if let Err(err) = session.handshake() { if let Err(err) = session.handshake() {
return Err(FileTransferError::new_ex(FileTransferErrorType::ConnectionError, format!("{}", err))); return Err(FileTransferError::new_ex(
FileTransferErrorType::ConnectionError,
format!("{}", err),
));
} }
// Try authentication // Try authentication
if let Err(err) = session.userauth_password( if let Err(err) = session.userauth_password(
username.unwrap_or(String::from("")).as_str(), username.unwrap_or(String::from("")).as_str(),
password.unwrap_or(String::from("")).as_str(), password.unwrap_or(String::from("")).as_str(),
) { ) {
return Err(FileTransferError::new_ex(FileTransferErrorType::AuthenticationFailed, format!("{}", err))); return Err(FileTransferError::new_ex(
FileTransferErrorType::AuthenticationFailed,
format!("{}", err),
));
} }
// Set blocking to true // Set blocking to true
session.set_blocking(true); session.set_blocking(true);
// Get Sftp client // Get Sftp client
let sftp: Sftp = match session.sftp() { let sftp: Sftp = match session.sftp() {
Ok(s) => s, Ok(s) => s,
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::ProtocolError, format!("{}", err))), Err(err) => {
return Err(FileTransferError::new_ex(
FileTransferErrorType::ProtocolError,
format!("{}", err),
))
}
}; };
// Get working directory // Get working directory
self.wrkdir = match sftp.realpath(PathBuf::from(".").as_path()) { self.wrkdir = match sftp.realpath(PathBuf::from(".").as_path()) {
Ok(p) => p, Ok(p) => p,
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::ProtocolError, format!("{}", err))), Err(err) => {
return Err(FileTransferError::new_ex(
FileTransferErrorType::ProtocolError,
format!("{}", err),
))
}
}; };
// Set session // Set session
self.session = Some(session); self.session = Some(session);
@@ -241,10 +278,15 @@ impl FileTransfer for SftpFileTransfer {
self.sftp = None; self.sftp = None;
Ok(()) Ok(())
} }
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::ConnectionError, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::ConnectionError,
format!("{}", err),
)),
} }
} }
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
@@ -258,10 +300,12 @@ impl FileTransfer for SftpFileTransfer {
/// ### pwd /// ### pwd
/// ///
/// Print working directory /// Print working directory
fn pwd(&self) -> Result<PathBuf, FileTransferError> { fn pwd(&mut self) -> Result<PathBuf, FileTransferError> {
match self.sftp { match self.sftp {
Some(_) => Ok(self.wrkdir.clone()), Some(_) => Ok(self.wrkdir.clone()),
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
@@ -278,14 +322,16 @@ impl FileTransfer for SftpFileTransfer {
}; };
Ok(self.wrkdir.clone()) Ok(self.wrkdir.clone())
} }
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
/// ### list_dir /// ### list_dir
/// ///
/// List directory entries /// List directory entries
fn list_dir(&self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError> { fn list_dir(&mut self, path: &Path) -> Result<Vec<FsEntry>, FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(sftp) => { Some(sftp) => {
// Get path // Get path
@@ -295,7 +341,12 @@ impl FileTransfer for SftpFileTransfer {
}; };
// Get files // Get files
match sftp.readdir(dir.as_path()) { match sftp.readdir(dir.as_path()) {
Err(err) => return Err(FileTransferError::new_ex(FileTransferErrorType::DirStatFailed, format!("{}", err))), Err(err) => {
return Err(FileTransferError::new_ex(
FileTransferErrorType::DirStatFailed,
format!("{}", err),
))
}
Ok(files) => { Ok(files) => {
// Allocate vector // Allocate vector
let mut entries: Vec<FsEntry> = Vec::with_capacity(files.len()); let mut entries: Vec<FsEntry> = Vec::with_capacity(files.len());
@@ -307,64 +358,75 @@ impl FileTransfer for SftpFileTransfer {
} }
} }
} }
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
/// ### mkdir /// ### mkdir
/// ///
/// Make directory /// Make directory
fn mkdir(&self, dir: &Path) -> Result<(), FileTransferError> { fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(sftp) => { Some(sftp) => {
// Make directory // Make directory
let path: PathBuf = self.get_abs_path(PathBuf::from(dir).as_path()); let path: PathBuf = self.get_abs_path(PathBuf::from(dir).as_path());
match sftp.mkdir(path.as_path(), 0o775) { match sftp.mkdir(path.as_path(), 0o775) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::FileCreateDenied, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::FileCreateDenied,
format!("{}", err),
)),
} }
} }
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
/// ### remove /// ### remove
/// ///
/// Remove a file or a directory /// Remove a file or a directory
fn remove(&self, file: &FsEntry) -> Result<(), FileTransferError> { fn remove(&mut self, file: &FsEntry) -> Result<(), FileTransferError> {
match self.sftp.as_ref() { if self.sftp.is_none() {
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), return Err(FileTransferError::new(
Some(sftp) => { FileTransferErrorType::UninitializedSession,
// Match if file is a file or a directory ));
match file { }
FsEntry::File(f) => { // Match if file is a file or a directory
// Remove file match file {
match sftp.unlink(f.abs_path.as_path()) { FsEntry::File(f) => {
Ok(_) => Ok(()), // Remove file
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::PexError, format!("{}", err))), match self.sftp.as_ref().unwrap().unlink(f.abs_path.as_path()) {
} Ok(_) => Ok(()),
} Err(err) => Err(FileTransferError::new_ex(
FsEntry::Directory(d) => { FileTransferErrorType::PexError,
// Remove recursively format!("{}", err),
// Get directory files )),
match self.list_dir(d.abs_path.as_path()) { }
Ok(entries) => { }
// Remove each entry FsEntry::Directory(d) => {
for entry in entries { // Remove recursively
if let Err(err) = self.remove(&entry) { // Get directory files
return Err(err); let directory_content: Vec<FsEntry> = match self.list_dir(d.abs_path.as_path()) {
} Ok(entries) => entries,
} Err(err) => return Err(err),
// Finally remove directory };
match sftp.rmdir(d.abs_path.as_path()) { for entry in directory_content.iter() {
Ok(_) => Ok(()), if let Err(err) = self.remove(&entry) {
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::PexError, format!("{}", err))), return Err(err);
}
}
Err(err) => return Err(err),
}
} }
} }
// Finally remove directory
match self.sftp.as_ref().unwrap().rmdir(d.abs_path.as_path()) {
Ok(_) => Ok(()),
Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::PexError,
format!("{}", err),
)),
}
} }
} }
} }
@@ -372,9 +434,11 @@ impl FileTransfer for SftpFileTransfer {
/// ### rename /// ### rename
/// ///
/// Rename file or a directory /// Rename file or a directory
fn rename(&self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError> { fn rename(&mut self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
Some(sftp) => { Some(sftp) => {
// Resolve destination path // Resolve destination path
let abs_dst: PathBuf = self.get_abs_path(dst); let abs_dst: PathBuf = self.get_abs_path(dst);
@@ -385,7 +449,10 @@ impl FileTransfer for SftpFileTransfer {
}; };
match sftp.rename(abs_src.as_path(), abs_dst.as_path(), None) { match sftp.rename(abs_src.as_path(), abs_dst.as_path(), None) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::FileCreateDenied, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::FileCreateDenied,
format!("{}", err),
)),
} }
} }
} }
@@ -394,7 +461,7 @@ impl FileTransfer for SftpFileTransfer {
/// ### stat /// ### stat
/// ///
/// Stat file and return FsEntry /// Stat file and return FsEntry
fn stat(&self, path: &Path) -> Result<FsEntry, FileTransferError> { fn stat(&mut self, path: &Path) -> Result<FsEntry, FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(sftp) => { Some(sftp) => {
// Get path // Get path
@@ -405,10 +472,15 @@ impl FileTransfer for SftpFileTransfer {
// Get file // Get file
match sftp.stat(dir.as_path()) { match sftp.stat(dir.as_path()) {
Ok(metadata) => Ok(self.make_fsentry(dir.as_path(), &metadata)), Ok(metadata) => Ok(self.make_fsentry(dir.as_path(), &metadata)),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::NoSuchFileOrDirectory, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
} }
} }
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
} }
} }
@@ -417,14 +489,19 @@ impl FileTransfer for SftpFileTransfer {
/// Send file to remote /// Send file to remote
/// File name is referred to the name of the file as it will be saved /// File name is referred to the name of the file as it will be saved
/// Data contains the file data /// Data contains the file data
fn send_file(&self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError> { fn send_file(&mut self, file_name: &Path) -> Result<Box<dyn Write>, FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
Some(sftp) => { Some(sftp) => {
let remote_path: PathBuf = self.get_abs_path(file_name); let remote_path: PathBuf = self.get_abs_path(file_name);
match sftp.create(remote_path.as_path()) { match sftp.create(remote_path.as_path()) {
Ok(file) => Ok(Box::new(file)), Ok(file) => Ok(Box::new(file)),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::FileCreateDenied, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::FileCreateDenied,
format!("{}", err),
)),
} }
} }
} }
@@ -433,9 +510,11 @@ impl FileTransfer for SftpFileTransfer {
/// ### recv_file /// ### recv_file
/// ///
/// Receive file from remote with provided name /// Receive file from remote with provided name
fn recv_file(&self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError> { fn recv_file(&mut self, file_name: &Path) -> Result<Box<dyn Read>, FileTransferError> {
match self.sftp.as_ref() { match self.sftp.as_ref() {
None => Err(FileTransferError::new(FileTransferErrorType::UninitializedSession)), None => Err(FileTransferError::new(
FileTransferErrorType::UninitializedSession,
)),
Some(sftp) => { Some(sftp) => {
// Get remote file name // Get remote file name
let remote_path: PathBuf = match self.get_remote_path(file_name) { let remote_path: PathBuf = match self.get_remote_path(file_name) {
@@ -445,7 +524,10 @@ impl FileTransfer for SftpFileTransfer {
// Open remote file // Open remote file
match sftp.open(remote_path.as_path()) { match sftp.open(remote_path.as_path()) {
Ok(file) => Ok(Box::new(file)), Ok(file) => Ok(Box::new(file)),
Err(err) => Err(FileTransferError::new_ex(FileTransferErrorType::NoSuchFileOrDirectory, format!("{}", err))), Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::NoSuchFileOrDirectory,
format!("{}", err),
)),
} }
} }
} }
@@ -633,7 +715,10 @@ mod tests {
assert!(client.session.is_some()); assert!(client.session.is_some());
assert!(client.sftp.is_some()); assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/")); assert_eq!(client.wrkdir, PathBuf::from("/"));
let file: FsEntry = client.stat(PathBuf::from("readme.txt").as_path()).ok().unwrap(); let file: FsEntry = client
.stat(PathBuf::from("readme.txt").as_path())
.ok()
.unwrap();
if let FsEntry::File(file) = file { if let FsEntry::File(file) = file {
assert_eq!(file.abs_path, PathBuf::from("/readme.txt")); assert_eq!(file.abs_path, PathBuf::from("/readme.txt"));
} else { } else {