1use std::
20{
21 str::FromStr,
22 fmt::Debug,
23 path::Path,
24 io::{ self, Cursor },
25 fs::{ self, File },
26};
27
28use toml_edit::{ DocumentMut, Value };
29
30use crate::{ options, misc };
31
32#[cfg(feature = "client")]
33use std::fmt::Write;
34
35#[cfg(feature = "client")]
36use crate::crypto;
37
38#[cfg(feature = "client")]
40pub enum TofuCode {
42 Valid, Unknown(String, String), Mismatch, }
46
47fn config_path(filename: &str) -> String {
50 misc::get_why2_dir() + filename
51}
52
53fn fetch_config(filename: &str) -> String {
55 misc::fetch_data(&(options::CONFIG_URL.to_owned() + filename)).expect("Fetching config failed")
56}
57
58fn init_config(filename: &str) {
60 misc::check_directory(); let config_path = config_path(filename);
63 if !Path::new(&config_path).is_file()
64 {
65 let mut config_file = File::create(config_path).expect("Failed to create WHY2 config"); let mut config = Cursor::new(fetch_config(filename));
68 io::copy(&mut config, &mut config_file).expect("Failed writing to config file");
69 }
70}
71
72fn get_data(path: &str) -> DocumentMut {
74 let content = fs::read_to_string(path).expect("Failed to read config"); content.parse::<DocumentMut>().expect("Failed to parse config") }
77
78fn config_read<T: FromStr>(filename: &str, key: &str) -> T where
80 T::Err: Debug,
81{
82 let data = get_data(&config_path(filename));
83
84 if let Some(value) = data.get(key) {
87 let string_value = match value.as_value().expect("Invalid config")
89 {
90 Value::String(s) => s.value().to_string(),
91 Value::Integer(i) => i.value().to_string(),
92 Value::Boolean(b) => b.value().to_string(),
93
94 _ => panic!("Unsupported config datatype")
95 };
96
97 return string_value.parse::<T>().expect("Parsing config value failed");
98 }
99
100 let mut new_config: DocumentMut = fetch_config(filename).parse().expect("Failed to parse config");
102
103 for (key, old_value) in data.as_table()
105 {
106 if let Some(item) = new_config.get_mut(key)
108 {
109 *item.as_value_mut().expect("Updating config failed") = old_value.as_value().expect("Invalid config").clone();
111 }
112 }
113
114 fs::write(&config_path(&filename), new_config.to_string()).expect("Updating config file failed");
116
117 config_read(filename, key)
119}
120
121fn config_write(filename: &str, key: &str, value: &str) {
123 let path = config_path(filename); let mut data = get_data(&path);
127
128 let table = data.as_table_mut();
130 if let Some(item) = table.get_mut(key)
131 {
132 *item.as_value_mut().expect("Updating config failed") = value.into();
133 } else
134 {
135 table.insert(key, value.into());
136 }
137
138 fs::write(&path, data.to_string()).expect("Saving config failed");
140}
141
142#[cfg(feature = "server")]
144pub fn init_server_config() {
146 init_config(options::SERVER_CONFIG); let users_dir_path = config_path(options::SERVER_USERS_CONFIG);
149 if !Path::new(&users_dir_path).is_file()
150 {
151 fs::write(&users_dir_path, "#*#**#*###**#***###*#").expect("Writing to config failed");
152 }
153}
154
155#[cfg(feature = "client")]
156pub fn init_client_config()
157{
158 init_config(options::CLIENT_CONFIG); let server_keys_path = config_path(options::SERVER_KEYS_CONFIG);
162 if !Path::new(&server_keys_path).is_file()
163 {
164 File::create(server_keys_path).expect("Failed to create server-keys config"); }
166}
167
168#[cfg(feature = "server")]
169pub fn server_config<T: FromStr>(key: &str) -> T where
171 T::Err: Debug,
172{
173 config_read(options::SERVER_CONFIG, key)
174}
175
176#[cfg(feature = "client")]
177pub fn client_config<T: FromStr>(key: &str) -> T where
179 T::Err: Debug,
180{
181 config_read(options::CLIENT_CONFIG, key)
182}
183
184#[cfg(feature = "server")]
185pub fn server_users_config(key: &str) -> String {
187 config_read(options::SERVER_USERS_CONFIG, key)
188}
189
190#[cfg(feature = "client")]
191pub fn client_write(key: &str, value: &str) {
193 config_write(options::CLIENT_CONFIG, key, value);
194}
195
196#[cfg(feature = "server")]
197pub fn server_users_write(key: &str, value: &str) {
199 config_write(options::SERVER_USERS_CONFIG, key, value);
200}
201
202#[cfg(feature = "server")]
203pub fn server_users_contains(key: &str) -> bool {
205 get_data(&config_path(options::SERVER_USERS_CONFIG)).get(key).is_some()
206}
207
208#[cfg(feature = "client")]
209pub fn server_keys_check(host: &str, pubkey: &str) -> TofuCode {
211 let pubkey_hash = crypto::sha256(pubkey);
213 let mut pubkey_string = String::with_capacity(64);
214
215 for byte in pubkey_hash
217 {
218 write!(pubkey_string, "{:02x}", byte).unwrap();
219 }
220
221 if get_data(&config_path(options::SERVER_KEYS_CONFIG)).get(host).is_some()
223 {
224 return if config_read::<String>(options::SERVER_KEYS_CONFIG, host) == pubkey_string
226 {
227 TofuCode::Valid
228 } else
229 {
230 TofuCode::Mismatch
231 }
232 }
233
234 TofuCode::Unknown(pubkey_string, host.to_string())
235}
236
237#[cfg(feature = "client")]
238pub fn server_keys_save(host: &str, pubkey_hash: &str) {
240 config_write(options::SERVER_KEYS_CONFIG, host, pubkey_hash);
242}