mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Scp: use oneshot channels instead of ptys (more stable; more reliable and overall works)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
//! ## SFTP_Transfer
|
//! ## SCP_Transfer
|
||||||
//!
|
//!
|
||||||
//! `sftp_transfer` is the module which provides the implementation for the SFTP file transfer
|
//! `scps_transfer` is the module which provides the implementation for the SCP file transfer
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@@ -42,10 +42,10 @@ use std::time::SystemTime;
|
|||||||
|
|
||||||
/// ## ScpFileTransfer
|
/// ## ScpFileTransfer
|
||||||
///
|
///
|
||||||
/// SFTP file transfer structure
|
/// SCP file transfer structure
|
||||||
pub struct ScpFileTransfer {
|
pub struct ScpFileTransfer {
|
||||||
session: Option<Session>,
|
session: Option<Session>,
|
||||||
pty: Option<Channel>,
|
wrkdir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScpFileTransfer {
|
impl ScpFileTransfer {
|
||||||
@@ -55,7 +55,7 @@ impl ScpFileTransfer {
|
|||||||
pub fn new() -> ScpFileTransfer {
|
pub fn new() -> ScpFileTransfer {
|
||||||
ScpFileTransfer {
|
ScpFileTransfer {
|
||||||
session: None,
|
session: None,
|
||||||
pty: None,
|
wrkdir: PathBuf::from("~"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,44 +230,54 @@ impl ScpFileTransfer {
|
|||||||
(filename, symlink)
|
(filename, symlink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ### perform_shell_cmd_with
|
||||||
|
///
|
||||||
|
/// Perform a shell command, but change directory to specified path first
|
||||||
|
fn perform_shell_cmd_with_path(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
cmd: &str,
|
||||||
|
) -> Result<String, FileTransferError> {
|
||||||
|
self.perform_shell_cmd(format!("cd \"{}\"; {}", path.display(), cmd).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
/// ### perform_shell_cmd
|
/// ### perform_shell_cmd
|
||||||
///
|
///
|
||||||
/// Perform a shell command and read the output from shell
|
/// Perform a shell command and read the output from shell
|
||||||
/// This operation is, obviously, blocking.
|
/// This operation is, obviously, blocking.
|
||||||
fn perform_shell_cmd(&mut self, cmd: &str) -> Result<String, FileTransferError> {
|
fn perform_shell_cmd(&mut self, cmd: &str) -> Result<String, FileTransferError> {
|
||||||
match self.pty.as_mut() {
|
match self.session.as_mut() {
|
||||||
Some(pty) => {
|
Some(session) => {
|
||||||
// Send command
|
// Create channel
|
||||||
let command: String = match cmd.ends_with("\n") {
|
let mut channel: Channel = match session.channel_session() {
|
||||||
true => String::from(cmd),
|
Ok(ch) => ch,
|
||||||
false => format!("{}\n", cmd),
|
Err(err) => {
|
||||||
|
return Err(FileTransferError::new_ex(
|
||||||
|
FileTransferErrorType::ProtocolError,
|
||||||
|
format!("Could not open channel: {}", err),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Err(err) = pty.write(command.as_str().as_bytes()) {
|
// Execute command
|
||||||
|
if let Err(err) = channel.exec(cmd) {
|
||||||
return Err(FileTransferError::new_ex(
|
return Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::BadAddress,
|
FileTransferErrorType::ProtocolError,
|
||||||
format!("{}", err),
|
format!("Could not execute command \"{}\": {}", cmd, err),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Read
|
// Read output
|
||||||
let mut output: String = String::new();
|
let mut output: String = String::new();
|
||||||
let mut buffer: [u8; 8192] = [0; 8192];
|
match channel.read_to_string(&mut output) {
|
||||||
loop {
|
Ok(_) => {
|
||||||
match pty.read(&mut buffer) {
|
// Wait close
|
||||||
Ok(bytes_read) => {
|
let _ = channel.wait_close();
|
||||||
if bytes_read == 0 {
|
Ok(output)
|
||||||
break;
|
|
||||||
}
|
|
||||||
output.push_str(std::str::from_utf8(&buffer[0..bytes_read]).unwrap());
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::BadAddress,
|
|
||||||
format!("{}", err),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
|
FileTransferErrorType::ProtocolError,
|
||||||
|
format!("Could not read output: {}", err),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
Ok(output)
|
|
||||||
}
|
}
|
||||||
None => Err(FileTransferError::new(
|
None => Err(FileTransferError::new(
|
||||||
FileTransferErrorType::UninitializedSession,
|
FileTransferErrorType::UninitializedSession,
|
||||||
@@ -316,70 +326,23 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
format!("{}", err),
|
format!("{}", err),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Try authentication
|
let username: String = match username {
|
||||||
if let Err(err) = session.userauth_password(
|
Some(u) => u.clone(),
|
||||||
username.unwrap_or(String::from("")).as_str(),
|
None => String::from(""),
|
||||||
password.unwrap_or(String::from("")).as_str(),
|
|
||||||
) {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::AuthenticationFailed,
|
|
||||||
format!("{}", err),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Request pty
|
|
||||||
let mut mode = ssh2::PtyModes::new();
|
|
||||||
mode.set_character(ssh2::PtyModeOpcode::VINTR, Some(3 as char));
|
|
||||||
let mut channel: Channel = match session.channel_session() {
|
|
||||||
Ok(ch) => ch,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::ProtocolError,
|
|
||||||
format!("Could not get channel: {}", err),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// Configure channel
|
// Try authenticating with user agent
|
||||||
if let Err(err) = channel.request_pty("vt100", Some(mode), None) {
|
if let Err(_) = session.userauth_agent(username.as_str()) {
|
||||||
return Err(FileTransferError::new_ex(
|
// Try authentication with password then
|
||||||
FileTransferErrorType::ProtocolError,
|
if let Err(err) = session.userauth_password(
|
||||||
format!("Could not get pty: {}", err),
|
username.as_str(),
|
||||||
));
|
password.unwrap_or(String::from("")).as_str(),
|
||||||
}
|
) {
|
||||||
// Start shell
|
|
||||||
if let Err(err) = channel.shell() {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::ProtocolError,
|
|
||||||
format!("Could not start shell: {}", err),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Set blocking to true
|
|
||||||
session.set_blocking(true);
|
|
||||||
// @! Prevent any strange shell such as fish (which I use actually); go for a basic sh
|
|
||||||
if let Err(err) = self.perform_shell_cmd("sh") {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::ProtocolError,
|
|
||||||
format!("Could not run sh: {}", err),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// @! Remove prompt line (for outputs)
|
|
||||||
if let Err(err) = self.perform_shell_cmd("unset PS1") {
|
|
||||||
return Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::ProtocolError,
|
|
||||||
format!("Could not unset PS1: {}", err),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Get working directory
|
|
||||||
/*
|
|
||||||
self.wrkdir = match self.perform_shell_cmd("realpath .") {
|
|
||||||
Ok(output) => PathBuf::from(output.as_str().trim()),
|
|
||||||
Err(err) => {
|
|
||||||
return Err(FileTransferError::new_ex(
|
return Err(FileTransferError::new_ex(
|
||||||
FileTransferErrorType::ProtocolError,
|
FileTransferErrorType::AuthenticationFailed,
|
||||||
format!("Could not get wrkdir: {}", err),
|
format!("{}", err),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
*/
|
|
||||||
// Get banner
|
// Get banner
|
||||||
let banner: Option<String> = match session.banner() {
|
let banner: Option<String> = match session.banner() {
|
||||||
Some(s) => Some(String::from(s)),
|
Some(s) => Some(String::from(s)),
|
||||||
@@ -387,8 +350,11 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
};
|
};
|
||||||
// Set session
|
// Set session
|
||||||
self.session = Some(session);
|
self.session = Some(session);
|
||||||
// Set pty channel
|
// Get working directory
|
||||||
self.pty = Some(channel);
|
match self.perform_shell_cmd("pwd") {
|
||||||
|
Ok(output) => self.wrkdir = PathBuf::from(output.as_str().trim()),
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
Ok(banner)
|
Ok(banner)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,18 +362,13 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
///
|
///
|
||||||
/// Disconnect from the remote server
|
/// Disconnect from the remote server
|
||||||
fn disconnect(&mut self) -> Result<(), FileTransferError> {
|
fn disconnect(&mut self) -> Result<(), FileTransferError> {
|
||||||
if self.pty.is_some() {
|
|
||||||
// Send exit, but don't give a f about the output
|
|
||||||
let _ = self.perform_shell_cmd("exit 0");
|
|
||||||
}
|
|
||||||
match self.session.as_ref() {
|
match self.session.as_ref() {
|
||||||
Some(session) => {
|
Some(session) => {
|
||||||
// Disconnect (greet server with 'Mandi' as they do in Friuli)
|
// Disconnect (greet server with 'Mandi' as they do in Friuli)
|
||||||
match session.disconnect(None, "Mandi!", None) {
|
match session.disconnect(None, "Mandi!", None) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
// Set session and sftp to none
|
// Set session to none
|
||||||
self.session = None;
|
self.session = None;
|
||||||
self.pty = None;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
@@ -426,8 +387,8 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
///
|
///
|
||||||
/// Indicates whether the client is connected to remote
|
/// Indicates whether the client is connected to remote
|
||||||
fn is_connected(&self) -> bool {
|
fn is_connected(&self) -> bool {
|
||||||
match self.pty.as_ref() {
|
match self.session.as_ref() {
|
||||||
Some(pty) => pty.eof(),
|
Some(_) => true,
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,13 +399,7 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
|
|
||||||
fn pwd(&mut self) -> Result<PathBuf, FileTransferError> {
|
fn pwd(&mut self) -> Result<PathBuf, FileTransferError> {
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => match self.perform_shell_cmd("pwd") {
|
true => Ok(self.wrkdir.clone()),
|
||||||
Ok(wrkdir) => Ok(PathBuf::from(wrkdir.as_str().trim())),
|
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
|
||||||
FileTransferErrorType::ProtocolError,
|
|
||||||
format!("{}", err),
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
false => Err(FileTransferError::new(
|
false => Err(FileTransferError::new(
|
||||||
FileTransferErrorType::UninitializedSession,
|
FileTransferErrorType::UninitializedSession,
|
||||||
)),
|
)),
|
||||||
@@ -458,13 +413,30 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
fn change_dir(&mut self, dir: &Path) -> Result<PathBuf, FileTransferError> {
|
fn change_dir(&mut self, dir: &Path) -> Result<PathBuf, FileTransferError> {
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
|
let remote_path: PathBuf = match dir.is_absolute() {
|
||||||
|
true => PathBuf::from(dir),
|
||||||
|
false => {
|
||||||
|
let mut p: PathBuf = PathBuf::from(".");
|
||||||
|
p.push(dir);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
};
|
||||||
// Change directory
|
// Change directory
|
||||||
match self.perform_shell_cmd(format!("cd \"{}\" && echo 0", dir.display()).as_str())
|
match self.perform_shell_cmd_with_path(
|
||||||
{
|
p.as_path(),
|
||||||
|
format!("cd \"{}\"; echo $?; pwd", remote_path.display()).as_str(),
|
||||||
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Check if output is 0
|
// Trim
|
||||||
match output.as_str().trim() == "0" {
|
let output: String = String::from(output.as_str().trim());
|
||||||
true => self.pwd(), // Return working directory
|
// Check if output starts with 0; should be 0{PWD}
|
||||||
|
match output.as_str().starts_with("0") {
|
||||||
|
true => {
|
||||||
|
// Set working directory
|
||||||
|
self.wrkdir = PathBuf::from(&output.as_str()[1..].trim());
|
||||||
|
Ok(self.wrkdir.clone())
|
||||||
|
}
|
||||||
false => Err(FileTransferError::new_ex(
|
false => Err(FileTransferError::new_ex(
|
||||||
// No such file or directory
|
// No such file or directory
|
||||||
FileTransferErrorType::NoSuchFileOrDirectory,
|
FileTransferErrorType::NoSuchFileOrDirectory,
|
||||||
@@ -492,12 +464,16 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
// Send ls -l to path
|
// Send ls -l to path
|
||||||
match self.perform_shell_cmd(format!("ls -l \"{}\"", path.display()).as_str()) {
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
|
match self.perform_shell_cmd_with_path(
|
||||||
|
p.as_path(),
|
||||||
|
format!("ls -l \"{}\"", path.display()).as_str(),
|
||||||
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Split output by (\r)\n
|
// Split output by (\r)\n
|
||||||
let lines: Vec<&str> = output.as_str().lines().collect();
|
let lines: Vec<&str> = output.as_str().lines().collect();
|
||||||
let mut entries: Vec<FsEntry> = Vec::with_capacity(lines.len());
|
let mut entries: Vec<FsEntry> = Vec::with_capacity(lines.len());
|
||||||
for line in lines[1..].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, line) {
|
||||||
@@ -525,10 +501,12 @@ 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 p: PathBuf = self.wrkdir.clone();
|
||||||
// Mkdir dir && echo 0
|
// Mkdir dir && echo 0
|
||||||
match self
|
match self.perform_shell_cmd_with_path(
|
||||||
.perform_shell_cmd(format!("mkdir \"{}\" && echo 0", dir.display()).as_str())
|
p.as_path(),
|
||||||
{
|
format!("mkdir \"{}\"; echo $?", dir.display()).as_str(),
|
||||||
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Check if output is 0
|
// Check if output is 0
|
||||||
match output.as_str().trim() == "0" {
|
match output.as_str().trim() == "0" {
|
||||||
@@ -564,9 +542,11 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
FsEntry::Directory(dir) => dir.abs_path.clone(),
|
FsEntry::Directory(dir) => dir.abs_path.clone(),
|
||||||
FsEntry::File(file) => file.abs_path.clone(),
|
FsEntry::File(file) => file.abs_path.clone(),
|
||||||
};
|
};
|
||||||
match self
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
.perform_shell_cmd(format!("rm -rf \"{}\" && echo 0", path.display()).as_str())
|
match self.perform_shell_cmd_with_path(
|
||||||
{
|
p.as_path(),
|
||||||
|
format!("rm -rf \"{}\"; echo $?", path.display()).as_str(),
|
||||||
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Check if output is 0
|
// Check if output is 0
|
||||||
match output.as_str().trim() == "0" {
|
match output.as_str().trim() == "0" {
|
||||||
@@ -601,8 +581,10 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
FsEntry::Directory(dir) => dir.abs_path.clone(),
|
FsEntry::Directory(dir) => dir.abs_path.clone(),
|
||||||
FsEntry::File(file) => file.abs_path.clone(),
|
FsEntry::File(file) => file.abs_path.clone(),
|
||||||
};
|
};
|
||||||
match self.perform_shell_cmd(
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
format!("mv -f \"{}\" {}\" && echo 0", path.display(), dst.display()).as_str(),
|
match self.perform_shell_cmd_with_path(
|
||||||
|
p.as_path(),
|
||||||
|
format!("mv -f \"{}\" {}\"; echo $?", path.display(), dst.display()).as_str(),
|
||||||
) {
|
) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Check if output is 0
|
// Check if output is 0
|
||||||
@@ -639,7 +621,11 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
}
|
}
|
||||||
match self.is_connected() {
|
match self.is_connected() {
|
||||||
true => {
|
true => {
|
||||||
match self.perform_shell_cmd(format!("ls -l \"{}\"", path.display()).as_str()) {
|
let p: PathBuf = self.wrkdir.clone();
|
||||||
|
match self.perform_shell_cmd_with_path(
|
||||||
|
p.as_path(),
|
||||||
|
format!("ls -l \"{}\"", path.display()).as_str(),
|
||||||
|
) {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
// Parse ls line
|
// Parse ls line
|
||||||
let parent: PathBuf = match path.parent() {
|
let parent: PathBuf = match path.parent() {
|
||||||
@@ -683,6 +669,8 @@ 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) => {
|
||||||
|
// Set blocking to true
|
||||||
|
session.set_blocking(true);
|
||||||
// Calculate file mode
|
// Calculate file mode
|
||||||
let mode: i32 = match local.unix_pex {
|
let mode: i32 = match local.unix_pex {
|
||||||
None => 0o644,
|
None => 0o644,
|
||||||
@@ -726,13 +714,17 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
/// Returns file and its size
|
/// Returns file and its size
|
||||||
fn recv_file(&mut self, file: &FsFile) -> Result<Box<dyn Read>, FileTransferError> {
|
fn recv_file(&mut self, file: &FsFile) -> Result<Box<dyn Read>, FileTransferError> {
|
||||||
match self.session.as_ref() {
|
match self.session.as_ref() {
|
||||||
Some(session) => match session.scp_recv(file.abs_path.as_path()) {
|
Some(session) => {
|
||||||
Ok(reader) => Ok(Box::new(reader.0)),
|
// Set blocking to true
|
||||||
Err(err) => Err(FileTransferError::new_ex(
|
session.set_blocking(true);
|
||||||
FileTransferErrorType::ProtocolError,
|
match session.scp_recv(file.abs_path.as_path()) {
|
||||||
format!("{}", err),
|
Ok(reader) => Ok(Box::new(reader.0)),
|
||||||
)),
|
Err(err) => Err(FileTransferError::new_ex(
|
||||||
},
|
FileTransferErrorType::ProtocolError,
|
||||||
|
format!("{}", err),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
None => Err(FileTransferError::new(
|
None => Err(FileTransferError::new(
|
||||||
FileTransferErrorType::UninitializedSession,
|
FileTransferErrorType::UninitializedSession,
|
||||||
)),
|
)),
|
||||||
@@ -747,7 +739,10 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
/// This is necessary for some protocols such as FTP.
|
/// This is necessary for some protocols such as FTP.
|
||||||
/// You must call this method each time you want to finalize the write of the remote file.
|
/// You must call this method each time you want to finalize the write of the remote file.
|
||||||
fn on_sent(&mut self, _writable: Box<dyn Write>) -> Result<(), FileTransferError> {
|
fn on_sent(&mut self, _writable: Box<dyn Write>) -> Result<(), FileTransferError> {
|
||||||
// Nothing to do
|
if let Some(session) = self.session.as_ref() {
|
||||||
|
// Set blocking to false
|
||||||
|
session.set_blocking(false);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,7 +754,10 @@ impl FileTransfer for ScpFileTransfer {
|
|||||||
/// This mighe be necessary for some protocols.
|
/// This mighe be necessary for some protocols.
|
||||||
/// You must call this method each time you want to finalize the read of the remote file.
|
/// You must call this method each time you want to finalize the read of the remote file.
|
||||||
fn on_recv(&mut self, _readable: Box<dyn Read>) -> Result<(), FileTransferError> {
|
fn on_recv(&mut self, _readable: Box<dyn Read>) -> Result<(), FileTransferError> {
|
||||||
// Nothing to do
|
if let Some(session) = self.session.as_ref() {
|
||||||
|
// Set blocking to false
|
||||||
|
session.set_blocking(false);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -770,15 +768,14 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_new() {
|
fn test_filetransfer_scp_new() {
|
||||||
let client: ScpFileTransfer = ScpFileTransfer::new();
|
let client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client.session.is_none());
|
assert!(client.session.is_none());
|
||||||
assert!(client.pty.is_none());
|
|
||||||
assert_eq!(client.is_connected(), false);
|
assert_eq!(client.is_connected(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_connect() {
|
fn test_filetransfer_scp_connect() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert_eq!(client.is_connected(), false);
|
assert_eq!(client.is_connected(), false);
|
||||||
assert!(client
|
assert!(client
|
||||||
@@ -789,17 +786,15 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
assert_eq!(client.is_connected(), true);
|
assert_eq!(client.is_connected(), true);
|
||||||
// Disconnect
|
// Disconnect
|
||||||
assert!(client.disconnect().is_ok());
|
assert!(client.disconnect().is_ok());
|
||||||
assert_eq!(client.is_connected(), false);
|
assert_eq!(client.is_connected(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_bad_auth() {
|
fn test_filetransfer_scp_bad_auth() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -812,7 +807,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_no_credentials() {
|
fn test_filetransfer_scp_no_credentials() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(String::from("test.rebex.net"), 22, None, None)
|
.connect(String::from("test.rebex.net"), 22, None, None)
|
||||||
@@ -820,7 +815,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_bad_server() {
|
fn test_filetransfer_scp_bad_server() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -831,9 +826,8 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_pwd() {
|
fn test_filetransfer_scp_pwd() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -843,9 +837,8 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
// Pwd
|
// Pwd
|
||||||
assert_eq!(client.pwd().ok().unwrap(), PathBuf::from("/"));
|
assert_eq!(client.pwd().ok().unwrap(), PathBuf::from("/"));
|
||||||
// Disconnect
|
// Disconnect
|
||||||
@@ -853,7 +846,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_cwd() {
|
fn test_filetransfer_scp_cwd() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -863,19 +856,18 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
// Cwd (relative)
|
// Cwd (relative)
|
||||||
assert!(client.change_dir(PathBuf::from("pub/").as_path()).is_ok());
|
assert!(client.change_dir(PathBuf::from("pub/").as_path()).is_ok());
|
||||||
// Cwd (absolute)
|
// Cwd (absolute)
|
||||||
assert!(client.change_dir(PathBuf::from("/").as_path()).is_ok());
|
assert!(client.change_dir(PathBuf::from("/pub").as_path()).is_ok());
|
||||||
// Disconnect
|
// Disconnect
|
||||||
assert!(client.disconnect().is_ok());
|
assert!(client.disconnect().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_cwd_error() {
|
fn test_filetransfer_scp_cwd_error() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -898,7 +890,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_ls() {
|
fn test_filetransfer_scp_ls() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -908,19 +900,18 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
// List dir
|
// List dir
|
||||||
let pwd: PathBuf = client.pwd().ok().unwrap();
|
let pwd: PathBuf = client.pwd().ok().unwrap();
|
||||||
let files: Vec<FsEntry> = client.list_dir(pwd.as_path()).ok().unwrap();
|
let files: Vec<FsEntry> = client.list_dir(pwd.as_path()).ok().unwrap();
|
||||||
assert_eq!(files.len(), 3); // There are 3 files
|
assert_eq!(files.len(), 5); // There are 5 files
|
||||||
// Disconnect
|
// Disconnect
|
||||||
assert!(client.disconnect().is_ok());
|
assert!(client.disconnect().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_stat() {
|
fn test_filetransfer_scp_stat() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -930,9 +921,8 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
let file: FsEntry = client
|
let file: FsEntry = client
|
||||||
.stat(PathBuf::from("readme.txt").as_path())
|
.stat(PathBuf::from("readme.txt").as_path())
|
||||||
.ok()
|
.ok()
|
||||||
@@ -945,7 +935,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_recv() {
|
fn test_filetransfer_scp_recv() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -955,9 +945,8 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
let file: FsFile = FsFile {
|
let file: FsFile = FsFile {
|
||||||
name: String::from("readme.txt"),
|
name: String::from("readme.txt"),
|
||||||
abs_path: PathBuf::from("/readme.txt"),
|
abs_path: PathBuf::from("/readme.txt"),
|
||||||
@@ -978,7 +967,7 @@ mod tests {
|
|||||||
assert!(client.disconnect().is_ok());
|
assert!(client.disconnect().is_ok());
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_recv_failed_nosuchfile() {
|
fn test_filetransfer_scp_recv_failed_nosuchfile() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client
|
assert!(client
|
||||||
.connect(
|
.connect(
|
||||||
@@ -988,9 +977,8 @@ mod tests {
|
|||||||
Some(String::from("password"))
|
Some(String::from("password"))
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
// Check session and sftp
|
// Check session and scp
|
||||||
assert!(client.session.is_some());
|
assert!(client.session.is_some());
|
||||||
assert!(client.pty.is_some());
|
|
||||||
// Receive file
|
// Receive file
|
||||||
let file: FsFile = FsFile {
|
let file: FsFile = FsFile {
|
||||||
name: String::from("omar.txt"),
|
name: String::from("omar.txt"),
|
||||||
@@ -1010,12 +998,11 @@ mod tests {
|
|||||||
// Disconnect
|
// Disconnect
|
||||||
assert!(client.disconnect().is_ok());
|
assert!(client.disconnect().is_ok());
|
||||||
}
|
}
|
||||||
|
// NOTE: other functions doesn't work with this test scp server
|
||||||
// NOTE: other functions doesn't work with this test SFTP server
|
|
||||||
|
|
||||||
/* NOTE: the server doesn't allow you to create directories
|
/* NOTE: the server doesn't allow you to create directories
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filetransfer_sftp_mkdir() {
|
fn test_filetransfer_scp_mkdir() {
|
||||||
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
let mut client: ScpFileTransfer = ScpFileTransfer::new();
|
||||||
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
|
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
|
||||||
let dir: String = String::from("foo");
|
let dir: String = String::from("foo");
|
||||||
|
|||||||
Reference in New Issue
Block a user