diff --git a/Cargo.lock b/Cargo.lock index d6f18fa..1a26388 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,9 +7,13 @@ name = "Iota" version = "0.1.0" dependencies = [ "axum", + "base64", "cmake", "futures-util", "json", + "once_cell", + "rand", + "reqwest", "rustls", "serde", "sysinfo", @@ -115,6 +119,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.9.3" @@ -172,6 +182,16 @@ dependencies = [ "cc", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -238,18 +258,75 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -351,6 +428,31 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + [[package]] name = "http" version = "1.3.1" @@ -407,6 +509,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -416,6 +519,39 @@ dependencies = [ "pin-utils", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -424,14 +560,141 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ + "base64", "bytes", + "futures-channel", "futures-core", + "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", + "system-configuration", "tokio", "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +dependencies = [ + "equivalent", + "hashbrown", ] [[package]] @@ -445,6 +708,22 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itoa" version = "1.0.15" @@ -473,6 +752,18 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.13" @@ -527,6 +818,23 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -551,6 +859,50 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.4" @@ -592,6 +944,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -684,6 +1051,46 @@ dependencies = [ "bitflags", ] +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -704,6 +1111,19 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.31" @@ -759,12 +1179,44 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.219" @@ -867,6 +1319,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "subtle" version = "2.6.1" @@ -889,6 +1347,20 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "sysinfo" @@ -905,6 +1377,40 @@ dependencies = [ "windows", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -925,6 +1431,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.47.1" @@ -956,6 +1472,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" @@ -982,6 +1508,19 @@ dependencies = [ "webpki-roots 0.26.11", ] +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.2" @@ -998,6 +1537,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1030,6 +1587,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.23.0" @@ -1068,12 +1631,30 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.18.0" @@ -1085,6 +1666,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1101,6 +1688,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1142,6 +1738,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -1174,6 +1783,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -1242,6 +1861,41 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1330,6 +1984,36 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -1350,8 +2034,62 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 571230b..4594042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,7 @@ uuid = { version = "*", features = ["v4"] } walkdir = "2.5.0" sysinfo = "*" serde = { version = "1.0.219", features = ["derive"] } +rand = "0.8.5" +base64 = "0.22.1" +once_cell = "1.21.3" +reqwest = "0.12.23" diff --git a/src/auth/auth_connector.rs b/src/auth/auth_connector.rs index 97039b2..efea34c 100644 --- a/src/auth/auth_connector.rs +++ b/src/auth/auth_connector.rs @@ -1,9 +1,10 @@ use std::time::Duration; -use std::collections::HashMap; -use uuid::Uuid; -use reqwest::blocking::{Client, Response}; +use uuid::{uuid, Uuid}; use reqwest::header::CONTENT_TYPE; use json::JsonValue; +use reqwest::{Client, Response}; +use crate::users::user_profile::UserProfile; +use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; #[derive(Debug, Clone)] pub struct AuthUser { @@ -29,8 +30,8 @@ impl AuthConnector { .unwrap() } - pub fn unregister_user(auth_server: &str, user_id: Uuid, reset_token: &str) -> bool { - let url = format!("https://{}/api/delete/{}", auth_server, user_id); + pub async fn unregister_user(user_id: Uuid, reset_token: &str) -> Option { + let url = format!("https:/auth.tensamin.methanium.net//api/delete/{}/", user_id); let client = Self::client(); let mut payload = JsonValue::new_object(); @@ -38,64 +39,61 @@ impl AuthConnector { let res = client.post(&url) .header(CONTENT_TYPE, "application/json") - .body(payload.dump()) - .send(); - - match res { - Ok(resp) => Self::handle_response(resp), - Err(_) => false, - } + .body(payload.dump()).send().await.ok()?; + let json = res.text().await.ok()?; + let cv = CommunicationValue::from_json(&json); + Option::from(cv.is_type(CommunicationType::Success)) + } - pub fn get_uuid(auth_server: &str, username: &str) -> Option { - let url = format!("https://{}/api/get/uuid/{}", auth_server, username); + pub async fn get_uuid(username: &str) -> Option { + let url = format!("https://auth.tensamin.methanium.net/api/get/uuid/{}/", username); let client = Self::client(); - let res = client.get(&url).send().ok()?; - let json = res.text().ok()?; - - let cv = CommunicationValue::from_string(&json); - if !cv.is_success() { + let res = client.get(&url).send().await.ok()?; + let json = res.text().await.ok()?; + let mut cv = CommunicationValue::from_json(&json); + if !cv.is_type(CommunicationType::Success) { return None; } - cv.get_user_id() + Uuid::parse_str(cv.get_data(DataTypes::UserId).unwrap()).ok() } - - pub fn get_user(auth_server: &str, user_id: Uuid) -> Option { - let url = format!("https://{}/api/get/{}", auth_server, user_id); + + pub async fn get_user(user_id: Uuid) -> Option { + let url = format!("https://auth.tensamin.methanium.net/api/get/{}/", user_id); let client = Self::client(); - let res = client.get(&url).send().ok()?; - let json = res.text().ok()?; - - let cv = CommunicationValue::from_string(&json); - if !cv.is_success() { + let res = client.get(&url).send().await.ok()?; + let json = res.text().await.ok()?; + + let mut cv = CommunicationValue::from_json(&json); + if cv.comm_type != CommunicationType::Success { return None; } Some(AuthUser { - created_at: cv.get_number("created_at")? as i64, - username: cv.get_string("username")?, - display: cv.get_string("display")?, - avatar: cv.get_string("avatar")?, - about: cv.get_string("about")?, - status: cv.get_string("status")?, - public_key: cv.get_string("public_key")?, - sub_level: cv.get_number("sub_level")? as i32, - sub_end: cv.get_number("sub_end")? as i32, + created_at: cv.get_data(DataTypes::CreatedAt).unwrap().to_string().parse::().unwrap_or(-1), + username: cv.get_data(DataTypes::Username).unwrap().to_string(), + display: cv.get_data(DataTypes::Display).unwrap().to_string(), + avatar: cv.get_data(DataTypes::Avatar).unwrap().to_string(), + about: cv.get_data(DataTypes::About).unwrap().to_string(), + status: cv.get_data(DataTypes::Status).unwrap().to_string(), + public_key: cv.get_data(DataTypes::PublicKey).unwrap().to_string(), + sub_level: cv.get_data(DataTypes::SubLevel).unwrap().to_string().parse::().unwrap_or(-1), + sub_end: cv.get_data(DataTypes::SubEnd).unwrap().to_string().parse::().unwrap_or(-1), }) } - pub fn get_register(auth_server: &str) -> Option { - let url = format!("https://{}/api/register/init/", auth_server); + pub async fn get_register() -> Option { + let url = "https://auth.tensamin.methanium.net/api/register/init/".to_string(); let client = Self::client(); - let res = client.get(&url).send().ok()?; - let json = res.text().ok()?; + let res = client.get(&url).send().await.ok()?; + let json = res.text().await.ok()?; - let cv = CommunicationValue::from_string(&json); - cv.get_user_id() + let mut cv = CommunicationValue::from_json(&json); + Uuid::parse_str(cv.get_data(DataTypes::UserId).unwrap()).ok() } - pub fn complete_register(auth_server: &str, user_profile: &UserProfile, iota_id: &str) -> bool { - let url = format!("https://{}/api/register/complete/", auth_server); + pub async fn complete_register(user_profile: &UserProfile, iota_id: &str) -> bool { + let url = "https://auth.tensamin.methanium.net/api/register/complete/"; let client = Self::client(); let mut payload = JsonValue::new_object(); @@ -106,19 +104,24 @@ impl AuthConnector { payload["iota_id"] = iota_id.into(); payload["reset_token"] = user_profile.reset_token.clone().into(); - let res = client.post(&url) + let res = client + .post(url) .header(CONTENT_TYPE, "application/json") .body(payload.dump()) - .send(); + .send() + .await; match res { - Ok(resp) => Self::handle_response(resp), + Ok(resp) => Self::handle_response(resp).await, Err(_) => false, } } - pub fn migrate_user(auth_server: &str, user_profile: &mut UserProfile, iota_id: &str) -> bool { - let url = format!("https://{}/api/change/iota-id/{}", auth_server, user_profile.user_id); + pub async fn migrate_user(user_profile: &mut UserProfile, iota_id: &str) -> bool { + let url = format!( + "https://auth.tensamin.methanium.net/api/change/iota-id/{}", + user_profile.user_id + ); let client = Self::client(); let mut payload = JsonValue::new_object(); @@ -126,48 +129,28 @@ impl AuthConnector { payload["reset_token"] = user_profile.reset_token.clone().into(); payload["new_token"] = user_profile.randomize_reset_token().into(); - let res = client.post(&url) + let res = client + .post(url) .header(CONTENT_TYPE, "application/json") .body(payload.dump()) - .send(); + .send() + .await; match res { - Ok(resp) => Self::handle_response(resp), + Ok(resp) => Self::handle_response(resp).await, Err(_) => false, } } - fn handle_response(resp: Response) -> bool { - if let Ok(text) = resp.text() { - let cv = CommunicationValue::from_string(&text); - return cv.is_success(); + async fn handle_response(resp: Response) -> bool { + match resp.text().await { + Ok(text) => { + let cv = CommunicationValue::from_json(&text); + cv.comm_type == CommunicationType::Success + } + Err(_) => false, } - false } -} -// --- Stubs for other modules --- -pub struct UserProfile { - pub user_id: Uuid, - pub public_key: String, - pub private_key_hash: String, - pub username: String, - pub reset_token: String, -} -impl UserProfile { - pub fn randomize_reset_token(&mut self) -> String { - // stub: generate new token - let new_tok = format!("{}-new", self.reset_token); - self.reset_token = new_tok.clone(); - new_tok - } -} - -pub struct CommunicationValue; -impl CommunicationValue { - pub fn from_string(s: &str) -> Self { Self } - pub fn is_success(&self) -> bool { true } - pub fn get_user_id(&self) -> Option { Some(Uuid::new_v4()) } - pub fn get_string(&self, _k: &str) -> Option { Some("demo".to_string()) } - pub fn get_number(&self, _k: &str) -> Option { Some(123) } -} + +} \ No newline at end of file diff --git a/src/data/communication.rs b/src/data/communication.rs index 53bf692..4a47fb8 100644 --- a/src/data/communication.rs +++ b/src/data/communication.rs @@ -1,8 +1,12 @@ -use json::{object, JsonValue}; +use std::any::Any; +use json::{object, parse, stringify, JsonValue}; use std::collections::HashMap; +use std::str::FromStr; +use std::time::{SystemTime, UNIX_EPOCH}; +use axum::Json; use uuid::Uuid; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Eq, Hash, PartialEq, Debug)] pub enum DataTypes { ErrorType, ChatPartnerId, @@ -71,7 +75,83 @@ pub enum DataTypes { Communities, } -#[derive(Debug, Clone, PartialEq, Eq)] +impl DataTypes { + pub fn parse(p0: String) -> DataTypes { + // normalize: lowercase + remove underscores + let normalized = p0.to_lowercase().replace('_', ""); + + match normalized.as_str() { + "errortype" => DataTypes::ErrorType, + "chatpartnerid" => DataTypes::ChatPartnerId, + "iotaid" => DataTypes::IotaId, + "userid" => DataTypes::UserId, + "userids" => DataTypes::UserIds, + "userstate" => DataTypes::UserState, + "userstates" => DataTypes::UserStates, + "userpings" => DataTypes::UserPings, + "callstate" => DataTypes::CallState, + "screenshare" => DataTypes::ScreenShare, + "privatekeyhash" => DataTypes::PrivateKeyHash, + "accepted" => DataTypes::Accepted, + "acceptedprofiles" => DataTypes::AcceptedProfiles, + "deniedprofiles" => DataTypes::DeniedProfiles, + "messagecontent" => DataTypes::MessageContent, + "messagechunk" => DataTypes::MessageChunk, + "sendtime" => DataTypes::SendTime, + "gettime" => DataTypes::GetTime, + "getvariant" => DataTypes::GetVariant, + "sharedsecretown" => DataTypes::SharedSecretOwn, + "sharedsecretother"=> DataTypes::SharedSecretOther, + "sharedsecretsign" => DataTypes::SharedSecretSign, + "sharedsecret" => DataTypes::SharedSecret, + "callid" => DataTypes::CallId, + "callname" => DataTypes::CallName, + "callsecretsha" => DataTypes::CallSecretSha, + "callsecret" => DataTypes::CallSecret, + "sharedcallsecret" => DataTypes::SharedCallSecret, + "startdate" => DataTypes::StartDate, + "enddate" => DataTypes::EndDate, + "receiverid" => DataTypes::ReceiverId, + "senderid" => DataTypes::SenderId, + "signature" => DataTypes::Signature, + "signed" => DataTypes::Signed, + "message" => DataTypes::Message, + "lastping" => DataTypes::LastPing, + "pingiota" => DataTypes::PingIota, + "pingclients" => DataTypes::PingClients, + "matches" => DataTypes::Matches, + "omikron" => DataTypes::Omikron, + "loadedmessages" => DataTypes::LoadedMessages, + "messageamount" => DataTypes::MessageAmount, + "position" => DataTypes::Position, + "name" => DataTypes::Name, + "path" => DataTypes::Path, + "codec" => DataTypes::Codec, + "function" => DataTypes::Function, + "payload" => DataTypes::Payload, + "result" => DataTypes::Result, + "interactables" => DataTypes::Interactables, + "wanttowatch" => DataTypes::WantToWatch, + "watcher" => DataTypes::Watcher, + "createdat" => DataTypes::CreatedAt, + "username" => DataTypes::Username, + "display" => DataTypes::Display, + "avatar" => DataTypes::Avatar, + "about" => DataTypes::About, + "status" => DataTypes::Status, + "publickey" => DataTypes::PublicKey, + "sublevel" => DataTypes::SubLevel, + "subend" => DataTypes::SubEnd, + "communityaddress" => DataTypes::CommunityAddress, + "challenge" => DataTypes::Challenge, + "communitytitle" => DataTypes::CommunityTitle, + "communities" => DataTypes::Communities, + _ => DataTypes::ErrorType, // fallback if unknown + } + } +} + +#[derive(PartialEq, Debug)] pub enum CommunicationType { Error, Success, @@ -117,7 +197,59 @@ Error, Function, Update, } -#[derive(Debug, Clone, PartialEq, Eq)] +impl CommunicationType { + pub fn parse(p0: String) -> CommunicationType { + let normalized = p0.to_lowercase().replace('_', ""); + + match normalized.as_str() { + "error" => CommunicationType::Error, + "success" => CommunicationType::Success, + "message" => CommunicationType::Message, + "messagelive" => CommunicationType::MessageLive, + "messageotheriota" => CommunicationType::MessageOtherIota, + "messagechunk" => CommunicationType::MessageChunk, + "messageget" => CommunicationType::MessageGet, + "changeconfirm" => CommunicationType::ChangeConfirm, + "confirmreceive" => CommunicationType::ConfirmReceive, + "confirmread" => CommunicationType::ConfirmRead, + "getchats" => CommunicationType::GetChats, + "getstates" => CommunicationType::GetStates, + "addcommunity" => CommunicationType::AddCommunity, + "removecommunity" => CommunicationType::RemoveCommunity, + "getcommunities" => CommunicationType::GetCommunities, + "challenge" => CommunicationType::Challenge, + "challengeresponse" => CommunicationType::ChallengeResponse, + "register" => CommunicationType::Register, + "registerresponse" => CommunicationType::RegisterResponse, + "identification" => CommunicationType::Identification, + "identificationresponse" => CommunicationType::IdentificationResponse, + "ping" => CommunicationType::Ping, + "pong" => CommunicationType::Pong, + "addchat" => CommunicationType::AddChat, + "sendchat" => CommunicationType::SendChat, + "iotaconnected" => CommunicationType::IotaConnected, + "iotaclosed" => CommunicationType::IotaClosed, + "clientchanged" => CommunicationType::ClientChanged, + "clientconnected" => CommunicationType::ClientConnected, + "clientclosed" => CommunicationType::ClientClosed, + "publickey" => CommunicationType::PublicKey, + "privatekey" => CommunicationType::PrivateKey, + "webrtcsdp" => CommunicationType::WebrtcSdp, + "webrtcice" => CommunicationType::WebrtcIce, + "startstream" => CommunicationType::StartStream, + "endstream" => CommunicationType::EndStream, + "watchstream" => CommunicationType::WatchStream, + "getcall" => CommunicationType::GetCall, + "newcall" => CommunicationType::NewCall, + "callinvite" => CommunicationType::CallInvite, + "endcall" => CommunicationType::EndCall, + "function" => CommunicationType::Function, + "update" => CommunicationType::Update, + _ => CommunicationType::Error, // fallback + } + } +} +#[derive(Debug, Clone)] pub enum LogLevel { Important = 2, Normal = 1, @@ -140,22 +272,21 @@ impl LogValue { } } - pub fn to_json(&self) -> JsonValue { + pub fn to_json(self) -> JsonValue { object! { message: self.message.clone(), - log_level: self.log_level.clone() as i32 + log_level: self.log_level as i32 } } } - -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct CommunicationValue { pub id: Uuid, pub comm_type: CommunicationType, pub log_value: Option, pub sender: Option, pub receiver: Option, - pub data: HashMap, + pub data: HashMap, } impl CommunicationValue { @@ -169,31 +300,49 @@ impl CommunicationValue { data: HashMap::new(), } } - + pub fn with_id(mut self, p0: Uuid) -> Self{ + self.id = p0; + self + } + pub fn get_id(&self) -> Uuid{ + self.id.clone() + } pub fn with_log(mut self, log: LogValue) -> Self { self.log_value = Some(log); self } - + pub fn get_log(&self) -> &Option { + &self.log_value + } pub fn with_sender(mut self, sender: Uuid) -> Self { self.sender = Some(sender); self } - + pub fn get_sender(&self) -> Option { + self.sender.clone() + } 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 { + pub fn get_receiver(&self) -> Option { + self.receiver.clone() + } + pub fn add_data(mut self, key: DataTypes, value: String) -> Self { self.data.insert(key, value); self } + pub fn get_data(&mut self, key: DataTypes) -> Option<&String> { + self.data.get(&key) + } + pub(crate) fn is_type(&self, p0: CommunicationType) -> bool { + self.comm_type == p0 + } pub fn to_json(&self) -> JsonValue { let mut jdata = object!{}; for (k, v) in &self.data { - jdata[&format!("{:?}", k)] = v.clone(); + jdata[&format!("{:?}", k)] = JsonValue::from(v.clone()); } object! { @@ -201,23 +350,15 @@ impl CommunicationValue { 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), + log: self.log_value.as_ref().map(|l| l.clone().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())?; + pub fn from_json(json_str: &str) -> Self { + let parsed = parse(json_str).unwrap(); - 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 comm_type = CommunicationType::parse(parsed["type"].to_string()); 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()); @@ -230,14 +371,52 @@ impl CommunicationValue { } else { None }; - - Ok(Self { - id: message_id, + let uuid = Uuid::parse_str(parsed["id"].as_str().unwrap_or("")).unwrap_or(Uuid::new_v4()); + let mut data = HashMap::new(); + if parsed["data"].is_object() { + for (k, v) in parsed["data"].entries() { + if let Some(val) = v.as_str() { + data.insert(DataTypes::parse(k.to_string()), val.to_string()); + } + } + } + Self { + id: uuid, comm_type, log_value, sender, receiver, - data: HashMap::new(), - }) + data, + } + } + pub fn ack_message(message_id: Uuid, sender: Option) -> CommunicationValue { + let mut cv = CommunicationValue::new(CommunicationType::Message) + .with_id(message_id); + + if let Some(s) = sender { + cv = cv.add_data(DataTypes::SenderId, s.to_string()); + } + cv + } + pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue { + let receiver = Uuid::from_str(original.get_data(DataTypes::ReceiverId).unwrap()).ok() + .or(Option::from(Uuid::nil())); + + let now_ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64; + + let mut cv = CommunicationValue::new(CommunicationType::MessageOtherIota) + .with_id(original.get_id()) + .with_receiver(receiver.unwrap()) + .add_data(DataTypes::SendTime, now_ms.to_string()) + .add_data(DataTypes::MessageContent, original.get_data(DataTypes::MessageContent).unwrap().to_string()); + + // include sender_id if the original had one + if let Some(sender) = original.get_sender() { + cv = cv.add_data(DataTypes::SenderId, sender.to_string()); + } + cv } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8b06a75..08a297e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,45 @@ -use std::time::Duration; -use json::{self, JsonValue}; -use tokio::time::sleep; +use std::process::{Command, ExitStatus}; +use json::{ + self +}; use uuid::Uuid; mod data; mod omikron; mod util; mod users; +mod auth; -use crate::omikron::omikronConnection::{OmikronConnection}; -use crate::data::communication::{CommunicationValue, LogLevel, LogValue, CommunicationType, DataTypes}; +use crate::auth::auth_connector::AuthConnector; +use crate::omikron::omikron_connection::{OmikronConnection}; +use crate::data::communication::{CommunicationValue, CommunicationType, DataTypes}; -fn main() { - let rt = tokio::runtime::Runtime::new().unwrap(); +#[tokio::main] +async fn main() { + let alois = AuthConnector::get_uuid("aloisianer").await; + if alois.is_none() { + println!("No UUID"); + return + } + println!("{}", alois.unwrap()); let omikron = OmikronConnection::new(); - rt.block_on(async { - omikron.connect().await; + omikron.connect().await; - omikron.send_message( - CommunicationValue::new( - CommunicationType::Identification - ) - .add_data(DataTypes::UserIds, json::JsonValue::String(Uuid::new_v4().to_string())) - .add_data(DataTypes::IotaId, json::JsonValue::String(Uuid::new_v4().to_string())) - .to_json() - .to_string() - .as_mut() - ).await; - omikron.close().await; - }); + OmikronConnection::send_message_static( + &omikron.writer, + CommunicationValue::new( + CommunicationType::Identification + ) + .add_data(DataTypes::UserIds, Uuid::new_v4().to_string()) + .add_data(DataTypes::IotaId, Uuid::new_v4().to_string()) + .to_json() + .to_string() + .as_mut().parse().unwrap() + ).await; + + + let mut child = Command::new("sleep").arg("2").spawn().unwrap(); + let _result = child.wait().unwrap(); + + println!("reached end of main"); } diff --git a/src/omikron/mod.rs b/src/omikron/mod.rs index e27e32b..4249352 100644 --- a/src/omikron/mod.rs +++ b/src/omikron/mod.rs @@ -1 +1 @@ -pub mod omikronConnection; \ No newline at end of file +pub mod omikron_connection; \ No newline at end of file diff --git a/src/omikron/omikron_connection.rs b/src/omikron/omikron_connection.rs index 179eb03..3bceaaf 100644 --- a/src/omikron/omikron_connection.rs +++ b/src/omikron/omikron_connection.rs @@ -1,28 +1,29 @@ use futures_util::{SinkExt, StreamExt}; use std::collections::HashMap; +use std::str::FromStr; use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; use tokio::sync::Mutex; use tokio::net::TcpStream; use tokio::time::{sleep, Duration}; use tokio_tungstenite::{ connect_async, - tungstenite::protocol::Message, + tungstenite::protocol::{Message}, MaybeTlsStream, WebSocketStream, }; -use uuid::Uuid; -use json::JsonValue; - -use crate::{ - data::communication::CommunicationValue, - data::communication::CommunicationType, - data::communication::DataTypes -}; +use uuid::{Uuid}; +use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; +use crate::users::contact::Contact; +use crate::users::user_community_util::UserCommunityUtil; +use crate::util::chat_files::ChatFiles; +use crate::util::chats_util::ChatsUtil; #[derive(Clone)] pub struct OmikronConnection { - writer: Arc>, Message>>>>, - waiting: Arc>>>, + pub(crate) writer: Arc>, Message>>>>, + waiting: Arc>>>, // waiting for responses + pingpong: Arc>>>, // ping-pong handler } impl OmikronConnection { @@ -30,79 +31,187 @@ impl OmikronConnection { Self { writer: Arc::new(Mutex::new(None)), waiting: Arc::new(Mutex::new(HashMap::new())), + pingpong: Arc::new(Mutex::new(None)), } } + /// Connect loop with retry 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; } } } } + /// Gracefully close connection pub async fn close(&self) { - let mut writer = self.writer.lock().await; - if let Some(mut ws) = writer.take() { + if let Some(handle) = self.pingpong.lock().await.take() { + handle.abort(); + } + if let Some(mut ws) = self.writer.lock().await.take() { let _ = ws.close().await; } - println!("[Omikron] Connection closed"); } + /// Listener for all incoming messages fn spawn_listener( &self, mut read_half: futures_util::stream::SplitStream>>, ) { let waiting = self.waiting.clone(); - + let writer = self.writer.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)) => { + Ok(Message::Close(Some(frame))) => { println!("[Omikron] Closed: {:?}", frame); break; } + Ok(Message::Text(text)) => { + let mut cv = CommunicationValue::from_json(&text); // needs CommunicationValue parser + println!("[Omikron] Received message: {:?}", cv); + if cv.is_type(CommunicationType::Pong) { + println!("[Omikron] Pong received"); + // handle pingpong reset here + } + + if waiting.lock().await.contains_key(&cv.get_id()) { + if let Some(callback) = waiting.lock().await.remove(&cv.get_id()) { + callback(cv); + } + continue; + } + + // ************************************************ // + // Direct messages // + // ************************************************ // + if cv.is_type(CommunicationType::MessageOtherIota) { + let sender_id = &cv.get_sender(); + let receiver_id = &cv.get_receiver(); + ChatFiles::add_message( + cv.get_data(DataTypes::SendTime).unwrap().parse::().unwrap_or(-1), + false, + receiver_id.unwrap(), + sender_id.unwrap(), + cv.get_data(DataTypes::MessageContent).unwrap().as_str(), + ); + let response = CommunicationValue::new(CommunicationType::MessageLive) + .with_id(cv.get_id()) + .with_receiver(cv.get_receiver().unwrap()) + .add_data(DataTypes::SendTime, cv.get_data(DataTypes::SendTime).unwrap().to_string()) + .add_data(DataTypes::Message, cv.get_data(DataTypes::MessageContent).unwrap().to_string()) + .add_data(DataTypes::SenderId, cv.get_sender().unwrap().to_string()); + Self::send_message_static(&writer, response.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::Message) { + let my_id = cv.get_sender(); + ChatFiles::add_message(SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64, + true, + my_id.unwrap(), + Uuid::from_str(&*cv.get_data(DataTypes::ReceiverId).unwrap().to_string()).unwrap(), + &*cv.get_data(DataTypes::MessageContent).unwrap().to_string() + ); + // ack + let ack = CommunicationValue::ack_message(cv.get_id(), my_id); + Self::send_message_static(&writer, ack.to_json().to_string()).await; + // forward + let forward = CommunicationValue::forward_to_other_iota(&mut cv); + Self::send_message_static(&writer, forward.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::MessageGet) { + let my_id = cv.get_sender().unwrap(); + let partner_id = Uuid::from_str(&*cv.get_data(DataTypes::ChatPartnerId).unwrap().to_string()).unwrap(); + let offset = cv.get_data(DataTypes::LoadedMessages).unwrap().to_string().parse::().unwrap(); + let amount = cv.get_data(DataTypes::MessageAmount).unwrap().to_string().parse::().unwrap(); + + let messages = ChatFiles::get_messages(my_id, partner_id, offset, amount); // needs ChatFiles + let mut resp = CommunicationValue::new(CommunicationType::MessageChunk) + .with_id(cv.get_id()) + .with_receiver(my_id); + if !messages.is_empty() { + resp = resp.add_data(DataTypes::MessageChunk, messages.to_string()); + } + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::GetChats) { + let user_id = cv.get_sender(); + let users = ChatsUtil::get_users(user_id.unwrap()); // needs ChatsUtil + let resp = CommunicationValue::new(CommunicationType::GetChats) + .with_id(cv.get_id()) + .with_receiver(user_id.unwrap()) + .add_data(DataTypes::UserIds, users.to_string()); + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::AddChat) { + let user_id = cv.get_sender(); + let other_id = Uuid::from_str(&*cv.get_data(DataTypes::ReceiverId).unwrap().to_string()).unwrap(); + let mut contact = ChatsUtil::get_user(user_id.unwrap(), other_id).unwrap_or(Contact::new(other_id)); // needs ChatsUtil + Contact + contact.set_last_message_at(SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64); + ChatsUtil::mod_user(user_id.unwrap(), &contact); + let resp = CommunicationValue::new(CommunicationType::AddChat) + .with_id(cv.get_id()) + .with_receiver(user_id.unwrap()); + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::AddCommunity) { + UserCommunityUtil::add_community( cv.get_sender().unwrap(), + cv.get_data(DataTypes::CommunityAddress).unwrap().to_string(), + cv.get_data(DataTypes::CommunityTitle).unwrap().to_string(), + cv.get_data(DataTypes::Position).unwrap().to_string() + ); + let resp = CommunicationValue::new(CommunicationType::AddCommunity) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender().unwrap()); + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::GetCommunities) { + let resp = CommunicationValue::new(CommunicationType::GetCommunities) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender().unwrap()) + .add_data(DataTypes::Communities, UserCommunityUtil::get_communities(cv.get_sender().unwrap()).to_string()); // needs UserCommunityUtil + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + + if cv.is_type(CommunicationType::RemoveCommunity) { + UserCommunityUtil::remove_community(cv.get_sender().unwrap(), cv.get_data(DataTypes::CommunityAddress).unwrap().to_string()); // needs UserCommunityUtil + let resp = CommunicationValue::new(CommunicationType::RemoveCommunity) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender().unwrap()); + Self::send_message_static(&writer, resp.to_json().to_string()).await; + continue; + } + } Err(e) => { - println!("[Omikron] Error: {}", e); + eprintln!("[Omikron] Error: {}", e); break; } _ => {} @@ -111,18 +220,19 @@ impl OmikronConnection { }); } - pub async fn send_message(&self, msg: &str) { - let mut guard = self.writer.lock().await; + pub async fn send_message_static( + writer: &Arc>, Message>>>>, + msg: String, + ) { + let mut guard = writer.lock().await; if let Some(writer) = guard.as_mut() { - if let Err(e) = writer.send(Message::Text(msg.to_string())).await { - println!("[Omikron] Send failed: {}", e); - } + let _ = writer.send(Message::Text(msg)).await; } } - + pub fn on_answer(&self, message_id: Uuid, callback: F) where - F: Fn(String) + Send + 'static, + F: Fn(CommunicationValue) + Send + 'static, { tokio::spawn({ let waiting = self.waiting.clone(); @@ -135,4 +245,6 @@ impl OmikronConnection { #[tokio::main] async fn main() { + let conn = OmikronConnection::new(); + conn.connect().await; } diff --git a/src/users/contact.rs b/src/users/contact.rs index d4a7687..b7a3d4d 100644 --- a/src/users/contact.rs +++ b/src/users/contact.rs @@ -58,9 +58,12 @@ impl Contact { about: None, } } + pub fn set_last_message_at(&mut self, p0: i64) { + self.last_message_at = Option::from(p0); + } pub fn to_json(&self) -> JsonValue { - let mut obj = self.to_json(); + let mut obj = JsonValue::new_object(); if let Some(id) = &self.user_id { obj["userID"] = JsonValue::from(id.to_string()); } diff --git a/src/users/mod.rs b/src/users/mod.rs index 9fd92c1..ff0baeb 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -1 +1,5 @@ -pub mod contact; \ No newline at end of file +pub mod contact; +pub mod user_manager; +pub mod user_profile_full; +pub mod user_profile; +pub mod user_community_util; \ No newline at end of file diff --git a/src/users/user_community_util.rs b/src/users/user_community_util.rs new file mode 100644 index 0000000..f7adeb6 --- /dev/null +++ b/src/users/user_community_util.rs @@ -0,0 +1,64 @@ +use json::{self, JsonValue}; +use std::fs; +use std::path::Path; +use uuid::Uuid; + +pub struct UserCommunityUtil; + +impl UserCommunityUtil { + pub fn add_community( + storage_owner: Uuid, + address: String, + title: String, + position: String, + ) { + let path = format!("users/{}/communities.json", storage_owner); + let mut communities: JsonValue = Self::load_array(&path); + + let mut community = JsonValue::new_object(); + community["title"] = JsonValue::String(title); + community["address"] = JsonValue::String(address); + community["position"] = JsonValue::String(position); + + communities.push(community); + Self::save_array(&path, communities); + } + + pub fn remove_community(storage_owner: Uuid, community_address: String) { + let path = format!("users/{}/communities.json", storage_owner); + let mut communities = Self::load_array(&path); + + let mut new_array = JsonValue::new_array(); + for entry in communities.members() { + if entry["address"].as_str() != Some(&community_address) { + new_array.push(entry.clone()).unwrap(); + } + } + + Self::save_array(&path, new_array); + } + + pub fn get_communities(storage_owner: Uuid) -> JsonValue { + let path = format!("users/{}/communities.json", storage_owner); + Self::load_array(&path) + } + + fn load_array(path: &str) -> JsonValue { + if !Path::new(path).exists() { + return JsonValue::new_object(); + } + + match fs::read_to_string(path) { + Ok(content) => json::parse(&content).unwrap_or_else(|_| JsonValue::new_object()), + Err(_) => JsonValue::new_object(), + } + } + + fn save_array(path: &str, arr: JsonValue) { + if let Some(parent) = Path::new(path).parent() { + let _ = fs::create_dir_all(parent); + } + + let _ = fs::write(path, arr.pretty(3)); + } +} diff --git a/src/users/user_manager.rs b/src/users/user_manager.rs index e69de29..51c334e 100644 --- a/src/users/user_manager.rs +++ b/src/users/user_manager.rs @@ -0,0 +1,106 @@ +use std::fs; +use std::sync::Mutex; +use std::io; +use std::path::Path; +use uuid::Uuid; +use rand::Rng; +use rand::rngs::OsRng; +use base64::{engine::general_purpose, Engine as _}; +use json::{JsonValue}; +use once_cell::sync::Lazy; +use crate::users::user_profile::UserProfile; +use crate::users::user_profile_full::UserProfileFull; + +pub struct UserManager; + +static USERS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); +static UNIQUE: Lazy> = Lazy::new(|| Mutex::new(false)); + +impl UserManager { + pub fn create_user(username: &str) -> Option { + // Stub: normally AuthConnector.getRegister() returns a UUID + let user_id = Uuid::new_v4(); + + // Stubbed: CryptoHelper.generateKeyPair() + let public_key = general_purpose::STANDARD.encode(b"dummy-public"); + let private_key = general_purpose::STANDARD.encode(b"dummy-private"); + let private_key_hash = format!("hash-{}", &private_key); + + let mut bytes = [0u8; 192]; + OsRng.fill(bytes.as_mut()); + let reset_token = general_purpose::STANDARD.encode(&bytes); + + let up = UserProfile::new( + user_id, + username.to_string(), + None, + public_key, + private_key_hash, + reset_token, + ); + + let up_full = UserProfileFull { user_profile: up.clone(), private_key }; + + USERS.lock().unwrap().push(up); + Self::save_users().ok(); + Some(up_full) + } + + pub fn get_user(user_id: Uuid) -> Option { + USERS.lock().unwrap().iter().cloned().find(|u| u.user_id == user_id) + } + + pub fn get_users() -> Vec { + USERS.lock().unwrap().clone() + } + + pub fn add_user(up: UserProfile) { + let mut users = USERS.lock().unwrap(); + users.retain(|u| u.user_id != up.user_id); + users.push(up); + *UNIQUE.lock().unwrap() = true; + } + + pub fn remove_user(user_id: Uuid) { + let mut users = USERS.lock().unwrap(); + users.retain(|u| u.user_id != user_id); + *UNIQUE.lock().unwrap() = true; + } + + pub fn save_users() -> io::Result<()> { + *UNIQUE.lock().unwrap() = false; + let users = USERS.lock().unwrap(); + let arr: Vec = users.iter().map(|u| u.to_json()).collect(); + let json_str = JsonValue::Array(arr).dump(); + fs::write("users.json", json_str)?; + Ok(()) + } + + pub async fn load_users() -> io::Result<()> { + let path = Path::new("users.json"); + if !path.exists() { + return Ok(()); + } + let content = fs::read_to_string(path)?; + if content.trim().is_empty() { + return Ok(()); + } + let parsed = json::parse(&content).map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + if let JsonValue::Array(arr) = parsed { + let mut users = USERS.lock().unwrap(); + for j in arr.iter() { + if let Some(up) = UserProfile::from_json(j).await { + users.push(up); + } + } + } + if *UNIQUE.lock().unwrap() { + Self::save_users().ok(); + } + Ok(()) + } + + pub fn set_unique(val: bool) { + *UNIQUE.lock().unwrap() = val; + } +} diff --git a/src/users/user_profile.rs b/src/users/user_profile.rs index e69de29..38364fd 100644 --- a/src/users/user_profile.rs +++ b/src/users/user_profile.rs @@ -0,0 +1,95 @@ +use std::fs; +use std::sync::Mutex; +use std::collections::HashMap; +use std::io; +use std::path::Path; +use uuid::Uuid; +use rand::Rng; +use rand::rngs::OsRng; +use base64::{engine::general_purpose, Engine as _}; +use json::{JsonValue, object, stringify}; +use crate::users::user_manager::UserManager; +use crate::auth::auth_connector::AuthConnector; + +// --- UserProfile --- +#[derive(Clone, Debug)] +pub struct UserProfile { + pub user_id: Uuid, + pub username: String, + pub public_key: String, + pub private_key_hash: String, + pub reset_token: String, + pub display_name: Option, +} + +impl UserProfile { + pub fn new( + user_id: Uuid, + username: String, + display_name: Option, + public_key: String, + private_key_hash: String, + reset_token: String, + ) -> Self { + Self { + user_id, + username, + display_name, + public_key, + private_key_hash, + reset_token, + } + } + + pub fn to_json(&self) -> JsonValue { + let mut obj = object! { + "UUID" => self.user_id.to_string(), + "username" => self.username.clone(), + "publicKey" => self.public_key.clone(), + "privateKeyHash" => self.private_key_hash.clone(), + "resetToken" => self.reset_token.clone() + }; + if let Some(d) = &self.display_name { + obj["displayName"] = d.clone().into(); + } + obj + } + + pub async fn from_json(j: &JsonValue) -> Option { + let uuid = Uuid::parse_str(j["UUID"].as_str()?).ok()?; + let username = j["username"].as_str()?.to_string(); + let public_key = j["publicKey"].as_str()?.to_string(); + let private_key_hash = j["privateKeyHash"].as_str()?.to_string(); + let reset_token = j["resetToken"].as_str()?.to_string(); + let display_name = j["displayName"].as_str().map(|s| s.to_string()); + + let mut up = UserProfile::new(uuid, username, display_name, public_key, private_key_hash, reset_token); + + // Migration hook (stubbed, since AuthConnector isn’t implemented here) + if j.has_key("migrate") + || j.has_key("migrating") + || j.has_key("changing") + || j.has_key("move") + || j.has_key("moving") { + if AuthConnector::migrate_user(&mut up, stringify!("{}", Uuid::new_v4())).await { + println!("[INFO] Migration triggered for {}", up.username); + UserManager::set_unique(true); + } + } + + Some(up) + } + + pub fn randomize_reset_token(&mut self) -> String { + let mut bytes = [0u8; 192]; + OsRng.fill(bytes.as_mut()); + let new_token = general_purpose::STANDARD.encode(&bytes); + self.reset_token = new_token.clone(); + UserManager::save_users().ok(); + new_token + } + + pub fn get_display_name(&self) -> String { + self.display_name.clone().unwrap_or_else(|| self.username.clone()) + } +} \ No newline at end of file diff --git a/src/users/user_profile_full.rs b/src/users/user_profile_full.rs index e69de29..ad0b6b1 100644 --- a/src/users/user_profile_full.rs +++ b/src/users/user_profile_full.rs @@ -0,0 +1,8 @@ +use crate::users::user_profile::UserProfile; + +// --- UserProfileFull --- +#[derive(Clone, Debug)] +pub struct UserProfileFull { + pub user_profile: UserProfile, + pub private_key: String, +} \ No newline at end of file diff --git a/src/util/chat_files.rs b/src/util/chat_files.rs index 0fcf691..7143873 100644 --- a/src/util/chat_files.rs +++ b/src/util/chat_files.rs @@ -134,8 +134,8 @@ impl ChatFiles { pub fn get_messages( storage_owner: Uuid, external_user: Uuid, - loaded_messages: usize, - amount: usize, + loaded_messages: i64, + amount: i64, ) -> JsonValue { let user_dir = format!("users/{}/chats/{}", storage_owner, external_user); let path = Path::new(&user_dir); diff --git a/src/util/chats_util.rs b/src/util/chats_util.rs index 50a62bd..3ab4163 100644 --- a/src/util/chats_util.rs +++ b/src/util/chats_util.rs @@ -31,7 +31,7 @@ impl ChatsUtil { file.write_all(content.as_bytes()) } - pub fn mod_user(storage_owner: Uuid, contact: Contact) -> std::io::Result<()> { + pub fn mod_user(storage_owner: Uuid, contact: &Contact){ let dir = format!("users/{}/contacts/", storage_owner); let file_name = "contacts.json"; let s = Self::load_file(&dir, file_name); @@ -50,7 +50,7 @@ impl ChatsUtil { } contacts.push(contact.to_json()).unwrap(); - Self::save_file(&dir, file_name, &contacts.dump()) + Self::save_file(&dir, file_name, &contacts.dump()); } pub fn get_user(storage_owner: Uuid, user_id: Uuid) -> Option { diff --git a/src/util/mod.rs b/src/util/mod.rs index 0b11ad1..6f1977e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,4 +1,4 @@ -pub mod fileUtil; -pub mod chatFiles; -pub mod configUtil; -pub mod chatsUtil; \ No newline at end of file +pub mod file_util; +pub mod chat_files; +pub mod config_util; +pub mod chats_util; \ No newline at end of file