Working on logging

This commit is contained in:
veeso
2021-05-16 22:26:16 +02:00
parent 1e3c859ae0
commit c1b3511c06
5 changed files with 258 additions and 87 deletions

View File

@@ -319,14 +319,14 @@ impl std::string::ToString for FileTransferProtocol {
} }
impl std::str::FromStr for FileTransferProtocol { impl std::str::FromStr for FileTransferProtocol {
type Err = (); type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_uppercase().as_str() { match s.to_ascii_uppercase().as_str() {
"FTP" => Ok(FileTransferProtocol::Ftp(false)), "FTP" => Ok(FileTransferProtocol::Ftp(false)),
"FTPS" => Ok(FileTransferProtocol::Ftp(true)), "FTPS" => Ok(FileTransferProtocol::Ftp(true)),
"SCP" => Ok(FileTransferProtocol::Scp), "SCP" => Ok(FileTransferProtocol::Scp),
"SFTP" => Ok(FileTransferProtocol::Sftp), "SFTP" => Ok(FileTransferProtocol::Sftp),
_ => Err(()), _ => Err(s.to_string()),
} }
} }
} }

View File

@@ -125,12 +125,17 @@ impl Localhost {
/// ///
/// Instantiates a new Localhost struct /// Instantiates a new Localhost struct
pub fn new(wrkdir: PathBuf) -> Result<Localhost, HostError> { pub fn new(wrkdir: PathBuf) -> Result<Localhost, HostError> {
debug!("Initializing localhost at {}", wrkdir.display());
let mut host: Localhost = Localhost { let mut host: Localhost = Localhost {
wrkdir, wrkdir,
files: Vec::new(), files: Vec::new(),
}; };
// Check if dir exists // Check if dir exists
if !host.file_exists(host.wrkdir.as_path()) { if !host.file_exists(host.wrkdir.as_path()) {
error!(
"Failed to initialize localhost: {} doesn't exist",
host.wrkdir.display()
);
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, None,
@@ -140,8 +145,15 @@ impl Localhost {
// Retrieve files for provided path // Retrieve files for provided path
host.files = match host.scan_dir(host.wrkdir.as_path()) { host.files = match host.scan_dir(host.wrkdir.as_path()) {
Ok(files) => files, Ok(files) => files,
Err(err) => return Err(err), Err(err) => {
error!(
"Failed to initialize localhost: could not scan wrkdir: {}",
err
);
return Err(err);
}
}; };
info!("Localhost initialized with success");
Ok(host) Ok(host)
} }
@@ -165,8 +177,10 @@ impl Localhost {
/// Change working directory with the new provided directory /// Change working directory with the new provided directory
pub fn change_wrkdir(&mut self, new_dir: &Path) -> Result<PathBuf, HostError> { pub fn change_wrkdir(&mut self, new_dir: &Path) -> Result<PathBuf, HostError> {
let new_dir: PathBuf = self.to_abs_path(new_dir); let new_dir: PathBuf = self.to_abs_path(new_dir);
info!("Changing localhost directory to {}...", new_dir.display());
// Check whether directory exists // Check whether directory exists
if !self.file_exists(new_dir.as_path()) { if !self.file_exists(new_dir.as_path()) {
error!("Could not change directory: No such file or directory");
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, None,
@@ -174,10 +188,11 @@ impl Localhost {
)); ));
} }
// Change directory // Change directory
if std::env::set_current_dir(new_dir.as_path()).is_err() { if let Err(err) = std::env::set_current_dir(new_dir.as_path()) {
error!("Could not enter directory: {}", err);
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, Some(err),
new_dir.as_path(), new_dir.as_path(),
)); ));
} }
@@ -189,11 +204,13 @@ impl Localhost {
self.files = match self.scan_dir(self.wrkdir.as_path()) { self.files = match self.scan_dir(self.wrkdir.as_path()) {
Ok(files) => files, Ok(files) => files,
Err(err) => { Err(err) => {
error!("Could not scan new directory: {}", err);
// Restore directory // Restore directory
self.wrkdir = prev_dir; self.wrkdir = prev_dir;
return Err(err); return Err(err);
} }
}; };
debug!("Changed directory to {}", self.wrkdir.display());
Ok(self.wrkdir.clone()) Ok(self.wrkdir.clone())
} }
@@ -210,6 +227,7 @@ impl Localhost {
/// ignex: don't report error if directory already exists /// ignex: don't report error if directory already exists
pub fn mkdir_ex(&mut self, dir_name: &Path, ignex: bool) -> Result<(), HostError> { pub fn mkdir_ex(&mut self, dir_name: &Path, ignex: bool) -> Result<(), HostError> {
let dir_path: PathBuf = self.to_abs_path(dir_name); let dir_path: PathBuf = self.to_abs_path(dir_name);
info!("Making directory {}", dir_path.display());
// If dir already exists, return Error // If dir already exists, return Error
if dir_path.exists() { if dir_path.exists() {
match ignex { match ignex {
@@ -229,13 +247,17 @@ impl Localhost {
if dir_name.is_relative() { if dir_name.is_relative() {
self.files = self.scan_dir(self.wrkdir.as_path())?; self.files = self.scan_dir(self.wrkdir.as_path())?;
} }
info!("Created directory {}", dir_path.display());
Ok(()) Ok(())
} }
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::CouldNotCreateFile, error!("Could not make directory: {}", err);
Some(err), Err(HostError::new(
dir_path.as_path(), HostErrorType::CouldNotCreateFile,
)), Some(err),
dir_path.as_path(),
))
}
} }
} }
@@ -246,7 +268,9 @@ impl Localhost {
match entry { match entry {
FsEntry::Directory(dir) => { FsEntry::Directory(dir) => {
// If file doesn't exist; return error // If file doesn't exist; return error
debug!("Removing directory {}", dir.abs_path.display());
if !dir.abs_path.as_path().exists() { if !dir.abs_path.as_path().exists() {
error!("Directory doesn't exist");
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, None,
@@ -258,18 +282,24 @@ impl Localhost {
Ok(_) => { Ok(_) => {
// Update dir // Update dir
self.files = self.scan_dir(self.wrkdir.as_path())?; self.files = self.scan_dir(self.wrkdir.as_path())?;
info!("Removed directory {}", dir.abs_path.display());
Ok(()) Ok(())
} }
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::DeleteFailed, error!("Could not remove directory: {}", err);
Some(err), Err(HostError::new(
dir.abs_path.as_path(), HostErrorType::DeleteFailed,
)), Some(err),
dir.abs_path.as_path(),
))
}
} }
} }
FsEntry::File(file) => { FsEntry::File(file) => {
// If file doesn't exist; return error // If file doesn't exist; return error
debug!("Removing file {}", file.abs_path.display());
if !file.abs_path.as_path().exists() { if !file.abs_path.as_path().exists() {
error!("File doesn't exist");
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, None,
@@ -281,13 +311,17 @@ impl Localhost {
Ok(_) => { Ok(_) => {
// Update dir // Update dir
self.files = self.scan_dir(self.wrkdir.as_path())?; self.files = self.scan_dir(self.wrkdir.as_path())?;
info!("Removed file {}", file.abs_path.display());
Ok(()) Ok(())
} }
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::DeleteFailed, error!("Could not remove file: {}", err);
Some(err), Err(HostError::new(
file.abs_path.as_path(), HostErrorType::DeleteFailed,
)), Some(err),
file.abs_path.as_path(),
))
}
} }
} }
} }
@@ -302,13 +336,26 @@ impl Localhost {
Ok(_) => { Ok(_) => {
// Scan dir // Scan dir
self.files = self.scan_dir(self.wrkdir.as_path())?; self.files = self.scan_dir(self.wrkdir.as_path())?;
debug!(
"Moved file {} to {}",
entry.get_abs_path().display(),
dst_path.display()
);
Ok(()) Ok(())
} }
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::CouldNotCreateFile, error!(
Some(err), "Failed to move {} to {}: {}",
abs_path.as_path(), entry.get_abs_path().display(),
)), dst_path.display(),
err
);
Err(HostError::new(
HostErrorType::CouldNotCreateFile,
Some(err),
abs_path.as_path(),
))
}
} }
} }
@@ -318,6 +365,11 @@ impl Localhost {
pub fn copy(&mut self, entry: &FsEntry, dst: &Path) -> Result<(), HostError> { pub fn copy(&mut self, entry: &FsEntry, dst: &Path) -> Result<(), HostError> {
// Get absolute path of dest // Get absolute path of dest
let dst: PathBuf = self.to_abs_path(dst); let dst: PathBuf = self.to_abs_path(dst);
info!(
"Copying file {} to {}",
entry.get_abs_path().display(),
dst.display()
);
// Match entry // Match entry
match entry { match entry {
FsEntry::File(file) => { FsEntry::File(file) => {
@@ -333,16 +385,19 @@ impl Localhost {
}; };
// Copy entry path to dst path // Copy entry path to dst path
if let Err(err) = std::fs::copy(file.abs_path.as_path(), dst.as_path()) { if let Err(err) = std::fs::copy(file.abs_path.as_path(), dst.as_path()) {
error!("Failed to copy file: {}", err);
return Err(HostError::new( return Err(HostError::new(
HostErrorType::CouldNotCreateFile, HostErrorType::CouldNotCreateFile,
Some(err), Some(err),
file.abs_path.as_path(), file.abs_path.as_path(),
)); ));
} }
info!("File copied");
} }
FsEntry::Directory(dir) => { FsEntry::Directory(dir) => {
// If destination path doesn't exist, create destination // If destination path doesn't exist, create destination
if !dst.exists() { if !dst.exists() {
debug!("Directory {} doesn't exist; creating it", dst.display());
self.mkdir(dst.as_path())?; self.mkdir(dst.as_path())?;
} }
// Scan dir // Scan dir
@@ -386,15 +441,17 @@ impl Localhost {
/// Stat file and create a FsEntry /// Stat file and create a FsEntry
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
pub fn stat(&self, path: &Path) -> Result<FsEntry, HostError> { pub fn stat(&self, path: &Path) -> Result<FsEntry, HostError> {
info!("Stating file {}", path.display());
let path: PathBuf = self.to_abs_path(path); let path: PathBuf = self.to_abs_path(path);
let attr: Metadata = match fs::metadata(path.as_path()) { let attr: Metadata = match fs::metadata(path.as_path()) {
Ok(metadata) => metadata, Ok(metadata) => metadata,
Err(err) => { Err(err) => {
error!("Could not read file metadata: {}", err);
return Err(HostError::new( return Err(HostError::new(
HostErrorType::FileNotAccessible, HostErrorType::FileNotAccessible,
Some(err), Some(err),
path.as_path(), path.as_path(),
)) ));
} }
}; };
let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or(""));
@@ -454,14 +511,16 @@ impl Localhost {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
pub fn stat(&self, path: &Path) -> Result<FsEntry, HostError> { pub fn stat(&self, path: &Path) -> Result<FsEntry, HostError> {
let path: PathBuf = self.to_abs_path(path); let path: PathBuf = self.to_abs_path(path);
info!("Stating file {}", path.display());
let attr: Metadata = match fs::metadata(path.as_path()) { let attr: Metadata = match fs::metadata(path.as_path()) {
Ok(metadata) => metadata, Ok(metadata) => metadata,
Err(err) => { Err(err) => {
error!("Could not read file metadata: {}", err);
return Err(HostError::new( return Err(HostError::new(
HostErrorType::FileNotAccessible, HostErrorType::FileNotAccessible,
Some(err), Some(err),
path.as_path(), path.as_path(),
)) ));
} }
}; };
let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or(""));
@@ -523,16 +582,23 @@ impl Localhost {
let args: Vec<&str> = cmd.split(' ').collect(); let args: Vec<&str> = cmd.split(' ').collect();
let cmd: &str = args.first().unwrap(); let cmd: &str = args.first().unwrap();
let argv: &[&str] = &args[1..]; let argv: &[&str] = &args[1..];
info!("Executing command: {} {:?}", cmd, argv);
match std::process::Command::new(cmd).args(argv).output() { match std::process::Command::new(cmd).args(argv).output() {
Ok(output) => match std::str::from_utf8(&output.stdout) { Ok(output) => match std::str::from_utf8(&output.stdout) {
Ok(s) => Ok(s.to_string()), Ok(s) => {
info!("Command output: {}", s);
Ok(s.to_string())
}
Err(_) => Ok(String::new()), Err(_) => Ok(String::new()),
}, },
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::ExecutionFailed, error!("Failed to run command: {}", err);
Some(err), Err(HostError::new(
self.wrkdir.as_path(), HostErrorType::ExecutionFailed,
)), Some(err),
self.wrkdir.as_path(),
))
}
} }
} }
@@ -548,19 +614,32 @@ impl Localhost {
let mut mpex = metadata.permissions(); let mut mpex = metadata.permissions();
mpex.set_mode(self.mode_to_u32(pex)); mpex.set_mode(self.mode_to_u32(pex));
match set_permissions(path.as_path(), mpex) { match set_permissions(path.as_path(), mpex) {
Ok(_) => Ok(()), Ok(_) => {
Err(err) => Err(HostError::new( info!("Changed mode for {} to {:?}", path.display(), pex);
HostErrorType::FileNotAccessible, Ok(())
Some(err), }
path.as_path(), Err(err) => {
)), error!("Could not change mode for file {}: {}", path.display(), err);
Err(HostError::new(
HostErrorType::FileNotAccessible,
Some(err),
path.as_path(),
))
}
} }
} }
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::FileNotAccessible, error!(
Some(err), "Chmod failed; could not read metadata for file {}: {}",
path.as_path(), path.display(),
)), err
);
Err(HostError::new(
HostErrorType::FileNotAccessible,
Some(err),
path.as_path(),
))
}
} }
} }
@@ -569,7 +648,9 @@ impl Localhost {
/// Open file for read /// Open file for read
pub fn open_file_read(&self, file: &Path) -> Result<File, HostError> { pub fn open_file_read(&self, file: &Path) -> Result<File, HostError> {
let file: PathBuf = self.to_abs_path(file); let file: PathBuf = self.to_abs_path(file);
info!("Opening file {} for read", file.display());
if !self.file_exists(file.as_path()) { if !self.file_exists(file.as_path()) {
error!("File doesn't exist!");
return Err(HostError::new( return Err(HostError::new(
HostErrorType::NoSuchFileOrDirectory, HostErrorType::NoSuchFileOrDirectory,
None, None,
@@ -583,11 +664,14 @@ impl Localhost {
.open(file.as_path()) .open(file.as_path())
{ {
Ok(f) => Ok(f), Ok(f) => Ok(f),
Err(err) => Err(HostError::new( Err(err) => {
HostErrorType::FileNotAccessible, error!("Could not open file for read: {}", err);
Some(err), Err(HostError::new(
file.as_path(), HostErrorType::FileNotAccessible,
)), Some(err),
file.as_path(),
))
}
} }
} }
@@ -596,6 +680,7 @@ impl Localhost {
/// Open file for write /// Open file for write
pub fn open_file_write(&self, file: &Path) -> Result<File, HostError> { pub fn open_file_write(&self, file: &Path) -> Result<File, HostError> {
let file: PathBuf = self.to_abs_path(file); let file: PathBuf = self.to_abs_path(file);
info!("Opening file {} for write", file.display());
match OpenOptions::new() match OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@@ -603,18 +688,21 @@ impl Localhost {
.open(file.as_path()) .open(file.as_path())
{ {
Ok(f) => Ok(f), Ok(f) => Ok(f),
Err(err) => match self.file_exists(file.as_path()) { Err(err) => {
true => Err(HostError::new( error!("Failed to open file: {}", err);
HostErrorType::ReadonlyFile, match self.file_exists(file.as_path()) {
Some(err), true => Err(HostError::new(
file.as_path(), HostErrorType::ReadonlyFile,
)), Some(err),
false => Err(HostError::new( file.as_path(),
HostErrorType::FileNotAccessible, )),
Some(err), false => Err(HostError::new(
file.as_path(), HostErrorType::FileNotAccessible,
)), Some(err),
}, file.as_path(),
)),
}
}
} }
} }
@@ -629,13 +717,15 @@ impl Localhost {
/// ///
/// Get content of the current directory as a list of fs entry /// Get content of the current directory as a list of fs entry
pub fn scan_dir(&self, dir: &Path) -> Result<Vec<FsEntry>, HostError> { pub fn scan_dir(&self, dir: &Path) -> Result<Vec<FsEntry>, HostError> {
info!("Reading directory {}", dir.display());
match std::fs::read_dir(dir) { match std::fs::read_dir(dir) {
Ok(e) => { Ok(e) => {
let mut fs_entries: Vec<FsEntry> = Vec::new(); let mut fs_entries: Vec<FsEntry> = Vec::new();
for entry in e.flatten() { for entry in e.flatten() {
// NOTE: 0.4.1, don't fail if stat for one file fails // NOTE: 0.4.1, don't fail if stat for one file fails
if let Ok(entry) = self.stat(entry.path().as_path()) { match self.stat(entry.path().as_path()) {
fs_entries.push(entry); Ok(entry) => fs_entries.push(entry),
Err(e) => error!("Failed to stat {}: {}", entry.path().display(), e),
} }
} }
Ok(fs_entries) Ok(fs_entries)

View File

@@ -68,9 +68,11 @@ impl BookmarksClient {
) -> Result<BookmarksClient, SerializerError> { ) -> Result<BookmarksClient, SerializerError> {
// Create default hosts // Create default hosts
let default_hosts: UserHosts = Default::default(); let default_hosts: UserHosts = Default::default();
debug!("Setting up bookmarks client...");
// Make a key storage (windows / macos) // Make a key storage (windows / macos)
#[cfg(any(target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "macos"))]
let (key_storage, service_id): (Box<dyn KeyStorage>, &str) = { let (key_storage, service_id): (Box<dyn KeyStorage>, &str) = {
debug!("Setting up KeyStorage");
let username: String = whoami::username(); let username: String = whoami::username();
let storage: KeyringStorage = KeyringStorage::new(username.as_str()); let storage: KeyringStorage = KeyringStorage::new(username.as_str());
// Check if keyring storage is supported // Check if keyring storage is supported
@@ -79,8 +81,14 @@ impl BookmarksClient {
#[cfg(test)] // NOTE: when running test, add -test #[cfg(test)] // NOTE: when running test, add -test
let app_name: &str = "termscp-test"; let app_name: &str = "termscp-test";
match storage.is_supported() { match storage.is_supported() {
true => (Box::new(storage), app_name), true => {
false => (Box::new(FileStorage::new(storage_path)), "bookmarks"), debug!("Using KeyringStorage");
(Box::new(storage), app_name)
}
false => {
warn!("KeyringStorage is not supported; using FileStorage");
(Box::new(FileStorage::new(storage_path)), "bookmarks")
}
} }
}; };
// Make a key storage (linux / unix) // Make a key storage (linux / unix)
@@ -90,16 +98,22 @@ impl BookmarksClient {
let app_name: &str = "bookmarks"; let app_name: &str = "bookmarks";
#[cfg(test)] // NOTE: when running test, add -test #[cfg(test)] // NOTE: when running test, add -test
let app_name: &str = "bookmarks-test"; let app_name: &str = "bookmarks-test";
debug!("Using FileStorage");
(Box::new(FileStorage::new(storage_path)), app_name) (Box::new(FileStorage::new(storage_path)), app_name)
}; };
// Load key // Load key
let key: String = match key_storage.get_key(service_id) { let key: String = match key_storage.get_key(service_id) {
Ok(k) => k, Ok(k) => {
debug!("Key loaded with success");
k
}
Err(e) => match e { Err(e) => match e {
KeyStorageError::NoSuchKey => { KeyStorageError::NoSuchKey => {
// If no such key, generate key and set it into the storage // If no such key, generate key and set it into the storage
let key: String = Self::generate_key(); let key: String = Self::generate_key();
debug!("Key doesn't exist yet or could not be loaded; generated a new key");
if let Err(e) = key_storage.set_key(service_id, key.as_str()) { if let Err(e) = key_storage.set_key(service_id, key.as_str()) {
error!("Failed to set new key into storage: {}", e);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::IoError,
format!("Could not write key to storage: {}", e), format!("Could not write key to storage: {}", e),
@@ -109,10 +123,11 @@ impl BookmarksClient {
key key
} }
_ => { _ => {
error!("Failed to get key from storage: {}", e);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::IoError,
format!("Could not get key from storage: {}", e), format!("Could not get key from storage: {}", e),
)) ));
} }
}, },
}; };
@@ -124,15 +139,19 @@ impl BookmarksClient {
}; };
// If bookmark file doesn't exist, initialize it // If bookmark file doesn't exist, initialize it
if !bookmarks_file.exists() { if !bookmarks_file.exists() {
info!("Bookmarks file doesn't exist yet; creating it...");
if let Err(err) = client.write_bookmarks() { if let Err(err) = client.write_bookmarks() {
error!("Failed to create bookmarks file: {}", err);
return Err(err); return Err(err);
} }
} else { } else {
// Load bookmarks from file // Load bookmarks from file
if let Err(err) = client.read_bookmarks() { if let Err(err) = client.read_bookmarks() {
error!("Failed to load bookmarks: {}", err);
return Err(err); return Err(err);
} }
} }
info!("Bookmarks client initialized");
// Load key // Load key
Ok(client) Ok(client)
} }
@@ -152,19 +171,29 @@ impl BookmarksClient {
key: &str, key: &str,
) -> Option<(String, u16, FileTransferProtocol, String, Option<String>)> { ) -> Option<(String, u16, FileTransferProtocol, String, Option<String>)> {
let entry: &Bookmark = self.hosts.bookmarks.get(key)?; let entry: &Bookmark = self.hosts.bookmarks.get(key)?;
debug!("Getting bookmark {}", key);
Some(( Some((
entry.address.clone(), entry.address.clone(),
entry.port, entry.port,
match FileTransferProtocol::from_str(entry.protocol.as_str()) { match FileTransferProtocol::from_str(entry.protocol.as_str()) {
Ok(proto) => proto, Ok(proto) => proto,
Err(_) => FileTransferProtocol::Sftp, // Default Err(err) => {
error!(
"Found invalid protocol in bookmarks: {}; defaulting to SFTP",
err
);
FileTransferProtocol::Sftp // Default
}
}, },
entry.username.clone(), entry.username.clone(),
match &entry.password { match &entry.password {
// Decrypted password if Some; if decryption fails return None // Decrypted password if Some; if decryption fails return None
Some(pwd) => match self.decrypt_str(pwd.as_str()) { Some(pwd) => match self.decrypt_str(pwd.as_str()) {
Ok(decrypted_pwd) => Some(decrypted_pwd), Ok(decrypted_pwd) => Some(decrypted_pwd),
Err(_) => None, Err(err) => {
error!("Failed to decrypt password for bookmark: {}", err);
None
}
}, },
None => None, None => None,
}, },
@@ -184,9 +213,11 @@ impl BookmarksClient {
password: Option<String>, password: Option<String>,
) { ) {
if name.is_empty() { if name.is_empty() {
error!("Fatal error; bookmark name is empty");
panic!("Bookmark name can't be empty"); panic!("Bookmark name can't be empty");
} }
// Make bookmark // Make bookmark
info!("Added bookmark {} with address {}", name, addr);
let host: Bookmark = self.make_bookmark(addr, port, protocol, username, password); let host: Bookmark = self.make_bookmark(addr, port, protocol, username, password);
self.hosts.bookmarks.insert(name, host); self.hosts.bookmarks.insert(name, host);
} }
@@ -196,6 +227,7 @@ impl BookmarksClient {
/// Delete entry from bookmarks /// Delete entry from bookmarks
pub fn del_bookmark(&mut self, name: &str) { pub fn del_bookmark(&mut self, name: &str) {
let _ = self.hosts.bookmarks.remove(name); let _ = self.hosts.bookmarks.remove(name);
info!("Removed bookmark {}", name);
} }
/// ### iter_recents /// ### iter_recents
/// ///
@@ -209,13 +241,20 @@ impl BookmarksClient {
/// Get recent associated to key /// Get recent associated to key
pub fn get_recent(&self, key: &str) -> Option<(String, u16, FileTransferProtocol, String)> { pub fn get_recent(&self, key: &str) -> Option<(String, u16, FileTransferProtocol, String)> {
// NOTE: password is not decrypted; recents will never have password // NOTE: password is not decrypted; recents will never have password
info!("Getting bookmark {}", key);
let entry: &Bookmark = self.hosts.recents.get(key)?; let entry: &Bookmark = self.hosts.recents.get(key)?;
Some(( Some((
entry.address.clone(), entry.address.clone(),
entry.port, entry.port,
match FileTransferProtocol::from_str(entry.protocol.as_str()) { match FileTransferProtocol::from_str(entry.protocol.as_str()) {
Ok(proto) => proto, Ok(proto) => proto,
Err(_) => FileTransferProtocol::Sftp, // Default Err(err) => {
error!(
"Found invalid protocol in bookmarks: {}; defaulting to SFTP",
err
);
FileTransferProtocol::Sftp // Default
}
}, },
entry.username.clone(), entry.username.clone(),
)) ))
@@ -236,6 +275,7 @@ impl BookmarksClient {
// Check if duplicated // Check if duplicated
for recent_host in self.hosts.recents.values() { for recent_host in self.hosts.recents.values() {
if *recent_host == host { if *recent_host == host {
debug!("Discarding recent since duplicated ({})", host.address);
// Don't save duplicates // Don't save duplicates
return; return;
} }
@@ -252,6 +292,7 @@ impl BookmarksClient {
// Delete keys starting from the last one // Delete keys starting from the last one
for key in keys.iter() { for key in keys.iter() {
let _ = self.hosts.recents.remove(key); let _ = self.hosts.recents.remove(key);
debug!("Removed recent bookmark {}", key);
// If length is < self.recents_size; break // If length is < self.recents_size; break
if self.hosts.recents.len() < self.recents_size { if self.hosts.recents.len() < self.recents_size {
break; break;
@@ -259,6 +300,7 @@ impl BookmarksClient {
} }
} }
let name: String = fmt_time(SystemTime::now(), "ISO%Y%m%dT%H%M%S"); let name: String = fmt_time(SystemTime::now(), "ISO%Y%m%dT%H%M%S");
info!("Saved recent host {} ({})", name, host.address);
self.hosts.recents.insert(name, host); self.hosts.recents.insert(name, host);
} }
@@ -267,6 +309,7 @@ impl BookmarksClient {
/// Delete entry from recents /// Delete entry from recents
pub fn del_recent(&mut self, name: &str) { pub fn del_recent(&mut self, name: &str) {
let _ = self.hosts.recents.remove(name); let _ = self.hosts.recents.remove(name);
info!("Removed recent host {}", name);
} }
/// ### write_bookmarks /// ### write_bookmarks
@@ -274,6 +317,7 @@ impl BookmarksClient {
/// Write bookmarks to file /// Write bookmarks to file
pub fn write_bookmarks(&self) -> Result<(), SerializerError> { pub fn write_bookmarks(&self) -> Result<(), SerializerError> {
// Open file // Open file
debug!("Writing bookmarks");
match OpenOptions::new() match OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@@ -284,10 +328,13 @@ impl BookmarksClient {
let serializer: BookmarkSerializer = BookmarkSerializer {}; let serializer: BookmarkSerializer = BookmarkSerializer {};
serializer.serialize(Box::new(writer), &self.hosts) serializer.serialize(Box::new(writer), &self.hosts)
} }
Err(err) => Err(SerializerError::new_ex( Err(err) => {
SerializerErrorKind::IoError, error!("Failed to write bookmarks: {}", err);
err.to_string(), Err(SerializerError::new_ex(
)), SerializerErrorKind::IoError,
err.to_string(),
))
}
} }
} }
@@ -296,6 +343,7 @@ impl BookmarksClient {
/// Read bookmarks from file /// Read bookmarks from file
fn read_bookmarks(&mut self) -> Result<(), SerializerError> { fn read_bookmarks(&mut self) -> Result<(), SerializerError> {
// Open bookmarks file for read // Open bookmarks file for read
debug!("Reading bookmarks");
match OpenOptions::new() match OpenOptions::new()
.read(true) .read(true)
.open(self.bookmarks_file.as_path()) .open(self.bookmarks_file.as_path())
@@ -311,10 +359,13 @@ impl BookmarksClient {
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
Err(err) => Err(SerializerError::new_ex( Err(err) => {
SerializerErrorKind::IoError, error!("Failed to read bookmarks: {}", err);
err.to_string(), Err(SerializerError::new_ex(
)), SerializerErrorKind::IoError,
err.to_string(),
))
}
} }
} }

View File

@@ -58,6 +58,11 @@ impl ConfigClient {
pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result<ConfigClient, SerializerError> { pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result<ConfigClient, SerializerError> {
// Initialize a default configuration // Initialize a default configuration
let default_config: UserConfig = UserConfig::default(); let default_config: UserConfig = UserConfig::default();
info!(
"Setting up config client with config path {} and SSH key directory {}",
config_path.display(),
ssh_key_dir.display()
);
// Create client // Create client
let mut client: ConfigClient = ConfigClient { let mut client: ConfigClient = ConfigClient {
config: default_config, config: default_config,
@@ -67,6 +72,7 @@ impl ConfigClient {
// If ssh key directory doesn't exist, create it // If ssh key directory doesn't exist, create it
if !ssh_key_dir.exists() { if !ssh_key_dir.exists() {
if let Err(err) = create_dir(ssh_key_dir) { if let Err(err) = create_dir(ssh_key_dir) {
error!("Failed to create SSH key dir: {}", err);
return Err(SerializerError::new_ex( return Err(SerializerError::new_ex(
SerializerErrorKind::IoError, SerializerErrorKind::IoError,
format!( format!(
@@ -76,17 +82,22 @@ impl ConfigClient {
), ),
)); ));
} }
debug!("Created SSH key directory");
} }
// If Config file doesn't exist, create it // If Config file doesn't exist, create it
if !config_path.exists() { if !config_path.exists() {
if let Err(err) = client.write_config() { if let Err(err) = client.write_config() {
error!("Couldn't create configuration file: {}", err);
return Err(err); return Err(err);
} }
debug!("Config file didn't exist; created file");
} else { } else {
// otherwise Load configuration from file // otherwise Load configuration from file
if let Err(err) = client.read_config() { if let Err(err) = client.read_config() {
error!("Couldn't read configuration file: {}", err);
return Err(err); return Err(err);
} }
debug!("Read configuration file");
} }
Ok(client) Ok(client)
} }
@@ -230,12 +241,18 @@ impl ConfigClient {
p.push(format!("{}.key", host_name)); p.push(format!("{}.key", host_name));
p p
}; };
info!(
"Writing SSH file to {} for host {}",
ssh_key_path.display(),
host_name
);
// Write key to file // Write key to file
let mut f: File = match File::create(ssh_key_path.as_path()) { let mut f: File = match File::create(ssh_key_path.as_path()) {
Ok(f) => f, Ok(f) => f,
Err(err) => return Self::make_io_err(err), Err(err) => return Self::make_io_err(err),
}; };
if let Err(err) = f.write_all(ssh_key.as_bytes()) { if let Err(err) = f.write_all(ssh_key.as_bytes()) {
error!("Failed to write SSH key to file: {}", err);
return Self::make_io_err(err); return Self::make_io_err(err);
} }
// Add host to keys // Add host to keys
@@ -251,6 +268,7 @@ impl ConfigClient {
/// and also commits changes to configuration, to prevent incoerent data /// and also commits changes to configuration, to prevent incoerent data
pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> { pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> {
// Remove key from configuration and get key path // Remove key from configuration and get key path
info!("Removing key for {}@{}", host, username);
let key_path: PathBuf = match self let key_path: PathBuf = match self
.config .config
.remote .remote
@@ -262,6 +280,7 @@ impl ConfigClient {
}; };
// Remove file // Remove file
if let Err(err) = remove_file(key_path.as_path()) { if let Err(err) = remove_file(key_path.as_path()) {
error!("Failed to remove key file {}: {}", key_path.display(), err);
return Self::make_io_err(err); return Self::make_io_err(err);
} }
// Commit changes to configuration // Commit changes to configuration
@@ -310,10 +329,13 @@ impl ConfigClient {
let serializer: ConfigSerializer = ConfigSerializer {}; let serializer: ConfigSerializer = ConfigSerializer {};
serializer.serialize(Box::new(writer), &self.config) serializer.serialize(Box::new(writer), &self.config)
} }
Err(err) => Err(SerializerError::new_ex( Err(err) => {
SerializerErrorKind::IoError, error!("Failed to write configuration file: {}", err);
err.to_string(), Err(SerializerError::new_ex(
)), SerializerErrorKind::IoError,
err.to_string(),
))
}
} }
} }
@@ -337,10 +359,13 @@ impl ConfigClient {
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
Err(err) => Err(SerializerError::new_ex( Err(err) => {
SerializerErrorKind::IoError, error!("Failed to read configuration: {}", err);
err.to_string(), Err(SerializerError::new_ex(
)), SerializerErrorKind::IoError,
err.to_string(),
))
}
} }
} }

View File

@@ -42,6 +42,7 @@ impl SshKeyStorage {
pub fn storage_from_config(cfg_client: &ConfigClient) -> Self { pub fn storage_from_config(cfg_client: &ConfigClient) -> Self {
let mut hosts: HashMap<String, PathBuf> = let mut hosts: HashMap<String, PathBuf> =
HashMap::with_capacity(cfg_client.iter_ssh_keys().count()); HashMap::with_capacity(cfg_client.iter_ssh_keys().count());
debug!("Setting up SSH key storage");
// Iterate over keys // Iterate over keys
for key in cfg_client.iter_ssh_keys() { for key in cfg_client.iter_ssh_keys() {
match cfg_client.get_ssh_key(key) { match cfg_client.get_ssh_key(key) {
@@ -52,8 +53,12 @@ impl SshKeyStorage {
} }
None => continue, None => continue,
}, },
Err(_) => continue, Err(err) => {
error!("Failed to get SSH key for {}: {}", key, err);
continue;
}
} }
info!("Got SSH key for {}", key);
} }
// Return storage // Return storage
SshKeyStorage { hosts } SshKeyStorage { hosts }