use crate::dirwrap::DirWrap; use crate::filewrap::FileWrap; use crate::sftp::types::{Metadata, OpenOptions, RenameOptions}; use crate::sftp::SftpChannelResult; use camino::{Utf8Path, Utf8PathBuf}; pub(crate) enum SftpWrap { #[cfg(feature = "ssh2")] Ssh2(ssh2::Sftp), #[cfg(feature = "libssh-rs")] LibSsh(libssh_rs::Sftp), } #[cfg(feature = "ssh2")] fn pathconv(path: std::path::PathBuf) -> SftpChannelResult { use crate::sftp::SftpChannelError; use std::convert::TryFrom; Ok(Utf8PathBuf::try_from(path).map_err(|x| { SftpChannelError::from(std::io::Error::new(std::io::ErrorKind::InvalidData, x)) })?) } impl SftpWrap { pub fn open(&self, filename: &Utf8Path, opts: OpenOptions) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => { let flags: ssh2::OpenFlags = opts.into(); let mode = opts.mode; let open_type: ssh2::OpenType = opts.ty.into(); let file = sftp.open_mode(filename.as_std_path(), flags, mode, open_type)?; Ok(FileWrap::Ssh2(file)) } #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => { use crate::sftp::types::WriteMode; use libc::{O_APPEND, O_RDONLY, O_RDWR, O_WRONLY}; use std::convert::TryInto; let accesstype = match (opts.write, opts.read) { (Some(WriteMode::Append), true) => O_RDWR | O_APPEND, (Some(WriteMode::Append), false) => O_WRONLY | O_APPEND, (Some(WriteMode::Write), false) => O_WRONLY, (Some(WriteMode::Write), true) => O_RDWR, (None, true) => O_RDONLY, (None, false) => 0, }; let file = sftp.open(filename.as_str(), accesstype, opts.mode.try_into().unwrap())?; Ok(FileWrap::LibSsh(file)) } } } pub fn symlink(&self, path: &Utf8Path, target: &Utf8Path) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.symlink(path.as_std_path(), target.as_std_path())?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.symlink(path.as_str(), target.as_str())?), } } pub fn read_link(&self, filename: &Utf8Path) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(pathconv(sftp.readlink(filename.as_std_path())?)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.read_link(filename.as_str())?.into()), } } pub fn canonicalize(&self, filename: &Utf8Path) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(pathconv(sftp.realpath(filename.as_std_path())?)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.canonicalize(filename.as_str())?.into()), } } pub fn unlink(&self, filename: &Utf8Path) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.unlink(filename.as_std_path())?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.remove_file(filename.as_str())?), } } pub fn remove_dir(&self, filename: &Utf8Path) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.rmdir(filename.as_std_path())?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.remove_dir(filename.as_str())?), } } pub fn create_dir(&self, filename: &Utf8Path, mode: i32) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.mkdir(filename.as_std_path(), mode)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => { use std::convert::TryInto; Ok(sftp.create_dir(filename.as_str(), mode.try_into().unwrap())?) } } } pub fn rename( &self, src: &Utf8Path, dest: &Utf8Path, #[cfg_attr(not(feature = "ssh2"), allow(unused_variables))] opts: RenameOptions, ) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => { Ok(sftp.rename(src.as_std_path(), dest.as_std_path(), Some(opts.into()))?) } #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.rename(src.as_str(), dest.as_str())?), } } pub fn symlink_metadata(&self, filename: &Utf8Path) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.lstat(filename.as_std_path()).map(Metadata::from)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp .symlink_metadata(filename.as_str()) .map(Metadata::from)?), } } pub fn metadata(&self, filename: &Utf8Path) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.stat(filename.as_std_path()).map(Metadata::from)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.metadata(filename.as_str()).map(Metadata::from)?), } } pub fn set_metadata(&self, filename: &Utf8Path, metadata: Metadata) -> SftpChannelResult<()> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.setstat(filename.as_std_path(), metadata.into())?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => { let attr: libssh_rs::SetAttributes = metadata.into(); Ok(sftp.set_metadata(filename.as_str(), &attr)?) } } } pub fn open_dir(&self, filename: &Utf8Path) -> SftpChannelResult { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => Ok(sftp.opendir(filename.as_std_path()).map(DirWrap::Ssh2)?), #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => Ok(sftp.open_dir(filename.as_str()).map(DirWrap::LibSsh)?), } } pub fn read_dir(&self, filename: &Utf8Path) -> SftpChannelResult> { match self { #[cfg(feature = "ssh2")] Self::Ssh2(sftp) => { let entries = sftp.readdir(filename.as_std_path())?; let mut mapped_entries = vec![]; for (path, stat) in entries { let path = pathconv(path)?; mapped_entries.push((path, Metadata::from(stat))); } Ok(mapped_entries) } #[cfg(feature = "libssh-rs")] Self::LibSsh(sftp) => { let entries = sftp.read_dir(filename.as_str())?; let mut mapped_entries = vec![]; for metadata in entries { let path = metadata .name() .expect("name to be present in read dir results"); if path == "." || path == ".." { continue; } mapped_entries.push((filename.join(path), metadata.into())); } Ok(mapped_entries) } } } }