mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Merge pull request #20 from veeso/issue-17-still-some-problems-with-symlinks
[BUG] Problems with symlinks
This commit is contained in:
@@ -19,8 +19,14 @@
|
|||||||
|
|
||||||
Released on FIXME:
|
Released on FIXME:
|
||||||
|
|
||||||
|
- Enhancements:
|
||||||
|
- SCP file transfer:
|
||||||
|
- Added possibility to stat directories.
|
||||||
- Bugfix:
|
- Bugfix:
|
||||||
- [Issue 18](https://github.com/veeso/termscp/issues/18): Set file transfer type to `Binary` for FTP
|
- [Issue 18](https://github.com/veeso/termscp/issues/18): Set file transfer type to `Binary` for FTP
|
||||||
|
- [Issue 17](https://github.com/veeso/termscp/issues/17)
|
||||||
|
- SCP: fixed symlink not properly detected
|
||||||
|
- FTP: added symlink support for Linux targets
|
||||||
- [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
|
- [Issue 9](https://github.com/veeso/termscp/issues/9): Fixed issues related to paths on remote when using Windows
|
||||||
- Dependencies:
|
- Dependencies:
|
||||||
@@ -39,6 +45,8 @@ Released on FIXME:
|
|||||||
|
|
||||||
Released on 27/03/2021
|
Released on 27/03/2021
|
||||||
|
|
||||||
|
> The UI refactoring update
|
||||||
|
|
||||||
- **New explorer features**:
|
- **New explorer features**:
|
||||||
- **Execute** a command pressing `X`. This feature is supported on both local and remote hosts (only SFTP/SCP protocols support this feature).
|
- **Execute** a command pressing `X`. This feature is supported on both local and remote hosts (only SFTP/SCP protocols support this feature).
|
||||||
- **Find**: search for files pressing `F` using wild matches.
|
- **Find**: search for files pressing `F` using wild matches.
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ impl FtpFileTransfer {
|
|||||||
/// ### 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
|
||||||
fn parse_list_line(&self, path: &Path, line: &str) -> Result<FsEntry, ()> {
|
fn parse_list_line(&mut self, path: &Path, line: &str) -> Result<FsEntry, ()> {
|
||||||
// Try to parse using UNIX syntax
|
// Try to parse using UNIX syntax
|
||||||
match self.parse_unix_list_line(path, line) {
|
match self.parse_unix_list_line(path, line) {
|
||||||
Ok(entry) => Ok(entry),
|
Ok(entry) => Ok(entry),
|
||||||
@@ -99,7 +99,7 @@ impl FtpFileTransfer {
|
|||||||
/// UNIX syntax has the following syntax:
|
/// UNIX syntax has the following syntax:
|
||||||
/// {FILE_TYPE}{UNIX_PEX} {HARD_LINKS} {USER} {GROUP} {SIZE} {DATE} {FILENAME}
|
/// {FILE_TYPE}{UNIX_PEX} {HARD_LINKS} {USER} {GROUP} {SIZE} {DATE} {FILENAME}
|
||||||
/// -rw-r--r-- 1 cvisintin staff 4968 27 Dic 10:46 CHANGELOG.md
|
/// -rw-r--r-- 1 cvisintin staff 4968 27 Dic 10:46 CHANGELOG.md
|
||||||
fn parse_unix_list_line(&self, path: &Path, line: &str) -> Result<FsEntry, ()> {
|
fn parse_unix_list_line(&mut self, path: &Path, line: &str) -> Result<FsEntry, ()> {
|
||||||
// Prepare list regex
|
// Prepare list regex
|
||||||
// NOTE: about this damn regex <https://stackoverflow.com/questions/32480890/is-there-a-regex-to-parse-the-values-from-an-ftp-directory-listing>
|
// NOTE: about this damn regex <https://stackoverflow.com/questions/32480890/is-there-a-regex-to-parse-the-values-from-an-ftp-directory-listing>
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -116,7 +116,8 @@ impl FtpFileTransfer {
|
|||||||
}
|
}
|
||||||
// Collect metadata
|
// Collect metadata
|
||||||
// Get if is directory and if is symlink
|
// Get if is directory and if is symlink
|
||||||
let (is_dir, _is_symlink): (bool, bool) = match metadata.get(1).unwrap().as_str() {
|
let (mut is_dir, is_symlink): (bool, bool) = match metadata.get(1).unwrap().as_str()
|
||||||
|
{
|
||||||
"-" => (false, false),
|
"-" => (false, false),
|
||||||
"l" => (false, true),
|
"l" => (false, true),
|
||||||
"d" => (true, false),
|
"d" => (true, false),
|
||||||
@@ -174,11 +175,60 @@ impl FtpFileTransfer {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let file_name: String = String::from(metadata.get(8).unwrap().as_str());
|
// Split filename if required
|
||||||
|
let (file_name, symlink_path): (String, Option<PathBuf>) = match is_symlink {
|
||||||
|
true => self.get_name_and_link(metadata.get(8).unwrap().as_str()),
|
||||||
|
false => (String::from(metadata.get(8).unwrap().as_str()), None),
|
||||||
|
};
|
||||||
// Check if file_name is '.' or '..'
|
// Check if file_name is '.' or '..'
|
||||||
if file_name.as_str() == "." || file_name.as_str() == ".." {
|
if file_name.as_str() == "." || file_name.as_str() == ".." {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
// Get symlink
|
||||||
|
let symlink: Option<Box<FsEntry>> = match symlink_path {
|
||||||
|
None => None,
|
||||||
|
Some(p) => Some(Box::new(match p.to_string_lossy().ends_with('/') {
|
||||||
|
true => {
|
||||||
|
// NOTE: is_dir becomes true
|
||||||
|
is_dir = true;
|
||||||
|
FsEntry::Directory(FsDirectory {
|
||||||
|
name: p
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or(&std::ffi::OsStr::new(""))
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
abs_path: p.clone(),
|
||||||
|
last_change_time: mtime,
|
||||||
|
last_access_time: mtime,
|
||||||
|
creation_time: mtime,
|
||||||
|
readonly: false,
|
||||||
|
symlink: None,
|
||||||
|
user: uid,
|
||||||
|
group: gid,
|
||||||
|
unix_pex: Some(unix_pex),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
false => FsEntry::File(FsFile {
|
||||||
|
name: p
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or(&std::ffi::OsStr::new(""))
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
abs_path: p.clone(),
|
||||||
|
last_change_time: mtime,
|
||||||
|
last_access_time: mtime,
|
||||||
|
creation_time: mtime,
|
||||||
|
readonly: false,
|
||||||
|
symlink: None,
|
||||||
|
size: filesize,
|
||||||
|
ftype: p.extension().map(|s| String::from(s.to_string_lossy())),
|
||||||
|
user: uid,
|
||||||
|
group: gid,
|
||||||
|
unix_pex: Some(unix_pex),
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
eprintln!("{:?};{:?}", is_dir, symlink);
|
||||||
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());
|
let abs_path: PathBuf = Self::resolve(abs_path.as_path());
|
||||||
@@ -197,7 +247,7 @@ impl FtpFileTransfer {
|
|||||||
last_access_time: mtime,
|
last_access_time: mtime,
|
||||||
creation_time: mtime,
|
creation_time: mtime,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
symlink: None,
|
symlink,
|
||||||
user: uid,
|
user: uid,
|
||||||
group: gid,
|
group: gid,
|
||||||
unix_pex: Some(unix_pex),
|
unix_pex: Some(unix_pex),
|
||||||
@@ -211,7 +261,7 @@ impl FtpFileTransfer {
|
|||||||
size: filesize,
|
size: filesize,
|
||||||
ftype: extension,
|
ftype: extension,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
symlink: None,
|
symlink,
|
||||||
user: uid,
|
user: uid,
|
||||||
group: gid,
|
group: gid,
|
||||||
unix_pex: Some(unix_pex),
|
unix_pex: Some(unix_pex),
|
||||||
@@ -309,6 +359,16 @@ impl FtpFileTransfer {
|
|||||||
None => Err(()), // Invalid syntax
|
None => Err(()), // Invalid syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ### get_name_and_link
|
||||||
|
///
|
||||||
|
/// Returns from a `ls -l` command output file name token, the name of the file and the symbolic link (if there is any)
|
||||||
|
fn get_name_and_link(&self, token: &str) -> (String, Option<PathBuf>) {
|
||||||
|
let tokens: Vec<&str> = token.split(" -> ").collect();
|
||||||
|
let filename: String = String::from(*tokens.get(0).unwrap());
|
||||||
|
let symlink: Option<PathBuf> = tokens.get(1).map(PathBuf::from);
|
||||||
|
(filename, symlink)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTransfer for FtpFileTransfer {
|
impl FileTransfer for FtpFileTransfer {
|
||||||
@@ -731,7 +791,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_ftp_parse_list_line_unix() {
|
fn test_filetransfer_ftp_parse_list_line_unix() {
|
||||||
let ftp: FtpFileTransfer = FtpFileTransfer::new(false);
|
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
|
||||||
// Simple file
|
// Simple file
|
||||||
let fs_entry: FsEntry = ftp
|
let fs_entry: FsEntry = ftp
|
||||||
.parse_list_line(
|
.parse_list_line(
|
||||||
@@ -854,7 +914,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_ftp_parse_list_line_dos() {
|
fn test_filetransfer_ftp_parse_list_line_dos() {
|
||||||
let ftp: FtpFileTransfer = FtpFileTransfer::new(false);
|
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
|
||||||
// Simple file
|
// Simple file
|
||||||
let fs_entry: FsEntry = ftp
|
let fs_entry: FsEntry = ftp
|
||||||
.parse_list_line(
|
.parse_list_line(
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ impl ScpFileTransfer {
|
|||||||
true => self.get_name_and_link(metadata.get(8).unwrap().as_str()),
|
true => self.get_name_and_link(metadata.get(8).unwrap().as_str()),
|
||||||
false => (String::from(metadata.get(8).unwrap().as_str()), None),
|
false => (String::from(metadata.get(8).unwrap().as_str()), None),
|
||||||
};
|
};
|
||||||
// Check if symlink points to a directory
|
// Check if file_name is '.' or '..'
|
||||||
if let Some(symlink_path) = symlink_path.as_ref() {
|
if file_name.as_str() == "." || file_name.as_str() == ".." {
|
||||||
is_dir = symlink_path.is_dir();
|
return Err(());
|
||||||
}
|
}
|
||||||
// Get symlink; PATH mustn't be equal to filename
|
// Get symlink; PATH mustn't be equal to filename
|
||||||
let symlink: Option<Box<FsEntry>> = match symlink_path {
|
let symlink: Option<Box<FsEntry>> = match symlink_path {
|
||||||
@@ -179,15 +179,18 @@ impl ScpFileTransfer {
|
|||||||
true => None,
|
true => None,
|
||||||
false => match self.stat(p.as_path()) {
|
false => match self.stat(p.as_path()) {
|
||||||
// If path match filename
|
// If path match filename
|
||||||
Ok(e) => Some(Box::new(e)),
|
Ok(e) => {
|
||||||
|
// If e is a directory, set is_dir to true
|
||||||
|
if e.is_dir() {
|
||||||
|
is_dir = true;
|
||||||
|
}
|
||||||
|
Some(Box::new(e))
|
||||||
|
}
|
||||||
Err(_) => None, // Ignore errors
|
Err(_) => None, // Ignore errors
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// Check if file_name is '.' or '..'
|
// Re-check if is directory
|
||||||
if file_name.as_str() == "." || file_name.as_str() == ".." {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
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());
|
let abs_path: PathBuf = Self::resolve(abs_path.as_path());
|
||||||
@@ -556,7 +559,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
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(),
|
||||||
format!("unset LANG; ls -la \"{}\"", path.display()).as_str(),
|
format!("unset LANG; ls -la \"{}/\"", path.display()).as_str(),
|
||||||
) {
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Split output by (\r)\n
|
// Split output by (\r)\n
|
||||||
@@ -703,12 +706,6 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
///
|
///
|
||||||
/// Stat file and return FsEntry
|
/// Stat file and return FsEntry
|
||||||
fn stat(&mut self, path: &Path) -> Result<FsEntry, FileTransferError> {
|
fn stat(&mut self, path: &Path) -> Result<FsEntry, FileTransferError> {
|
||||||
if path.is_dir() {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::UnsupportedFeature,
|
|
||||||
String::from("stat is not supported for directories"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let path: PathBuf = match path.is_absolute() {
|
let path: PathBuf = match path.is_absolute() {
|
||||||
true => PathBuf::from(path),
|
true => PathBuf::from(path),
|
||||||
false => {
|
false => {
|
||||||
@@ -720,10 +717,12 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
let p: PathBuf = self.wrkdir.clone();
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
match self.perform_shell_cmd_with_path(
|
// make command; Directories require `-d` option
|
||||||
p.as_path(),
|
let cmd: String = match path.to_string_lossy().ends_with('/') {
|
||||||
format!("ls -l \"{}\"", path.display()).as_str(),
|
true => format!("ls -ld \"{}\"", path.display()),
|
||||||
) {
|
false => format!("ls -l \"{}\"", path.display()),
|
||||||
|
};
|
||||||
|
match self.perform_shell_cmd_with_path(p.as_path(), cmd.as_str()) {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
// Parse ls line
|
// Parse ls line
|
||||||
let parent: PathBuf = match path.as_path().parent() {
|
let parent: PathBuf = match path.as_path().parent() {
|
||||||
|
|||||||
Reference in New Issue
Block a user