From e15b2b407ac6923e2732c7411e7b10caab4acd93 Mon Sep 17 00:00:00 2001 From: Alex Emmet Date: Sat, 30 Aug 2025 22:37:16 +0200 Subject: [PATCH] basic creation --- Cargo.lock | 307 ++++++++++++++++++++++++++++++- Cargo.toml | 17 +- src/data/communication.rs | 243 ++++++++++++++++++++++++ src/data/mod.rs | 1 + src/main.rs | 15 +- src/omikron/mod.rs | 1 + src/omikron/omikronConnection.rs | 147 +++++++++++++++ src/users/contact.rs | 0 src/users/mod.rs | 1 + src/util/chatFiles.rs | 212 +++++++++++++++++++++ src/util/chatsUtil.rs | 91 +++++++++ src/util/configUtil.rs | 65 +++++++ src/util/fileUtil.rs | 162 ++++++++++++++++ src/util/mod.rs | 4 + 14 files changed, 1258 insertions(+), 8 deletions(-) create mode 100644 src/data/communication.rs create mode 100644 src/data/mod.rs create mode 100644 src/omikron/mod.rs create mode 100644 src/omikron/omikronConnection.rs create mode 100644 src/users/contact.rs create mode 100644 src/users/mod.rs create mode 100644 src/util/chatFiles.rs create mode 100644 src/util/chatsUtil.rs create mode 100644 src/util/configUtil.rs create mode 100644 src/util/fileUtil.rs create mode 100644 src/util/mod.rs diff --git a/Cargo.lock b/Cargo.lock index bbdc698..4c74a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,8 +7,11 @@ name = "Iota" version = "0.1.0" dependencies = [ "axum", + "futures-util", "json", + "tokio", "tokio-tungstenite", + "uuid", ] [[package]] @@ -32,6 +35,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "axum" version = "0.8.4" @@ -116,12 +125,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.3" @@ -193,6 +217,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -212,6 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-sink", "futures-task", "pin-project-lite", @@ -229,6 +265,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -347,6 +394,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "json" version = "0.12.4" @@ -359,6 +416,16 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.27" @@ -400,7 +467,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -418,6 +485,29 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -495,7 +585,30 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] @@ -504,6 +617,39 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -516,6 +662,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.219" @@ -581,6 +733,21 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.11" @@ -600,9 +767,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.59.0", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.106" @@ -651,11 +824,13 @@ dependencies = [ "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "slab", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -669,6 +844,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.27.0" @@ -677,8 +862,12 @@ checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" dependencies = [ "futures-util", "log", + "rustls", + "rustls-pki-types", "tokio", + "tokio-rustls", "tungstenite", + "webpki-roots 0.26.11", ] [[package]] @@ -741,6 +930,8 @@ dependencies = [ "httparse", "log", "rand", + "rustls", + "rustls-pki-types", "sha1", "thiserror", "utf-8", @@ -758,12 +949,29 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "uuid" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "version_check" version = "0.9.5" @@ -785,6 +993,91 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -883,3 +1176,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index a98a334..1cb536f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,19 @@ version = "0.1.0" edition = "2024" [dependencies] -tokio-tungstenite = "*" json = "*" -axum = "*" \ No newline at end of file +axum = "*" +futures-util = "*" + +[dependencies.tokio-tungstenite] +version = "*" +default-features = false +features = ["rustls-tls-webpki-roots"] + +[dependencies.tokio] +version = "*" +features = ["full"] + +[dependencies.uuid] +version = "*" +features = ["v4"] diff --git a/src/data/communication.rs b/src/data/communication.rs new file mode 100644 index 0000000..53bf692 --- /dev/null +++ b/src/data/communication.rs @@ -0,0 +1,243 @@ +use json::{object, JsonValue}; +use std::collections::HashMap; +use uuid::Uuid; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum DataTypes { + ErrorType, + ChatPartnerId, + IotaId, + UserId, + UserIds, + UserState, + UserStates, + UserPings, + CallState, + ScreenShare, + PrivateKeyHash, + Accepted, + AcceptedProfiles, + DeniedProfiles, + MessageContent, + MessageChunk, + SendTime, + GetTime, + GetVariant, + SharedSecretOwn, + SharedSecretOther, + SharedSecretSign, + SharedSecret, + CallId, + CallName, + CallSecretSha, + CallSecret, + SharedCallSecret, + StartDate, + EndDate, + ReceiverId, + SenderId, + Signature, + Signed, + Message, + LastPing, + PingIota, + PingClients, + Matches, + Omikron, + LoadedMessages, + MessageAmount, + Position, + Name, + Path, + Codec, + Function, + Payload, + Result, + Interactables, + WantToWatch, + Watcher, + CreatedAt, + Username, + Display, + Avatar, + About, + Status, + PublicKey, + SubLevel, + SubEnd, + CommunityAddress, + Challenge, + CommunityTitle, + Communities, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CommunicationType { +Error, + Success, + Message, + MessageLive, + MessageOtherIota, + MessageChunk, + MessageGet, + ChangeConfirm, + ConfirmReceive, + ConfirmRead, + GetChats, + GetStates, + AddCommunity, + RemoveCommunity, + GetCommunities, + Challenge, + ChallengeResponse, + Register, + RegisterResponse, + Identification, + IdentificationResponse, + Ping, + Pong, + AddChat, + SendChat, + IotaConnected, + IotaClosed, + ClientChanged, + ClientConnected, + ClientClosed, + PublicKey, + PrivateKey, + WebrtcSdp, + WebrtcIce, + StartStream, + EndStream, + WatchStream, + GetCall, + NewCall, + CallInvite, + EndCall, + Function, + Update, +} +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LogLevel { + Important = 2, + Normal = 1, + Debug = 0, + None = -1, + DebugOnly = -2, +} + +#[derive(Debug, Clone)] +pub struct LogValue { + pub message: String, + pub log_level: LogLevel, +} + +impl LogValue { + pub fn new(message: impl Into, log_level: LogLevel) -> Self { + Self { + message: message.into(), + log_level, + } + } + + pub fn to_json(&self) -> JsonValue { + object! { + message: self.message.clone(), + log_level: self.log_level.clone() as i32 + } + } +} + +#[derive(Debug, Clone)] +pub struct CommunicationValue { + pub id: Uuid, + pub comm_type: CommunicationType, + pub log_value: Option, + pub sender: Option, + pub receiver: Option, + pub data: HashMap, +} + +impl CommunicationValue { + pub fn new(comm_type: CommunicationType) -> Self { + Self { + id: Uuid::new_v4(), + comm_type, + log_value: None, + sender: None, + receiver: None, + data: HashMap::new(), + } + } + + pub fn with_log(mut self, log: LogValue) -> Self { + self.log_value = Some(log); + self + } + + pub fn with_sender(mut self, sender: Uuid) -> Self { + self.sender = Some(sender); + self + } + + pub fn with_receiver(mut self, receiver: Uuid) -> Self { + self.receiver = Some(receiver); + self + } + + pub fn add_data(mut self, key: DataTypes, value: JsonValue) -> Self { + self.data.insert(key, value); + self + } + + pub fn to_json(&self) -> JsonValue { + let mut jdata = object!{}; + for (k, v) in &self.data { + jdata[&format!("{:?}", k)] = v.clone(); + } + + object! { + id: self.id.to_string(), + type: format!("{:?}", self.comm_type), + sender: self.sender.map(|u| u.to_string()).unwrap_or_default(), + receiver: self.receiver.map(|u| u.to_string()).unwrap_or_default(), + log: self.log_value.as_ref().map(|l| l.to_json()).unwrap_or(JsonValue::Null), + data: jdata + } + } + + pub fn from_json(json_str: &str) -> Result { + let parsed = json::parse(json_str).map_err(|e| e.to_string())?; + + let message_id = parsed["id"].as_str() + .and_then(|s| Uuid::parse_str(s).ok()) + .unwrap_or_else(|| Uuid::new_v4()); + + let comm_type = match parsed["type"].as_str() { + Some("Message") => CommunicationType::Message, + Some("Success") => CommunicationType::Success, + _ => CommunicationType::Error, + }; + + let sender = parsed["sender"].as_str().and_then(|s| Uuid::parse_str(s).ok()); + let receiver = parsed["receiver"].as_str().and_then(|s| Uuid::parse_str(s).ok()); + + let log_value = if parsed["log"].is_object() { + Some(LogValue::new( + parsed["log"]["message"].as_str().unwrap_or("").to_string(), + LogLevel::Normal, + )) + } else { + None + }; + + Ok(Self { + id: message_id, + comm_type, + log_value, + sender, + receiver, + data: HashMap::new(), + }) + } +} \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..7575266 --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1 @@ +pub mod communication; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4a35b6d..fd2a7d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,16 @@ -use json; +use json::{self, JsonValue}; +use uuid::Uuid; + +mod data; +mod omikron; + +use crate::omikron::omikronConnection::{OmikronConnection}; +use crate::data::communication::{CommunicationValue, LogLevel, LogValue, CommunicationType, DataTypes}; fn main() { - println!("Hello, world!"); + let rt = tokio::runtime::Runtime::new().unwrap(); + let omikron = OmikronConnection::new(); + rt.block_on(async { + omikron.connect().await; + }); } diff --git a/src/omikron/mod.rs b/src/omikron/mod.rs new file mode 100644 index 0000000..e27e32b --- /dev/null +++ b/src/omikron/mod.rs @@ -0,0 +1 @@ +pub mod omikronConnection; \ No newline at end of file diff --git a/src/omikron/omikronConnection.rs b/src/omikron/omikronConnection.rs new file mode 100644 index 0000000..343a25a --- /dev/null +++ b/src/omikron/omikronConnection.rs @@ -0,0 +1,147 @@ +use futures_util::{SinkExt, StreamExt}; +use tokio_tungstenite::tungstenite::Utf8Bytes; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::Mutex; +use tokio::net::TcpStream; +use tokio::time::{sleep, Duration}; +use tokio_tungstenite::{ + client_async, + tungstenite::protocol::Message, + MaybeTlsStream, + WebSocketStream, +}; +use uuid::Uuid; + +#[derive(Clone)] +pub struct OmikronConnection { + writer: Arc>, Message>>>>, + waiting: Arc>>>, +} + +impl OmikronConnection { + pub fn new() -> Self { + Self { + writer: Arc::new(Mutex::new(None)), + waiting: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub async fn connect(&self) { + loop { + match connect_async("wss://tensamin.methanium.net/ws/iota/").await { + Ok((ws_stream, _)) => { + println!("[Omikron] Connected to server"); + + // Split into writer + reader + let (write_half, read_half) = ws_stream.split(); + *self.writer.lock().await = Some(write_half); + + // Spawn listener with read_half + self.spawn_listener(read_half); + break; + } + Err(e) => { + println!( + "[Omikron] Connection failed: {}. Retrying in 2s...", + e + ); + sleep(Duration::from_secs(2)).await; + } + } + } + } + + pub async fn close(&self) { + let mut writer = self.writer.lock().await; + if let Some(mut ws) = writer.take() { + let _ = ws.close().await; + } + println!("[Omikron] Connection closed"); + } + + fn spawn_listener( + &self, + mut read_half: futures_util::stream::SplitStream>>, + ) { + let waiting = self.waiting.clone(); + + tokio::spawn(async move { + while let Some(msg) = read_half.next().await { + match msg { + Ok(Message::Text(text)) => { + println!("[Omikron] Message received: {}", text); + + if text.contains("\"type\":\"pong\"") { + println!("[Omikron] Pong received"); + } + + // Example: trigger callback if message_id is present + if let Some(id_pos) = text.find("\"message_id\":\"") { + let s = &text[id_pos + 14..]; + if let Some(end) = s.find('"') { + let mid = &s[..end]; + if let Ok(uuid) = Uuid::parse_str(mid) { + if let Some(callback) = + waiting.lock().await.remove(&uuid) + { + callback(text.to_string()); + } + } + } + } + } + Ok(Message::Close(frame)) => { + println!("[Omikron] Closed: {:?}", frame); + break; + } + Err(e) => { + println!("[Omikron] Error: {}", e); + break; + } + _ => {} + } + } + }); + } + + pub async fn send_message(&self, msg: &str) { + let mut guard = self.writer.lock().await; + if let Some(writer) = guard.as_mut() { + let utf8: Utf8Bytes = Utf8Bytes::from(msg.to_string()); + if let Err(e) = writer.send(Message::Text(utf8)).await { + println!("[Omikron] Send failed: {}", e); + } + } + } + + pub fn on_answer(&self, message_id: Uuid, callback: F) + where + F: Fn(String) + Send + 'static, + { + tokio::spawn({ + let waiting = self.waiting.clone(); + async move { + waiting.lock().await.insert(message_id, Box::new(callback)); + } + }); + } +} + +#[tokio::main] +async fn main() { + let omikron = OmikronConnection::new(); + omikron.connect().await; + + let identification = r#"{ + "type": "identification", + "iota_id": "iota-12345", + "user_ids": ["user1", "user2"] + }"#; + + omikron.send_message(identification).await; + + loop { + sleep(Duration::from_secs(30)).await; + } +} diff --git a/src/users/contact.rs b/src/users/contact.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/users/mod.rs b/src/users/mod.rs new file mode 100644 index 0000000..9fd92c1 --- /dev/null +++ b/src/users/mod.rs @@ -0,0 +1 @@ +pub mod contact; \ No newline at end of file diff --git a/src/util/chatFiles.rs b/src/util/chatFiles.rs new file mode 100644 index 0000000..0852f29 --- /dev/null +++ b/src/util/chatFiles.rs @@ -0,0 +1,212 @@ + +use json::{self, Value}; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; +use uuid::Uuid; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MessageState { + Read, + Received, + Sending, + Error, +} + +impl MessageState { + fn as_str(&self) -> &'static str { + match self { + MessageState::Read => "READ", + MessageState::Received => "RECEIVED", + MessageState::Sending => "SENDING", + MessageState::Error => "ERROR", + } + } +} + +pub struct ChatFiles; + +impl ChatFiles { + fn load_file(dir: &str, file_name: &str) -> String { + let path = Path::new(dir).join(file_name); + if let Ok(mut f) = File::open(&path) { + let mut content = String::new(); + let _ = f.read_to_string(&mut content); + content + } else { + String::new() + } + } + + fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> { + fs::create_dir_all(dir)?; + let path = Path::new(dir).join(file_name); + let mut file = File::create(path)?; + file.write_all(content.as_bytes()) + } + + pub fn add_message( + send_time: i64, + storage_owner_is_sender: bool, + storage_owner: Uuid, + external_user: Uuid, + message: &str, + ) -> std::io::Result<()> { + let user_dir = format!("users/{}/chats/{}", storage_owner, external_user); + + let mut chunk_index = 0; + let mut message_chunk = array![]; + + // find latest chunk not full (max 800 msgs) + loop { + let file_name = format!("msgs_{}.json", chunk_index); + let file_content = Self::load_file(&user_dir, &file_name); + + if !file_content.is_empty() { + if let Ok(current_chunk) = json::parse(&file_content) { + if current_chunk.len() < 800 { + message_chunk = current_chunk; + break; + } + } + } else { + break; + } + chunk_index += 1; + } + + let json_obj = object! { + "message_time" => send_time, + "message_content" => message, + "sender_is_me" => storage_owner_is_sender, + "message_state" => MessageState::Sending.as_str() + }; + message_chunk.push(json_obj).unwrap(); + + Self::save_file(&user_dir, &format!("msgs_{}.json", chunk_index), &message_chunk.dump()) + } + + pub fn change_message_state( + storage_owner: Uuid, + external_user: Uuid, + timestamp: i64, + new_state: MessageState, + ) -> std::io::Result<()> { + let user_dir = format!("users/{}/chats/{}", storage_owner, external_user); + let path = Path::new(&user_dir); + + if !path.exists() { + return Ok(()); + } + + let entries = fs::read_dir(path)?; + for entry in entries { + let entry = entry?; + let fname = entry.file_name(); + let fname_str = fname.to_string_lossy(); + + if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") { + let file_content = Self::load_file(&user_dir, &fname_str); + if file_content.is_empty() { + continue; + } + + if let Ok(mut chunk) = json::parse(&file_content) { + let mut modified = false; + for i in 0..chunk.len() { + if chunk[i]["message_time"].as_i64() == Some(timestamp) { + chunk[i]["message_state"] = JsonValue::from(new_state.as_str()); + modified = true; + break; + } + } + + if modified { + Self::save_file(&user_dir, &fname_str, &chunk.dump())?; + break; + } + } + } + } + Ok(()) + } + + pub fn get_messages( + storage_owner: Uuid, + external_user: Uuid, + loaded_messages: usize, + amount: usize, + ) -> JsonValue { + let user_dir = format!("users/{}/chats/{}", storage_owner, external_user); + let path = Path::new(&user_dir); + let mut messages = array![]; + + if !path.exists() { + return messages; + } + + let mut latest_chunk_index: i32 = -1; + if let Ok(entries) = fs::read_dir(path) { + for entry in entries.flatten() { + let fname = entry.file_name(); + let fname_str = fname.to_string_lossy(); + if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") { + if let Some(num) = fname_str + .strip_prefix("msgs_") + .and_then(|s| s.strip_suffix(".json")) + { + if let Ok(index) = num.parse::() { + if index > latest_chunk_index { + latest_chunk_index = index; + } + } + } + } + } + } + + if latest_chunk_index == -1 { + return messages; + } + + let mut to_skip = loaded_messages; + let mut needed = amount; + + for chunk_index in (0..=latest_chunk_index).rev() { + if needed == 0 { + break; + } + let file_name = format!("msgs_{}.json", chunk_index); + let file_content = Self::load_file(&user_dir, &file_name); + if file_content.is_empty() { + continue; + } + if let Ok(chunk) = json::parse(&file_content) { + for i in (0..chunk.len()).rev() { + if needed == 0 { + break; + } + if to_skip > 0 { + to_skip -= 1; + continue; + } + messages.push(chunk[i].clone()).unwrap(); + needed -= 1; + } + } + } + + messages + } +} + +fn main() { + let owner = Uuid::new_v4(); + let external = Uuid::new_v4(); + + ChatFiles::add_message(1234567890, true, owner, external, "Hello!").unwrap(); + ChatFiles::change_message_state(owner, external, 1234567890, MessageState::Read).unwrap(); + let msgs = ChatFiles::get_messages(owner, external, 0, 10); + + println!("Messages: {}", msgs.dump()); +} diff --git a/src/util/chatsUtil.rs b/src/util/chatsUtil.rs new file mode 100644 index 0000000..d285c61 --- /dev/null +++ b/src/util/chatsUtil.rs @@ -0,0 +1,91 @@ + +use json::{self, Value}; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; +use uuid::Uuid; + +use crate::users::Contact::Contact; // assuming you have a Contact struct in a module + +pub struct ChatsUtil; + +impl ChatsUtil { + fn load_file(dir: &str, file_name: &str) -> String { + let path = Path::new(dir).join(file_name); + if let Ok(mut f) = File::open(&path) { + let mut content = String::new(); + let _ = f.read_to_string(&mut content); + content + } else { + String::new() + } + } + + fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> { + fs::create_dir_all(dir)?; + let path = Path::new(dir).join(file_name); + let mut file = File::create(path)?; + file.write_all(content.as_bytes()) + } + + pub fn mod_user(storage_owner: Uuid, contact: Contact) -> std::io::Result<()> { + let dir = format!("users/{}/contacts/", storage_owner); + let file_name = "contacts.json"; + let s = Self::load_file(&dir, file_name); + + let mut contacts = if !s.is_empty() { + json::parse(&s).unwrap_or(array![]) + } else { + array![] + }; + + for i in 0..contacts.len() { + if contacts[i]["userID"].as_str() == Some(&contact.user_id.to_string()) { + contacts.remove(i); + break; + } + } + + contacts.push(contact.to_json()).unwrap(); + Self::save_file(&dir, file_name, &contacts.dump()) + } + + pub fn get_user(storage_owner: Uuid, user_id: Uuid) -> Option { + let dir = format!("users/{}/contacts/", storage_owner); + let file_name = "contacts.json"; + let s = Self::load_file(&dir, file_name); + if s.is_empty() { + return None; + } + + if let Ok(contacts) = json::parse(&s) { + for i in 0..contacts.len() { + if let Some(uid) = contacts[i]["userID"].as_str() { + if Uuid::parse_str(uid).ok()? == user_id { + return Contact::from_json(&contacts[i]); + } + } + } + } + None + } + + pub fn get_users(storage_owner: Uuid) -> Value { + let dir = format!("/users/{}/contacts/", storage_owner); + let file_name = "contacts.json"; + let s = Self::load_file(&dir, file_name); + + let mut contacts_out = array![]; + if !s.is_empty() { + if let Ok(contacts) = json::parse(&s) { + for i in 0..contacts.len() { + if let Some(c) = Contact::from_json(&contacts[i]) { + contacts_out.push(c.info()).unwrap(); + } + } + } + } + + contacts_out + } +} \ No newline at end of file diff --git a/src/util/configUtil.rs b/src/util/configUtil.rs new file mode 100644 index 0000000..0b74673 --- /dev/null +++ b/src/util/configUtil.rs @@ -0,0 +1,65 @@ +use json::JsonValue; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; + +use crate::files::Files; + +pub struct ConfigUtil { + pub config: JsonValue, + pub unique: bool, +} + +impl ConfigUtil { + pub fn new() -> Self { + Self { + config: JsonValue::new_object(), + unique: false, + } + } + + fn load_file(path: &str) -> String { + if let Ok(mut f) = File::open(path) { + let mut content = String::new(); + let _ = f.read_to_string(&mut content); + content + } else { + String::new() + } + } + + fn save_file(path: &str, content: &str) -> std::io::Result<()> { + if let Some(parent) = Path::new(path).parent() { + fs::create_dir_all(parent)?; + } + let mut file = File::create(path)?; + file.write_all(content.as_bytes()) + } + + pub fn load(&mut self) { + let s = Self::load_file(Files::MAIN); + if !s.is_empty() { + self.config = json::parse(&s).unwrap_or(JsonValue::new_object()); + } + if self.config.has("port") { + if let Some(port) = self.config["port"].as_i32() { + // Set community manager port + } + } + } + + pub fn change(&mut self, key: &str, value: JsonValue) { + self.config[key] = value; + self.unique = true; + } + + pub fn update(&mut self) { + if self.unique { + let _ = self.save(); + } + } + + pub fn save(&self) -> std::io::Result<()> { + Self::save_file(Files::MAIN, &self.config.dump()) + } +} diff --git a/src/util/fileUtil.rs b/src/util/fileUtil.rs new file mode 100644 index 0000000..5fd3d13 --- /dev/null +++ b/src/util/fileUtil.rs @@ -0,0 +1,162 @@ +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::time::SystemTime; +use std::ffi::OsStr; +use std::process; +use walkdir::WalkDir; +use std::fmt::Write as FmtWrite; +use uuid::Uuid; + +pub struct FileUtil; + +impl FileUtil { + pub fn delete_file(path: &str, name: &str) -> bool { + let dir = Path::new(&Self::get_jar_directory()).join(path); + let file = dir.join(name); + if !file.exists() { + return false; + } + fs::remove_file(file).is_ok() + } + + pub fn delete_directory(path: &str) -> bool { + let dir = Path::new(&Self::get_jar_directory()).join(path); + Self::delete_dir_recursive(&dir) + } + + fn delete_dir_recursive(directory: &Path) -> bool { + if !directory.exists() { + return false; + } + if let Err(e) = fs::remove_dir_all(directory) { + println!("[IMPORTANT] Couldn't delete directory {}: {}", directory.display(), e); + return false; + } + true + } + + pub fn delete_user_directory(user_id: Uuid) { + let user_dir = Path::new(&Self::get_jar_directory()) + .join("users") + .join(user_id.to_string()); + let _ = Self::delete_dir_recursive(&user_dir); + } + + pub fn load_file(path: &str, name: &str) -> String { + let dir = Path::new(&Self::get_jar_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + println!("[IMPORTANT] Couldn't create directories: {}", e); + return String::new(); + } + return String::new(); + } + + if !file_path.exists() { + if let Err(e) = File::create(&file_path) { + println!("[IMPORTANT] Couldn't create file: {}", e); + } + return String::new(); + } + + let mut content = String::new(); + if let Ok(mut f) = File::open(&file_path) { + let _ = f.read_to_string(&mut content); + } + content + } + + pub fn save_file(path: &str, name: &str, value: &str) { + let dir = Path::new(&Self::get_jar_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + println!("[IMPORTANT] Couldn't create directories: {}", e); + return; + } + } + + if let Err(e) = fs::write(&file_path, value) { + println!("[IMPORTANT] Couldn't write file {}: {}", file_path.display(), e); + } + } + + pub fn get_jar_directory() -> String { + // In Rust, use current_exe as a proxy for JAR directory + let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); + exe.parent() + .unwrap_or(Path::new(".")) + .to_string_lossy() + .to_string() + } + + pub fn used_space() -> u64 { + Self::get_directory_size(&PathBuf::from(Self::get_jar_directory())) + } + + pub fn get_directory_size(directory: &Path) -> u64 { + let mut size = 0; + for entry in WalkDir::new(directory).into_iter().filter_map(|e| e.ok()) { + let path = entry.path(); + if path.is_file() { + if let Ok(metadata) = path.metadata() { + size += path + .file_name() + .unwrap_or(OsStr::new("")) + .len() as u64; + size += metadata.len(); + } + } + } + size + } + + pub fn get_designed_storage(user_id: Uuid) -> String { + let user_dir = Path::new(&Self::get_jar_directory()) + .join("users") + .join(user_id.to_string()); + Self::design_byte(Self::get_directory_size(&user_dir)) + } + + pub fn design_byte(bytes: u64) -> String { + let mut hr_size = format!("{:.2}B", bytes as f64); + let k = bytes as f64 / 1024.0; + let m = k / 1024.0; + let g = m / 1024.0; + let t = g / 1024.0; + + if t >= 1.0 { + hr_size = format!("{:.2}TB", t); + } else if g >= 1.0 { + hr_size = format!("{:.2}GB", g); + } else if m >= 1.0 { + hr_size = format!("{:.2}MB", m); + } else if k >= 1.0 { + hr_size = format!("{:.2}KB", k); + } + hr_size + } + + pub fn get_used_ram() -> String { + // Rust has no direct Runtime like Java. + // As a placeholder, read process memory usage from sysinfo. + use sysinfo::{System, SystemExt, ProcessExt}; + + let mut sys = System::new_all(); + sys.refresh_all(); + if let Some(process) = sys.process(process::id() as i32) { + let used = process.memory() * 1024; // kB to bytes + let total = sys.total_memory() * 1024; + return format!( + "{}/{}", + Self::design_byte(used), + Self::design_byte(total) + ); + } + "Unknown".to_string() + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..0b11ad1 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,4 @@ +pub mod fileUtil; +pub mod chatFiles; +pub mod configUtil; +pub mod chatsUtil; \ No newline at end of file