diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e370a..dcadfb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ Released on FIXME: - Bugfix: - [Issue 10](https://github.com/veeso/termscp/issues/10): Fixed port not being loaded from bookmarks into gui + - [Issue 9](https://github.com/veeso/termscp/issues/9): Fixed issues related to paths on remote when using Windows +- Dependencies: + - Added `path-slash 0.1.4` (Windows only) ## 0.4.0 diff --git a/Cargo.lock b/Cargo.lock index 29c97b7..93090d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -930,6 +930,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "path-slash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cacbb3c4ff353b534a67fb8d7524d00229da4cb1dc8c79f4db96e375ab5b619" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1397,6 +1403,7 @@ dependencies = [ "keyring", "lazy_static", "magic-crypt", + "path-slash", "rand 0.8.2", "regex", "rpassword", diff --git a/Cargo.toml b/Cargo.toml index e3c2f65..bda1615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,12 +41,18 @@ ureq = { version = "2.0.2", features = ["json"] } whoami = "1.1.0" wildmatch = "1.0.13" +# POSIX dependencies [target.'cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))'.dependencies] users = "0.11.0" +# Windows + MacOS [target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] keyring = "0.10.1" +# Windows dependencies +[target.'cfg(target_os = "windows")'.dependencies] +path-slash = "0.1.4" + # Features [features] githubActions = [] # used to run particular on github actions diff --git a/src/filetransfer/ftp_transfer.rs b/src/filetransfer/ftp_transfer.rs index fce9dc3..cef0d15 100644 --- a/src/filetransfer/ftp_transfer.rs +++ b/src/filetransfer/ftp_transfer.rs @@ -28,6 +28,8 @@ // Dependencies extern crate chrono; extern crate ftp4; +#[cfg(os_target = "windows")] +extern crate path_slash; extern crate regex; use super::{FileTransfer, FileTransferError, FileTransferErrorType}; @@ -61,6 +63,20 @@ impl FtpFileTransfer { FtpFileTransfer { stream: None, ftps } } + /// ### resolve + /// + /// Fix provided path; on Windows fixes the backslashes, converting them to slashes + /// While on POSIX does nothing + #[cfg(target_os = "windows")] + fn resolve(p: &Path) -> PathBuf { + PathBuf::from(path_slash::PathExt::to_slash_lossy(p).as_str()) + } + + #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] + fn resolve(p: &Path) -> PathBuf { + p.to_path_buf() + } + /// ### parse_list_line /// /// Parse a line of LIST command output and instantiates an FsEntry from it @@ -165,6 +181,7 @@ impl FtpFileTransfer { } let mut abs_path: PathBuf = PathBuf::from(path); abs_path.push(file_name.as_str()); + let abs_path: PathBuf = Self::resolve(abs_path.as_path()); // get extension let extension: Option = abs_path .as_path() @@ -253,6 +270,7 @@ impl FtpFileTransfer { // Get absolute path let mut abs_path: PathBuf = PathBuf::from(path); abs_path.push(file_name.as_str()); + let abs_path: PathBuf = Self::resolve(abs_path.as_path()); // Get extension let extension: Option = abs_path .as_path() @@ -411,9 +429,10 @@ impl FileTransfer for FtpFileTransfer { /// Change working directory fn change_dir(&mut self, dir: &Path) -> Result { + let dir: PathBuf = Self::resolve(dir); match &mut self.stream { - Some(stream) => match stream.cwd(&dir.to_string_lossy()) { - Ok(_) => Ok(PathBuf::from(dir)), + Some(stream) => match stream.cwd(&dir.as_path().to_string_lossy()) { + Ok(_) => Ok(dir), Err(err) => Err(FileTransferError::new_ex( FileTransferErrorType::ConnectionError, format!("{}", err), @@ -440,14 +459,15 @@ impl FileTransfer for FtpFileTransfer { /// List directory entries fn list_dir(&mut self, path: &Path) -> Result, FileTransferError> { + let dir: PathBuf = Self::resolve(path); match &mut self.stream { - Some(stream) => match stream.list(Some(&path.to_string_lossy())) { + Some(stream) => match stream.list(Some(&dir.as_path().to_string_lossy())) { Ok(entries) => { // Prepare result let mut result: Vec = Vec::with_capacity(entries.len()); // Iterate over entries for entry in entries.iter() { - if let Ok(file) = self.parse_list_line(path, entry) { + if let Ok(file) = self.parse_list_line(dir.as_path(), entry) { result.push(file); } } @@ -468,8 +488,9 @@ impl FileTransfer for FtpFileTransfer { /// /// Make directory fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> { + let dir: PathBuf = Self::resolve(dir); match &mut self.stream { - Some(stream) => match stream.mkdir(&dir.to_string_lossy()) { + Some(stream) => match stream.mkdir(&dir.as_path().to_string_lossy()) { Ok(_) => Ok(()), Err(err) => Err(FileTransferError::new_ex( FileTransferErrorType::FileCreateDenied, @@ -538,6 +559,7 @@ impl FileTransfer for FtpFileTransfer { /// /// Rename file or a directory fn rename(&mut self, file: &FsEntry, dst: &Path) -> Result<(), FileTransferError> { + let dst: PathBuf = Self::resolve(dst); match &mut self.stream { Some(stream) => { // Get name @@ -603,8 +625,9 @@ impl FileTransfer for FtpFileTransfer { _local: &FsFile, file_name: &Path, ) -> Result, FileTransferError> { + let file_name: PathBuf = Self::resolve(file_name); 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.as_path().to_string_lossy()) { Ok(writer) => Ok(Box::new(writer)), // NOTE: don't use BufWriter here, since already returned by the library Err(err) => Err(FileTransferError::new_ex( FileTransferErrorType::FileCreateDenied, diff --git a/src/filetransfer/scp_transfer.rs b/src/filetransfer/scp_transfer.rs index af4abec..5028cee 100644 --- a/src/filetransfer/scp_transfer.rs +++ b/src/filetransfer/scp_transfer.rs @@ -26,6 +26,8 @@ * SOFTWARE. */ // Dependencies +#[cfg(os_target = "windows")] +extern crate path_slash; extern crate regex; extern crate ssh2; @@ -65,6 +67,20 @@ impl ScpFileTransfer { } } + /// ### resolve + /// + /// Fix provided path; on Windows fixes the backslashes, converting them to slashes + /// While on POSIX does nothing + #[cfg(target_os = "windows")] + fn resolve(p: &Path) -> PathBuf { + PathBuf::from(path_slash::PathExt::to_slash_lossy(p).as_str()) + } + + #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] + fn resolve(p: &Path) -> PathBuf { + p.to_path_buf() + } + /// ### parse_ls_output /// /// Parse a line of `ls -l` output and tokenize the output into a `FsEntry` @@ -174,6 +190,7 @@ impl ScpFileTransfer { } let mut abs_path: PathBuf = PathBuf::from(path); abs_path.push(file_name.as_str()); + let abs_path: PathBuf = Self::resolve(abs_path.as_path()); // Get extension let extension: Option = abs_path .as_path() @@ -448,7 +465,7 @@ impl FileTransfer for ScpFileTransfer { false => { let mut p: PathBuf = PathBuf::from("."); p.push(dir); - p + Self::resolve(p.as_path()) } }; // Change directory @@ -491,6 +508,7 @@ impl FileTransfer for ScpFileTransfer { fn copy(&mut self, src: &FsEntry, dst: &Path) -> Result<(), FileTransferError> { match self.is_connected() { true => { + let dst: PathBuf = Self::resolve(dst); // Run `cp -rf` let p: PathBuf = self.wrkdir.clone(); match self.perform_shell_cmd_with_path( @@ -534,6 +552,7 @@ impl FileTransfer for ScpFileTransfer { match self.is_connected() { true => { // Send ls -l to path + let path: PathBuf = Self::resolve(path); let p: PathBuf = self.wrkdir.clone(); match self.perform_shell_cmd_with_path( p.as_path(), @@ -546,7 +565,7 @@ impl FileTransfer for ScpFileTransfer { for line in lines.iter() { // First line must always be ignored // Parse row, if ok push to entries - if let Ok(entry) = self.parse_ls_output(path, line) { + if let Ok(entry) = self.parse_ls_output(path.as_path(), line) { entries.push(entry); } } @@ -571,6 +590,7 @@ impl FileTransfer for ScpFileTransfer { fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> { match self.is_connected() { true => { + let dir: PathBuf = Self::resolve(dir); let p: PathBuf = self.wrkdir.clone(); // Mkdir dir && echo 0 match self.perform_shell_cmd_with_path( @@ -644,6 +664,7 @@ impl FileTransfer for ScpFileTransfer { match self.is_connected() { true => { // Get path + let dst: PathBuf = Self::resolve(dst); let path: PathBuf = file.get_abs_path(); let p: PathBuf = self.wrkdir.clone(); match self.perform_shell_cmd_with_path( @@ -693,7 +714,7 @@ impl FileTransfer for ScpFileTransfer { false => { let mut p: PathBuf = self.wrkdir.clone(); p.push(path); - p + Self::resolve(p.as_path()) } }; match self.is_connected() { @@ -767,6 +788,7 @@ impl FileTransfer for ScpFileTransfer { ) -> Result, FileTransferError> { match self.session.as_ref() { Some(session) => { + let file_name: PathBuf = Self::resolve(file_name); // Set blocking to true session.set_blocking(true); // Calculate file mode @@ -798,7 +820,7 @@ impl FileTransfer for ScpFileTransfer { Err(_) => local.size as u64, // NOTE: fallback to fsentry size }; // Send file - match session.scp_send(file_name, mode, file_size, Some(times)) { + match session.scp_send(file_name.as_path(), mode, file_size, Some(times)) { Ok(channel) => Ok(Box::new(BufWriter::with_capacity(65536, channel))), Err(err) => Err(FileTransferError::new_ex( FileTransferErrorType::ProtocolError, @@ -1053,7 +1075,7 @@ mod tests { } #[test] - #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] + //#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] fn test_filetransfer_scp_find() { let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); assert!(client