//! ## ConfigClient
//!
//! `config_client` is the module which provides an API between the Config module and the system
/*
*
* Copyright (C) 2020-2021 Christian Visintin - christian.visintin1997@gmail.com
*
* This file is part of "TermSCP"
*
* TermSCP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TermSCP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TermSCP. If not, see .
*
*/
// Deps
extern crate rand;
// Locals
use crate::config::serializer::ConfigSerializer;
use crate::config::{SerializerError, SerializerErrorKind, UserConfig};
use crate::filetransfer::FileTransferProtocol;
use crate::fs::explorer::GroupDirs;
// Ext
use std::fs::{create_dir, remove_file, File, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::string::ToString;
// Types
pub type SshHost = (String, String, PathBuf); // 0: host, 1: username, 2: RSA key path
/// ## ConfigClient
///
/// ConfigClient provides a high level API to communicate with the termscp configuration
pub struct ConfigClient {
config: UserConfig, // Configuration loaded
config_path: PathBuf, // Configuration TOML Path
ssh_key_dir: PathBuf, // SSH Key storage directory
}
impl ConfigClient {
/// ### new
///
/// Instantiate a new `ConfigClient` with provided path
pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result {
// Initialize a default configuration
let default_config: UserConfig = UserConfig::default();
// Create client
let mut client: ConfigClient = ConfigClient {
config: default_config,
config_path: PathBuf::from(config_path),
ssh_key_dir: PathBuf::from(ssh_key_dir),
};
// If ssh key directory doesn't exist, create it
if !ssh_key_dir.exists() {
if let Err(err) = create_dir(ssh_key_dir) {
return Err(SerializerError::new_ex(
SerializerErrorKind::IoError,
format!(
"Could not create SSH key directory \"{}\": {}",
ssh_key_dir.display(),
err
),
));
}
}
// If Config file doesn't exist, create it
if !config_path.exists() {
if let Err(err) = client.write_config() {
return Err(err);
}
} else {
// otherwise Load configuration from file
if let Err(err) = client.read_config() {
return Err(err);
}
}
Ok(client)
}
// Text editor
/// ### get_text_editor
///
/// Get text editor from configuration
pub fn get_text_editor(&self) -> PathBuf {
self.config.user_interface.text_editor.clone()
}
/// ### set_text_editor
///
/// Set text editor path
pub fn set_text_editor(&mut self, path: PathBuf) {
self.config.user_interface.text_editor = path;
}
// Default protocol
/// ### get_default_protocol
///
/// Get default protocol from configuration
pub fn get_default_protocol(&self) -> FileTransferProtocol {
match FileTransferProtocol::from_str(self.config.user_interface.default_protocol.as_str()) {
Ok(p) => p,
Err(_) => FileTransferProtocol::Sftp,
}
}
/// ### set_default_protocol
///
/// Set default protocol to configuration
pub fn set_default_protocol(&mut self, proto: FileTransferProtocol) {
self.config.user_interface.default_protocol = proto.to_string();
}
/// ### get_show_hidden_files
///
/// Get value of `show_hidden_files`
pub fn get_show_hidden_files(&self) -> bool {
self.config.user_interface.show_hidden_files
}
/// ### set_show_hidden_files
///
/// Set new value for `show_hidden_files`
pub fn set_show_hidden_files(&mut self, value: bool) {
self.config.user_interface.show_hidden_files = value;
}
/// ### get_check_for_updates
///
/// Get value of `check_for_updates`
pub fn get_check_for_updates(&self) -> bool {
self.config.user_interface.check_for_updates.unwrap_or(true)
}
/// ### set_check_for_updates
///
/// Set new value for `check_for_updates`
pub fn set_check_for_updates(&mut self, value: bool) {
self.config.user_interface.check_for_updates = Some(value);
}
/// ### get_group_dirs
///
/// Get GroupDirs value from configuration (will be converted from string)
pub fn get_group_dirs(&self) -> Option {
// Convert string to `GroupDirs`
match &self.config.user_interface.group_dirs {
None => None,
Some(val) => match GroupDirs::from_str(val.as_str()) {
Ok(val) => Some(val),
Err(_) => None,
},
}
}
/// ### set_group_dirs
///
/// Set value for group_dir in configuration.
/// Provided value, if `Some` will be converted to `GroupDirs`
pub fn set_group_dirs(&mut self, val: Option) {
self.config.user_interface.group_dirs = match val {
None => None,
Some(val) => Some(val.to_string()),
};
}
/// ### get_file_fmt
///
/// Get current file fmt
pub fn get_file_fmt(&self) -> Option {
self.config.user_interface.file_fmt.clone()
}
/// ### set_file_fmt
///
/// Set file fmt parameter
pub fn set_file_fmt(&mut self, s: String) {
self.config.user_interface.file_fmt = match s.is_empty() {
true => None,
false => Some(s),
};
}
// SSH Keys
/// ### save_ssh_key
///
/// Save a SSH key into configuration.
/// This operation also creates the key file in `ssh_key_dir`
/// and also commits changes to configuration, to prevent incoerent data
pub fn add_ssh_key(
&mut self,
host: &str,
username: &str,
ssh_key: &str,
) -> Result<(), SerializerError> {
let host_name: String = Self::make_ssh_host_key(host, username);
// Get key path
let ssh_key_path: PathBuf = {
let mut p: PathBuf = self.ssh_key_dir.clone();
p.push(format!("{}.key", host_name));
p
};
// Write key to file
let mut f: File = match File::create(ssh_key_path.as_path()) {
Ok(f) => f,
Err(err) => return Self::make_io_err(err),
};
if let Err(err) = f.write_all(ssh_key.as_bytes()) {
return Self::make_io_err(err);
}
// Add host to keys
self.config.remote.ssh_keys.insert(host_name, ssh_key_path);
// Write config
self.write_config()
}
/// ### del_ssh_key
///
/// Delete a ssh key from configuration, using host as key.
/// This operation also unlinks the key file in `ssh_key_dir`
/// and also commits changes to configuration, to prevent incoerent data
pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> {
// Remove key from configuration and get key path
let key_path: PathBuf = match self
.config
.remote
.ssh_keys
.remove(&Self::make_ssh_host_key(host, username))
{
Some(p) => p,
None => return Ok(()), // Return ok if host doesn't exist
};
// Remove file
if let Err(err) = remove_file(key_path.as_path()) {
return Self::make_io_err(err);
}
// Commit changes to configuration
self.write_config()
}
/// ### get_ssh_key
///
/// Get ssh key from host.
/// None is returned if key doesn't exist
/// `std::io::Error` is returned in case it was not possible to read the key file
pub fn get_ssh_key(&self, mkey: &str) -> std::io::Result