use std::
{
str::FromStr,
fmt::Debug,
path::Path,
io::{ self, Cursor },
fs::{ self, File },
};
use toml_edit::{ DocumentMut, Value };
use crate::{ consts, misc };
#[cfg(feature = "client")]
use std::fmt::Write;
#[cfg(feature = "client")]
use crate::crypto;
#[cfg(feature = "client")]
pub enum TofuCode {
Valid, Unknown(String, String), Mismatch, }
fn config_path(filename: &str) -> String {
misc::get_why2_dir() + filename
}
fn get_config() -> &'static str {
#[cfg(feature = "client")]
{
include_str!("./client.toml")
}
#[cfg(feature = "server")]
{
include_str!("./server.toml")
}
}
fn get_data(path: &str) -> DocumentMut {
let content = fs::read_to_string(path).expect("Failed to read config"); content.parse::<DocumentMut>().expect("Failed to parse config") }
fn config_read<T: FromStr>(filename: &str, key: &str) -> T where
T::Err: Debug,
{
let data = get_data(&config_path(filename));
if let Some(value) = data.get(key) {
let string_value = match value.as_value().expect("Invalid config")
{
Value::String(s) => s.value().to_string(),
Value::Integer(i) => i.value().to_string(),
Value::Boolean(b) => b.value().to_string(),
_ => panic!("Unsupported config datatype")
};
return string_value.parse::<T>().expect("Parsing config value failed");
}
let mut new_config: DocumentMut = get_config().parse().expect("Failed to parse config");
for (key, old_value) in data.as_table()
{
if let Some(item) = new_config.get_mut(key)
{
*item.as_value_mut().expect("Updating config failed") = old_value.as_value().expect("Invalid config").clone();
}
}
fs::write(&config_path(&filename), new_config.to_string()).expect("Updating config file failed");
config_read(filename, key)
}
fn config_write(filename: &str, key: &str, value: &str) {
let path = config_path(filename);
let mut data = get_data(&path);
let table = data.as_table_mut();
if let Some(item) = table.get_mut(key)
{
*item.as_value_mut().expect("Updating config failed") = value.into();
} else
{
table.insert(key, value.into());
}
fs::write(&path, data.to_string()).expect("Saving config failed");
}
pub fn init_config() {
misc::check_directory();
{
let filename =
{
#[cfg(feature = "client")]
{
consts::CLIENT_CONFIG
}
#[cfg(feature = "server")]
{
consts::SERVER_CONFIG
}
};
let config_path = config_path(filename);
if !Path::new(&config_path).is_file()
{
let mut config_file = File::create(config_path).expect("Failed to create WHY2 config");
let mut config = Cursor::new(get_config());
io::copy(&mut config, &mut config_file).expect("Failed writing to config file");
}
}
let runtime_path =
{
#[cfg(feature = "client")]
{
config_path(consts::SERVER_KEYS_CONFIG)
}
#[cfg(feature = "server")]
{
config_path(consts::SERVER_USERS_CONFIG)
}
};
if !Path::new(&runtime_path).is_file()
{
fs::write(&runtime_path, "#*#**#*###**#***###*#").expect("Writing to config failed");
}
}
pub fn read_config<T: FromStr>(key: &str) -> T where
T::Err: Debug,
{
#[cfg(feature = "client")]
{
config_read(consts::CLIENT_CONFIG, key)
}
#[cfg(feature = "server")]
{
config_read(consts::SERVER_CONFIG, key)
}
}
#[cfg(feature = "server")]
pub fn server_users_config(key: &str) -> String {
config_read(consts::SERVER_USERS_CONFIG, key)
}
#[cfg(feature = "client")]
pub fn client_write(key: &str, value: &str) {
config_write(consts::CLIENT_CONFIG, key, value);
}
#[cfg(feature = "server")]
pub fn server_users_write(key: &str, value: &str) {
config_write(consts::SERVER_USERS_CONFIG, key, value);
}
#[cfg(feature = "server")]
pub fn server_users_contains(key: &str) -> bool {
get_data(&config_path(consts::SERVER_USERS_CONFIG)).get(key).is_some()
}
#[cfg(feature = "client")]
pub fn server_keys_check(host: &str, pubkey: &str) -> TofuCode {
let pubkey_hash = crypto::sha256(pubkey);
let mut pubkey_string = String::with_capacity(64);
for byte in pubkey_hash
{
write!(pubkey_string, "{:02x}", byte).unwrap();
}
if get_data(&config_path(consts::SERVER_KEYS_CONFIG)).get(host).is_some()
{
return if config_read::<String>(consts::SERVER_KEYS_CONFIG, host) == pubkey_string
{
TofuCode::Valid
} else
{
TofuCode::Mismatch
}
}
TofuCode::Unknown(pubkey_string, host.to_string())
}
#[cfg(feature = "client")]
pub fn server_keys_save(host: &str, pubkey_hash: &str) {
config_write(consts::SERVER_KEYS_CONFIG, host, pubkey_hash);
}