mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 01:26:04 -08:00
Localhost implementation
This commit is contained in:
88
Cargo.lock
generated
88
Cargo.lock
generated
@@ -92,6 +92,17 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.8"
|
||||
@@ -222,12 +233,68 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -272,12 +339,27 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termscp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossterm 0.18.2",
|
||||
"getopts",
|
||||
"tempfile",
|
||||
"tui",
|
||||
"users",
|
||||
]
|
||||
@@ -317,6 +399,12 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -20,6 +20,9 @@ getopts = "0.2.21"
|
||||
tui = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||
users = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
[[bin]]
|
||||
name = "termscp"
|
||||
path = "src/main.rs"
|
||||
|
||||
@@ -42,7 +42,7 @@ pub enum FsEntry {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FsDirectory {
|
||||
pub name: PathBuf,
|
||||
pub name: String,
|
||||
pub last_change_time: SystemTime,
|
||||
pub last_access_time: SystemTime,
|
||||
pub creation_time: SystemTime,
|
||||
@@ -59,7 +59,7 @@ pub struct FsDirectory {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FsFile {
|
||||
pub name: PathBuf,
|
||||
pub name: String,
|
||||
pub last_change_time: SystemTime,
|
||||
pub last_access_time: SystemTime,
|
||||
pub creation_time: SystemTime,
|
||||
|
||||
213
src/host/mod.rs
213
src/host/mod.rs
@@ -23,14 +23,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use std::fs::{self, Metadata};
|
||||
use std::fs::{self, File, Metadata, OpenOptions};
|
||||
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};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
use users::{get_group_by_gid, get_user_by_uid};
|
||||
|
||||
@@ -44,6 +44,7 @@ pub enum HostError {
|
||||
NoSuchFileOrDirectory,
|
||||
ReadonlyFile,
|
||||
DirNotAccessible,
|
||||
FileNotAccessible,
|
||||
}
|
||||
|
||||
/// ## Localhost
|
||||
@@ -113,6 +114,42 @@ impl Localhost {
|
||||
Ok(self.wrkdir.clone())
|
||||
}
|
||||
|
||||
/// ### open_file_read
|
||||
///
|
||||
/// Open file for read
|
||||
pub fn open_file_read(&self, file: &Path) -> Result<File, HostError> {
|
||||
if !self.file_exists(file) {
|
||||
return Err(HostError::NoSuchFileOrDirectory);
|
||||
}
|
||||
match OpenOptions::new()
|
||||
.create(false)
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(file)
|
||||
{
|
||||
Ok(f) => Ok(f),
|
||||
Err(_) => Err(HostError::FileNotAccessible),
|
||||
}
|
||||
}
|
||||
|
||||
/// ### open_file_write
|
||||
///
|
||||
/// Open file for write
|
||||
pub fn open_file_write(&self, file: &Path) -> Result<File, HostError> {
|
||||
match OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(file)
|
||||
{
|
||||
Ok(f) => Ok(f),
|
||||
Err(_) => match self.file_exists(file) {
|
||||
true => Err(HostError::ReadonlyFile),
|
||||
false => Err(HostError::FileNotAccessible),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// ### file_exists
|
||||
///
|
||||
/// Returns whether provided file path exists
|
||||
@@ -137,19 +174,21 @@ impl Localhost {
|
||||
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("")),
|
||||
Some(user) => Some(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("")),
|
||||
Some(group) => Some(String::from(group.name().to_str().unwrap_or(""))),
|
||||
None => None,
|
||||
};
|
||||
let file_name: String =
|
||||
String::from(path.file_name().unwrap().to_str().unwrap_or(""));
|
||||
// Match dir / file
|
||||
fs_entries.push(match path.is_dir() {
|
||||
true => {
|
||||
// Is dir
|
||||
FsEntry::Directory(FsDirectory {
|
||||
name: path.file_name(),
|
||||
name: 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),
|
||||
@@ -176,7 +215,7 @@ impl Localhost {
|
||||
None => None,
|
||||
};
|
||||
FsEntry::File(FsFile {
|
||||
name: path.file_name(),
|
||||
name: 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),
|
||||
@@ -219,11 +258,13 @@ impl Localhost {
|
||||
if let Ok(entry) = entry {
|
||||
let path: PathBuf = entry.path();
|
||||
let attr: Metadata = fs::metadata(path.clone()).unwrap();
|
||||
let file_name: String =
|
||||
String::from(path.file_name().unwrap().to_str().unwrap_or(""));
|
||||
fs_entries.push(match path.is_dir() {
|
||||
true => {
|
||||
// Is dir
|
||||
FsEntry::Directory(FsDirectory {
|
||||
name: path,
|
||||
name: 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),
|
||||
@@ -241,7 +282,7 @@ impl Localhost {
|
||||
None => None,
|
||||
};
|
||||
FsEntry::File(FsFile {
|
||||
name: path,
|
||||
name: 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),
|
||||
@@ -271,3 +312,159 @@ impl Localhost {
|
||||
(user, group, others)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_new() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
assert_eq!(host.wrkdir, PathBuf::from("/bin"));
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from("/bin").as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
}
|
||||
assert_eq!(host.files.len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_host_localhost_new() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("C:\\")).ok().unwrap();
|
||||
assert_eq!(host.wrkdir, PathBuf::from("C:\\"));
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from("C:\\").as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
}
|
||||
assert_eq!(host.files.len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_host_localhost_new_bad() {
|
||||
Localhost::new(PathBuf::from("/omargabber/123/345"))
|
||||
.ok()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_pwd() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
assert_eq!(host.pwd(), PathBuf::from("/bin"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_list_files() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from("/bin").as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
}
|
||||
assert_eq!(host.list_dir().len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_change_dir() {
|
||||
let mut host: Localhost = Localhost::new(PathBuf::from("/usr")).ok().unwrap();
|
||||
let new_dir: PathBuf = PathBuf::from("/usr");
|
||||
assert!(host.change_wrkdir(new_dir.clone()).is_ok());
|
||||
// Verify new files
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from(new_dir).as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
}
|
||||
assert_eq!(host.files.len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
#[should_panic]
|
||||
fn test_host_localhost_change_dir_failed() {
|
||||
let mut host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
let new_dir: PathBuf = PathBuf::from("/omar/gabber/123/456");
|
||||
assert!(host.change_wrkdir(new_dir.clone()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_open_read() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
// Create temp file
|
||||
let file: tempfile::NamedTempFile = create_sample_file();
|
||||
assert!(host.open_file_read(file.path()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
#[should_panic]
|
||||
fn test_host_localhost_open_read_err_no_such_file() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
assert!(host
|
||||
.open_file_read(PathBuf::from("/bin/foo-bar-test-omar-123-456-789.txt").as_path())
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_open_read_err_not_accessible() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
let file: tempfile::NamedTempFile = create_sample_file();
|
||||
//let mut perms = fs::metadata(file.path())?.permissions();
|
||||
fs::set_permissions(file.path(), PermissionsExt::from_mode(0o222)).unwrap();
|
||||
//fs::set_permissions(file.path(), perms)?;
|
||||
assert!(host.open_file_read(file.path()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_open_write() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
// Create temp file
|
||||
let file: tempfile::NamedTempFile = create_sample_file();
|
||||
assert!(host.open_file_write(file.path()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn test_host_localhost_open_write_err() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/bin")).ok().unwrap();
|
||||
let file: tempfile::NamedTempFile = create_sample_file();
|
||||
//let mut perms = fs::metadata(file.path())?.permissions();
|
||||
fs::set_permissions(file.path(), PermissionsExt::from_mode(0o444)).unwrap();
|
||||
//fs::set_permissions(file.path(), perms)?;
|
||||
assert!(host.open_file_write(file.path()).is_err());
|
||||
}
|
||||
|
||||
/// ### create_sample_file
|
||||
///
|
||||
/// Create a sample file
|
||||
#[cfg(any(unix, macos, linux))]
|
||||
fn create_sample_file() -> tempfile::NamedTempFile {
|
||||
// Write
|
||||
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||
write!(
|
||||
tmpfile,
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nMauris ultricies consequat eros,\nnec scelerisque magna imperdiet metus.\n"
|
||||
)
|
||||
.unwrap();
|
||||
tmpfile
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user