mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Fix remote paths for Windows
This commit is contained in:
@@ -21,6 +21,9 @@ Released on FIXME:
|
|||||||
|
|
||||||
- Bugfix:
|
- Bugfix:
|
||||||
- [Issue 10](https://github.com/veeso/termscp/issues/10): Fixed port not being loaded from bookmarks into gui
|
- [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
|
## 0.4.0
|
||||||
|
|
||||||
|
|||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -930,6 +930,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "path-slash"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cacbb3c4ff353b534a67fb8d7524d00229da4cb1dc8c79f4db96e375ab5b619"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -1397,6 +1403,7 @@ dependencies = [
|
|||||||
"keyring",
|
"keyring",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"magic-crypt",
|
"magic-crypt",
|
||||||
|
"path-slash",
|
||||||
"rand 0.8.2",
|
"rand 0.8.2",
|
||||||
"regex",
|
"regex",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
|
|||||||
@@ -41,12 +41,18 @@ ureq = { version = "2.0.2", features = ["json"] }
|
|||||||
whoami = "1.1.0"
|
whoami = "1.1.0"
|
||||||
wildmatch = "1.0.13"
|
wildmatch = "1.0.13"
|
||||||
|
|
||||||
|
# POSIX dependencies
|
||||||
[target.'cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))'.dependencies]
|
[target.'cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))'.dependencies]
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
|
||||||
|
# Windows + MacOS
|
||||||
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
|
||||||
keyring = "0.10.1"
|
keyring = "0.10.1"
|
||||||
|
|
||||||
|
# Windows dependencies
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
path-slash = "0.1.4"
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
[features]
|
[features]
|
||||||
githubActions = [] # used to run particular on github actions
|
githubActions = [] # used to run particular on github actions
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
// Dependencies
|
// Dependencies
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate ftp4;
|
extern crate ftp4;
|
||||||
|
#[cfg(os_target = "windows")]
|
||||||
|
extern crate path_slash;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
||||||
@@ -61,6 +63,20 @@ impl FtpFileTransfer {
|
|||||||
FtpFileTransfer { stream: None, ftps }
|
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_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
|
||||||
@@ -165,6 +181,7 @@ impl FtpFileTransfer {
|
|||||||
}
|
}
|
||||||
let mut abs_path: PathBuf = PathBuf::from(path);
|
let mut abs_path: PathBuf = PathBuf::from(path);
|
||||||
abs_path.push(file_name.as_str());
|
abs_path.push(file_name.as_str());
|
||||||
|
let abs_path: PathBuf = Self::resolve(abs_path.as_path());
|
||||||
// get extension
|
// get extension
|
||||||
let extension: Option<String> = abs_path
|
let extension: Option<String> = abs_path
|
||||||
.as_path()
|
.as_path()
|
||||||
@@ -253,6 +270,7 @@ impl FtpFileTransfer {
|
|||||||
// Get absolute path
|
// Get absolute path
|
||||||
let mut abs_path: PathBuf = PathBuf::from(path);
|
let mut abs_path: PathBuf = PathBuf::from(path);
|
||||||
abs_path.push(file_name.as_str());
|
abs_path.push(file_name.as_str());
|
||||||
|
let abs_path: PathBuf = Self::resolve(abs_path.as_path());
|
||||||
// Get extension
|
// Get extension
|
||||||
let extension: Option<String> = abs_path
|
let extension: Option<String> = abs_path
|
||||||
.as_path()
|
.as_path()
|
||||||
@@ -411,9 +429,10 @@ 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> {
|
||||||
|
let dir: PathBuf = Self::resolve(dir);
|
||||||
match &mut self.stream {
|
match &mut self.stream {
|
||||||
Some(stream) => match stream.cwd(&dir.to_string_lossy()) {
|
Some(stream) => match stream.cwd(&dir.as_path().to_string_lossy()) {
|
||||||
Ok(_) => Ok(PathBuf::from(dir)),
|
Ok(_) => Ok(dir),
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::ConnectionError,
|
FileTransferErrorType::ConnectionError,
|
||||||
format!("{}", err),
|
format!("{}", err),
|
||||||
@@ -440,14 +459,15 @@ 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> {
|
||||||
|
let dir: PathBuf = Self::resolve(path);
|
||||||
match &mut self.stream {
|
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) => {
|
Ok(entries) => {
|
||||||
// Prepare result
|
// Prepare result
|
||||||
let mut result: Vec<FsEntry> = Vec::with_capacity(entries.len());
|
let mut result: Vec<FsEntry> = Vec::with_capacity(entries.len());
|
||||||
// Iterate over entries
|
// Iterate over entries
|
||||||
for entry in entries.iter() {
|
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);
|
result.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,8 +488,9 @@ impl FileTransfer for FtpFileTransfer {
|
|||||||
///
|
///
|
||||||
/// Make directory
|
/// Make directory
|
||||||
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
|
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
|
||||||
|
let dir: PathBuf = Self::resolve(dir);
|
||||||
match &mut self.stream {
|
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(()),
|
Ok(_) => Ok(()),
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::FileCreateDenied,
|
FileTransferErrorType::FileCreateDenied,
|
||||||
@@ -538,6 +559,7 @@ 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> {
|
||||||
|
let dst: PathBuf = Self::resolve(dst);
|
||||||
match &mut self.stream {
|
match &mut self.stream {
|
||||||
Some(stream) => {
|
Some(stream) => {
|
||||||
// Get name
|
// Get name
|
||||||
@@ -603,8 +625,9 @@ impl FileTransfer for FtpFileTransfer {
|
|||||||
_local: &FsFile,
|
_local: &FsFile,
|
||||||
file_name: &Path,
|
file_name: &Path,
|
||||||
) -> Result<Box<dyn Write>, FileTransferError> {
|
) -> Result<Box<dyn Write>, FileTransferError> {
|
||||||
|
let file_name: PathBuf = Self::resolve(file_name);
|
||||||
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.as_path().to_string_lossy()) {
|
||||||
Ok(writer) => Ok(Box::new(writer)), // NOTE: don't use BufWriter here, since already returned by the library
|
Ok(writer) => Ok(Box::new(writer)), // NOTE: don't use BufWriter here, since already returned by the library
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::FileCreateDenied,
|
FileTransferErrorType::FileCreateDenied,
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
// Dependencies
|
// Dependencies
|
||||||
|
#[cfg(os_target = "windows")]
|
||||||
|
extern crate path_slash;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
extern crate ssh2;
|
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_ls_output
|
||||||
///
|
///
|
||||||
/// Parse a line of `ls -l` output and tokenize the output into a `FsEntry`
|
/// 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);
|
let mut abs_path: PathBuf = PathBuf::from(path);
|
||||||
abs_path.push(file_name.as_str());
|
abs_path.push(file_name.as_str());
|
||||||
|
let abs_path: PathBuf = Self::resolve(abs_path.as_path());
|
||||||
// Get extension
|
// Get extension
|
||||||
let extension: Option<String> = abs_path
|
let extension: Option<String> = abs_path
|
||||||
.as_path()
|
.as_path()
|
||||||
@@ -448,7 +465,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
false => {
|
false => {
|
||||||
let mut p: PathBuf = PathBuf::from(".");
|
let mut p: PathBuf = PathBuf::from(".");
|
||||||
p.push(dir);
|
p.push(dir);
|
||||||
p
|
Self::resolve(p.as_path())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Change directory
|
// Change directory
|
||||||
@@ -491,6 +508,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
fn copy(&mut self, src: &FsEntry, dst: &Path) -> Result<(), FileTransferError> {
|
fn copy(&mut self, src: &FsEntry, dst: &Path) -> Result<(), FileTransferError> {
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
|
let dst: PathBuf = Self::resolve(dst);
|
||||||
// Run `cp -rf`
|
// Run `cp -rf`
|
||||||
let p: PathBuf = self.wrkdir.clone();
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
match self.perform_shell_cmd_with_path(
|
match self.perform_shell_cmd_with_path(
|
||||||
@@ -534,6 +552,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
// Send ls -l to path
|
// Send ls -l to path
|
||||||
|
let path: PathBuf = Self::resolve(path);
|
||||||
let p: PathBuf = self.wrkdir.clone();
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
match self.perform_shell_cmd_with_path(
|
match self.perform_shell_cmd_with_path(
|
||||||
p.as_path(),
|
p.as_path(),
|
||||||
@@ -546,7 +565,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
for line in lines.iter() {
|
for line in lines.iter() {
|
||||||
// First line must always be ignored
|
// First line must always be ignored
|
||||||
// Parse row, if ok push to entries
|
// 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);
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,6 +590,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
|
fn mkdir(&mut self, dir: &Path) -> Result<(), FileTransferError> {
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
|
let dir: PathBuf = Self::resolve(dir);
|
||||||
let p: PathBuf = self.wrkdir.clone();
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
// Mkdir dir && echo 0
|
// Mkdir dir && echo 0
|
||||||
match self.perform_shell_cmd_with_path(
|
match self.perform_shell_cmd_with_path(
|
||||||
@@ -644,6 +664,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
// Get path
|
// Get path
|
||||||
|
let dst: PathBuf = Self::resolve(dst);
|
||||||
let path: PathBuf = file.get_abs_path();
|
let path: PathBuf = file.get_abs_path();
|
||||||
let p: PathBuf = self.wrkdir.clone();
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
match self.perform_shell_cmd_with_path(
|
match self.perform_shell_cmd_with_path(
|
||||||
@@ -693,7 +714,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
false => {
|
false => {
|
||||||
let mut p: PathBuf = self.wrkdir.clone();
|
let mut p: PathBuf = self.wrkdir.clone();
|
||||||
p.push(path);
|
p.push(path);
|
||||||
p
|
Self::resolve(p.as_path())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
@@ -767,6 +788,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
) -> Result<Box<dyn Write>, FileTransferError> {
|
) -> Result<Box<dyn Write>, FileTransferError> {
|
||||||
match self.session.as_ref() {
|
match self.session.as_ref() {
|
||||||
Some(session) => {
|
Some(session) => {
|
||||||
|
let file_name: PathBuf = Self::resolve(file_name);
|
||||||
// Set blocking to true
|
// Set blocking to true
|
||||||
session.set_blocking(true);
|
session.set_blocking(true);
|
||||||
// Calculate file mode
|
// Calculate file mode
|
||||||
@@ -798,7 +820,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
Err(_) => local.size as u64, // NOTE: fallback to fsentry size
|
Err(_) => local.size as u64, // NOTE: fallback to fsentry size
|
||||||
};
|
};
|
||||||
// Send file
|
// 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))),
|
Ok(channel) => Ok(Box::new(BufWriter::with_capacity(65536, channel))),
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::ProtocolError,
|
FileTransferErrorType::ProtocolError,
|
||||||
@@ -1053,7 +1075,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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() {
|
fn test_filetransfer_scp_find() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
|
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
|
||||||
assert!(client
|
assert!(client
|
||||||
|
|||||||
Reference in New Issue
Block a user