mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Working on Host
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -279,6 +279,7 @@ dependencies = [
|
||||
"crossterm 0.18.2",
|
||||
"getopts",
|
||||
"tui",
|
||||
"users",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -306,6 +307,16 @@ version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -18,6 +18,7 @@ readme = "README.md"
|
||||
crossterm = "0.18.2"
|
||||
getopts = "0.2.21"
|
||||
tui = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||
users = "0.11.0"
|
||||
|
||||
[[bin]]
|
||||
name = "termscp"
|
||||
|
||||
@@ -45,7 +45,14 @@ pub enum FileTransferProtocol {
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum FileTransferError {
|
||||
|
||||
ConnectionError,
|
||||
BadAddress,
|
||||
AuthenticationFailed,
|
||||
NoSuchFileOrDirectory,
|
||||
DirStatFailed,
|
||||
FileReadonly,
|
||||
DownloadError,
|
||||
UnknownError,
|
||||
}
|
||||
|
||||
/// ## FileTransfer
|
||||
|
||||
@@ -24,12 +24,13 @@
|
||||
*/
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// ## FsEntry
|
||||
///
|
||||
/// FsEntry represents a generic entry in a directory
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FsEntry {
|
||||
Directory(FsDirectory),
|
||||
File(FsFile)
|
||||
@@ -39,11 +40,12 @@ pub enum FsEntry {
|
||||
///
|
||||
/// Directory provides an interface to file system directories
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FsDirectory {
|
||||
pub name: PathBuf,
|
||||
pub last_change_time: Instant,
|
||||
pub last_access_time: Instant,
|
||||
pub creation_time: Instant,
|
||||
pub last_change_time: SystemTime,
|
||||
pub last_access_time: SystemTime,
|
||||
pub creation_time: SystemTime,
|
||||
pub readonly: bool,
|
||||
pub symlink: Option<PathBuf>, // UNIX only
|
||||
pub user: Option<String>, // UNIX only
|
||||
@@ -55,13 +57,14 @@ pub struct FsDirectory {
|
||||
///
|
||||
/// FsFile provides an interface to file system files
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FsFile {
|
||||
pub name: PathBuf,
|
||||
pub last_change_time: Instant,
|
||||
pub last_access_time: Instant,
|
||||
pub creation_time: Instant,
|
||||
pub last_change_time: SystemTime,
|
||||
pub last_access_time: SystemTime,
|
||||
pub creation_time: SystemTime,
|
||||
pub size: usize,
|
||||
pub ftype: String, // File type
|
||||
pub ftype: Option<String>, // File type
|
||||
pub readonly: bool,
|
||||
pub symlink: Option<PathBuf>, // UNIX only
|
||||
pub user: Option<String>, // UNIX only
|
||||
|
||||
248
src/host/mod.rs
248
src/host/mod.rs
@@ -23,3 +23,251 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use std::fs::{self, Metadata};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
// Metadata ext
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
extern crate users;
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
use users::{get_group_by_gid, get_user_by_uid};
|
||||
|
||||
// Locals
|
||||
use crate::fs::{FsDirectory, FsEntry, FsFile};
|
||||
|
||||
/// ## HostError
|
||||
///
|
||||
/// HostError provides errors related to local host file system
|
||||
pub enum HostError {
|
||||
NoSuchFileOrDirectory,
|
||||
ReadonlyFile,
|
||||
DirNotAccessible,
|
||||
}
|
||||
|
||||
/// ## Localhost
|
||||
///
|
||||
/// Localhost is the entity which holds the information about the current directory and host.
|
||||
/// It provides functions to navigate across the local host file system
|
||||
pub struct Localhost {
|
||||
wrkdir: PathBuf,
|
||||
files: Vec<FsEntry>,
|
||||
}
|
||||
|
||||
impl Localhost {
|
||||
/// ### new
|
||||
///
|
||||
/// Instantiates a new Localhost struct
|
||||
pub fn new(wrkdir: PathBuf) -> Result<Localhost, HostError> {
|
||||
let mut host: Localhost = Localhost {
|
||||
wrkdir: wrkdir,
|
||||
files: Vec::new(),
|
||||
};
|
||||
// Check if dir exists
|
||||
if !host.file_exists(host.wrkdir.as_path()) {
|
||||
return Err(HostError::NoSuchFileOrDirectory);
|
||||
}
|
||||
// Retrieve files for provided path
|
||||
host.files = match host.scan_dir() {
|
||||
Ok(files) => files,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
Ok(host)
|
||||
}
|
||||
|
||||
/// ### pwd
|
||||
///
|
||||
/// Print working directory
|
||||
pub fn pwd(&self) -> PathBuf {
|
||||
self.wrkdir.clone()
|
||||
}
|
||||
|
||||
/// ### list_dir
|
||||
///
|
||||
/// List files in current directory
|
||||
pub fn list_dir(&self) -> Vec<FsEntry> {
|
||||
self.files.clone()
|
||||
}
|
||||
|
||||
/// ### change_wrkdir
|
||||
///
|
||||
/// Change working directory with the new provided directory
|
||||
pub fn change_wrkdir(&mut self, new_dir: PathBuf) -> Result<PathBuf, HostError> {
|
||||
// Check whether directory exists
|
||||
if !self.file_exists(new_dir.as_path()) {
|
||||
return Err(HostError::NoSuchFileOrDirectory);
|
||||
}
|
||||
let prev_dir: PathBuf = self.wrkdir.clone(); // Backup location
|
||||
// Update working directory
|
||||
self.wrkdir = new_dir;
|
||||
// Scan new directory
|
||||
self.files = match self.scan_dir() {
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
// Restore directory
|
||||
self.wrkdir = prev_dir;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
Ok(self.wrkdir.clone())
|
||||
}
|
||||
|
||||
/// ### file_exists
|
||||
///
|
||||
/// Returns whether provided file path exists
|
||||
fn file_exists(&self, path: &Path) -> bool {
|
||||
path.exists()
|
||||
}
|
||||
|
||||
/// ### scan_dir
|
||||
///
|
||||
/// Get content of the current directory as a list of fs entry (Windows)
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn scan_dir(&self) -> Result<Vec<FsEntry>, HostError> {
|
||||
let entries = match std::fs::read_dir(self.wrkdir.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(_) => return Err(HostError::DirNotAccessible),
|
||||
};
|
||||
let mut fs_entries: Vec<FsEntry> = Vec::new();
|
||||
for entry in entries {
|
||||
if let Ok(entry) = entry {
|
||||
let path: PathBuf = entry.path();
|
||||
let attr: Metadata = fs::metadata(path.clone()).unwrap();
|
||||
let is_symlink: bool = attr.file_type().is_symlink();
|
||||
// Get user stuff
|
||||
let user: Option<String> = match get_user_by_uid(attr.uid()) {
|
||||
Some(user) => String::from(user.name().to_str().unwrap_or("")),
|
||||
None => None,
|
||||
};
|
||||
let group: Option<String> = match get_group_by_gid(attr.gid()) {
|
||||
Some(gruop) => String::from(group.name().to_str().unwrap_or("")),
|
||||
None => None,
|
||||
};
|
||||
// Match dir / file
|
||||
fs_entries.push(match path.is_dir() {
|
||||
true => {
|
||||
// Is dir
|
||||
FsEntry::Directory(FsDirectory {
|
||||
name: path.file_name(),
|
||||
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
readonly: attr.permissions().readonly(),
|
||||
symlink: match is_symlink {
|
||||
true => {
|
||||
// Read link
|
||||
match fs::read_link(path) {
|
||||
Ok(p) => Some(p),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
false => None,
|
||||
},
|
||||
user: user,
|
||||
group: group,
|
||||
unix_pex: Some(self.u32_to_mode(attr.mode())),
|
||||
})
|
||||
}
|
||||
false => {
|
||||
// Is File
|
||||
let extension: Option<String> = match path.extension() {
|
||||
Some(s) => Some(String::from(s.to_str().unwrap_or(""))),
|
||||
None => None,
|
||||
};
|
||||
FsEntry::File(FsFile {
|
||||
name: path.file_name(),
|
||||
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
readonly: attr.permissions().readonly(),
|
||||
size: attr.len() as usize,
|
||||
ftype: extension,
|
||||
symlink: match is_symlink {
|
||||
true => {
|
||||
// Read link
|
||||
match fs::read_link(path) {
|
||||
Ok(p) => Some(p),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
false => None,
|
||||
},
|
||||
user: user,
|
||||
group: group,
|
||||
unix_pex: Some(self.u32_to_mode(attr.mode())),
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(fs_entries)
|
||||
}
|
||||
|
||||
/// ### scan_dir
|
||||
///
|
||||
/// Get content of the current directory as a list of fs entry (Windows)
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn scan_dir(&self) -> Result<Vec<FsEntry>, HostError> {
|
||||
let entries = match std::fs::read_dir(self.wrkdir.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(_) => return Err(HostError::DirNotAccessible),
|
||||
};
|
||||
let mut fs_entries: Vec<FsEntry> = Vec::new();
|
||||
for entry in entries {
|
||||
if let Ok(entry) = entry {
|
||||
let path: PathBuf = entry.path();
|
||||
let attr: Metadata = fs::metadata(path.clone()).unwrap();
|
||||
fs_entries.push(match path.is_dir() {
|
||||
true => {
|
||||
// Is dir
|
||||
FsEntry::Directory(FsDirectory {
|
||||
name: path,
|
||||
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
readonly: attr.permissions().readonly(),
|
||||
symlink: None,
|
||||
user: None,
|
||||
group: None,
|
||||
unix_pex: None,
|
||||
})
|
||||
}
|
||||
false => {
|
||||
// Is File
|
||||
let extension: Option<String> = match path.extension() {
|
||||
Some(s) => Some(String::from(s.to_str().unwrap_or(""))),
|
||||
None => None,
|
||||
};
|
||||
FsEntry::File(FsFile {
|
||||
name: path,
|
||||
last_change_time: attr.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
last_access_time: attr.accessed().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
creation_time: attr.created().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||
readonly: attr.permissions().readonly(),
|
||||
size: attr.len() as usize,
|
||||
ftype: extension,
|
||||
symlink: None,
|
||||
user: None,
|
||||
group: None,
|
||||
unix_pex: None,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(fs_entries)
|
||||
}
|
||||
|
||||
/// ### u32_to_mode
|
||||
///
|
||||
/// Return string with format xxxxxx to tuple of permissions (user, group, others)
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn u32_to_mode(&self, mode: u32) -> (u8, u8, u8) {
|
||||
let user: u8 = ((mode >> 6) & 0x7) as u8;
|
||||
let group: u8 = ((mode >> 3) & 0x7) as u8;
|
||||
let others: u8 = (mode & 0x7) as u8;
|
||||
(user, group, others)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,5 @@
|
||||
pub mod filetransfer;
|
||||
pub mod fs;
|
||||
pub mod host;
|
||||
|
||||
// TODO: struct holder of filetransfer, host and gfx client
|
||||
Reference in New Issue
Block a user