diff --git a/Cargo.lock b/Cargo.lock index 76267a3..8fd8645 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,15 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + [[package]] name = "asn1-rs" version = "0.7.2" @@ -1505,11 +1514,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1519,9 +1526,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -2160,6 +2169,7 @@ name = "iota-storage" version = "0.1.0" dependencies = [ "aes-gcm", + "arc-swap", "base64", "hex", "hkdf 0.12.4", @@ -2174,6 +2184,8 @@ dependencies = [ "ratatui", "reqwest", "rusqlite", + "serde", + "serde_json", "sha2 0.10.9", "sysinfo", "tokio", @@ -2655,7 +2667,7 @@ dependencies = [ [[package]] name = "mtp" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "mtp-client", "mtp-codec", @@ -2663,14 +2675,13 @@ dependencies = [ "mtp-crypto", "mtp-files", "mtp-host", - "mtp-transport", "mtp-type-map", ] [[package]] name = "mtp-client" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "mtp-codec", "mtp-common", @@ -2683,7 +2694,7 @@ dependencies = [ [[package]] name = "mtp-codec" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "base64", "byteorder", @@ -2696,7 +2707,7 @@ dependencies = [ [[package]] name = "mtp-common" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "quinn", "rustls", @@ -2707,7 +2718,7 @@ dependencies = [ [[package]] name = "mtp-crypto" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "base64", "chacha20poly1305", @@ -2726,7 +2737,7 @@ dependencies = [ [[package]] name = "mtp-files" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "mtp-crypto", "thiserror 1.0.69", @@ -2735,7 +2746,7 @@ dependencies = [ [[package]] name = "mtp-host" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "mtp-codec", "mtp-common", @@ -2748,7 +2759,7 @@ dependencies = [ [[package]] name = "mtp-transport" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "log", "mtp-codec", @@ -2762,7 +2773,7 @@ dependencies = [ [[package]] name = "mtp-type-map" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#b96c072a0f87de7828e45cf3dcd44aef4a9e459d" +source = "git+https://git.methanium.net/Methanium/mtp.git#2e7c0b489360d5d05fe53326811616e730c1a556" dependencies = [ "serde", "serde_yaml", @@ -3142,9 +3153,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" dependencies = [ "memchr", "ucd-trie", @@ -3152,9 +3163,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" dependencies = [ "pest", "pest_generator", @@ -3162,9 +3173,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" dependencies = [ "pest", "pest_meta", @@ -3175,12 +3186,11 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" dependencies = [ "pest", - "sha2 0.10.9", ] [[package]] @@ -3468,15 +3478,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -3490,16 +3501,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2 0.6.4", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3605,6 +3616,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "ratatui" version = "0.30.2" @@ -5395,16 +5415,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -5422,31 +5433,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -5464,96 +5458,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "wit-bindgen" version = "0.57.1" diff --git a/client/src/client_connection.rs b/client/src/client_connection.rs index c367f7e..a386a4e 100644 --- a/client/src/client_connection.rs +++ b/client/src/client_connection.rs @@ -943,9 +943,7 @@ impl ClientConnection { } async fn handle_challenge(&self, cv: &CommunicationValue) { - let conf = CONFIG.read().await; - let kr_str = conf.get_keyring().unwrap(); - drop(conf); + let kr_str = CONFIG.load().keyring.clone().unwrap(); let Some(keyring) = keyring_from_base64(&kr_str) else { return; diff --git a/iota-cli/src/elements/console_card.rs b/iota-cli/src/elements/console_card.rs index ea09717..6e9a7a0 100644 --- a/iota-cli/src/elements/console_card.rs +++ b/iota-cli/src/elements/console_card.rs @@ -2,7 +2,7 @@ use crossterm::event::{KeyCode, KeyEvent}; use iota_logger::{log, log_command, log_cv}; use iota_state::{ACTIVE_TASKS, RELOAD, SHUTDOWN}; use iota_storage::users::{user_manager, user_profile::UserProfile}; -use iota_storage::util::config_util::CONFIG; +use iota_storage::util::config_util::modify_config; use iota_util::file_util; use mtp::codec::{CommunicationType, CommunicationValue}; use omikron_connector::omikron_connection::OMIKRON_CONNECTION; @@ -479,13 +479,11 @@ pub async fn run_command(command: &str) { } ["regenerate", "keys"] => { log!("Regenerating Iota key pair..."); - { - let mut conf = CONFIG.write().await; - conf.remove("public_key"); - conf.remove("private_key"); - conf.remove("iota_id"); - conf.update(); - } + modify_config(|cfg| { + cfg.public_key = None; + cfg.private_key = None; + cfg.iota_id = None; + }); log!("Key pair regenerated. Reconnecting to Omikron server..."); OMIKRON_CONNECTION.reconnect().await; log!("Reconnected with new key pair"); diff --git a/iota-core/src/main.rs b/iota-core/src/main.rs index d95f268..2baf39b 100644 --- a/iota-core/src/main.rs +++ b/iota-core/src/main.rs @@ -82,7 +82,7 @@ async fn main() { logger::startup(); // BASIC CONFIGURATION - &CONFIG.write().await.load(); + iota_storage::util::config_util::load_config(); // USER MANAGEMENT if let Err(_) = user_manager::load_users().await { @@ -102,7 +102,7 @@ async fn main() { } log!( "IOTA ID: {}", - CONFIG.read().await.get_iota_id().to_string() + CONFIG.load().iota_id.map(|id| id.to_string()).unwrap_or_else(|| "N/A".to_string()) ); log!("User IDS: {}", sb); @@ -121,7 +121,7 @@ async fn main() { sb1 = sb1 + ","; } log!("Community IDS: {}", sb1); */ - let _port = CONFIG.read().await.get_port(); + let _port = CONFIG.load().port; let mut _ip = "0.0.0.0".to_string(); for iface in pnet::datalink::interfaces() { let iface: NetworkInterface = iface; @@ -176,7 +176,7 @@ async fn main() { } sleep(Duration::from_secs(1)).await; } - &CONFIG.write().await.clear(); + iota_storage::util::config_util::clear_config(); user_manager::clear(); // Commhnities have not been implemented yet. /*community_manager::clear();*/ diff --git a/iota-storage/Cargo.toml b/iota-storage/Cargo.toml index 6f694a4..78359be 100644 --- a/iota-storage/Cargo.toml +++ b/iota-storage/Cargo.toml @@ -15,7 +15,10 @@ base64 = "0.22.1" hex = "*" hkdf = "0.12.4" json = "*" +arc-swap = "1" once_cell = "1.21.3" +serde = { version = "1", features = ["derive"] } +serde_json = "1" rand = "0.8" rand_core = { version = "0.6", features = ["getrandom", "std"] } ratatui = "0.30.0" diff --git a/iota-storage/src/util/config_util.rs b/iota-storage/src/util/config_util.rs index 74e75e8..450ffb4 100644 --- a/iota-storage/src/util/config_util.rs +++ b/iota-storage/src/util/config_util.rs @@ -1,93 +1,85 @@ +use arc_swap::ArcSwap; use iota_util::file_util::{load_file, save_file}; -use json::JsonValue; use once_cell::sync::Lazy; -use tokio::sync::RwLock; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; -pub static CONFIG: Lazy> = Lazy::new(|| RwLock::new(ConfigUtil::new())); +pub static CONFIG: Lazy> = + Lazy::new(|| ArcSwap::new(Arc::new(IotaConfig::default()))); -pub struct ConfigUtil { - pub config: JsonValue, - pub unique: bool, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IotaConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub iota_id: Option, + #[serde(default = "default_port")] + pub port: u16, + #[serde(skip_serializing_if = "Option::is_none")] + pub omikron_host: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub omikron_port: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub keyring: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub public_key: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub private_key: Option, + #[serde(default = "default_read_receipts_enabled")] + pub read_receipts_enabled: bool, } -impl ConfigUtil { - pub fn new() -> Self { +const fn default_port() -> u16 { + 1984 +} + +const fn default_read_receipts_enabled() -> bool { + true +} + +impl Default for IotaConfig { + fn default() -> Self { Self { - config: JsonValue::new_object(), - unique: false, - } - } - pub fn clear(&mut self) { - self.config = JsonValue::new_object(); - self.unique = false; - } - pub fn load(&mut self) { - let s = load_file("", "config.json"); - if s.is_empty() { - // File might be missing or empty/being written. - // We don't want to wipe the current config if it already has data. - // But if it's the first load, it will stay empty. - return; - } - - match json::parse(&s) { - Ok(parsed) => { - self.config = parsed; - self.unique = false; - } - Err(e) => { - eprintln!("Failed to parse config.json: {}. Content: '{}'", e, s); - // Keep the current config rather than wiping it. - } - } - } - - pub fn get_iota_id(&self) -> i64 { - self.config["iota_id"].as_i64().unwrap_or(0) - } - - pub fn get_port(&self) -> u16 { - self.config["port"].as_u16().unwrap_or(1984) - } - - pub fn get_omikron_host(&self) -> Option { - self.config["omikron_host"].as_str().map(String::from) - } - - pub fn get_omikron_port(&self) -> Option { - self.config["omikron_port"].as_u16() - } - - pub fn get_keyring(&self) -> Option { - self.config["keyring"].as_str().map(String::from) - } - - pub fn get_public_key(&self) -> Option { - self.config["public_key"].as_str().map(String::from) - } - - pub fn get_private_key(&self) -> Option { - self.config["private_key"].as_str().map(String::from) - } - - pub fn get(&self, key: &str) -> &JsonValue { - &self.config[key] - } - - pub fn change(&mut self, key: &str, value: JsonValue) { - self.config[key] = value; - self.unique = true; - } - - pub fn remove(&mut self, key: &str) { - self.config.remove(key); - self.unique = true; - } - - pub fn update(&mut self) { - if self.unique { - save_file("", "config.json", &self.config.to_string()); - self.unique = false; + iota_id: None, + port: default_port(), + omikron_host: None, + omikron_port: None, + keyring: None, + public_key: None, + private_key: None, + read_receipts_enabled: default_read_receipts_enabled(), } } } + +pub fn load_config() { + let s = load_file("", "config.json"); + if s.is_empty() { + return; + } + + match serde_json::from_str::(&s) { + Ok(parsed) => { + CONFIG.store(Arc::new(parsed)); + } + Err(e) => { + eprintln!("Failed to parse config.json: {}. Content: '{}'", e, s); + } + } +} + +pub fn clear_config() { + CONFIG.store(Arc::new(IotaConfig::default())); + save_config(); +} + +pub fn save_config() { + if let Ok(json) = serde_json::to_string(&**CONFIG.load()) { + save_file("", "config.json", &json); + } +} + +pub fn modify_config(f: impl FnOnce(&mut IotaConfig)) { + let mut cfg = IotaConfig::clone(&**CONFIG.load()); + f(&mut cfg); + CONFIG.store(Arc::new(cfg)); + save_config(); +} diff --git a/iota-util/src/file_util.rs b/iota-util/src/file_util.rs index 8769c7f..286c2f2 100755 --- a/iota-util/src/file_util.rs +++ b/iota-util/src/file_util.rs @@ -126,12 +126,25 @@ pub fn save_file(path: &str, name: &str, value: &str) { } } - if let Err(e) = fs::write(&file_path, value) { + // Write to a temp file first, then atomically rename to prevent partial writes. + let tmp_name = format!(".{}.tmp", name); + let tmp_path = dir.join(&tmp_name); + if let Err(e) = fs::write(&tmp_path, value) { println!( - "[IMPORTANT] Couldn't write file {}: {}", + "[IMPORTANT] Couldn't write temp file {}: {}", + tmp_path.display(), + e + ); + return; + } + if let Err(e) = fs::rename(&tmp_path, &file_path) { + println!( + "[IMPORTANT] Couldn't rename {} to {}: {}", + tmp_path.display(), file_path.display(), e ); + let _ = fs::remove_file(&tmp_path); } } diff --git a/omikron-connector/src/omega_discovery.rs b/omikron-connector/src/omega_discovery.rs index 7957cf0..e623daf 100644 --- a/omikron-connector/src/omega_discovery.rs +++ b/omikron-connector/src/omega_discovery.rs @@ -3,7 +3,7 @@ use std::time::Duration; use mtp::crypto::PublicKeyBundle; -const OMEGA_API_BASE_DEFAULT: &str = "https://tensamin.net:9188"; +const OMEGA_API_BASE_DEFAULT: &str = "https://omega.tensamin.net"; const REQUEST_TIMEOUT: Duration = Duration::from_secs(10); pub struct OmikronEndpoint { diff --git a/omikron-connector/src/omikron_connection.rs b/omikron-connector/src/omikron_connection.rs index bf8a570..0d07db9 100755 --- a/omikron-connector/src/omikron_connection.rs +++ b/omikron-connector/src/omikron_connection.rs @@ -5,11 +5,10 @@ use iota_storage::users::contact::Contact; use iota_storage::util::chat_files::{self, MessageState, change_message_state}; use iota_storage::util::chats_util::{self, get_user, mod_user}; use iota_storage::util::communities_util::CommunitiesUtil; -use iota_storage::util::config_util::CONFIG; +use iota_storage::util::config_util::{modify_config, CONFIG}; use iota_util::crypto_helper::{self, keyring_from_base64}; use iota_util::crypto_util::{self}; use iota_util::file_util::{get_children, has_file, load_file, save_file}; -use json::JsonValue; use mtp::client::{Client, ClientConfig, Policy, Receiver, SendMode, Sender}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp::crypto::{Keyring, PublicKeyBundle}; @@ -37,14 +36,7 @@ fn typed_container(items: Vec<(DataType, DataValue)>) -> DataValue { // Helper function to check if read receipts are enabled globally async fn is_read_receipts_enabled() -> bool { - // Check global config for read receipts setting - // Default to true if not set - let conf = CONFIG.read().await; - let value = conf.get("read_receipts_enabled"); - match value { - JsonValue::Boolean(b) => *b, - _ => true, - } + CONFIG.load().read_receipts_enabled } // ============================================================================ @@ -247,15 +239,14 @@ impl OmikronConnection { let keyring = self.load_or_migrate_keyring().await; - let existing_iota_id = match CONFIG.read().await.get_iota_id() { - 0 => None, - id => Some(id as u64), - }; + let existing_iota_id = CONFIG.load().iota_id; let (host, port, omikron_public_key) = self.resolve_omikron_endpoint(existing_iota_id).await?; - let addr_str = format!("https://{}:{}/ws/iota/", host, port); + let addr_str = format!("https://{}:{}", host, port); + + log!("Connecting to Omikron at {}", addr_str); let client_config = ClientConfig::new(&addr_str) .with_description("iota") @@ -301,10 +292,7 @@ impl OmikronConnection { log_t!("omikron_connection_success"); if existing_iota_id.is_none() { - let mut conf_write = CONFIG.write().await; - conf_write.change("iota_id", JsonValue::from(connection.client_id as i64)); - conf_write.update(); - drop(conf_write); + modify_config(|cfg| cfg.iota_id = Some(connection.client_id)); log!("Registered with Iota-ID: {}", connection.client_id); } @@ -372,7 +360,7 @@ impl OmikronConnection { return kr; } - let legacy = CONFIG.read().await.get_keyring(); + let legacy = CONFIG.load().keyring.clone(); let keyring = legacy .and_then(|b64| keyring_from_base64(&b64)) .unwrap_or_else(crypto_helper::generate_keyring); @@ -382,10 +370,7 @@ impl OmikronConnection { } let b64 = crypto_helper::keyring_to_base64(&keyring); - let mut conf = CONFIG.write().await; - conf.change("keyring", JsonValue::from(b64)); - conf.update(); - drop(conf); + modify_config(|cfg| cfg.keyring = Some(b64)); keyring } @@ -430,9 +415,9 @@ impl OmikronConnection { let cached_key = mtp::files::load_public_key_bundle(OMIKRON_PUBLIC_KEY_PATH).ok(); let cached_host_port = { - let conf = CONFIG.read().await; - match (conf.get_omikron_host(), conf.get_omikron_port()) { - (Some(host), Some(port)) => Some((host, port)), + let conf = CONFIG.load(); + match (&conf.omikron_host, conf.omikron_port) { + (Some(host), Some(port)) => Some((host.clone(), port)), _ => None, } }; @@ -482,17 +467,14 @@ impl OmikronConnection { (host.clone(), *port, cached.clone()) } else { return Err( - "Omega discovery failed and no cached Omikron address/key is available" - .to_string(), + "Omega discovery failed and no cached Omikron address/key is available".to_string(), ); }; - { - let mut conf = CONFIG.write().await; - conf.change("omikron_host", JsonValue::from(host.clone())); - conf.change("omikron_port", JsonValue::from(port)); - conf.update(); - } + modify_config(|cfg| { + cfg.omikron_host = Some(host.clone()); + cfg.omikron_port = Some(port); + }); Ok((host, port, public_key)) } @@ -609,9 +591,7 @@ impl OmikronConnection { if let Some(app_pub_bundle) = iota_util::crypto_helper::public_key_bundle_from_base64(&app_public_key) { - let conf = CONFIG.read().await; - let kr_str = conf.get_keyring().unwrap_or_default(); - drop(conf); + let kr_str = CONFIG.load().keyring.clone().unwrap_or_default(); if let Some(keyring) = keyring_from_base64(&kr_str) { if let Ok(encrypted_challenge) = diff --git a/type-maps.yaml b/type-maps.yaml index 5cd196f..7fecac8 100644 --- a/type-maps.yaml +++ b/type-maps.yaml @@ -1,46 +1,6 @@ # The version a Client should use protocol_version: "1.0" -# Note that markers 0 to 31 are reserved for default use, manually working with them is not recommended -# Fixed CommunicationType markers are: -# Error: 0 -# ErrorParsing: 1 -# ErrorBadVersion: 2 -# Disconnect: 3 -# Redirect: 4 -# Shutdown: 5 -# BadRequest: 6 -# Unauthorized: 7 -# Forbidden: 8 -# NotFound: 9 -# TooManyRequests: 10 -# InternalServerError: 11 -# BadGateway: 12 -# ServiceUnavailable: 13 -# GatewayTimeout: 14 -# Identification: 15 -# IdentificationResponse: 16 -# Register: 17 -# RegisterResponse: 18 -# Ping: 19 -# Pong: 20 -# -# Fixed Data Type markers are: -# Error: 0 -# ErrorParsing: 1 -# ErrorMessage: 2 -# Version: 3 -# Description: 4 -# Timestamp: 5 -# Id: 6 -# ClientNonce: 7 -# ServerNonce: 8 -# PublicKeys: 9 -# Signature: 10 -# Connected: 11 -# -# If a Type can't be used it will be mapped to 0 - type_maps: "1.0": # Protocol version 1.0 CommunicationTypes: diff --git a/web-ui/src/api.rs b/web-ui/src/api.rs index 6cbdf78..2114029 100755 --- a/web-ui/src/api.rs +++ b/web-ui/src/api.rs @@ -1,6 +1,6 @@ use crate::server::is_local_network; use actix_web::{HttpRequest, HttpResponse, Responder, web}; -use iota_storage::util::config_util::CONFIG; +use iota_storage::util::config_util::{modify_config, CONFIG}; use serde_json::{Value, json}; use std::net::SocketAddr; @@ -29,12 +29,25 @@ async fn settings_set(req: HttpRequest, ssl: web::Data) -> impl Responder match (key, value) { (Some(k), Some(v)) => { - let _ = CONFIG - .write() - .await - .config - .insert(&k.to_string(), v.to_string()); - + modify_config(|cfg| match k { + "port" => { + if let Ok(port) = v.parse::() { + cfg.port = port; + } + } + "omikron_host" => { + cfg.omikron_host = Some(v.to_string()); + } + "omikron_port" => { + if let Ok(port) = v.parse::() { + cfg.omikron_port = Some(port); + } + } + "read_receipts_enabled" => { + cfg.read_receipts_enabled = v == "true"; + } + _ => {} + }); success() } _ => error(), @@ -45,8 +58,7 @@ async fn settings_get(req: HttpRequest, ssl: web::Data) -> impl Responder if !is_allowed_req(&req, *ssl.get_ref()) { return forbidden(); } - let config = CONFIG.read().await.config.clone(); - let serde_config: Value = serde_json::to_value(config.to_string()).unwrap(); + let serde_config: Value = serde_json::to_value(&**CONFIG.load()).unwrap(); HttpResponse::Ok().json(serde_config) }