From c9d21fd3c68aaffc96f89af739e2db9251bfec9d Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Fri, 27 Feb 2026 23:44:47 +0100 Subject: [PATCH] [FIX] Migrated to Tensamin Transport Protocol --- Cargo.lock | 618 ++++++++++++------ Cargo.toml | 13 +- .../anonymous_client_connection.rs | 316 ++++----- src/anonymous_clients/anonymous_manager.rs | 8 +- src/calls/call_group.rs | 13 +- src/calls/call_manager.rs | 8 +- src/calls/call_util.rs | 4 +- src/calls/caller.rs | 4 +- src/data/communication.rs | 403 ------------ src/data/mod.rs | 1 - src/main.rs | 200 +----- src/omega/mod.rs | 1 - src/omega/omega_connection.rs | 557 ++++------------ src/omega/ping_pong_task.rs | 23 +- src/rho/client_connection.rs | 496 ++++---------- src/rho/connection.rs | 174 +++++ src/rho/iota_connection.rs | 514 ++++----------- src/rho/mod.rs | 2 + src/rho/rho_connection.rs | 30 +- src/rho/rho_manager.rs | 2 +- src/rho/server.rs | 57 ++ src/util/file_util.rs | 179 +++++ src/util/logger.rs | 89 ++- src/util/mod.rs | 1 + 24 files changed, 1456 insertions(+), 2257 deletions(-) delete mode 100755 src/data/communication.rs create mode 100644 src/rho/connection.rs create mode 100644 src/rho/server.rs create mode 100644 src/util/file_util.rs diff --git a/Cargo.lock b/Cargo.lock index c269643..682ac52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,8 +19,10 @@ dependencies = [ "cmake", "crossterm", "dashmap", - "der", + "der 0.8.0", "dotenv", + "epsilon-core", + "epsilon-native", "futures", "futures-util", "hex", @@ -35,14 +37,16 @@ dependencies = [ "loom", "once_cell", "pkcs8", + "quinn", "rand 0.8.5", "rand_core 0.6.4", - "reqwest 0.13.1", - "rustls 0.23.36", + "reqwest 0.13.2", + "rustls 0.23.37", + "rustls-pemfile 2.2.0", "serde", "sha2", - "strum", - "strum_macros", + "strum 0.27.2", + "strum_macros 0.27.2", "sys-info", "sysinfo", "tokio", @@ -138,9 +142,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-channel" @@ -167,9 +171,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", @@ -280,6 +284,17 @@ dependencies = [ "webpki-roots 0.22.6", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-tungstenite" version = "0.25.1" @@ -322,7 +337,7 @@ dependencies = [ "tokio-openssl", "tokio-rustls", "tungstenite 0.28.0", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] @@ -339,9 +354,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.4" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" dependencies = [ "aws-lc-sys", "zeroize", @@ -349,9 +364,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -431,9 +446,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -484,9 +499,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -496,9 +511,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bzip2" @@ -531,9 +546,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.54" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -571,9 +586,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -594,18 +609,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.55" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.55" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstyle", "clap_lex", @@ -614,9 +629,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cmake" @@ -814,7 +829,7 @@ dependencies = [ "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", - "foldhash", + "foldhash 0.2.0", "link-cplusplus", ] @@ -896,10 +911,16 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.5.5" +name = "der" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -995,6 +1016,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "epsilon-core" +version = "0.1.0" +source = "git+https://github.com/Tensamin/Epsilon.git#dc17684bc9308c79559e0d4648c6e730cc567c89" +dependencies = [ + "byteorder", + "rand 0.8.5", + "strum 0.28.0", + "strum_macros 0.28.0", +] + +[[package]] +name = "epsilon-native" +version = "0.1.0" +source = "git+https://github.com/Tensamin/Epsilon.git#dc17684bc9308c79559e0d4648c6e730cc567c89" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "epsilon-core", + "quinn", + "rustls 0.23.37", + "thiserror 2.0.18", + "tokio", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1038,6 +1085,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fastbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" +dependencies = [ + "getrandom 0.3.4", + "libm", + "rand 0.9.2", + "siphasher", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1052,9 +1111,9 @@ checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -1064,9 +1123,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1078,6 +1137,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1126,9 +1191,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1141,9 +1206,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1151,15 +1216,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1168,9 +1233,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1187,9 +1252,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -1198,21 +1263,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1222,7 +1287,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1278,6 +1342,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "ghash" version = "0.5.1" @@ -1421,6 +1498,9 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] [[package]] name = "hashbrown" @@ -1582,7 +1662,7 @@ dependencies = [ "http 1.4.0", "hyper", "hyper-util", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -1608,14 +1688,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.4.0", "http-body", @@ -1737,6 +1816,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.1.0" @@ -1766,6 +1851,8 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1852,9 +1939,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -1895,10 +1982,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libc" -version = "0.2.180" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libloading" @@ -1911,10 +2004,16 @@ dependencies = [ ] [[package]] -name = "libwebrtc" -version = "0.3.24" +name = "libm" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56165903078b7282ae45bd423439955b81e5a841e3a2fa46106c25cd62eda6" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libwebrtc" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c68cb670823a24fc58a4640585acab53a1e9c224ed995733e650f395564d6f" dependencies = [ "cxx", "glib", @@ -1946,9 +2045,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1964,10 +2063,11 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "livekit" -version = "0.7.29" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda2731d17a94d3a671b273d998961263bc962f264dce9cf85a7a056b6d4cf71" +checksum = "13f049dbbdfe8bd9a2217b03e4f14eb67e3ba12c807d97a9ffaf34359baa53c0" dependencies = [ + "base64 0.22.1", "bmrng", "bytes", "chrono", @@ -1990,9 +2090,9 @@ dependencies = [ [[package]] name = "livekit-api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d72127ba24841e773fd4311d4e64be727c9707fce1f632c4422c2a4b44441697" +checksum = "fa90c56d727f9e51b6ab5ce6b14262d4817900c72a8a346a4f7208bde98207e6" dependencies = [ "async-tungstenite 0.25.1", "base64 0.21.7", @@ -2019,9 +2119,9 @@ dependencies = [ [[package]] name = "livekit-protocol" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b8d7580e14aa9039d6d1d13c06c894de7757b9a49c1aa7c188cabf461cf069" +checksum = "01875112fb961da484dedf0c2d5705d8875c5e99da6d5aa1d567be1b7d03532a" dependencies = [ "futures-util", "livekit-runtime", @@ -2098,9 +2198,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -2148,26 +2248,26 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -2234,12 +2334,6 @@ dependencies = [ "syn", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -2365,18 +2459,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", @@ -2385,9 +2479,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -2412,7 +2506,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", + "der 0.7.10", "spki", ] @@ -2565,7 +2659,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.36", + "rustls 0.23.37", "socket2", "thiserror 2.0.18", "tokio", @@ -2581,13 +2675,15 @@ checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "aws-lc-rs", "bytes", + "fastbloom", "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", "rustc-hash", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", + "rustls-platform-verifier", "slab", "thiserror 2.0.18", "tinyvec", @@ -2720,9 +2816,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2732,9 +2828,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2743,9 +2839,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" @@ -2771,7 +2867,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-native-certs", "rustls-pki-types", "serde", @@ -2792,9 +2888,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64 0.22.1", "bytes", @@ -2813,7 +2909,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", "rustls-platform-verifier", "sync_wrapper", @@ -2859,9 +2955,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -2884,9 +2980,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", "log", @@ -2904,10 +3000,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -2949,11 +3045,11 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki 0.103.9", - "security-framework 3.5.1", + "security-framework", "security-framework-sys", "webpki-root-certs", "windows-sys 0.61.2", @@ -2995,9 +3091,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -3047,22 +3143,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation 0.10.1", @@ -3073,9 +3156,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3237,10 +3320,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] -name = "slab" -version = "0.4.11" +name = "siphasher" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3265,7 +3354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", ] [[package]] @@ -3286,6 +3375,12 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" + [[package]] name = "strum_macros" version = "0.27.2" @@ -3298,6 +3393,18 @@ dependencies = [ "syn", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3306,9 +3413,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -3362,9 +3469,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ "bitflags", "core-foundation 0.9.4", @@ -3402,12 +3509,12 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3473,9 +3580,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "num-conv", @@ -3570,7 +3677,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.36", + "rustls 0.23.37", "tokio", ] @@ -3635,7 +3742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79af8a3a0690c5b3d6de188a962472c1f94e0830c0432fcc03380dd5ef74d65c" dependencies = [ "futures-util", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pemfile 2.2.0", "serde", "tokio", @@ -3647,9 +3754,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", @@ -3683,9 +3790,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -3880,7 +3987,7 @@ dependencies = [ "log", "native-tls", "rand 0.9.2", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", "sha1", "thiserror 2.0.18", @@ -3902,9 +4009,9 @@ checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -3918,6 +4025,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -3960,11 +4073,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "serde_core", "wasm-bindgen", @@ -4062,10 +4175,19 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.108" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -4076,9 +4198,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", "futures-util", @@ -4090,9 +4212,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4100,9 +4222,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", @@ -4113,18 +4235,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] -name = "web-sys" -version = "0.3.85" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -4152,9 +4308,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -4170,18 +4326,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] [[package]] name = "webrtc-sys" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599fd49417d23c328aa4235906383939dc6530d36e00959f09df6edc7d3afbe0" +checksum = "9d612cde159dea4736ca54e567b0867881d7c181b3a62d239231ea797fa00857" dependencies = [ "cc", "cxx", @@ -4194,9 +4350,9 @@ dependencies = [ [[package]] name = "webrtc-sys-build" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94b613d4d2c358e193b87b074eb919cee06d015a315fc898788c8cfd8f53a5d" +checksum = "ded4070a382b4d8ca4530f6557f75431756a6ac8f5552b59827ad9afe32a0e2f" dependencies = [ "anyhow", "fs2", @@ -4563,6 +4719,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -4616,18 +4854,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.35" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.35" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", @@ -4716,9 +4954,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.17" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 2ca159d..a21795c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] +epsilon-core = { git = "https://github.com/Tensamin/Epsilon.git", package = "epsilon-core" } +epsilon-native = { git = "https://github.com/Tensamin/Epsilon.git", package = "epsilon-native" } + ansi_term = "*" async-tungstenite = { version = "0.32.1", features = [ "futures-03-sink", @@ -56,7 +59,7 @@ pkcs8 = { version = "*", features = ["alloc"] } rand = "0.8" rand_core = { version = "0.6", features = ["getrandom", "std"] } reqwest = "*" -rustls = { version = "*", default-features = false, features = ["ring"] } +rustls = { version = "0.23.37", default-features = false, features = ["ring"] } serde = { version = "1.0.219", features = ["derive"] } sha2 = "*" sys-info = "*" @@ -85,7 +88,9 @@ cbc = "0.1.2" aes = "0.8.4" strum = "0.27.2" strum_macros = "0.27.2" -livekit = "0.7.25" -livekit-api = { version = "0.4.10", features = ["native-tls"] } +livekit = "0.7.32" +livekit-api = { version = "0.4.14", features = ["native-tls"] } hyper = { version = "1.8.1", features = ["http1", "http2"] } -livekit-protocol = "0.6.0" +livekit-protocol = "0.7.1" +rustls-pemfile = "2.2.0" +quinn = "0.11.9" diff --git a/src/anonymous_clients/anonymous_client_connection.rs b/src/anonymous_clients/anonymous_client_connection.rs index c8bf33d..7c15ba8 100644 --- a/src/anonymous_clients/anonymous_client_connection.rs +++ b/src/anonymous_clients/anonymous_client_connection.rs @@ -1,27 +1,24 @@ -use async_tungstenite::tungstenite::Message; -use async_tungstenite::{WebSocketReceiver, WebSocketSender}; -use json::JsonValue; -use json::number::Number; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use epsilon_native::{Receiver, Sender}; use std::str::FromStr; use std::sync::Arc; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::Duration; use tokio::sync::RwLock; -use tokio_util::compat::Compat; -use tungstenite::Utf8Bytes; use uuid::Uuid; use crate::anonymous_clients::anonymous_manager::{self, generate_username}; use crate::calls::call_manager; -use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::omega::omega_connection::get_omega_connection; +use crate::rho::connection::GeneralConnection; use crate::rho::rho_manager; use crate::util::logger::PrintType; -use crate::{log_in, log_out}; +use crate::{log_cv_in, log_cv_out, log_out}; pub struct AnonymousClientConnection { - pub sender: Arc>>>, - pub receiver: Arc>>>, - pub user_id: Arc>, + user_id: u64, + + pub sender: Arc, + pub receiver: Arc, pub ping: Arc>, pub interested_users: Arc>>, is_open: Arc>, @@ -31,33 +28,33 @@ pub struct AnonymousClientConnection { } impl AnonymousClientConnection { - /// Create a new AnonymousClientConnection - pub fn new( - sender: WebSocketSender>, - receiver: WebSocketReceiver>, - ) -> Arc { + pub async fn from_general(general: Arc, user_id: u64) -> Arc { let username: String = generate_username(); Arc::new(Self { - sender: Arc::new(RwLock::new(sender)), - receiver: Arc::new(RwLock::new(receiver)), - user_id: Arc::new(RwLock::new( - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as i64, - )), - ping: Arc::new(RwLock::new(-1)), + user_id: user_id, + + ping: Arc::new(RwLock::new(0)), interested_users: Arc::new(RwLock::new(Vec::new())), is_open: Arc::new(RwLock::new(true)), + sender: general.sender.clone(), + receiver: general.receiver.clone(), user_name: Arc::new(RwLock::new(username.to_lowercase())), display_name: Arc::new(RwLock::new(username)), avatar: Arc::new(RwLock::new(String::new())), }) } + pub fn start(self: Arc) { + let self_clone = self.clone(); + tokio::spawn(async move { + while let Ok(cv) = self_clone.receiver.receive().await { + self_clone.clone().handle_message(cv).await; + } + }); + } /// Get the user ID - pub async fn get_user_id(&self) -> i64 { - *self.user_id.read().await + pub fn get_user_id(&self) -> u64 { + self.user_id } /// Get the user name @@ -79,66 +76,35 @@ impl AnonymousClientConnection { self.avatar.read().await.clone() } - /// Send a string message to the client - pub async fn send_message_str(self: Arc, message: &str) { - let mut session = self.sender.write().await; - if let Err(e) = session - .send(Message::Text(Utf8Bytes::from(message.to_string()))) - .await - { - log_out!( - self.get_user_id().await, - PrintType::Client, - "Failed to send message to anonymous client: {}", - e, - ); - } - } - /// Send a CommunicationValue to the client pub async fn send_message(self: Arc, cv: &CommunicationValue) { if !*self.is_open.read().await { log_out!( - self.get_user_id().await, + self.user_id as i64, PrintType::Client, "Attempted to send message to a closed connection." ); return; } if !cv.is_type(CommunicationType::pong) { - log_out!( - self.get_user_id().await, - PrintType::Client, - "{}", - &cv.to_json().to_string() - ); + log_cv_out!(PrintType::Client, &cv); } - self.send_message_str(&cv.to_json().to_string()).await; + self.sender.send(&cv).await; } /// Handle incoming message from client - pub async fn handle_message(self: Arc, message: Utf8Bytes) { + pub async fn handle_message(self: Arc, cv: CommunicationValue) { tokio::spawn(async move { - let cv = CommunicationValue::from_json(&message); if cv.is_type(CommunicationType::ping) { self.handle_ping(cv).await; return; } - log_in!( - self.get_user_id().await, - PrintType::Client, - "Anonymous: {}", - &cv.to_json().to_string() - ); + log_cv_in!(PrintType::Client, &cv); if cv.is_type(CommunicationType::identification) { - let call_id = Uuid::parse_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap_or(Uuid::new_v4()); + let call_id = + Uuid::parse_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")) + .unwrap_or(Uuid::new_v4()); let call = if let Some(call) = call_manager::get_call(call_id).await { if call.is_anonymous().await { @@ -160,79 +126,62 @@ impl AnonymousClientConnection { return; }; - let mut invited = JsonValue::new_array(); + let mut invited = Vec::new(); for call_invitee in call.members.read().await.clone() { let call_invitee_cv = get_omega_connection() .await_response( &CommunicationValue::new(CommunicationType::get_user_data).add_data( DataTypes::user_id, - JsonValue::from(call_invitee.user_id), + DataValue::Number(call_invitee.user_id as i64), ), Some(Duration::from_secs(2)), ) .await .unwrap(); - let mut json_invitee = JsonValue::new_object(); - let _ = json_invitee.insert( - "user_id", - call_invitee_cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .clone(), - ); - let _ = json_invitee.insert( - "username", - call_invitee_cv - .get_data(DataTypes::username) - .unwrap_or(&JsonValue::Null) - .clone(), - ); - let _ = json_invitee.insert( - "display", - call_invitee_cv - .get_data(DataTypes::display) - .unwrap_or(&JsonValue::Null) - .clone(), - ); - let _ = json_invitee.insert( - "avatar", - call_invitee_cv - .get_data(DataTypes::avatar) - .unwrap_or(&JsonValue::Null) - .clone(), - ); + let mut json_invitee = Vec::new(); + let _ = json_invitee.push(( + DataTypes::user_id, + call_invitee_cv.get_data(DataTypes::user_id).clone(), + )); + let _ = json_invitee.push(( + DataTypes::username, + call_invitee_cv.get_data(DataTypes::username).clone(), + )); + let _ = json_invitee.push(( + DataTypes::display, + call_invitee_cv.get_data(DataTypes::display).clone(), + )); + let _ = json_invitee.push(( + DataTypes::avatar, + call_invitee_cv.get_data(DataTypes::avatar).clone(), + )); - let _ = invited.push(json_invitee); + let _ = invited.push(DataValue::Container(json_invitee)); } - let token = call.create_anonymous_token(self.get_user_id().await).await; + let token = call.create_anonymous_token(self.get_user_id()).await; - let mut serialized = JsonValue::new_object(); - let _ = serialized.insert("call_id", JsonValue::String(call_id.to_string())); - let _ = serialized.insert("call_invited", invited.clone()); - let _ = serialized.insert("call_members", invited); - let _ = serialized.insert("call_token", JsonValue::String(token.unwrap())); + let mut serialized = Vec::new(); + let _ = serialized.push((DataTypes::call_id, DataValue::Str(call_id.to_string()))); + let _ = + serialized.push((DataTypes::call_invited, DataValue::Array(invited.clone()))); + let _ = serialized.push((DataTypes::call_members, DataValue::Array(invited))); + let _ = serialized.push((DataTypes::call_token, DataValue::Str(token.unwrap()))); self.clone() .send_message( &&CommunicationValue::new(CommunicationType::identification_response) .with_id(cv.get_id()) - .add_data( - DataTypes::user_id, - JsonValue::from(self.get_user_id().await), - ) + .add_data(DataTypes::user_id, DataValue::Number(self.user_id as i64)) .add_data( DataTypes::username, - JsonValue::String(self.clone().get_user_name().await), + DataValue::Str(self.clone().get_user_name().await), ) .add_data( DataTypes::display, - JsonValue::String(self.get_display_name().await), + DataValue::Str(self.get_display_name().await), ) - .add_data( - DataTypes::avatar, - JsonValue::String(self.get_avatar().await), - ) - .add_data(DataTypes::call_state, serialized), + .add_data(DataTypes::avatar, DataValue::Str(self.get_avatar().await)) + .add_data(DataTypes::call_state, DataValue::Container(serialized)), ) .await; } @@ -271,11 +220,7 @@ impl AnonymousClientConnection { } if cv.is_type(CommunicationType::change_user_data) { - if let Some(display_name) = cv - .get_data(DataTypes::display) - .unwrap_or(&JsonValue::Null) - .as_str() - { + if let Some(display_name) = cv.get_data(DataTypes::display).as_str() { let _ = self.set_display_name(display_name.to_string()).await; } @@ -284,17 +229,9 @@ impl AnonymousClientConnection { if cv.is_type(CommunicationType::get_user_data) { if let Some(anonymous) = { - if let Some(user_id) = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - { - anonymous_manager::get_anonymous_user(user_id).await - } else if let Some(username) = cv - .get_data(DataTypes::username) - .unwrap_or(&JsonValue::Null) - .as_str() - { + if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { + anonymous_manager::get_anonymous_user(user_id as u64).await + } else if let Some(username) = cv.get_data(DataTypes::username).as_str() { anonymous_manager::get_anonymous_user_by_name(username.to_string()).await } else { None @@ -302,14 +239,23 @@ impl AnonymousClientConnection { } { let response = CommunicationValue::new(CommunicationType::get_user_data) .with_id(cv.get_id()) - .add_data_str(DataTypes::username, anonymous.get_user_name().await) + .add_data( + DataTypes::username, + DataValue::Str(anonymous.get_user_name().await), + ) .add_data( DataTypes::user_id, - JsonValue::Number(Number::from(anonymous.get_user_id().await)), + DataValue::Number(anonymous.user_id as i64), ) - .add_data_str(DataTypes::display, anonymous.get_display_name().await) - .add_data_str(DataTypes::user_state, "online".to_string()) - .add_data_str(DataTypes::avatar, anonymous.get_avatar().await); + .add_data( + DataTypes::display, + DataValue::Str(anonymous.get_display_name().await), + ) + .add_data(DataTypes::user_state, DataValue::Str("online".to_string())) + .add_data( + DataTypes::avatar, + DataValue::Str(anonymous.get_avatar().await), + ); self.send_message(&response).await; @@ -330,10 +276,7 @@ impl AnonymousClientConnection { let client_for_closure = self.clone(); tokio::spawn(async move { let response_cv = get_omega_connection() - .await_response( - &cv.with_sender(*self.user_id.read().await), - Some(Duration::from_secs(20)), - ) + .await_response(&cv.with_sender(self.user_id), Some(Duration::from_secs(20))) .await; if let Ok(response_cv) = response_cv { client_for_closure.send_message(&response_cv).await; @@ -344,7 +287,7 @@ impl AnonymousClientConnection { /// Handle ping message async fn handle_ping(self: Arc, cv: CommunicationValue) { // Update our ping if provided - if let Some(last_ping) = cv.get_data(DataTypes::last_ping) { + if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) { if let Ok(ping_val) = last_ping.to_string().parse::() { let mut ping_guard = self.ping.write().await; *ping_guard = ping_val; @@ -367,11 +310,7 @@ impl AnonymousClientConnection { /// Handle call invite async fn handle_call_invite(self: Arc, cv: CommunicationValue) { - let receiver_id: i64 = cv - .get_data(DataTypes::receiver_id) - .unwrap_or(&json::JsonValue::Number(Number::from(0))) - .as_i64() - .unwrap_or(0); + let receiver_id: i64 = cv.get_data(DataTypes::receiver_id).as_number().unwrap_or(0); if receiver_id == 0 { self.send_error_response(&cv.get_id(), CommunicationType::error_no_user_id) .await; @@ -379,7 +318,7 @@ impl AnonymousClientConnection { } let call_id = match cv.get_data(DataTypes::call_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { + DataValue::Str(id_str) => match Uuid::parse_str(&id_str.to_string()) { Ok(id) => id, Err(_) => { self.send_error_response( @@ -397,8 +336,7 @@ impl AnonymousClientConnection { } }; - let invited = - call_manager::add_invite(call_id, *self.user_id.read().await, receiver_id).await; + let invited = call_manager::add_invite(call_id, self.user_id, receiver_id as u64).await; if !invited { self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id) .await; @@ -416,15 +354,18 @@ impl AnonymousClientConnection { }; // Get sender user ID - let sender_id = self.get_user_id().await; + let sender_id = self.get_user_id(); // Create and send call distribution message let forward = CommunicationValue::new(CommunicationType::call_invite) - .with_receiver(receiver_id) + .with_receiver(receiver_id as u64) .with_sender(sender_id) - .add_data_str(DataTypes::call_id, call_id.to_string()) - .add_data_str(DataTypes::receiver_id, receiver_id.to_string()) - .add_data_str(DataTypes::sender_id, sender_id.to_string()); + .add_data(DataTypes::call_id, DataValue::Str(call_id.to_string())) + .add_data( + DataTypes::receiver_id, + DataValue::Str(receiver_id.to_string()), + ) + .add_data(DataTypes::sender_id, DataValue::Str(sender_id.to_string())); target_rho.message_to_client(forward).await; @@ -434,10 +375,10 @@ impl AnonymousClientConnection { /// Handle get call request async fn handle_get_call(self: Arc, cv: CommunicationValue) { - let user_id = self.get_user_id().await; + let user_id = self.get_user_id(); let call_id = match cv.get_data(DataTypes::call_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { + DataValue::Str(id_str) => match Uuid::parse_str(&id_str.to_string()) { Ok(id) => id, Err(_) => { self.send_error_response(&cv.get_id(), CommunicationType::error) @@ -456,7 +397,7 @@ impl AnonymousClientConnection { let response = CommunicationValue::new(CommunicationType::call_token) .with_id(cv.get_id()) .with_receiver(user_id) - .add_data_str(DataTypes::call_token, token); + .add_data(DataTypes::call_token, DataValue::Str(token.to_string())); self.send_message(&response).await; } else { self.send_error_response(&cv.get_id(), CommunicationType::error) @@ -465,33 +406,20 @@ impl AnonymousClientConnection { } } async fn handle_call_timeout_user(self: Arc, cv: CommunicationValue) { - let call_id = Uuid::from_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap(); - let user_id = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - let untill = cv - .get_data(DataTypes::untill) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); + let call_id = + Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap(); + let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0); + let untill = cv.get_data(DataTypes::untill).as_number().unwrap_or(0); let call = call_manager::get_call(call_id).await; if let Some(call) = call { if call - .get_caller(self.get_user_id().await) + .get_caller(self.get_user_id()) .await .unwrap() .has_admin() { - call.get_caller(user_id) + call.get_caller(user_id as u64) .await .unwrap() .set_timeout(untill) @@ -500,38 +428,25 @@ impl AnonymousClientConnection { } } async fn handle_call_disconnect_user(self: Arc, cv: CommunicationValue) { - let call_id = Uuid::from_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap(); - let user_id = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); + let call_id = + Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap(); + let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0); let call = call_manager::get_call(call_id).await; if let Some(call) = call { if call - .get_caller(self.get_user_id().await) + .get_caller(self.get_user_id()) .await .unwrap() .has_admin() { - call.remove_caller(user_id).await; + call.remove_caller(user_id as u64).await; } } } /// Send error response - async fn send_error_response( - self: Arc, - message_id: &Uuid, - error_type: CommunicationType, - ) { + async fn send_error_response(self: Arc, message_id: &u32, error_type: CommunicationType) { let error = CommunicationValue::new(error_type).with_id(*message_id); self.send_message(&error).await; } @@ -544,8 +459,7 @@ impl AnonymousClientConnection { } *is_open_guard = false; - let mut session = self.sender.write().await; - let _ = session.close(None).await; + let _ = self.sender.close(); } /// Set interested users list @@ -563,8 +477,8 @@ impl AnonymousClientConnection { let interested_guard = self.clone().get_interested_users().await; if interested_guard.contains(&user_id) { let notification = CommunicationValue::new(CommunicationType::client_changed) - .add_data_str(DataTypes::user_id, user_id.to_string()) - .add_data_str(DataTypes::user_state, format!("online")); + .add_data(DataTypes::user_id, DataValue::Str(user_id.to_string())) + .add_data(DataTypes::user_state, DataValue::Str("online".to_string())); self.send_message(¬ification).await; } @@ -583,7 +497,7 @@ impl Clone for AnonymousClientConnection { Self { sender: Arc::clone(&self.sender), receiver: Arc::clone(&self.receiver), - user_id: Arc::clone(&self.user_id), + user_id: self.user_id, ping: Arc::clone(&self.ping), interested_users: Arc::clone(&self.interested_users), is_open: Arc::clone(&self.is_open), diff --git a/src/anonymous_clients/anonymous_manager.rs b/src/anonymous_clients/anonymous_manager.rs index b72bb30..70d8068 100644 --- a/src/anonymous_clients/anonymous_manager.rs +++ b/src/anonymous_clients/anonymous_manager.rs @@ -6,18 +6,18 @@ use std::sync::Arc; use crate::anonymous_clients::anonymous_client_connection::AnonymousClientConnection; -static ANONYMOUS_USERS: Lazy>> = +static ANONYMOUS_USERS: Lazy>> = Lazy::new(|| DashMap::new()); pub async fn add_anonymous_user(connection: Arc) { - ANONYMOUS_USERS.insert(connection.get_user_id().await, connection); + ANONYMOUS_USERS.insert(connection.get_user_id(), connection); } -pub async fn remove_anonymous_user(user_id: i64) { +pub async fn remove_anonymous_user(user_id: u64) { ANONYMOUS_USERS.remove(&user_id); } -pub async fn get_anonymous_user(user_id: i64) -> Option> { +pub async fn get_anonymous_user(user_id: u64) -> Option> { ANONYMOUS_USERS.get(&user_id).map(|c| c.clone()) } diff --git a/src/calls/call_group.rs b/src/calls/call_group.rs index 474a90d..2bc8c0b 100644 --- a/src/calls/call_group.rs +++ b/src/calls/call_group.rs @@ -1,11 +1,11 @@ -use json::JsonValue; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; + use std::{env, sync::Arc, time::Duration}; use tokio::sync::RwLock; use uuid::Uuid; use crate::{ calls::{call_util, caller::Caller}, - data::communication::{CommunicationType, CommunicationValue, DataTypes}, omega::omega_connection::get_omega_connection, }; @@ -28,7 +28,7 @@ impl CallGroup { } } - pub async fn get_caller(&self, user_id: i64) -> Option> { + pub async fn get_caller(&self, user_id: u64) -> Option> { self.members .read() .await @@ -62,7 +62,7 @@ impl CallGroup { let response_cv = get_omega_connection() .await_response( &CommunicationValue::new(CommunicationType::shorten_link) - .add_data(DataTypes::link, JsonValue::from(long_link)), + .add_data(DataTypes::link, DataValue::Str(long_link)), Some(Duration::from_secs(20)), ) .await; @@ -70,7 +70,6 @@ impl CallGroup { *self.short_link.write().await = Some( response .get_data(DataTypes::link) - .unwrap() .as_str() .unwrap() .to_string(), @@ -84,7 +83,7 @@ impl CallGroup { } } - pub async fn create_anonymous_token(&self, user_id: i64) -> Option { + pub async fn create_anonymous_token(&self, user_id: u64) -> Option { if self.is_anonymous().await { if let Ok(token) = call_util::create_token(user_id, self.call_id, false) { return Some(token); @@ -93,7 +92,7 @@ impl CallGroup { None } - pub async fn remove_caller(&self, user_id: i64) { + pub async fn remove_caller(&self, user_id: u64) { let _ = call_util::remove_participant(self.call_id, user_id).await; self.members .write() diff --git a/src/calls/call_manager.rs b/src/calls/call_manager.rs index 73e7722..051a262 100644 --- a/src/calls/call_manager.rs +++ b/src/calls/call_manager.rs @@ -7,7 +7,7 @@ use crate::calls::{call_group::CallGroup, caller::Caller}; pub static CALL_GROUPS: Lazy>> = Lazy::new(|| DashMap::new()); -pub async fn get_call_invites(user_id: i64) -> Vec> { +pub async fn get_call_invites(user_id: u64) -> Vec> { let mut callers = Vec::new(); for (_, cg) in CALL_GROUPS.clone().into_iter() { let members = cg.members.read().await; @@ -28,7 +28,7 @@ pub async fn get_call(call_id: Uuid) -> Option> { } } -pub async fn get_call_groups(user_id: i64) -> Vec> { +pub async fn get_call_groups(user_id: u64) -> Vec> { let mut call_groups = Vec::new(); for (_, cg) in CALL_GROUPS.clone().into_iter() { let is_member = { @@ -43,7 +43,7 @@ pub async fn get_call_groups(user_id: i64) -> Vec> { call_groups } -pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option { +pub async fn get_call_token(user_id: u64, call_id: Uuid) -> Option { if let Some(cg) = CALL_GROUPS.get(&call_id) { let mut members = cg.members.write().await; @@ -80,7 +80,7 @@ pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option { Some(caller.create_token()) } -pub async fn add_invite(call_id: Uuid, inviter_id: i64, invitee_id: i64) -> bool { +pub async fn add_invite(call_id: Uuid, inviter_id: u64, invitee_id: u64) -> bool { if let Some(cg) = CALL_GROUPS.get(&call_id) { let mut members = cg.members.write().await; diff --git a/src/calls/call_util.rs b/src/calls/call_util.rs index 796fcdb..3d832c3 100644 --- a/src/calls/call_util.rs +++ b/src/calls/call_util.rs @@ -35,7 +35,7 @@ pub fn get_livekit() -> Result<(String, String, String), ()> { Ok((hostname, api_key, api_secret)) } -pub fn create_token(user_id: i64, call_id: Uuid, has_admin: bool) -> Result { +pub fn create_token(user_id: u64, call_id: Uuid, has_admin: bool) -> Result { let (_, api_key, api_secret) = get_livekit()?; let token = access_token::AccessToken::with_api_key(&api_key, &api_secret) @@ -69,7 +69,7 @@ pub async fn get_room(call_id: Uuid) -> Result<(RoomClient, Room), ()> { return Err(()); } -pub async fn remove_participant(call_id: Uuid, user_id: i64) -> Result<(), ()> { +pub async fn remove_participant(call_id: Uuid, user_id: u64) -> Result<(), ()> { if let Ok((hostname, api_key, api_secret)) = get_livekit() { let room_service = RoomClient::with_api_key(&hostname, &api_key, &api_secret); if let Ok(_) = room_service diff --git a/src/calls/caller.rs b/src/calls/caller.rs index ceff545..d4ef493 100644 --- a/src/calls/caller.rs +++ b/src/calls/caller.rs @@ -6,14 +6,14 @@ use uuid::Uuid; use crate::calls::call_util; pub struct Caller { - pub user_id: i64, + pub user_id: u64, pub call_id: Uuid, pub has_admin: bool, pub timeout: RwLock, } impl Caller { - pub fn new(user_id: i64, call_id: Uuid, has_admin: bool) -> Self { + pub fn new(user_id: u64, call_id: Uuid, has_admin: bool) -> Self { Caller { user_id, call_id, diff --git a/src/data/communication.rs b/src/data/communication.rs deleted file mode 100755 index 99229f5..0000000 --- a/src/data/communication.rs +++ /dev/null @@ -1,403 +0,0 @@ -use json::number::Number; -use json::{Array, JsonValue, object, parse}; -use std::collections::HashMap; -use std::time::{SystemTime, UNIX_EPOCH}; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; -use uuid::Uuid; - -#[derive(Eq, Hash, PartialEq, EnumIter, Clone, Debug)] -#[allow(non_camel_case_types, dead_code)] -pub enum DataTypes { - error_type, - accepted_ids, - uuid, - register_id, - - link, - - settings, - settings_name, - chat_partner_id, - chat_partner_name, - iota_id, - user_id, - user_ids, - iota_ids, - user_state, - user_states, - user_pings, - call_state, - screen_share, - private_key_hash, - accepted, - accepted_profiles, - denied_profiles, - content, - messages, - notifications, - send_time, - get_time, - get_variant, - shared_secret_own, - shared_secret_other, - shared_secret_sign, - shared_secret, - call_id, - call_token, - untill, - enabled, - start_date, - end_date, - receiver_id, - sender_id, - signature, - signed, - message, - message_state, - last_ping, - ping_iota, - ping_clients, - matches, - omikron, - offset, - amount, - position, - name, - path, - codec, - function, - payload, - result, - interactables, - want_to_watch, - watcher, - created_at, - username, - display, - avatar, - about, - status, - public_key, - sub_level, - sub_end, - community_address, - challenge, - community_title, - communities, - rho_connections, - user, - online_status, - omikron_id, - omikron_connections, - reset_token, - new_token, -} - -impl DataTypes { - pub fn parse(p0: String) -> DataTypes { - for datatype in DataTypes::iter() { - if datatype.to_string().to_lowercase().replace('_', "") - == p0.to_lowercase().replace('_', "") - { - return datatype; - } - } - DataTypes::error_type - } - pub fn to_string(&self) -> String { - return format!("{:?}", self); - } -} - -#[derive(PartialEq, Clone, EnumIter, Debug)] -#[allow(non_camel_case_types, dead_code)] -pub enum CommunicationType { - error, - error_anonymous, - error_internal, - error_invalid_data, - error_invalid_user_id, - error_invalid_omikron_id, - error_not_found, - error_not_authenticated, - error_no_iota, - error_invalid_challenge, - error_invalid_secret, - error_invalid_private_key, - error_invalid_public_key, - error_no_user_id, - error_no_call_id, - error_invalid_call_id, - success, - - shorten_link, - - settings_save, - settings_load, - settings_list, - message, - message_state, - message_send, - message_live, - message_other_iota, - message_chunk, - messages_get, - - push_notification, - read_notification, - get_notifications, - - change_confirm, - confirm_receive, - confirm_read, - get_chats, - get_states, - add_community, - remove_community, - get_communities, - challenge, - challenge_response, - register, - register_response, - identification, - identification_response, - register_iota, - register_iota_success, - ping, - pong, - add_conversation, - send_chat, - client_changed, - client_connected, - client_disconnected, - client_closed, - public_key, - private_key, - webrtc_sdp, - webrtc_ice, - start_stream, - end_stream, - watch_stream, - call_token, - call_invite, - call_disconnect_user, - call_timeout_user, - call_set_anonymous_joining, - end_call, - function, - update, - create_user, - rho_update, - - user_connected, - user_disconnected, - iota_connected, - iota_disconnected, - sync_client_iota_status, - - get_user_data, - get_iota_data, - iota_user_data, - - change_user_data, - change_iota_data, - - get_register, - complete_register_user, - complete_register_iota, - delete_user, - delete_iota, - - start_register, - complete_register, -} -impl CommunicationType { - pub fn parse(p0: String) -> CommunicationType { - for datatype in CommunicationType::iter() { - if datatype.to_string().to_lowercase().replace('_', "") - == p0.to_lowercase().replace('_', "") - { - return datatype; - } - } - CommunicationType::error - } - pub fn to_string(&self) -> String { - return format!("{:?}", self); - } -} - -#[derive(Debug, Clone)] -pub struct CommunicationValue { - id: Uuid, - comm_type: CommunicationType, - sender: i64, - receiver: i64, - data: HashMap, -} - -#[allow(dead_code)] -impl CommunicationValue { - pub fn new(comm_type: CommunicationType) -> Self { - Self { - id: Uuid::new_v4(), - comm_type, - sender: 0, - receiver: 0, - 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_sender(mut self, sender: i64) -> Self { - self.sender = sender; - self - } - pub fn get_sender(&self) -> i64 { - self.sender.clone() - } - pub fn with_receiver(mut self, receiver: i64) -> Self { - self.receiver = receiver; - self - } - pub fn get_receiver(&self) -> i64 { - self.receiver.clone() - } - pub fn add_data_num(mut self, key: DataTypes, value: Number) -> Self { - self.data.insert(key, JsonValue::Number(value)); - self - } - pub fn add_data_str(mut self, key: DataTypes, value: String) -> Self { - self.data.insert(key, JsonValue::String(value)); - self - } - pub fn add_data(mut self, key: DataTypes, value: JsonValue) -> Self { - self.data.insert(key, value); - self - } - pub fn add_array(mut self, key: DataTypes, value: Array) -> Self { - self.data.insert(key, JsonValue::Array(value)); - self - } - pub fn get_data(&self, key: DataTypes) -> Option<&JsonValue> { - self.data.get(&key) - } - - pub fn get_type(&self) -> CommunicationType { - self.comm_type.clone() - } - pub 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)] = JsonValue::from(v.clone()); - } - if self.sender > 0 && self.receiver > 0 { - object! { - id: self.id.to_string(), - type: format!("{:?}", self.comm_type), - sender: self.sender, - receiver: self.receiver, - data: jdata - } - } else if self.sender > 0 { - object! { - id: self.id.to_string(), - type: format!("{:?}", self.comm_type), - sender: self.sender, - data: jdata - } - } else if self.receiver > 0 { - object! { - id: self.id.to_string(), - type: format!("{:?}", self.comm_type), - receiver: self.receiver, - data: jdata - } - } else { - object! { - id: self.id.to_string(), - type: format!("{:?}", self.comm_type), - data: jdata - } - } - } - - pub fn from_json(json_str: &str) -> Self { - if let Ok(parsed) = parse(json_str) { - let comm_type = CommunicationType::parse(parsed["type"].to_string()); - let mut sender: i64 = 0; - if parsed.has_key("sender") { - sender = parsed["sender"].as_i64().unwrap_or(0); - } - let mut receiver: i64 = 0; - if parsed.has_key("receiver") { - receiver = parsed["receiver"].as_i64().unwrap_or(0); - } - - 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() { - data.insert(DataTypes::parse(k.to_string()), v.clone()); - } - } - - Self { - id: uuid, - comm_type, - sender, - receiver, - data, - } - } else { - Self { - id: Uuid::new_v4(), - comm_type: CommunicationType::error, - sender: 0, - receiver: 0, - data: HashMap::new(), - } - } - } - pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue { - let receiver = original - .get_data(DataTypes::receiver_id) - .unwrap_or(&JsonValue::Number(Number::from(0))) - .as_i64() - .unwrap_or(0); - - let now_ms = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as i64; - - let sender = original.get_sender(); - CommunicationValue::new(CommunicationType::message_other_iota) - .with_id(original.get_id()) - .with_receiver(receiver) - .add_data( - DataTypes::receiver_id, - JsonValue::Number(Number::from(receiver)), - ) - .with_sender(sender) - .add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string())) - .add_data( - DataTypes::sender_id, - JsonValue::Number(Number::from(sender)), - ) - .add_data( - DataTypes::content, - JsonValue::String(original.get_data(DataTypes::content).unwrap().to_string()), - ) - } -} diff --git a/src/data/mod.rs b/src/data/mod.rs index 7b9e99f..22d12a3 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,2 +1 @@ -pub mod communication; pub mod user; diff --git a/src/main.rs b/src/main.rs index 1e92720..82b8f7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,24 +5,17 @@ mod omega; mod rho; mod util; -use async_tungstenite::accept_hdr_async; +use std::env; + use dotenv::dotenv; -use futures::StreamExt; use once_cell::sync::Lazy; -use std::{env, sync::Arc}; -use tokio::net::TcpListener; -use tokio_util::compat::TokioAsyncReadCompatExt; -use tungstenite::handshake::server::{Request, Response}; use crate::{ - anonymous_clients::{ - anonymous_client_connection::AnonymousClientConnection, anonymous_manager, - }, calls::call_util::garbage_collect_calls, - rho::{client_connection::ClientConnection, iota_connection::IotaConnection}, + rho::server::start, util::{ crypto_helper::{load_public_key, load_secret_key}, - logger::{PrintType, startup}, + logger::startup, }, }; @@ -39,191 +32,8 @@ pub fn get_public_key() -> x448::PublicKey { async fn main() { dotenv().ok(); startup(); - let address = format!( - "{}:{}", - env::var("IP").unwrap_or("0.0.0.0".to_string()), - env::var("PORT").unwrap_or("959".to_string()) - ); - let listener = TcpListener::bind(&address).await.unwrap(); - log!( - 0, - PrintType::General, - "WebSocket server listening on {}", - address, - ); + start(959).await; garbage_collect_calls(); - - while let Ok((stream, _)) = listener.accept().await { - tokio::spawn(async move { - let mut path: String = "/".to_string(); - - let callback = |req: &Request, response: Response| { - path = req.uri().path().to_string(); - Ok(response) - }; - let ws_stream = match accept_hdr_async(stream.compat(), callback).await { - Ok(ws) => ws, - Err(e) => { - log!(0, PrintType::General, "WebSocket upgrade failed: {}", e,); - return; - } - }; - let (sender, receiver) = ws_stream.split(); - if path.starts_with("/ws/client") { - log_in!(0, PrintType::Client, "New Client connection"); - let client_conn: Arc = - Arc::from(ClientConnection::new(sender, receiver)); - loop { - let msg_result = { - let mut session_lock = client_conn.receiver.write().await; - session_lock.next().await - }; - - match msg_result { - Some(Ok(msg)) => { - if msg.is_text() { - let text = msg.into_text().unwrap(); - client_conn.clone().handle_message(text).await; - } else if msg.is_close() { - log_in!( - client_conn.get_user_id().await, - PrintType::Client, - "Client disconnected" - ); - client_conn.handle_close().await; - return; - } - } - Some(Err(e)) => { - log_err!( - client_conn.get_user_id().await, - PrintType::Client, - "WebSocket error: {}", - e - ); - client_conn.handle_close().await; - return; - } - _ => { - log_in!( - client_conn.get_user_id().await, - PrintType::Client, - "Client stream ended" - ); - client_conn.handle_close().await; - return; - } - } - } - } else if path.starts_with("/ws/anonymous_client") { - log_in!(0, PrintType::Client, "New Anonymous Client connection"); - let client_conn: Arc = - Arc::from(AnonymousClientConnection::new(sender, receiver)); - anonymous_manager::add_anonymous_user(client_conn.clone()).await; - loop { - let msg_result = { - let mut session_lock = client_conn.receiver.write().await; - session_lock.next().await - }; - - match msg_result { - Some(Ok(msg)) => { - if msg.is_text() { - let text = msg.into_text().unwrap(); - client_conn.clone().handle_message(text).await; - } else if msg.is_close() { - log_in!( - client_conn.get_user_id().await, - PrintType::Client, - "Anonymous Client disconnected" - ); - anonymous_manager::remove_anonymous_user( - client_conn.get_user_id().await, - ) - .await; - client_conn.handle_close().await; - return; - } - } - Some(Err(e)) => { - log_err!( - client_conn.get_user_id().await, - PrintType::Client, - "WebSocket error: {}", - e - ); - anonymous_manager::remove_anonymous_user( - client_conn.get_user_id().await, - ) - .await; - client_conn.handle_close().await; - return; - } - _ => { - log_in!( - client_conn.get_user_id().await, - PrintType::Client, - "Anonymous Client stream ended" - ); - anonymous_manager::remove_anonymous_user( - client_conn.get_user_id().await, - ) - .await; - client_conn.handle_close().await; - return; - } - } - } - } else if path.starts_with("/ws/iota") { - log_in!(0, PrintType::Iota, "New Iota connection"); - let iota_conn: Arc = - Arc::from(IotaConnection::new(sender, receiver)); - loop { - let msg_result = { - let mut session_lock = iota_conn.receiver.write().await; - session_lock.next().await - }; - - match msg_result { - Some(Ok(msg)) => { - if msg.is_text() { - let text = msg.into_text().unwrap(); - iota_conn.clone().handle_message(text).await; - } else if msg.is_close() { - log_in!( - iota_conn.get_iota_id().await, - PrintType::Iota, - "Iota disconnected" - ); - iota_conn.handle_close().await; - return; - } - } - Some(Err(e)) => { - log_err!( - iota_conn.get_iota_id().await, - PrintType::Iota, - "WebSocket error: {}", - e - ); - iota_conn.handle_close().await; - return; - } - _ => { - // Stream ended - log_in!( - iota_conn.get_iota_id().await, - PrintType::Iota, - "Iota stream ended" - ); - iota_conn.handle_close().await; - return; - } - } - } - } - }); - } } diff --git a/src/omega/mod.rs b/src/omega/mod.rs index cb00438..cbe77d9 100644 --- a/src/omega/mod.rs +++ b/src/omega/mod.rs @@ -1,2 +1 @@ pub mod omega_connection; -pub mod ping_pong_task; diff --git a/src/omega/omega_connection.rs b/src/omega/omega_connection.rs index 5869455..30e9cf5 100755 --- a/src/omega/omega_connection.rs +++ b/src/omega/omega_connection.rs @@ -1,392 +1,165 @@ -use async_tungstenite::{ - WebSocketReceiver, WebSocketSender, - stream::Stream, - tokio::{TokioAdapter, connect_async}, - tungstenite::protocol::Message, +use std::{ + sync::Arc, + time::{Duration, Instant}, }; -use crossterm::style::Print; + use dashmap::DashMap; -use futures::prelude::*; -use json::{JsonValue, number::Number}; use once_cell::sync::Lazy; -use std::{collections::HashMap, env, sync::Arc, time::Duration}; -use tokio::{ - net::TcpStream, - sync::{Mutex, RwLock, mpsc}, - time::{Instant, sleep}, -}; -use tokio_native_tls::TlsStream; -use uuid::Uuid; +use tokio::sync::{Mutex, RwLock, mpsc}; -use crate::{ - data::{ - communication::{CommunicationType, CommunicationValue, DataTypes}, - user::UserStatus, - }, - get_private_key, log, log_cv_out, log_in, log_out, - rho::rho_manager::{self, RHO_CONNECTIONS, connection_count}, - util::{ - crypto_helper::{decrypt_b64, secret_key_to_base64}, - logger::PrintType, - }, -}; -use crate::{log_cv_in, log_err}; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use epsilon_native::{Receiver, Sender, connect}; -pub struct WaitingTask { - pub task: Box, CommunicationValue) -> bool + Send + Sync>, - pub inserted_at: Instant, -} +use crate::{data::user::UserStatus, rho::rho_manager}; -pub static WAITING_TASKS: Lazy> = Lazy::new(DashMap::new); +static WAITING: Lazy< + DashMap< + u32, + ( + Instant, + Box, CommunicationValue) -> bool + Send + Sync>, + ), + >, +> = Lazy::new(DashMap::new); -static OMEGA_CONNECTION: Lazy> = Lazy::new(|| { - let conn = Arc::new(OmegaConnection::new()); - let conn_clone = conn.clone(); - tokio::spawn(async move { - conn_clone.connect_internal(0).await; - }); +static OMEGA_CONNECTION: Lazy> = Lazy::new(|| OmegaConnection::new()); - tokio::spawn(async { - loop { - sleep(Duration::from_secs(60)).await; - WAITING_TASKS.retain(|_, v| v.inserted_at.elapsed() < Duration::from_secs(60)); - } - }); - - conn -}); - -#[allow(dead_code)] pub fn get_omega_connection() -> Arc { OMEGA_CONNECTION.clone() } -#[derive(Clone)] -pub struct OmegaConnection { - write: Arc< - Mutex< - Option< - WebSocketSender< - Stream, TokioAdapter>>, - >, - >, - >, - >, - read: Arc< - Mutex< - Option< - WebSocketReceiver< - Stream, TokioAdapter>>, - >, - >, - >, - >, - pingpong: Arc>>>, - pub last_ping: Arc>, - pub message_send_times: Arc>>, - state: Arc>, -} - -#[derive(Clone, PartialEq, Eq)] -enum ConnectionState { +#[derive(Clone, PartialEq)] +enum State { Disconnected, Connecting, Connected, } +pub struct OmegaConnection { + sender: Arc>>, + receiver: Arc>>, + state: Arc>, +} + impl OmegaConnection { - pub fn new() -> Self { - OmegaConnection { - read: Arc::new(Mutex::new(None)), - write: Arc::new(Mutex::new(None)), - pingpong: Arc::new(Mutex::new(None)), - last_ping: Arc::new(Mutex::new(-1)), - message_send_times: Arc::new(Mutex::new(HashMap::new())), - state: Arc::new(RwLock::new(ConnectionState::Disconnected)), - } + pub fn new() -> Arc { + Arc::new(Self { + sender: Arc::new(Mutex::new(None)), + receiver: Arc::new(Mutex::new(None)), + state: Arc::new(RwLock::new(State::Disconnected)), + }) } - async fn connect_internal(self: Arc, mut retry: usize) { - if self.state.read().await.eq(&ConnectionState::Connected) { - return; - } - if self.state.read().await.eq(&ConnectionState::Connecting) { - let start = Instant::now(); - let timeout = Duration::from_secs(10); - while self.state.read().await.eq(&ConnectionState::Connecting) - && start.elapsed() < timeout - { - sleep(Duration::from_millis(100)).await; - } - return; - } - *self.state.write().await = ConnectionState::Connecting; + pub async fn connect(self: Arc, addr: &str) -> Result<(), String> { + *self.state.write().await = State::Connecting; - if let Some(handle) = self.pingpong.lock().await.take() { - handle.abort(); - } + let (sender, receiver) = connect(addr) + .await + .map_err(|e| format!("Connect error: {e:?}"))?; - loop { - if retry > 500 { - log_err!( - 0, - PrintType::Omega, - "Max retry attempts reached, giving up." - ); - *self.state.write().await = ConnectionState::Disconnected; - return; - } + *self.sender.lock().await = Some(sender); + *self.receiver.lock().await = Some(receiver); - let url_str = - env::var("OMEGA_HOST").unwrap_or("wss://omega.tensamin.net/ws/omikron".to_string()); - match connect_async(&url_str).await { - Ok((ws_stream, _)) => { - log_in!(0, PrintType::Omega, "WebSocket connected to {}", url_str); - retry = 0; - let (write, read) = ws_stream.split(); - *self.read.lock().await = Some(read); - *self.write.lock().await = Some(write); + *self.state.write().await = State::Connected; - *self.state.write().await = ConnectionState::Connected; + let read_self = self.clone(); + tokio::spawn(async move { + read_self.read_loop().await; + }); - let read_loop_self = self.clone(); - let read_loop_handle = tokio::spawn(async move { - read_loop_self.read_loop().await; - }); + self.identify().await?; - let cloned_self = self.clone(); - tokio::spawn(async move { - let id = Uuid::new_v4(); + Ok(()) + } - let identify_msg = - CommunicationValue::new(CommunicationType::identification) - .with_id(id) - .add_data( - DataTypes::omikron, - JsonValue::Number(Number::from( - env::var("ID") - .unwrap_or("0".to_string()) - .parse::() - .unwrap_or(0), - )), - ); - WAITING_TASKS.insert( - id, - WaitingTask { - task: Box::new(|selfc, cv| { - if cv.is_type(CommunicationType::error_not_found) { - log_err!(0, - PrintType::Omega, - "Identification failed: Omikron ID not found on Omega.", - ); - return false; - } - if !cv.is_type(CommunicationType::challenge) { - return false; - } - tokio::spawn(async move { - let task = async move { - let challenge = cv - .get_data(DataTypes::challenge) - .and_then(|v| v.as_str()) - .ok_or_else(|| { - "Challenge not found or not a string".to_string() - })?; + async fn identify(&self) -> Result<(), String> { + let msg = CommunicationValue::new(CommunicationType::identification) + .add_data(DataTypes::omikron, DataValue::Number(1)); - let server_pub_key = cv - .get_data(DataTypes::public_key) - .and_then(|v| v.as_str()) - .ok_or_else(|| { - "Public key from server not found or not a string" - .to_string() - })?; + self.await_response(&msg, Some(Duration::from_secs(10))) + .await?; - let decrypted_challenge = decrypt_b64( - &secret_key_to_base64(&get_private_key()), - server_pub_key, - challenge, - ) - .map_err(|e| { - format!("Failed to decrypt challenge: {:?}", e) - })?; - - let response_msg = CommunicationValue::new( - CommunicationType::challenge_response, - ) - .with_id(cv.get_id()) - .add_data( - DataTypes::challenge, - JsonValue::String(decrypted_challenge), - ); - - let response_id = response_msg.get_id(); - WAITING_TASKS.insert( - response_id, - WaitingTask { - task: Box::new(|selfc, final_cv| { - if !final_cv - .is_type(CommunicationType::identification_response) - { - log_err!(0, - PrintType::Omega, - "Expected identification_response, got something else.", - ); - return false; - } - - if let Some(accepted) = final_cv.get_data(DataTypes::accepted).and_then(|v| v.as_bool()) { - if !accepted { - log_err!(0, PrintType::Omega, "Omega did not accept identification."); - return false; - } - } else { - log_err!(0, PrintType::Omega, "Omega response did not contain 'accepted' field."); - return false; - } - - tokio::spawn(async move { - let mut connected_iota_ids: Vec = Vec::new(); - let mut connected_user_ids: Vec = Vec::new(); - let rho_connections_reader = RHO_CONNECTIONS.read().await; - - for iota_id in rho_connections_reader.keys() { - connected_iota_ids.push(JsonValue::from(*iota_id)); - } - - for rho in rho_connections_reader.values() { - for client_conn in rho.get_client_connections().await { - connected_user_ids.push(JsonValue::from(client_conn.get_user_id().await)); - } - } - - drop(rho_connections_reader); - - let sync_msg = CommunicationValue::new(CommunicationType::sync_client_iota_status) - .add_data(DataTypes::iota_ids, JsonValue::Array(connected_iota_ids)) - .add_data(DataTypes::user_ids, JsonValue::Array(connected_user_ids)) - .add_data(DataTypes::rho_connections, JsonValue::from(connection_count().await)); - - selfc.send_message(&sync_msg).await; - }); - log!(0, - PrintType::Omega, - "Successfully identified with Omega.", - ); - true - }), - inserted_at: Instant::now(), - } - ); - - selfc.send_message(&response_msg).await; - - Ok::<(), String>(()) - }; - - if let Err(e) = task.await { - log_err!(0, PrintType::Omega, "{}", &e); - } - }); - - true - }), - inserted_at: Instant::now(), - } - ); - cloned_self.send_message(&identify_msg).await - }); - - let ping_pong_self = self.clone(); - let handle = tokio::spawn(async move { - loop { - if *ping_pong_self.state.read().await != ConnectionState::Connected { - break; - } - ping_pong_self.send_ping().await; - sleep(Duration::from_secs(5)).await; - } - }); - - *self.pingpong.lock().await = Some(handle); - - read_loop_handle.await.unwrap_or_else(|e| { - log_err!(0, PrintType::Omega, "Read loop task failed: {}", e) - }); - - *self.read.lock().await = None; - *self.write.lock().await = None; - *self.state.write().await = ConnectionState::Disconnected; - - log_err!(0, PrintType::Omega, "Connection lost. Retrying..."); - retry += 1; - sleep(Duration::from_secs(2)).await; - } - Err(e) => { - *self.state.write().await = ConnectionState::Disconnected; - log_err!( - 0, - PrintType::Omega, - "WebSocket connection failed (attempt {}): {}", - retry + 1, - e, - ); - retry += 1; - sleep(Duration::from_secs(2)).await; - continue; - } - } - } + Ok(()) } async fn read_loop(self: Arc) { - let mut reader = match self.read.lock().await.take() { - Some(reader) => reader, - None => return, - }; - loop { - let msg = reader.next().await; - - match msg { - Some(Ok(Message::Text(msg))) => { - let cv = CommunicationValue::from_json(&msg); - log_cv_in!(PrintType::Omega, cv); - if cv.is_type(CommunicationType::pong) || cv.is_type(CommunicationType::ping) { - self.handle_pong(&cv, true).await; - continue; - } - let msg_id = cv.get_id(); - if let Some(task) = WAITING_TASKS.remove(&msg_id) { - if (task.1.task)(self.clone(), cv.clone()) { - continue; - } - } else { - } + let result = { + let mut guard = self.receiver.lock().await; + match guard.as_mut() { + Some(receiver) => receiver.receive().await, + None => return, } - #[allow(non_snake_case)] - Some(Ok(Message::Close(_))) | None => break, - Some(Err(_)) => break, - _ => {} + }; + + let cv = match result { + Ok(v) => v, + Err(_) => { + *self.state.write().await = State::Disconnected; + return; + } + }; + + let id = cv.get_id(); + + if let Some((_, task)) = WAITING.remove(&id) { + (task.1)(self.clone(), cv); + continue; + } + + if cv.is_type(CommunicationType::ping) { + let pong = CommunicationValue::new(CommunicationType::pong).with_id(id); + let _ = self.send(&pong).await; } } } - pub async fn send_message(&self, cv: &CommunicationValue) { - log_cv_out!(PrintType::Omega, cv); - let msg = cv.to_json().to_string(); - - let mut guard = self.write.lock().await; - if let Some(ws) = guard.as_mut() { - let _ = ws.send(Message::Text(msg.into())).await; + pub async fn send(&self, cv: &CommunicationValue) -> Result<(), String> { + let guard = self.sender.lock().await; + if let Some(sender) = guard.as_ref() { + sender + .send(cv) + .await + .map_err(|e| format!("Send error: {e:?}")) + } else { + Err("Not connected".into()) } } + pub async fn await_response( + &self, + cv: &CommunicationValue, + timeout: Option, + ) -> Result { + let (tx, mut rx) = mpsc::channel(1); + let id = cv.get_id(); + + WAITING.insert( + id.into(), + ( + Instant::now(), + Box::new(move |_, response| { + let _ = tx.try_send(response); + true + }), + ), + ); + + self.send(cv).await?; + + match tokio::time::timeout(timeout.unwrap_or(Duration::from_secs(10)), rx.recv()).await { + Ok(Some(v)) => Ok(v), + _ => { + WAITING.remove(&id.into()); + Err("Timeout waiting for response".into()) + } + } + } pub async fn close_iota(iota_id: i64) { let cv = CommunicationValue::new(CommunicationType::iota_disconnected) - .add_data(DataTypes::iota_id, JsonValue::from(iota_id)); + .add_data(DataTypes::iota_id, DataValue::Number(iota_id)); OmegaConnection::send_global(cv).await; } - pub async fn client_changed(_iota_id: i64, user_id: i64, state: UserStatus) { let msg_type = match state { UserStatus::iota_offline => Some(CommunicationType::user_disconnected), @@ -396,25 +169,28 @@ impl OmegaConnection { if let Some(t) = msg_type { let cv = - CommunicationValue::new(t).add_data(DataTypes::user_id, JsonValue::from(user_id)); + CommunicationValue::new(t).add_data(DataTypes::user_id, DataValue::Number(user_id)); OmegaConnection::send_global(cv).await; } } + async fn send_global(cv: CommunicationValue) { + OMEGA_CONNECTION.send(&cv).await; + } pub async fn user_states(user_id: i64, user_ids: Vec) { let user_ids_str = user_ids .iter() - .map(|id| id.to_string()) - .collect::>() - .join(","); + .map(|id| DataValue::Number(*id)) + .collect::>(); let cv = CommunicationValue::new(CommunicationType::get_states) - .add_data(DataTypes::user_ids, JsonValue::from(user_ids_str)); + .add_data(DataTypes::user_ids, DataValue::Array(user_ids_str)); let msg_id = cv.get_id(); - WAITING_TASKS.insert( + WAITING.insert( msg_id, - WaitingTask { - task: Box::new( + ( + Instant::now(), + Box::new( move |_: Arc, response: CommunicationValue| { tokio::spawn(async move { let rho = rho_manager::get_rho_con_for_user(user_id).await; @@ -427,84 +203,9 @@ impl OmegaConnection { true }, ), - inserted_at: Instant::now(), - }, + ), ); OmegaConnection::send_global(cv).await; } - - async fn send_global(cv: CommunicationValue) { - OMEGA_CONNECTION.send_message(&cv).await; - } - - pub async fn await_connection(&self, timeout_duration: Option) -> Result<(), String> { - if *self.state.read().await == ConnectionState::Connected { - return Ok(()); - } - let timeout = timeout_duration.unwrap_or(Duration::from_secs(10)); - - let start = Instant::now(); - loop { - if *self.state.read().await == ConnectionState::Connected { - return Ok(()); - } - - if start.elapsed() >= timeout { - return Err(format!( - "Connection not established within {} seconds", - timeout.as_secs() - )); - } - - sleep(Duration::from_millis(100)).await; // short interval polling - } - } - - pub async fn await_response( - &self, - cv: &CommunicationValue, - timeout_duration: Option, - ) -> Result { - self.await_connection(timeout_duration).await?; - let (tx, mut rx) = mpsc::channel(1); - let msg_id = cv.get_id(); - - let task_tx = tx.clone(); - WAITING_TASKS.insert( - msg_id, - WaitingTask { - task: Box::new(move |_, response_cv| { - let inner_tx = task_tx.clone(); - tokio::spawn(async move { - if inner_tx.send(response_cv).await.is_err() { - log_err!( - 0, - PrintType::Omega, - "Failed to send response back to awaiter", - ); - } - }); - true - }), - inserted_at: Instant::now(), - }, - ); - - self.send_message(cv).await; - - let timeout = timeout_duration.unwrap_or(Duration::from_secs(10)); - - match tokio::time::timeout(timeout, rx.recv()).await { - Ok(Some(response_cv)) => Ok(response_cv), - Ok(_) => Err("Failed to receive response, channel was closed.".to_string()), - Err(_) => { - WAITING_TASKS.remove(&msg_id); - Err(format!( - "Request timed out after {} seconds.", - timeout.as_secs() - )) - } - } - } } diff --git a/src/omega/ping_pong_task.rs b/src/omega/ping_pong_task.rs index 695ef75..c528abf 100644 --- a/src/omega/ping_pong_task.rs +++ b/src/omega/ping_pong_task.rs @@ -1,30 +1,29 @@ -use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::omega::omega_connection::OmegaConnection; -use json::number::Number; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue, rand_u32}; use std::time::Duration; use tokio::time::Instant; -use uuid::Uuid; + +use crate::omega::omega_connection::OmegaConnection; const PING_TIMEOUT: Duration = Duration::from_secs(30); impl OmegaConnection { pub async fn send_ping(&self) { - let uuid = Uuid::new_v4(); + let id = rand_u32(); let send_time = Instant::now(); let mut message_send_times = self.message_send_times.lock().await; message_send_times.retain(|_uuid, time| time.elapsed() < PING_TIMEOUT); - message_send_times.insert(uuid, send_time); + message_send_times.insert(id as i64, send_time); - self.send_ping_message(uuid).await; + self.send_ping_message(id).await; } - pub async fn send_ping_message(&self, uuid: Uuid) { + pub async fn send_ping_message(&self, id: u32) { let ping_message = CommunicationValue::new(CommunicationType::ping) - .with_id(uuid) - .add_data_num( + .with_id(id) + .add_data( DataTypes::last_ping, - Number::from(*self.last_ping.lock().await), + DataValue::Number(self.last_ping.lock().await.unwrap()), ); self.send_message(&ping_message).await; @@ -34,7 +33,7 @@ impl OmegaConnection { pub async fn handle_pong(&self, cv: &CommunicationValue, _log: bool) { let id = cv.get_id(); let mut message_send_times = self.message_send_times.lock().await; - if let Some(send_time) = message_send_times.remove(&id) { + if let Some(send_time) = message_send_times.remove(&(id as i64)) { let ping = Instant::now().duration_since(send_time).as_millis() as i64; *self.last_ping.lock().await = ping; } diff --git a/src/rho/client_connection.rs b/src/rho/client_connection.rs index a6793a1..13f4e8f 100644 --- a/src/rho/client_connection.rs +++ b/src/rho/client_connection.rs @@ -1,43 +1,24 @@ -use async_tungstenite::tungstenite::Message; -use async_tungstenite::{WebSocketReceiver, WebSocketSender}; -use base64::Engine; -use base64::engine::general_purpose::STANDARD; -use json::JsonValue; -use json::number::Number; -use rand::Rng; -use rand::distributions::Alphanumeric; -use std::str::FromStr; -use std::sync::Arc; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use sysinfo::System; -use tokio::sync::RwLock; -use tokio_util::compat::Compat; -use tungstenite::Utf8Bytes; -use uuid::Uuid; - -use super::{rho_connection::RhoConnection, rho_manager}; use crate::anonymous_clients::anonymous_manager; use crate::calls::{call_manager, call_util}; use crate::omega::omega_connection::get_omega_connection; -use crate::util::crypto_helper::{load_public_key, public_key_to_base64}; -use crate::util::crypto_util::{DataFormat, SecurePayload}; +use crate::rho::connection::GeneralConnection; +use crate::rho::{rho_connection::RhoConnection, rho_manager}; use crate::util::logger::PrintType; -use crate::{ - data::{ - communication::{CommunicationType, CommunicationValue, DataTypes}, - user::UserStatus, - }, - omega::omega_connection::OmegaConnection, -}; -use crate::{get_private_key, get_public_key, log_cv_in, log_in, log_out}; +use crate::{data::user::UserStatus, omega::omega_connection::OmegaConnection}; +use crate::{log_cv_in, log_cv_out, log_out}; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use epsilon_native::{Receiver, Sender}; +use std::str::FromStr; +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tokio::sync::RwLock; +use uuid::Uuid; pub struct ClientConnection { - pub sender: Arc>>>, - pub receiver: Arc>>>, - pub user_id: Arc>, - identified: Arc>, - challenged: Arc>, - challenge: Arc>, + pub user_id: u64, + + pub sender: Arc, + pub receiver: Arc, pub ping: Arc>, pub_key: Arc>>>, pub rho_connection: Arc>>>, @@ -46,34 +27,30 @@ pub struct ClientConnection { } impl ClientConnection { - /// Create a new ClientConnection - pub fn new( - sender: WebSocketSender>, - receiver: WebSocketReceiver>, - ) -> Arc { + pub async fn from_general(general: Arc, user_id: u64) -> Arc { Arc::new(Self { - sender: Arc::new(RwLock::new(sender)), - receiver: Arc::new(RwLock::new(receiver)), - user_id: Arc::new(RwLock::new(0)), - identified: Arc::new(RwLock::new(false)), - challenged: Arc::new(RwLock::new(false)), - challenge: Arc::new(RwLock::new(String::new())), - ping: Arc::new(RwLock::new(-1)), + ping: Arc::new(RwLock::new(0)), pub_key: Arc::new(RwLock::new(None)), rho_connection: Arc::new(RwLock::new(None)), interested_users: Arc::new(RwLock::new(Vec::new())), is_open: Arc::new(RwLock::new(true)), + sender: general.sender.clone(), + receiver: general.receiver.clone(), + user_id: user_id, }) } + pub fn start(self: Arc) { + let self_clone = self.clone(); + tokio::spawn(async move { + while let Ok(cv) = self_clone.receiver.receive().await { + self_clone.clone().handle_message(cv).await; + } + }); + } /// Get the user ID - pub async fn get_user_id(&self) -> i64 { - *self.user_id.read().await - } - - /// Check if connection is identified - pub async fn is_identified(&self) -> bool { - *self.identified.read().await + pub async fn get_user_id(&self) -> u64 { + self.user_id } /// Get current ping @@ -86,211 +63,30 @@ impl ClientConnection { self.rho_connection.read().await.clone() } - /// Send a string message to the client - pub async fn send_message_str(self: Arc, message: &str) { - let mut session = self.sender.write().await; - if let Err(e) = session - .send(Message::Text(Utf8Bytes::from(message.to_string()))) - .await - { - log_out!( - self.get_user_id().await, - PrintType::Client, - "Failed to send message to client: {}", - e, - ); - } - } - /// Send a CommunicationValue to the client pub async fn send_message(self: Arc, cv: &CommunicationValue) { if !*self.is_open.read().await { log_out!( - self.get_user_id().await, + self.user_id as i64, PrintType::Client, "Attempted to send message to a closed connection." ); return; } if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) { - log_out!( - self.get_user_id().await, - PrintType::Client, - "{}", - &cv.to_json().to_string() - ); + log_cv_out!(PrintType::Client, &cv); } - self.send_message_str(&cv.to_json().to_string()).await; + self.sender.send(&cv).await; } /// Handle incoming message from client - pub async fn handle_message(self: Arc, message: Utf8Bytes) { + pub async fn handle_message(self: Arc, cv: CommunicationValue) { tokio::spawn(async move { - let cv = CommunicationValue::from_json(&message); if cv.is_type(CommunicationType::ping) { self.handle_ping(cv).await; return; } log_cv_in!(PrintType::Client, cv); - let identified = *self.identified.read().await; - let challenged = *self.challenged.read().await; - - // Handle identification - if !identified && cv.is_type(CommunicationType::identification) { - let user_id = cv - .get_data(DataTypes::user_id) - .and_then(|v| v.as_i64()) - .unwrap_or(0); - if user_id == 0 { - log_out!( - self.get_user_id().await, - PrintType::Client, - "Invalid USER ID" - ); - self.clone() - .send_error_response(&cv.get_id(), CommunicationType::error_invalid_data) - .await; - self.close().await; - return; - } - - *self.user_id.write().await = user_id; - - let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_user_data) - .with_id(cv.get_id()) - .add_data(DataTypes::user_id, JsonValue::from(user_id)); - - let response_cv = get_omega_connection() - .await_response(&get_pub_key_msg, Some(Duration::from_secs(20))) - .await; - - if let Ok(response_cv) = response_cv { - if !response_cv.is_type(CommunicationType::get_user_data) { - self.clone() - .send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - self.close().await; - return; - } - - let base64_pub = response_cv - .get_data(DataTypes::public_key) - .and_then(|v| v.as_str()) - .unwrap_or(""); - - let pub_key = match load_public_key(base64_pub) { - Some(pk) => pk, - _ => { - self.clone() - .send_error_response( - &cv.get_id(), - CommunicationType::error_invalid_public_key, - ) - .await; - self.close().await; - return; - } - }; - - *self.pub_key.write().await = Some(pub_key.as_bytes().to_vec()); - - let challenge: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(32) - .map(char::from) - .collect(); - - *self.challenge.write().await = challenge.clone(); - - let encrypted_challenge = - SecurePayload::new(&challenge, DataFormat::Raw, get_private_key()) - .unwrap() - .encrypt_x448(pub_key) - .unwrap() - .export(DataFormat::Base64); - - *self.identified.write().await = true; - - let challenge_msg = CommunicationValue::new(CommunicationType::challenge) - .with_id(cv.get_id()) - .add_data_str( - DataTypes::public_key, - public_key_to_base64(&get_public_key()), - ) - .add_data_str(DataTypes::challenge, encrypted_challenge); - - self.send_message(&challenge_msg).await; - } else { - self.clone() - .send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - self.close().await; - return; - } - - return; - } - - if identified && !challenged && cv.is_type(CommunicationType::challenge_response) { - let client_response = cv - .get_data(DataTypes::challenge) - .and_then(|v| v.as_str()) - .unwrap_or(""); - - let debase64d = STANDARD.decode(&client_response).unwrap(); - if String::from_utf8(debase64d.clone()).unwrap() == *self.challenge.read().await { - *self.challenged.write().await = true; - - let user_id = self.get_user_id().await; - - let rho_connection = match rho_manager::get_rho_con_for_user(user_id).await { - Some(rho) => rho, - _ => { - self.send_error_response( - &cv.get_id(), - CommunicationType::error_no_iota, - ) - .await; - return; - } - }; - rho_connection.add_client_connection(self.clone()).await; - - // Set identification data - { - let mut user_id_guard = self.user_id.write().await; - *user_id_guard = user_id; - } - { - let mut identified_guard = self.identified.write().await; - *identified_guard = true; - } - *self.rho_connection.write().await = Some(Arc::clone(&rho_connection)); - - let response = - CommunicationValue::new(CommunicationType::identification_response) - .with_id(cv.get_id()); - self.send_message(&response).await; - } else { - self.clone() - .send_error_response( - &cv.get_id(), - CommunicationType::error_not_authenticated, - ) - .await; - self.close().await; - return; - } - return; - } - - if !self.is_identified().await { - self.clone() - .send_error_response(&cv.get_id(), CommunicationType::error_not_authenticated) - .await; - self.close().await; - return; - } // Handle client status changes if cv.is_type(CommunicationType::client_changed) { @@ -326,17 +122,9 @@ impl ClientConnection { } if cv.is_type(CommunicationType::get_user_data) { if let Some(anonymous) = { - if let Some(user_id) = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - { - anonymous_manager::get_anonymous_user(user_id).await - } else if let Some(username) = cv - .get_data(DataTypes::username) - .unwrap_or(&JsonValue::Null) - .as_str() - { + if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { + anonymous_manager::get_anonymous_user(user_id as u64).await + } else if let Some(username) = cv.get_data(DataTypes::username).as_str() { anonymous_manager::get_anonymous_user_by_name(username.to_string()).await } else { None @@ -344,14 +132,23 @@ impl ClientConnection { } { let response = CommunicationValue::new(CommunicationType::get_user_data) .with_id(cv.get_id()) - .add_data_str(DataTypes::username, anonymous.get_user_name().await) + .add_data( + DataTypes::username, + DataValue::Str(anonymous.get_user_name().await), + ) .add_data( DataTypes::user_id, - JsonValue::Number(Number::from(anonymous.get_user_id().await)), + DataValue::Number(anonymous.get_user_id() as i64), ) - .add_data_str(DataTypes::display, anonymous.get_display_name().await) - .add_data_str(DataTypes::user_state, "online".to_string()) - .add_data_str(DataTypes::avatar, anonymous.get_avatar().await); + .add_data( + DataTypes::display, + DataValue::Str(anonymous.get_display_name().await), + ) + .add_data( + DataTypes::avatar, + DataValue::Str(anonymous.get_avatar().await), + ) + .add_data(DataTypes::user_state, DataValue::Str("online".to_string())); self.send_message(&response).await; @@ -367,7 +164,8 @@ impl ClientConnection { || cv.is_type(CommunicationType::delete_user) { let sender = self.get_user_id().await; - self.handle_omega_forward(cv.with_sender(sender)).await; + self.handle_omega_forward(cv.with_sender(sender as u64)) + .await; return; } // Forward other messages to Iota @@ -378,10 +176,7 @@ impl ClientConnection { let client_for_closure = self.clone(); tokio::spawn(async move { let response_cv = get_omega_connection() - .await_response( - &cv.with_sender(*self.user_id.read().await), - Some(Duration::from_secs(20)), - ) + .await_response(&cv.with_sender(self.user_id), Some(Duration::from_secs(20))) .await; if let Ok(response_cv) = response_cv { client_for_closure.send_message(&response_cv).await; @@ -392,13 +187,13 @@ impl ClientConnection { /// Handle ping message async fn handle_ping(self: Arc, cv: CommunicationValue) { // Update our ping if provided - if let Some(last_ping) = cv.get_data(DataTypes::last_ping) { + if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) { let current = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis(); let mut ping_guard = self.ping.write().await; - *ping_guard = current as i64 - last_ping.as_i64().unwrap(); + *ping_guard = current as i64 - last_ping; } // Get Iota ping from RhoConnection @@ -411,7 +206,7 @@ impl ClientConnection { // Send pong response let response = CommunicationValue::new(CommunicationType::pong) .with_id(cv.get_id()) - .add_data(DataTypes::ping_iota, JsonValue::from(iota_ping)); + .add_data(DataTypes::ping_iota, DataValue::Number(iota_ping)); self.send_message(&response).await; } @@ -419,51 +214,47 @@ impl ClientConnection { /// Handle client status change async fn handle_client_changed(self: Arc, cv: CommunicationValue) { let user_id = self.get_user_id().await; - if let Some(_status_str) = cv.get_data(DataTypes::user_state) { + if let DataValue::Str(_status_str) = cv.get_data(DataTypes::user_state) { let user_status = UserStatus::user_online; if let Some(rho_conn) = self.get_rho_connection().await { - OmegaConnection::client_changed(rho_conn.get_iota_id().await, user_id, user_status) - .await; + OmegaConnection::client_changed( + rho_conn.get_iota_id().await as i64, + user_id as i64, + user_status, + ) + .await; } } } /// Handle call invite async fn handle_call_invite(self: Arc, cv: CommunicationValue) { - let receiver_id: i64 = cv - .get_data(DataTypes::receiver_id) - .unwrap_or(&json::JsonValue::Number(Number::from(0))) - .as_i64() - .unwrap_or(0); + let receiver_id: i64 = cv.get_data(DataTypes::receiver_id).as_number().unwrap_or(0); if receiver_id == 0 { - self.send_error_response(&cv.get_id(), CommunicationType::error_no_user_id) + self.send_error_response(cv.get_id(), CommunicationType::error_no_user_id) .await; return; } let call_id = match cv.get_data(DataTypes::call_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { + DataValue::Str(id_str) => match Uuid::parse_str(id_str.as_str()) { Ok(id) => id, Err(_) => { - self.send_error_response( - &cv.get_id(), - CommunicationType::error_invalid_call_id, - ) - .await; + self.send_error_response(cv.get_id(), CommunicationType::error_invalid_call_id) + .await; return; } }, _ => { - self.send_error_response(&cv.get_id(), CommunicationType::error_no_call_id) + self.send_error_response(cv.get_id(), CommunicationType::error_no_call_id) .await; return; } }; - let invited = - call_manager::add_invite(call_id, *self.user_id.read().await, receiver_id).await; + let invited = call_manager::add_invite(call_id, self.user_id, receiver_id as u64).await; if !invited { - self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id) + self.send_error_response(cv.get_id(), CommunicationType::error_invalid_call_id) .await; return; } @@ -472,7 +263,7 @@ impl ClientConnection { let target_rho = match rho_manager::get_rho_con_for_user(receiver_id).await { Some(rho) => rho, _ => { - self.send_error_response(&cv.get_id(), CommunicationType::error) + self.send_error_response(cv.get_id(), CommunicationType::error) .await; return; } @@ -483,11 +274,14 @@ impl ClientConnection { // Create and send call distribution message let forward = CommunicationValue::new(CommunicationType::call_invite) - .with_receiver(receiver_id) - .with_sender(sender_id) - .add_data_str(DataTypes::call_id, call_id.to_string()) - .add_data_str(DataTypes::receiver_id, receiver_id.to_string()) - .add_data_str(DataTypes::sender_id, sender_id.to_string()); + .with_receiver(receiver_id as u64) + .with_sender(sender_id as u64) + .add_data(DataTypes::call_id, DataValue::Str(call_id.to_string())) + .add_data( + DataTypes::receiver_id, + DataValue::Str(receiver_id.to_string()), + ) + .add_data(DataTypes::sender_id, DataValue::Str(sender_id.to_string())); target_rho.message_to_client(forward).await; @@ -500,16 +294,16 @@ impl ClientConnection { let user_id = self.get_user_id().await; let call_id = match cv.get_data(DataTypes::call_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { + DataValue::Str(id_str) => match Uuid::parse_str(id_str.as_str()) { Ok(id) => id, Err(_) => { - self.send_error_response(&cv.get_id(), CommunicationType::error) + self.send_error_response(cv.get_id(), CommunicationType::error) .await; return; } }, _ => { - self.send_error_response(&cv.get_id(), CommunicationType::error) + self.send_error_response(cv.get_id(), CommunicationType::error) .await; return; } @@ -518,33 +312,20 @@ impl ClientConnection { if let Some(token) = call_manager::get_call_token(user_id, call_id).await { let response = CommunicationValue::new(CommunicationType::call_token) .with_id(cv.get_id()) - .with_receiver(user_id) - .add_data_str(DataTypes::call_token, token); + .with_receiver(user_id as u64) + .add_data(DataTypes::call_token, DataValue::Str(token)); self.send_message(&response).await; } else { - self.send_error_response(&cv.get_id(), CommunicationType::error) + self.send_error_response(cv.get_id(), CommunicationType::error) .await; return; } } async fn handle_call_timeout_user(self: Arc, cv: CommunicationValue) { - let call_id = Uuid::from_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap(); - let user_id = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - let untill = cv - .get_data(DataTypes::untill) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); + let call_id = + Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap(); + let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0); + let untill = cv.get_data(DataTypes::untill).as_number().unwrap_or(0); let call = call_manager::get_call(call_id).await; if let Some(call) = call { @@ -554,8 +335,8 @@ impl ClientConnection { .unwrap() .has_admin() { - let _ = call_util::remove_participant(call_id, user_id).await; - call.get_caller(user_id) + let _ = call_util::remove_participant(call_id, user_id as u64).await; + call.get_caller(user_id as u64) .await .unwrap() .set_timeout(untill) @@ -564,18 +345,9 @@ impl ClientConnection { } } async fn handle_call_disconnect_user(self: Arc, cv: CommunicationValue) { - let call_id = Uuid::from_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap(); - let user_id = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); + let call_id = + Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap(); + let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0); let call = call_manager::get_call(call_id).await; if let Some(call) = call { @@ -585,23 +357,14 @@ impl ClientConnection { .unwrap() .has_admin() { - call.remove_caller(user_id).await; + call.remove_caller(user_id as u64).await; } } } async fn handle_call_set_anonymous_joining(self: Arc, cv: CommunicationValue) { - let call_id = Uuid::from_str( - cv.get_data(DataTypes::call_id) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .unwrap(); - let enable = cv - .get_data(DataTypes::enabled) - .unwrap_or(&JsonValue::Null) - .as_bool() - .unwrap_or(true); + let call_id = + Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap(); + let enable = cv.get_data(DataTypes::enabled).as_bool().unwrap_or(true); let call = call_manager::get_call(call_id).await; @@ -620,10 +383,10 @@ impl ClientConnection { let mut response_cv = CommunicationValue::new(CommunicationType::call_set_anonymous_joining) .with_id(cv.get_id()) - .add_data(DataTypes::call_id, JsonValue::String(call_id.to_string())) - .add_data(DataTypes::enabled, JsonValue::Boolean(enable)); + .add_data(DataTypes::call_id, DataValue::Str(call_id.to_string())) + .add_data(DataTypes::enabled, DataValue::Bool(enable)); if let Some(short_link) = short_link { - response_cv = response_cv.add_data(DataTypes::link, JsonValue::String(short_link)); + response_cv = response_cv.add_data(DataTypes::link, DataValue::Str(short_link)); } self.send_message(&response_cv).await; } @@ -631,11 +394,13 @@ impl ClientConnection { /// Forward message to Iota async fn forward_to_iota(self: Arc, cv: CommunicationValue) { if cv.is_type(CommunicationType::add_conversation) - && cv.get_data(DataTypes::chat_partner_id).is_none() + && cv + .get_data(DataTypes::chat_partner_id) + .as_number() + .is_some() { let chat_partner_name = cv .get_data(DataTypes::chat_partner_name) - .unwrap_or(&JsonValue::Null) .as_str() .unwrap_or("") .to_string(); @@ -644,7 +409,7 @@ impl ClientConnection { .await .is_some() { - self.send_error_response(&cv.get_id(), CommunicationType::error_anonymous) + self.send_error_response(cv.get_id(), CommunicationType::error_anonymous) .await; return; } @@ -655,25 +420,22 @@ impl ClientConnection { .with_id(cv.clone().get_id()) .add_data( DataTypes::username, - JsonValue::from(chat_partner_name.clone()), + DataValue::Str(chat_partner_name.clone()), ), Some(Duration::from_secs(20)), ) .await; let chat_partner_id = { if let Ok(load_uuid_response) = load_uuid_response { - load_uuid_response - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .clone() + load_uuid_response.get_data(DataTypes::user_id).clone() } else { - JsonValue::Null + DataValue::Null } }; if let Some(rho_conn) = self.get_rho_connection().await { let updated_cv = cv - .with_sender(self.get_user_id().await) + .with_sender(self.get_user_id().await as u64) .add_data(DataTypes::chat_partner_id, chat_partner_id); rho_conn.message_to_iota(updated_cv).await; } @@ -681,18 +443,14 @@ impl ClientConnection { } if let Some(rho_conn) = self.get_rho_connection().await { - let updated_cv = cv.with_sender(self.get_user_id().await); + let updated_cv = cv.with_sender(self.get_user_id().await as u64); rho_conn.message_to_iota(updated_cv).await; } } /// Send error response - async fn send_error_response( - self: Arc, - message_id: &Uuid, - error_type: CommunicationType, - ) { - let error = CommunicationValue::new(error_type).with_id(*message_id); + async fn send_error_response(self: Arc, message_id: u32, error_type: CommunicationType) { + let error = CommunicationValue::new(error_type).with_id(message_id); self.send_message(&error).await; } @@ -704,8 +462,7 @@ impl ClientConnection { } *is_open_guard = false; - let mut session = self.sender.write().await; - let _ = session.close(None).await; + let _ = self.sender.close(); } /// Set interested users list @@ -723,8 +480,8 @@ impl ClientConnection { let interested_guard = self.clone().get_interested_users().await; if interested_guard.contains(&user_id) { let notification = CommunicationValue::new(CommunicationType::client_changed) - .add_data_str(DataTypes::user_id, user_id.to_string()) - .add_data_str(DataTypes::user_state, format!("online")); + .add_data(DataTypes::user_id, DataValue::Str(user_id.to_string())) + .add_data(DataTypes::user_state, DataValue::Str("online".to_string())); self.send_message(¬ification).await; } @@ -732,13 +489,11 @@ impl ClientConnection { /// Handle connection close pub async fn handle_close(&self) { - if self.is_identified().await { - let user_id = self.get_user_id().await; - if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id).await { - rho_conn - .close_client_connection(Arc::new(self.clone())) - .await; - } + let user_id = self.get_user_id().await; + if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id as i64).await { + rho_conn + .close_client_connection(Arc::new(self.clone())) + .await; } } } @@ -749,10 +504,7 @@ impl Clone for ClientConnection { Self { sender: Arc::clone(&self.sender), receiver: Arc::clone(&self.receiver), - user_id: Arc::clone(&self.user_id), - identified: Arc::clone(&self.identified), - challenged: Arc::clone(&self.challenged), - challenge: Arc::clone(&self.challenge), + user_id: self.user_id, ping: Arc::clone(&self.ping), pub_key: Arc::clone(&self.pub_key), rho_connection: Arc::clone(&self.rho_connection), diff --git a/src/rho/connection.rs b/src/rho/connection.rs new file mode 100644 index 0000000..21bd7fe --- /dev/null +++ b/src/rho/connection.rs @@ -0,0 +1,174 @@ +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use epsilon_native::{Receiver, Sender}; +use rand::{Rng, distributions::Alphanumeric}; +use std::{sync::Arc, time::Duration}; +use tokio::sync::RwLock; + +use crate::{ + anonymous_clients::anonymous_client_connection::AnonymousClientConnection, + get_private_key, get_public_key, + omega::omega_connection::get_omega_connection, + rho::{client_connection::ClientConnection, iota_connection::IotaConnection}, + util::{ + crypto_helper::{load_public_key, public_key_to_base64}, + crypto_util::{DataFormat, SecurePayload}, + }, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ConnectionKind { + Client, + Iota, + AnonymousClient, + Phi, +} + +pub struct GeneralConnection { + pub sender: Arc, + pub receiver: Arc, + + identified: Arc>, + challenged: Arc>, + challenge: Arc>, + + connection_kind: Arc>>, + id: Arc>, + + pub_key: Arc>>>, +} +impl GeneralConnection { + pub fn new(sender: Sender, receiver: Receiver) -> Arc { + Arc::new(Self { + sender: Arc::new(sender), + receiver: Arc::new(receiver), + identified: Arc::new(RwLock::new(false)), + challenged: Arc::new(RwLock::new(false)), + challenge: Arc::new(RwLock::new(String::new())), + connection_kind: Arc::new(RwLock::new(None)), + id: Arc::new(RwLock::new(0)), + pub_key: Arc::new(RwLock::new(None)), + }) + } +} +impl GeneralConnection { + pub async fn handle(self: Arc) { + loop { + let cv = match self.receiver.receive().await { + Ok(v) => v, + Err(_) => break, + }; + + if !*self.identified.read().await { + self.handle_identification(cv).await; + continue; + } + + if !*self.challenged.read().await { + self.handle_challenge_response(cv).await; + continue; + } + + if self.migrate().await { + break; + } + } + } + async fn handle_identification(self: &Arc, cv: CommunicationValue) { + if !cv.is_type(CommunicationType::identification) { + return; + } + + if let DataValue::Number(iota_id) = cv.get_data(DataTypes::iota_id) { + *self.id.write().await = *iota_id as u64; + *self.connection_kind.write().await = Some(ConnectionKind::Iota); + + let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_iota_data) + .add_data(DataTypes::iota_id, DataValue::Number(*iota_id)); + + let response_cv = get_omega_connection() + .await_response(&get_pub_key_msg, Some(Duration::from_secs(20))) + .await; + + let response_cv = match response_cv { + Ok(r) => r, + Err(_) => return, + }; + + let base64_pub = response_cv + .get_data(DataTypes::public_key) + .as_str() + .unwrap_or(""); + + let pub_key = match load_public_key(base64_pub) { + Some(pk) => pk, + None => return, + }; + + *self.pub_key.write().await = Some(pub_key.as_bytes().to_vec()); + + let challenge: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(32) + .map(char::from) + .collect(); + + *self.challenge.write().await = challenge.clone(); + *self.identified.write().await = true; + + let encrypted_challenge = + SecurePayload::new(&challenge, DataFormat::Base64, get_private_key()) + .unwrap() + .encrypt_x448(pub_key) + .unwrap() + .export(DataFormat::Base64); + + let response = CommunicationValue::new(CommunicationType::challenge) + .add_data( + DataTypes::public_key, + DataValue::Str(public_key_to_base64(&get_public_key())), + ) + .add_data(DataTypes::challenge, DataValue::Str(encrypted_challenge)); + + let _ = self.sender.send(&response).await; + } + } + async fn handle_challenge_response(self: &Arc, cv: CommunicationValue) { + if !cv.is_type(CommunicationType::challenge_response) { + return; + } + + if let DataValue::Str(response) = cv.get_data(DataTypes::challenge) { + if *response == *self.challenge.read().await { + *self.challenged.write().await = true; + } + } + } + + async fn migrate(self: &Arc) -> bool { + let kind = match *self.connection_kind.read().await { + Some(kind) => kind, + None => return false, + }; + let id = *self.id.read().await; + + match kind { + ConnectionKind::Client => { + let client = ClientConnection::from_general(self.clone(), id).await; + client.start(); + } + ConnectionKind::Iota => { + let iota = IotaConnection::from_general(self.clone(), id).await; + iota.start(); + } + ConnectionKind::AnonymousClient => { + let client = AnonymousClientConnection::from_general(self.clone(), id).await; + client.start(); + } + ConnectionKind::Phi => { + let iota = ClientConnection::from_general(self.clone(), id).await; + iota.start(); + } + } + true + } +} diff --git a/src/rho/iota_connection.rs b/src/rho/iota_connection.rs index e59a6eb..cc7ff64 100755 --- a/src/rho/iota_connection.rs +++ b/src/rho/iota_connection.rs @@ -1,24 +1,18 @@ use crate::calls::call_group::CallGroup; use crate::calls::call_manager; -use crate::get_private_key; -use crate::get_public_key; use crate::log_cv_in; +use crate::log_cv_out; use crate::log_err; -use crate::log_in; -use crate::log_out; use crate::omega::omega_connection::get_omega_connection; -use crate::util::crypto_helper::load_public_key; -use crate::util::crypto_helper::public_key_to_base64; -use crate::util::crypto_util::DataFormat; -use crate::util::crypto_util::SecurePayload; +use crate::rho::connection::GeneralConnection; use crate::util::logger::PrintType; -use async_tungstenite::WebSocketReceiver; -use async_tungstenite::WebSocketSender; -use async_tungstenite::tungstenite::Message; use dashmap::DashMap; -use json::JsonValue; -use rand::Rng; -use rand::distributions::Alphanumeric; +use epsilon_core::CommunicationType; +use epsilon_core::CommunicationValue; +use epsilon_core::DataTypes; +use epsilon_core::DataValue; +use epsilon_native::Receiver; +use epsilon_native::Sender; use std::{ collections::HashMap, sync::{Arc, Weak}, @@ -26,56 +20,48 @@ use std::{ }; use tokio::sync::RwLock; use tokio::sync::mpsc; -use tokio_util::compat::Compat; -use tungstenite::Utf8Bytes; -use uuid::Uuid; use x448::PublicKey; use super::{rho_connection::RhoConnection, rho_manager}; -use crate::{ - data::communication::{CommunicationType, CommunicationValue, DataTypes}, - omega::omega_connection::OmegaConnection, -}; +use crate::omega::omega_connection::OmegaConnection; pub struct IotaConnection { - pub sender: Arc>>>, - pub receiver: Arc>>>, - pub iota_id: Arc>, - pub user_ids: Arc>>, - identified: Arc>, - challenged: Arc>, - challenge: Arc>, + pub iota_id: u64, + pub sender: Arc, + pub receiver: Arc, + pub user_ids: Arc>>, pub ping: Arc>, pub_key: Arc>>>, pub waiting_tasks: - DashMap, CommunicationValue) -> bool + Send + Sync>>, + DashMap, CommunicationValue) -> bool + Send + Sync>>, pub rho_connection: Arc>>>, } impl IotaConnection { - /// Create a new IotaConnection - pub fn new( - sender: WebSocketSender>, - receiver: WebSocketReceiver>, - ) -> Arc { + pub async fn from_general(general: Arc, iota_id: u64) -> Arc { Arc::new(Self { - sender: Arc::new(RwLock::new(sender)), - receiver: Arc::new(RwLock::new(receiver)), - iota_id: Arc::new(RwLock::new(0)), - user_ids: Arc::new(RwLock::new(Vec::new())), - identified: Arc::new(RwLock::new(false)), - challenged: Arc::new(RwLock::new(false)), - challenge: Arc::new(RwLock::new(String::new())), ping: Arc::new(RwLock::new(0)), pub_key: Arc::new(RwLock::new(None)), - waiting_tasks: DashMap::new(), rho_connection: Arc::new(RwLock::new(None)), + user_ids: Arc::new(RwLock::new(Vec::new())), + sender: general.sender.clone(), + receiver: general.receiver.clone(), + iota_id: iota_id, + waiting_tasks: DashMap::new(), }) } + pub fn start(self: Arc) { + let self_clone = self.clone(); + tokio::spawn(async move { + while let Ok(cv) = self_clone.receiver.receive().await { + self_clone.clone().handle_message(cv).await; + } + }); + } /// Get the Iota ID - pub async fn get_iota_id(&self) -> i64 { - *self.iota_id.read().await + pub async fn get_iota_id(&self) -> u64 { + self.iota_id } pub async fn get_public_key(&self) -> Option { @@ -87,15 +73,10 @@ impl IotaConnection { } /// Get the user IDs - pub async fn get_user_ids(&self) -> Vec { + pub async fn get_user_ids(&self) -> Vec { self.user_ids.read().await.clone() } - /// Check if connection is identified - pub async fn is_identified(&self) -> bool { - *self.identified.read().await - } - /// Get current ping pub async fn get_ping(&self) -> i64 { *self.ping.read().await @@ -117,38 +98,16 @@ impl IotaConnection { } } - /// Send a message to the Iota - pub async fn send_message_str(&self, message: &str) { - let mut session = self.sender.write().await; - if let Err(e) = session - .send(Message::Text(Utf8Bytes::from(message.to_string()))) - .await - { - log_err!( - self.get_iota_id().await, - PrintType::Iota, - "Failed to send WebSocket message: {:?}", - e, - ); - } - } - /// Send a CommunicationValue to the Iota pub async fn send_message(&self, cv: &CommunicationValue) { if !cv.is_type(CommunicationType::pong) { - log_out!( - self.get_iota_id().await, - PrintType::Iota, - "{}", - cv.to_json().to_string() - ); + log_cv_out!(PrintType::Iota, cv); } - self.send_message_str(&cv.to_json().to_string()).await; + self.sender.send(&cv).await; } /// Handle incoming message from Iota - pub async fn handle_message(self: Arc, message: Utf8Bytes) { - let cv = CommunicationValue::from_json(&message); + pub async fn handle_message(self: Arc, cv: CommunicationValue) { // Handle ping if cv.is_type(CommunicationType::ping) || cv.is_type(CommunicationType::pong) { self.handle_ping(cv).await; @@ -157,261 +116,6 @@ impl IotaConnection { log_cv_in!(PrintType::Iota, cv); - let identified = *self.identified.read().await; - let challenged = *self.challenged.read().await; - - if !identified && cv.is_type(CommunicationType::identification) { - let iota_id = cv - .get_data(DataTypes::iota_id) - .and_then(|v| v.as_i64()) - .unwrap_or(0); - if iota_id == 0 { - log_out!(self.get_iota_id().await, PrintType::Iota, "Invalid IOTA ID"); - self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_data) - .await; - self.close().await; - return; - } - - *self.iota_id.write().await = iota_id; - - let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_iota_data) - .with_id(cv.get_id()) - .add_data(DataTypes::iota_id, JsonValue::from(iota_id)); - - let response_cv = get_omega_connection() - .await_response(&get_pub_key_msg, Some(Duration::from_secs(20))) - .await; - - if let Ok(response_cv) = response_cv { - if !response_cv.is_type(CommunicationType::get_iota_data) { - self.send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - self.close().await; - return; - } - - let base64_pub = response_cv - .get_data(DataTypes::public_key) - .and_then(|v| v.as_str()) - .unwrap_or(""); - - let pub_key = match load_public_key(base64_pub) { - Some(pk) => pk, - _ => { - self.send_error_response( - &cv.get_id(), - CommunicationType::error_invalid_public_key, - ) - .await; - self.close().await; - return; - } - }; - - *self.pub_key.write().await = Some(pub_key.as_bytes().to_vec()); - - let challenge: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(32) - .map(char::from) - .collect(); - - *self.challenge.write().await = challenge.clone(); - - let encrypted_challenge = - SecurePayload::new(&challenge, DataFormat::Base64, get_private_key()) - .unwrap() - .encrypt_x448(pub_key) - .unwrap() - .export(DataFormat::Base64); - - *self.identified.write().await = true; - - let challenge_msg = CommunicationValue::new(CommunicationType::challenge) - .with_id(cv.get_id()) - .add_data_str( - DataTypes::public_key, - public_key_to_base64(&get_public_key()), - ) - .add_data_str(DataTypes::challenge, encrypted_challenge); - - self.send_message(&challenge_msg).await; - } else { - self.send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - self.close().await; - return; - } - - return; - } else if !identified && cv.is_type(CommunicationType::register_iota) { - let base64_pub = cv - .get_data(DataTypes::public_key) - .and_then(|v| v.as_str()) - .unwrap_or(""); - - if base64_pub.is_empty() { - self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_public_key) - .await; - self.close().await; - return; - } - - let register_msg = CommunicationValue::new(CommunicationType::complete_register_iota) - .with_id(cv.get_id()) - .add_data( - DataTypes::public_key, - JsonValue::String(base64_pub.to_string()), - ); - - let iota_conn_clone = self.clone(); - let register_response: Result = get_omega_connection() - .await_response(®ister_msg, Some(Duration::from_secs(20))) - .await; - let iota_conn_for_task = iota_conn_clone.clone(); - if let Ok(register_response) = register_response { - if !register_response.is_type(CommunicationType::complete_register_iota) { - iota_conn_for_task - .send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - iota_conn_for_task.close().await; - return; - } - - let new_iota_id = register_response - .get_data(DataTypes::iota_id) - .and_then(|v| v.as_i64()) - .unwrap_or(0); - - if new_iota_id == 0 { - iota_conn_for_task - .send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - iota_conn_for_task.close().await; - return; - } - - *iota_conn_for_task.iota_id.write().await = new_iota_id; - *iota_conn_for_task.identified.write().await = true; - - let success_msg = CommunicationValue::new(CommunicationType::success) - .with_id(cv.get_id()) - .add_data(DataTypes::iota_id, JsonValue::from(new_iota_id)); - - iota_conn_for_task.send_message(&success_msg).await; - } else { - iota_conn_for_task - .send_error_response(&cv.get_id(), CommunicationType::error_internal) - .await; - iota_conn_for_task.close().await; - } - return; - } - - if identified && !challenged && cv.is_type(CommunicationType::challenge_response) { - let client_response = cv - .get_data(DataTypes::challenge) - .and_then(|v| v.as_str()) - .unwrap_or(""); - - if client_response == *self.challenge.read().await { - *self.challenged.write().await = true; - - let iota_id = self.get_iota_id().await; - - if rho_manager::contains_iota(iota_id).await { - if let Some(existing_rho) = rho_manager::get_rho_by_iota(iota_id).await { - existing_rho.close_iota_connection().await; - } - } - - // Inform Omega & Verify Users - let iota_users_cv = get_omega_connection() - .await_response( - &CommunicationValue::new(CommunicationType::iota_connected).add_data( - DataTypes::iota_id, - JsonValue::from(self.get_iota_id().await), - ), - Some(Duration::from_secs(20)), - ) - .await; - - let mut user_ids: Vec = Vec::new(); - if let Ok(iota_users_cv) = iota_users_cv { - if !iota_users_cv.is_type(CommunicationType::iota_user_data) { - log_err!( - self.get_iota_id().await, - PrintType::Omikron, - "Invalid communication type {:?}", - iota_users_cv.get_type() - ); - return; - } - let val_user_ids = iota_users_cv.get_data(DataTypes::user_ids).unwrap().clone(); - - match val_user_ids { - JsonValue::Array(arr) => { - for item in arr { - if let JsonValue::Number(_) = item { - user_ids.push(item.as_i64().unwrap_or(0)); - } - } - } - _ => {} - } - } else { - log_err!( - self.get_iota_id().await, - PrintType::Omikron, - "Failed to retrieve user IDs" - ); - } - log_in!( - self.get_iota_id().await, - PrintType::General, - "User IDs: {:?}", - user_ids.clone() - ); - - *self.user_ids.write().await = user_ids.clone(); - let rho_connection = - Arc::new(RhoConnection::new(self.clone(), user_ids.clone()).await); - - self.set_rho_connection(Arc::downgrade(&rho_connection)) - .await; - - rho_manager::add_rho(rho_connection).await; - - let mut str = String::new(); - for id in &user_ids { - str.push_str(&format!(",{}", id)); - } - if !str.is_empty() { - str.remove(0); - } - - self.send_message( - &CommunicationValue::new(CommunicationType::identification_response) - .with_id(cv.get_id()) - .add_data_str(DataTypes::accepted_ids, str) - .add_data_str(DataTypes::accepted, user_ids.len().to_string()), - ) - .await; - } else { - self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_challenge) - .await; - self.close().await; - } - return; - } - - if !self.is_identified().await { - self.send_error_response(&cv.get_id(), CommunicationType::error_not_authenticated) - .await; - self.close().await; - return; - } // Handle GET_CHATS if cv.is_type(CommunicationType::get_chats) { self.handle_get_chats(cv).await; @@ -420,7 +124,7 @@ impl IotaConnection { // Handle forwarding to other Iotas or clients let receiver_id = cv.get_receiver(); - if (receiver_id != 0 && !self.get_user_ids().await.contains(&receiver_id)) + if (receiver_id != 0 && !self.get_user_ids().await.contains(&(receiver_id as u64))) || cv.is_type(CommunicationType::message_other_iota) || cv.is_type(CommunicationType::send_chat) { @@ -437,31 +141,28 @@ impl IotaConnection { || cv.is_type(CommunicationType::delete_iota) { let sender = self.get_iota_id().await; - self.handle_omega_forward(cv.with_sender(sender)).await; + self.handle_omega_forward(cv.with_sender(sender as u64)) + .await; return; } // Forward to client self.forward_to_client(cv).await; } - async fn send_error_response(&self, message_id: &Uuid, error_type: CommunicationType) { - let error = CommunicationValue::new(error_type).with_id(*message_id); + async fn send_error_response(&self, message_id: u32, error_type: CommunicationType) { + let error = CommunicationValue::new(error_type).with_id(message_id); self.send_message(&error).await; } async fn close(&self) { - let mut sender = self.sender.write().await; - let _ = sender.close(None).await; + let _ = self.sender.close(); } async fn handle_omega_forward(self: Arc, cv: CommunicationValue) { let iota_for_closure = self.clone(); tokio::spawn(async move { let response_cv = get_omega_connection() - .await_response( - &cv.with_sender(*self.iota_id.read().await), - Some(Duration::from_secs(20)), - ) + .await_response(&cv.with_sender(self.iota_id), Some(Duration::from_secs(20))) .await; if let Ok(response_cv) = response_cv { iota_for_closure.send_message(&response_cv).await; @@ -470,7 +171,7 @@ impl IotaConnection { } /// Handle ping message async fn handle_ping(&self, cv: CommunicationValue) { - if let Some(last_ping) = cv.get_data(DataTypes::last_ping) { + if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) { if let Ok(ping_val) = last_ping.to_string().parse::() { let mut ping_guard = self.ping.write().await; *ping_guard = ping_val; @@ -483,13 +184,13 @@ impl IotaConnection { HashMap::new() }; - let pings = client_pings + let pings: Vec<(DataTypes, DataValue)> = client_pings .into_iter() - .map(|(k, v)| (k, JsonValue::String(v.to_string()))) + .map(|(k, v)| (DataTypes::parse(k), DataValue::Number(v))) .collect(); let response = CommunicationValue::new(CommunicationType::pong) .with_id(cv.get_id()) - .add_data(DataTypes::ping_clients, JsonValue::Object(pings)); + .add_data(DataTypes::ping_clients, DataValue::Container(pings)); self.send_message(&response).await; } @@ -498,8 +199,8 @@ impl IotaConnection { let receiver_id = cv.get_receiver(); let sender_id = cv.get_sender(); - if self.get_user_ids().await.contains(&sender_id) { - if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id).await { + if self.get_user_ids().await.contains(&(sender_id as u64)) { + if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id as i64).await { target_rho.message_to_iota(cv).await; } else { let error = CommunicationValue::new(CommunicationType::error_no_iota) @@ -511,7 +212,7 @@ impl IotaConnection { self.send_message( &CommunicationValue::new(CommunicationType::error_invalid_user_id).add_data( DataTypes::error_type, - JsonValue::String( + DataValue::Str( "You are sending to another User without authority.".to_string(), ), ), @@ -525,77 +226,98 @@ impl IotaConnection { let receiver_id = cv.get_receiver(); let mut interested_ids: Vec = Vec::new(); - // loading Calls + // ============================ + // Load Calls + // ============================ let calls: Vec> = call_manager::get_call_groups(receiver_id).await; - let mut invites: HashMap> = HashMap::new(); - let empty = &calls.is_empty(); + + let mut invites: HashMap> = HashMap::new(); + let empty = calls.is_empty(); + for call in calls { for inviter in call.members.read().await.iter() { let call_self = call.get_caller(receiver_id).await.unwrap(); - let admin = call_self.has_admin(); + let inviter_id = inviter.user_id; let timeout = *call_self.timeout.read().await; + let admin = call_self.has_admin(); + + // Build call container + let mut call_map: HashMap = HashMap::new(); + + call_map.insert(DataTypes::call_id, DataValue::Str(call.call_id.to_string())); - let mut call_obj = JsonValue::new_object(); - let _ = call_obj.insert("call_id", JsonValue::String(call.call_id.to_string())); if timeout > 0 { - let _ = call_obj.insert("timeout", JsonValue::from(timeout)); - } - if admin { - let _ = call_obj.insert("admin", JsonValue::Boolean(admin)); + call_map.insert(DataTypes::timeout, DataValue::Number(timeout as i64)); } - if let Some(call_ids) = invites.get_mut(&inviter_id) { - call_ids.push(call_obj); - } else { - invites.insert(inviter_id, vec![call_obj]); + if admin { + call_map.insert(DataTypes::has_admin, DataValue::Bool(true)); } + + let call_container = DataValue::container_from_map(&call_map); + + invites + .entry(inviter_id as i64) + .or_insert_with(Vec::new) + .push(call_container); } } - // Process contacts and add call information - let enriched_contacts = if *empty { - if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) { - log_in!(self.get_iota_id().await, PrintType::Call, "Call empty"); - contacts_data.clone() - } else { - log_in!( - self.get_iota_id().await, - PrintType::Call, - "Call empty No Data" - ); - JsonValue::new_array() + // ============================ + // Enrich Contacts + // ============================ + let enriched_contacts = if empty { + match cv.get_data(DataTypes::user_ids) { + DataValue::Array(arr) => DataValue::Array(arr.clone()), + _ => DataValue::Array(vec![]), } } else { - let mut enrc_contacts = JsonValue::new_array(); - if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) { - if let JsonValue::Array(user_ids) = contacts_data { - for user_json in user_ids { - let user_id = user_json["user_id"].as_i64().unwrap_or(0); - interested_ids.push(user_id); - let mut enriched_contact = user_json.clone(); - if let Some(calls) = invites.get(&user_id) { - let _ = - enriched_contact.insert("calls", JsonValue::Array(calls.clone())); + let mut enriched: Vec = Vec::new(); + + if let DataValue::Array(users) = cv.get_data(DataTypes::user_ids) { + for user_val in users { + if let DataValue::Container(entries) = user_val { + let mut user_map: HashMap = + entries.iter().cloned().collect(); + + // extract user_id + if let Some(DataValue::Number(user_id)) = user_map.get(&DataTypes::user_id) + { + interested_ids.push(*user_id); + + // attach calls if exists + if let Some(call_list) = invites.get(user_id) { + user_map + .insert(DataTypes::calls, DataValue::Array(call_list.clone())); + } } - let _ = enrc_contacts.push(enriched_contact); + + enriched.push(DataValue::container_from_map(&user_map)); } - } else { - enrc_contacts = contacts_data.clone(); } } - enrc_contacts + + DataValue::Array(enriched) }; - // Notify OmegaConnection about user states - OmegaConnection::user_states(receiver_id, interested_ids.clone()).await; + // ============================ + // Notify Omega + // ============================ + OmegaConnection::user_states(receiver_id as i64, interested_ids.clone()).await; - // Set interested users in RhoConnection + // ============================ + // Notify Rho + // ============================ if let Some(rho_conn) = self.get_rho_connection().await { - rho_conn.set_interested(receiver_id, interested_ids).await; + rho_conn + .set_interested(receiver_id as i64, interested_ids) + .await; } + // ============================ // Forward to client + // ============================ self.forward_to_client(cv.add_data(DataTypes::user_ids, enriched_contacts)) .await; } @@ -607,7 +329,7 @@ impl IotaConnection { rho_conn.message_to_client(updated_cv).await; } else { log_err!( - self.get_iota_id().await, + self.get_iota_id().await as i64, PrintType::General, "Failed to forward message to client" ); @@ -615,10 +337,8 @@ impl IotaConnection { } pub async fn handle_close(&self) { - if self.is_identified().await { - if let Some(rho_conn) = self.get_rho_connection().await { - rho_conn.close_iota_connection().await; - } + if let Some(rho_conn) = self.get_rho_connection().await { + rho_conn.close_iota_connection().await; } } @@ -638,7 +358,7 @@ impl IotaConnection { tokio::spawn(async move { if let Err(e) = inner_tx.send(response_cv).await { log_err!( - io.get_iota_id().await, + io.get_iota_id().await as i64, PrintType::Iota, "Failed to send response back to awaiter: {}", e diff --git a/src/rho/mod.rs b/src/rho/mod.rs index 72799fc..ac498f3 100644 --- a/src/rho/mod.rs +++ b/src/rho/mod.rs @@ -1,4 +1,6 @@ pub mod client_connection; +pub mod connection; pub mod iota_connection; pub mod rho_connection; pub mod rho_manager; +pub mod server; diff --git a/src/rho/rho_connection.rs b/src/rho/rho_connection.rs index 71f042f..9286d0b 100644 --- a/src/rho/rho_connection.rs +++ b/src/rho/rho_connection.rs @@ -1,10 +1,8 @@ use super::{client_connection::ClientConnection, iota_connection::IotaConnection, rho_manager}; -use crate::data::{ - communication::{CommunicationType, CommunicationValue, DataTypes}, - user::UserStatus, -}; + +use crate::data::user::UserStatus; use crate::omega::omega_connection::OmegaConnection; -use json::{JsonValue, number::Number}; +use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; @@ -27,8 +25,8 @@ impl RhoConnection { rho_connection } - pub async fn get_iota_id(&self) -> i64 { - self.iota_connection.get_iota_id().await + pub async fn get_iota_id(&self) -> u64 { + self.iota_connection.iota_id } pub fn get_user_ids(&self) -> &Vec { @@ -52,7 +50,7 @@ impl RhoConnection { let connections = self.client_connections.read().await; let mut collections = Vec::new(); for con in connections.iter() { - if con.get_user_id().await == user_id { + if con.get_user_id().await == user_id as u64 { collections.push(con.clone()); } } @@ -63,7 +61,7 @@ impl RhoConnection { pub async fn add_client_connection(&self, connection: Arc) { let notification = CommunicationValue::new(CommunicationType::client_connected).add_data( DataTypes::user_id, - JsonValue::Number(Number::from(connection.get_user_id().await)), + DataValue::Number(connection.get_user_id().await as i64), ); self.iota_connection.send_message(¬ification).await; @@ -74,8 +72,8 @@ impl RhoConnection { } OmegaConnection::client_changed( - self.get_iota_id().await, - connection.get_user_id().await, + self.get_iota_id().await as i64, + connection.get_user_id().await as i64, UserStatus::user_online, ) .await; @@ -93,8 +91,8 @@ impl RhoConnection { // Notify OmegaConnection OmegaConnection::client_changed( - self.get_iota_id().await, - connection.get_user_id().await, + self.get_iota_id().await as i64, + connection.get_user_id().await as i64, UserStatus::user_offline, ) .await; @@ -109,10 +107,10 @@ impl RhoConnection { } // Remove from manager - rho_manager::remove_rho(self.get_iota_id().await).await; + rho_manager::remove_rho(self.get_iota_id().await as i64).await; // Notify OmegaConnection - OmegaConnection::close_iota(self.get_iota_id().await).await; + OmegaConnection::close_iota(self.get_iota_id().await as i64).await; } /// Send message from Iota to specific client @@ -136,7 +134,7 @@ impl RhoConnection { let connections = self.client_connections.read().await; for connection in connections.iter() { let conn_user_id = connection.get_user_id().await; - if conn_user_id == user_id { + if conn_user_id == user_id as u64 { connection .clone() .set_interested_users(interested_ids.clone()) diff --git a/src/rho/rho_manager.rs b/src/rho/rho_manager.rs index 55f67af..f87dd3c 100644 --- a/src/rho/rho_manager.rs +++ b/src/rho/rho_manager.rs @@ -41,7 +41,7 @@ pub async fn remove_rho(iota_id: i64) -> Option> { pub async fn add_rho(rho_connection: Arc) { let mut connections = RHO_CONNECTIONS.write().await; let iota_id = rho_connection.get_iota_id().await; - connections.insert(iota_id, rho_connection); + connections.insert(iota_id as i64, rho_connection); } /// Get a RhoConnection by Iota ID directly diff --git a/src/rho/server.rs b/src/rho/server.rs new file mode 100644 index 0000000..f0a25cf --- /dev/null +++ b/src/rho/server.rs @@ -0,0 +1,57 @@ +use crate::{rho::connection::GeneralConnection, util::file_util::load_file_buf}; +use epsilon_native::Host; +use quinn::ServerConfig; +use rustls::pki_types::PrivateKeyDer; +use std::sync::Arc; +use tokio::io::unix::AsyncFd; + +pub async fn start(port: u16) { + let tls_cfg = load_tls().expect("TLS config failed"); + + let server_crypto = quinn::crypto::rustls::QuicServerConfig::try_from(tls_cfg) + .expect("Failed to convert to QuicServerConfig"); + + let server_cfg = ServerConfig::with_crypto(Arc::new(server_crypto)); + + let mut host: Host = epsilon_native::host(port, server_cfg).await.unwrap(); + tokio::spawn(async move { + while let Some((sender, receiver)) = host.next().await { + tokio::spawn(async move { + GeneralConnection::new(sender, receiver).handle().await; + }); + } + }); +} + +fn load_tls() -> Option { + let mut cert_file_buf = load_file_buf("certs", "cert.pem").ok()?; + let mut key_file_buf = load_file_buf("certs", "cert.key").ok()?; + + let cert_chain = rustls_pemfile::certs(&mut cert_file_buf) + .collect::, _>>() + .ok()?; + + let mut keys: Vec = rustls_pemfile::pkcs8_private_keys(&mut key_file_buf) + .map(|k| k.map(Into::into)) + .collect::, _>>() + .ok()?; + + if keys.is_empty() { + let mut key_file_buf = load_file_buf("certs", "cert.key").ok()?; + keys = rustls_pemfile::rsa_private_keys(&mut key_file_buf) + .map(|k| k.map(Into::into)) + .collect::, _>>() + .ok()?; + } + + if keys.is_empty() { + return None; + } + + let cfg = rustls::ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(cert_chain, keys.remove(0)) + .ok()?; + + Some(cfg) +} diff --git a/src/util/file_util.rs b/src/util/file_util.rs new file mode 100644 index 0000000..116ef37 --- /dev/null +++ b/src/util/file_util.rs @@ -0,0 +1,179 @@ +use std::fs::{self, File}; +use std::io::{self, BufReader, Read}; +use std::path::{Path, PathBuf}; + +use crate::log; +use crate::util::logger::PrintType; + +#[allow(dead_code)] +pub fn delete_directory(path: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + delete_dir_recursive(&dir) +} + +#[allow(dead_code)] +fn delete_dir_recursive(directory: &Path) -> bool { + if !directory.exists() { + return false; + } + if let Err(e) = fs::remove_dir_all(directory) { + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't delete directory {}: {}", + directory.display(), + e, + ); + return false; + } + true +} + +#[allow(dead_code)] +pub fn delete_user_directory(user_id: i64) { + let user_dir = Path::new(&get_directory()) + .join("users") + .join(user_id.to_string()); + let _ = delete_dir_recursive(&user_dir); +} + +pub fn load_file_buf(path: &str, name: &str) -> io::Result> { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + // Ensure the directory exists, create if necessary + if !dir.exists() { + if let Err(_) = fs::create_dir_all(&dir) { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Directory creation failed", + )); + } + } + + // Create the file if it doesn't exist + if !file_path.exists() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "File creation failed", + )); + } + + // Open the file and return a BufReader for efficient reading + let file = File::open(&file_path)?; + Ok(BufReader::new(file)) +} +pub fn has_file(path: &str, name: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + return false; + } + + if !file_path.exists() { + return false; + } + + true +} +pub fn has_dir(path: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + + if !dir.exists() { + return false; + } + + true +} + +pub fn load_file(path: &str, name: &str) -> String { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't create directories: {}", + e + ); + return String::new(); + } + return String::new(); + } + + if !file_path.exists() { + if let Err(e) = File::create(&file_path) { + log!( + 0, + PrintType::General, + "[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 load_file_vec(path: &str, name: &str) -> Result, std::io::Error> { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + std::fs::read(file_path) +} + +pub fn save_file(path: &str, name: &str, value: &str) { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't create directories: {}", + e + ); + return; + } + } + + if let Err(e) = fs::write(&file_path, value) { + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't write file {}: {}", + file_path.display(), + e + ); + } +} + +pub fn get_children(path: &str) -> Vec { + let dir = Path::new(&get_directory()).join(path); + let mut children = Vec::new(); + if let Ok(entries) = fs::read_dir(&dir) { + for entry in entries { + if let Ok(entry) = entry { + children.push(entry.file_name().to_string_lossy().to_string()); + } + } + } + children +} + +pub fn get_directory() -> String { + let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); + exe.parent() + .unwrap_or(Path::new(".")) + .to_string_lossy() + .to_string() +} diff --git a/src/util/logger.rs b/src/util/logger.rs index edb6f6f..d9c40cb 100644 --- a/src/util/logger.rs +++ b/src/util/logger.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, fs::{self, OpenOptions}, io::Write, path::Path, @@ -8,9 +9,7 @@ use std::{ }; use ansi_term::Color; -use json::JsonValue; - -use crate::data::communication::CommunicationValue; +use epsilon_core::{CommunicationValue, DataTypes, DataValue}; static LOGGER: OnceLock> = OnceLock::new(); @@ -151,7 +150,7 @@ pub fn log_cv_internal( let formatted = format_cv(cv); log_internal( - cv.get_sender(), + cv.get_sender() as i64, print_type.unwrap_or(PrintType::General), prefix, false, @@ -176,24 +175,80 @@ pub fn format_cv(cv: &CommunicationValue) -> String { let comm_type = cv.get_type().to_string(); parts.push(format!("{}", comm_type)); - let mut data_parts = Vec::new(); - if let JsonValue::Object(data) = &cv.clone().to_json()["data"] { - for (key, value) in data.iter() { - let val_string = match value { - JsonValue::String(s) => s.clone(), - _ => value.dump(), - }; + let data: &HashMap = cv.get_data_container(); - data_parts.push(format!("{} {}", key, val_string)); - } - } + let formated_data = + format_data_container(data.iter().map(|(k, v)| (k.clone(), v.clone())).collect()); - if !data_parts.is_empty() { - parts.push(format!("{}", data_parts.join(", "))); - } + parts.push(format!("{}", formated_data)); parts.join(": ") } + +fn format_data_container(data: Vec<(DataTypes, DataValue)>) -> String { + let parts: Vec = data + .into_iter() + .map(|(key, value)| { + let key_str = key.to_string(); + + match value { + DataValue::Str(s) => format!("{}=\"{}\"", key_str, s), + + DataValue::Container(inner) => { + let inner_formatted = format_data_container(inner); + format!("{}={{ {} }}", key_str, inner_formatted) + } + + DataValue::Array(arr) => { + let arr_formatted = format_array(arr); + format!("{}=[{}]", key_str, arr_formatted) + } + + DataValue::Bool(b) => format!("{}={}", key_str, b), + + DataValue::BoolTrue => format!("{}=true", key_str), + DataValue::BoolFalse => format!("{}=false", key_str), + + DataValue::Number(num) => format!("{}={}", key_str, num), + + _ => "".to_string(), + } + }) + .collect(); + + parts.join(", ") +} + +fn format_array(arr: Vec) -> String { + let parts: Vec = arr + .into_iter() + .map(|value| match value { + DataValue::Str(s) => format!("\"{}\"", s), + + DataValue::Container(inner) => { + let inner_formatted = format_data_container(inner); + format!("{{ {} }}", inner_formatted) + } + + DataValue::Array(inner_arr) => { + let formatted = format_array(inner_arr); + format!("[{}]", formatted) + } + + DataValue::Bool(b) => b.to_string(), + + DataValue::BoolTrue => "true".to_string(), + DataValue::BoolFalse => "false".to_string(), + + DataValue::Number(num) => num.to_string(), + + _ => String::new(), + }) + .collect(); + + parts.join(", ") +} + #[macro_export] macro_rules! log_cv { ($kind:expr, $cv:expr) => { diff --git a/src/util/mod.rs b/src/util/mod.rs index 3472249..1897223 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ pub mod crypto_helper; pub mod crypto_util; +pub mod file_util; pub mod logger;