Tricky-copy in case copy is not supported

This commit is contained in:
veeso
2021-05-15 22:07:20 +02:00
parent 37ce54051e
commit 8c9c331d7e
5 changed files with 143 additions and 17 deletions

View File

@@ -33,13 +33,17 @@ Released on FIXME: ??
- Select a file with `<M>`, the file when selected will have a `*` prepended to its name - Select a file with `<M>`, the file when selected will have a `*` prepended to its name
- Select all files in the current directory with `<CTRL+A>` - Select all files in the current directory with `<CTRL+A>`
- Read more on manual: [Work on multiple files](docs/man.md#Work-on-multiple-files-) - Read more on manual: [Work on multiple files](docs/man.md#Work-on-multiple-files-)
- **File transfer changes**
- *SFTP*
- Added **COPY** command to SFTP (Please note that Copy command is not supported by SFTP natively, so here it just uses the `cp` shell command as it does in SCP).
- *FTP*
- Added support for file copy (achieved through *tricky-copy*: the file is first downloaded, then uploaded with a different file name)
- Enhancements - Enhancements
- Added a status bar in the file explorer showing whether the sync browser is enabled and which file sorting mode is selected - Added a status bar in the file explorer showing whether the sync browser is enabled and which file sorting mode is selected
- Removed the goold old figlet title - Removed the goold old figlet title
- Protocol input as first field in UI - Protocol input as first field in UI
- Port is now updated to standard for selected protocol - Port is now updated to standard for selected protocol
- when you change the protocol in the authentication form and the current port is standard (`< 1024`), the port will be automatically changed to default value for the selected protocol (e.g. current port: `123`, protocol is changes to `FTP`, port becomes `21`) - when you change the protocol in the authentication form and the current port is standard (`< 1024`), the port will be automatically changed to default value for the selected protocol (e.g. current port: `123`, protocol is changes to `FTP`, port becomes `21`)
- Added **COPY** command to SFTP (Please note that Copy command is not supported by SFTP natively, so here it just uses the `cp` shell command as it does in SCP).
- Bugfix: - Bugfix:
- Fixed wrong text wrap in log box - Fixed wrong text wrap in log box
- Fixed error message not being shown after an upload failure - Fixed error message not being shown after an upload failure

View File

@@ -59,11 +59,19 @@ pub struct FileTransferError {
msg: Option<String>, msg: Option<String>,
} }
impl FileTransferError {
/// ### kind
///
/// Returns the error kind
pub fn kind(&self) -> FileTransferErrorType {
self.code
}
}
/// ## 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)] #[derive(Error, Debug, Clone, Copy, PartialEq)]
#[derive(Error, Debug)]
pub enum FileTransferErrorType { pub enum FileTransferErrorType {
#[error("Authentication failed")] #[error("Authentication failed")]
AuthenticationFailed, AuthenticationFailed,
@@ -77,8 +85,6 @@ pub enum FileTransferErrorType {
DirStatFailed, DirStatFailed,
#[error("Failed to create file")] #[error("Failed to create file")]
FileCreateDenied, FileCreateDenied,
#[error("IO error: {0}")]
IoErr(std::io::Error),
#[error("No such file or directory")] #[error("No such file or directory")]
NoSuchFileOrDirectory, NoSuchFileOrDirectory,
#[error("Not enough permissions")] #[error("Not enough permissions")]
@@ -397,13 +403,13 @@ mod tests {
#[test] #[test]
fn test_filetransfer_mod_error() { fn test_filetransfer_mod_error() {
let err: FileTransferError = FileTransferError::new_ex( let err: FileTransferError = FileTransferError::new_ex(
FileTransferErrorType::IoErr(std::io::Error::from(std::io::ErrorKind::AddrInUse)), FileTransferErrorType::NoSuchFileOrDirectory,
String::from("non va una mazza"), String::from("non va una mazza"),
); );
assert_eq!(*err.msg.as_ref().unwrap(), String::from("non va una mazza")); assert_eq!(*err.msg.as_ref().unwrap(), String::from("non va una mazza"));
assert_eq!( assert_eq!(
format!("{}", err), format!("{}", err),
String::from("IO error: address in use (non va una mazza)") String::from("No such file or directory (non va una mazza)")
); );
assert_eq!( assert_eq!(
format!( format!(
@@ -482,5 +488,7 @@ mod tests {
), ),
String::from("Unsupported feature") String::from("Unsupported feature")
); );
let err = FileTransferError::new(FileTransferErrorType::UnsupportedFeature);
assert_eq!(err.kind(), FileTransferErrorType::UnsupportedFeature);
} }
} }

View File

@@ -25,8 +25,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
extern crate tempfile;
// locals // locals
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry}; use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry};
use crate::filetransfer::FileTransferErrorType;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
impl FileTransferActivity { impl FileTransferActivity {
@@ -120,7 +122,12 @@ impl FileTransferActivity {
), ),
); );
} }
Err(err) => self.log_and_alert( Err(err) => match err.kind() {
FileTransferErrorType::UnsupportedFeature => {
// If copy is not supported, perform the tricky copy
self.tricky_copy(entry, dest);
}
_ => self.log_and_alert(
LogLevel::Error, LogLevel::Error,
format!( format!(
"Could not copy \"{}\" to \"{}\": {}", "Could not copy \"{}\" to \"{}\": {}",
@@ -129,6 +136,7 @@ impl FileTransferActivity {
err err
), ),
), ),
},
} }
} }
} }

View File

@@ -899,4 +899,110 @@ impl FileTransferActivity {
} }
Ok(()) Ok(())
} }
/// ### tricky_copy
///
/// Tricky copy will be used whenever copy command is not available on remote host
pub(super) fn tricky_copy(&mut self, entry: &FsEntry, dest: &Path) {
// match entry
match entry {
FsEntry::File(entry) => {
// Create tempfile
let tmpfile: tempfile::NamedTempFile = match tempfile::NamedTempFile::new() {
Ok(f) => f,
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Copy failed: could not create temporary file: {}", err),
);
return;
}
};
// Download file
if let Err(err) =
self.filetransfer_recv_file(tmpfile.path(), entry, entry.name.clone())
{
self.log_and_alert(
LogLevel::Error,
format!("Copy failed: could not download to temporary file: {}", err),
);
return;
}
// Get local fs entry
let tmpfile_entry: FsEntry = match self.host.stat(tmpfile.path()) {
Ok(e) => e,
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Copy failed: could not stat \"{}\": {}",
tmpfile.path().display(),
err
),
);
return;
}
};
let tmpfile_entry = match &tmpfile_entry {
FsEntry::Directory(_) => panic!("tempfile is a directory for some reason"),
FsEntry::File(f) => f,
};
// Upload file to destination
if let Err(err) = self.filetransfer_send_file(
tmpfile_entry,
dest,
String::from(dest.to_string_lossy()),
) {
self.log_and_alert(
LogLevel::Error,
format!(
"Copy failed: could not write file {}: {}",
entry.abs_path.display(),
err
),
);
return;
}
}
FsEntry::Directory(_) => {
let tempdir: tempfile::TempDir = match tempfile::TempDir::new() {
Ok(d) => d,
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Copy failed: could not create temporary directory: {}", err),
);
return;
}
};
// Download file
self.filetransfer_recv(entry, tempdir.path(), None);
// Get path of dest
let mut tempdir_path: PathBuf = tempdir.path().to_path_buf();
tempdir_path.push(entry.get_name());
// Stat dir
let tempdir_entry: FsEntry = match self.host.stat(tempdir_path.as_path()) {
Ok(e) => e,
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Copy failed: could not stat \"{}\": {}",
tempdir.path().display(),
err
),
);
return;
}
};
// Upload to destination
let wrkdir: PathBuf = self.remote().wrkdir.clone();
self.filetransfer_send(
&tempdir_entry,
wrkdir.as_path(),
Some(String::from(dest.to_string_lossy())),
);
}
}
}
} }

View File

@@ -456,7 +456,7 @@ impl FileTransferActivity {
Box::new(Input::new( Box::new(Input::new(
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_label(String::from("Insert destination name")) .with_label(String::from("Copy file(s) to..."))
.build(), .build(),
)), )),
); );
@@ -586,7 +586,7 @@ impl FileTransferActivity {
Box::new(Input::new( Box::new(Input::new(
InputPropsBuilder::default() InputPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White) .with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_label(String::from("Insert new name")) .with_label(String::from("Move file(s) to..."))
.build(), .build(),
)), )),
); );