From b23eff24a384b1d1ef561928ed971dc82e9dc8c9 Mon Sep 17 00:00:00 2001 From: Alois Date: Thu, 2 Apr 2026 22:25:10 +0200 Subject: [PATCH] Sync --- .gitignore | 16 +- Cargo.lock | 2527 ++++++----------- Cargo.toml | 38 +- README.md | 3 +- .../anonymous_client_connection.rs | 519 ++++ src/anonymous_clients/anonymous_manager.rs | 53 + src/anonymous_clients/mod.rs | 2 + src/calls/call_group.rs | 106 + src/calls/call_manager.rs | 97 + src/calls/call_util.rs | 151 + src/calls/caller.rs | 49 + src/calls/mod.rs | 4 + src/data/mod.rs | 1 + .../connection_status.rs => data/user.rs} | 0 src/main.rs | 56 +- src/omega/mod.rs | 1 + src/omega/omega_connection.rs | 741 +++++ src/omega/ping_pong_task.rs | 41 + src/rho/client_connection.rs | 583 ++++ src/rho/connection.rs | 358 +++ src/rho/iota_connection.rs | 461 +++ src/rho/mod.rs | 6 + src/rho/rho_connection.rs | 199 ++ src/rho/rho_manager.rs | 83 + src/rho/server.rs | 25 + src/server/api.rs | 275 -- src/server/mod.rs | 3 - src/server/server.rs | 67 - src/server/short_link.rs | 87 - src/sql/mod.rs | 3 - src/sql/sql.rs | 765 ----- src/sql/user_online_tracker.rs | 144 - src/transport/mod.rs | 2 - src/transport/omikron_connection.rs | 1139 -------- src/transport/omikron_manager.rs | 39 - src/util/crypto_helper.rs | 22 +- src/util/crypto_util.rs | 59 +- src/util/file_util.rs | 166 +- src/util/logger.rs | 100 +- 39 files changed, 4548 insertions(+), 4443 deletions(-) mode change 100755 => 100644 Cargo.toml create mode 100644 src/anonymous_clients/anonymous_client_connection.rs create mode 100644 src/anonymous_clients/anonymous_manager.rs create mode 100644 src/anonymous_clients/mod.rs create mode 100644 src/calls/call_group.rs create mode 100644 src/calls/call_manager.rs create mode 100644 src/calls/call_util.rs create mode 100644 src/calls/caller.rs create mode 100644 src/calls/mod.rs create mode 100644 src/data/mod.rs rename src/{sql/connection_status.rs => data/user.rs} (100%) create mode 100644 src/omega/mod.rs create mode 100644 src/omega/omega_connection.rs create mode 100644 src/omega/ping_pong_task.rs create mode 100644 src/rho/client_connection.rs create mode 100755 src/rho/connection.rs create mode 100755 src/rho/iota_connection.rs create mode 100644 src/rho/mod.rs create mode 100644 src/rho/rho_connection.rs create mode 100644 src/rho/rho_manager.rs create mode 100644 src/rho/server.rs delete mode 100644 src/server/api.rs delete mode 100644 src/server/mod.rs delete mode 100644 src/server/server.rs delete mode 100644 src/server/short_link.rs delete mode 100644 src/sql/mod.rs delete mode 100644 src/sql/sql.rs delete mode 100644 src/sql/user_online_tracker.rs delete mode 100644 src/transport/mod.rs delete mode 100644 src/transport/omikron_connection.rs delete mode 100644 src/transport/omikron_manager.rs mode change 100644 => 100755 src/util/crypto_helper.rs diff --git a/.gitignore b/.gitignore index e3d273c..1baef92 100644 --- a/.gitignore +++ b/.gitignore @@ -8,22 +8,12 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb - +*.env # Generated by cargo mutants # Contains mutation testing data -**/mutants.out*/ +**/mutants.out*/W -# RustRover -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ - - -.env -/logs # Added by cargo - /target +/logs diff --git a/Cargo.lock b/Cargo.lock index 1444c40..41cef35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,248 +3,35 @@ version = 4 [[package]] -name = "Omega" +name = "Omikron" version = "0.1.0" dependencies = [ - "actix-web", "aes-gcm", "ansi_term", - "anyhow", - "base64", + "base64 0.22.1", "dashmap", "dotenv", - "epsilon-core", - "epsilon-native", + "futures", "hex", "hkdf", - "json", + "livekit-api", + "livekit-protocol", + "log", "once_cell", "rand 0.8.5", "rand_core 0.6.4", - "reqwest", "rustls", - "rustls-pemfile", "sha2", - "sqlx", - "strum 0.27.2", - "strum_macros 0.27.2", + "strum", + "strum_macros", "thiserror 2.0.18", "tokio", + "ttp-core", + "ttp-native", "uuid", "x448", - "zip", ] -[[package]] -name = "actix-codec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" -dependencies = [ - "bitflags 2.11.0", - "bytes", - "futures-core", - "futures-sink", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-http" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-tls", - "actix-utils", - "base64", - "bitflags 2.11.0", - "brotli", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "foldhash", - "futures-core", - "h2 0.3.27", - "http 0.2.12", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "mime", - "percent-encoding", - "pin-project-lite", - "rand 0.9.2", - "sha1", - "smallvec", - "tokio", - "tokio-util", - "tracing", - "zstd", -] - -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7" -dependencies = [ - "bytestring", - "cfg-if", - "http 0.2.12", - "regex", - "regex-lite", - "serde", - "tracing", -] - -[[package]] -name = "actix-rt" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "mio", - "socket2 0.5.10", - "tokio", - "tracing", -] - -[[package]] -name = "actix-service" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "actix-tls" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6176099de3f58fbddac916a7f8c6db297e021d706e7a6b99947785fee14abe9f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "impl-more", - "pin-project-lite", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-utils" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-tls", - "actix-utils", - "actix-web-codegen", - "bytes", - "bytestring", - "cfg-if", - "cookie", - "derive_more", - "encoding_rs", - "foldhash", - "futures-core", - "futures-util", - "impl-more", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "regex-lite", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2 0.6.3", - "time", - "tracing", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aead" version = "0.5.2" @@ -290,26 +77,14 @@ dependencies = [ ] [[package]] -name = "alloc-no-stdlib" -version = "2.0.4" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "alloc-no-stdlib", + "libc", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "ansi_term" version = "0.12.1" @@ -325,15 +100,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "asn1-rs" version = "0.7.1" @@ -404,8 +170,8 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] @@ -418,33 +184,13 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.5.0", "async-executor", - "async-io 2.6.0", - "async-lock 3.4.2", + "async-io", + "async-lock", "blocking", - "futures-lite 2.6.1", + "futures-lite", "once_cell", ] -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - [[package]] name = "async-io" version = "2.6.0" @@ -455,23 +201,14 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", - "polling 3.11.0", - "rustix 1.1.4", + "polling", + "rustix", "slab", "windows-sys 0.61.2", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.4.2" @@ -483,6 +220,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + [[package]] name = "async-std" version = "1.13.2" @@ -491,13 +240,13 @@ checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 2.6.0", - "async-lock 3.4.2", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -516,12 +265,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] -name = "atoi" -version = "2.0.0" +name = "async-tungstenite" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +checksum = "2cca750b12e02c389c1694d35c16539f88b8bbaa5945934fdc1b41a776688589" dependencies = [ - "num-traits", + "async-native-tls", + "async-std", + "futures-io", + "futures-util", + "log", + "pin-project-lite", + "tungstenite 0.21.0", ] [[package]] @@ -538,9 +293,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -549,9 +304,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.38.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" dependencies = [ "cc", "cmake", @@ -559,6 +314,18 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -571,20 +338,11 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -dependencies = [ - "serde_core", -] [[package]] name = "block-buffer" @@ -595,6 +353,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -604,31 +371,10 @@ dependencies = [ "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] -[[package]] -name = "brotli" -version = "8.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -647,29 +393,11 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "bytestring" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" -dependencies = [ - "bytes", -] - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - [[package]] name = "cc" -version = "1.2.57" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -677,12 +405,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -695,6 +417,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + [[package]] name = "cipher" version = "0.4.4" @@ -707,23 +438,13 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -739,42 +460,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -800,45 +485,24 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -859,6 +523,33 @@ dependencies = [ "cipher", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -879,12 +570,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" -[[package]] -name = "deflate64" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" - [[package]] name = "der" version = "0.7.10" @@ -919,40 +604,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn", - "unicode-xid", -] - [[package]] name = "digest" version = "0.10.7" @@ -965,6 +616,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -982,25 +643,57 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "ed448-goldilocks" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b5fa9e9e3dd5fe1369f380acd3dcdfa766dbd0a1cd5b048fb40e38a6a78e79" dependencies = [ - "fiat-crypto", + "fiat-crypto 0.1.20", "hex", "subtle", ] @@ -1010,43 +703,26 @@ name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -dependencies = [ - "serde", -] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "cfg-if", -] - -[[package]] -name = "epsilon-core" -version = "0.1.0" -source = "git+https://github.com/Tensamin/Epsilon.git#d5edc693a91954834a38c39517b79092983eac5c" -dependencies = [ - "base64", - "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#d5edc693a91954834a38c39517b79092983eac5c" -dependencies = [ - "epsilon-core", - "quinn", - "rustls", - "rustls-native-certs", - "thiserror 2.0.18", - "tokio", - "wtransport", + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", ] [[package]] @@ -1065,17 +741,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1103,27 +768,34 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1131,26 +803,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "flate2" -version = "1.1.9" +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", -] - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fnv" @@ -1164,6 +820,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1179,6 +850,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -1206,51 +892,36 @@ dependencies = [ "futures-util", ] -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - [[package]] name = "futures-io" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand", "futures-core", "futures-io", "parking", "pin-project-lite", ] +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -1269,8 +940,10 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1286,6 +959,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1351,41 +1025,14 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.27" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.4.0", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "ff", + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -1400,8 +1047,6 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", "foldhash", ] @@ -1412,13 +1057,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] -name = "hashlink" -version = "0.10.0" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -1426,12 +1068,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -1462,15 +1098,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "httlib-huffman" version = "0.3.4" @@ -1527,47 +1154,39 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.13", "http 1.4.0", "http-body", "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", ] [[package]] -name = "hyper-rustls" -version = "0.27.7" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "http 1.4.0", + "bytes", + "http-body-util", "hyper", "hyper-util", - "rustls", - "rustls-pki-types", + "native-tls", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower-service", ] @@ -1577,7 +1196,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -1589,21 +1208,20 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2 0.6.3", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1611,9 +1229,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1624,9 +1242,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1638,15 +1256,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1658,15 +1276,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1704,12 +1322,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "impl-more" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" - [[package]] name = "indexmap" version = "2.13.0" @@ -1731,26 +1343,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1759,41 +1351,37 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", ] [[package]] -name = "itoa" -version = "1.0.17" +name = "itertools" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", + "either", ] [[package]] -name = "jni-sys" -version = "0.3.0" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" @@ -1807,19 +1395,36 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] [[package]] -name = "json" -version = "0.12.4" +name = "jsonwebtoken" +version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" +checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" +dependencies = [ + "base64 0.22.1", + "ed25519-dalek", + "getrandom 0.2.17", + "hmac", + "js-sys", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde_json", + "sha2", + "signature", +] [[package]] name = "kv-log-macro" @@ -1830,12 +1435,6 @@ dependencies = [ "log", ] -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.5.0" @@ -1851,17 +1450,11 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - [[package]] name = "libc" -version = "0.2.183" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" @@ -1869,34 +1462,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "bitflags 2.11.0", - "libc", - "plain", - "redox_syscall 0.7.3", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1905,26 +1470,63 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] -name = "local-channel" -version = "0.1.5" +name = "livekit-api" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +checksum = "020c7eaab7cec16b69a058659f9acff32067e0e2d4f81e80639f3ffa29464b09" dependencies = [ - "futures-core", - "futures-sink", - "local-waker", + "async-tungstenite", + "base64 0.21.7", + "http 1.4.0", + "jsonwebtoken", + "livekit-protocol", + "log", + "os_info", + "parking_lot", + "pbjson-types", + "prost", + "rand 0.9.2", + "reqwest", + "scopeguard", + "serde", + "serde_json", + "sha2", + "thiserror 1.0.69", + "tokio-tungstenite", + "url", ] [[package]] -name = "local-waker" -version = "0.1.4" +name = "livekit-protocol" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" +checksum = "c21d56b4dc2598b1296ca891abb225bf1c05ea007d6f43bc4fd48c31be64bf59" +dependencies = [ + "futures-util", + "livekit-runtime", + "parking_lot", + "pbjson", + "pbjson-types", + "prost", + "serde", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "livekit-runtime" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532e84c6cdc5fe774f2b5d9912597b5f3bea561927a48296d03e24549d21c3f6" +dependencies = [ + "tokio", + "tokio-stream", +] [[package]] name = "lock_api" @@ -1950,38 +1552,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lzma-rust2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a" -dependencies = [ - "crc", - "sha2", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1989,25 +1565,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "miniz_oxide" -version = "0.8.9" +name = "mio" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ - "adler2", - "simd-adler32", + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "mio" -version = "1.1.1" +name = "multimap" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", - "wasi", - "windows-sys 0.61.2", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", ] [[package]] @@ -2048,9 +1648,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -2082,6 +1682,165 @@ dependencies = [ "libm", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "octets" version = "0.3.5" @@ -2109,12 +1868,90 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_info" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking" version = "2.2.1" @@ -2139,19 +1976,46 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "pbjson" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" dependencies = [ - "digest", - "hmac", + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "prost", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost", + "prost-build", + "serde", ] [[package]] @@ -2160,7 +2024,7 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64", + "base64 0.22.1", "serde_core", ] @@ -2179,6 +2043,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2198,7 +2072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand", "futures-io", ] @@ -2229,28 +2103,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.11.0" @@ -2259,9 +2111,9 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -2279,9 +2131,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2292,12 +2144,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppmd-rust" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2317,6 +2163,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -2326,6 +2181,59 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + [[package]] name = "quinn" version = "0.11.9" @@ -2487,16 +2395,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", + "bitflags", ] [[package]] @@ -2522,12 +2421,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-lite" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" - [[package]] name = "regex-syntax" version = "0.8.10" @@ -2536,33 +2429,31 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.13.2" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", + "base64 0.22.1", "bytes", - "encoding_rs", "futures-core", - "h2 0.4.13", "http 1.4.0", "http-body", "http-body-util", "hyper", - "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", - "mime", + "native-tls", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pki-types", - "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower", "tower-http", "tower-service", @@ -2572,6 +2463,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -2608,9 +2509,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -2630,30 +2531,16 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -2685,15 +2572,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -2704,38 +2582,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "aws-lc-rs", "ring", @@ -2755,15 +2606,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.29" @@ -2779,14 +2621,28 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", + "bitflags", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -2911,12 +2767,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - [[package]] name = "slab" version = "0.4.12" @@ -2928,19 +2778,6 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] [[package]] name = "socket2" @@ -2967,9 +2804,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -2981,242 +2815,25 @@ dependencies = [ "der", ] -[[package]] -name = "sqlx" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" -dependencies = [ - "async-io 1.13.0", - "async-std", - "base64", - "bytes", - "crc", - "crossbeam-queue", - "either", - "event-listener 5.4.1", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.15.5", - "hashlink", - "indexmap", - "log", - "memchr", - "once_cell", - "percent-encoding", - "serde", - "serde_json", - "sha2", - "smallvec", - "thiserror 2.0.18", - "tracing", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" -dependencies = [ - "async-std", - "dotenvy", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" -dependencies = [ - "atoi", - "base64", - "bitflags 2.11.0", - "byteorder", - "bytes", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand 0.8.5", - "rsa", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 2.0.18", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" -dependencies = [ - "atoi", - "base64", - "bitflags 2.11.0", - "byteorder", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand 0.8.5", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 2.0.18", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" -dependencies = [ - "atoi", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "thiserror 2.0.18", - "tracing", - "url", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "strum" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "strum_macros" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -3260,24 +2877,16 @@ dependencies = [ ] [[package]] -name = "system-configuration" -version = "0.7.0" +name = "tempfile" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] @@ -3353,9 +2962,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3405,28 +3014,40 @@ dependencies = [ ] [[package]] -name = "tokio-rustls" -version = "0.26.4" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "rustls", + "native-tls", "tokio", ] [[package]] -name = "tokio-util" -version = "0.7.18" +name = "tokio-stream" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ - "bytes", "futures-core", - "futures-sink", "pin-project-lite", "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "native-tls", + "tokio", + "tokio-native-tls", + "tungstenite 0.20.1", +] + [[package]] name = "tower" version = "0.5.3" @@ -3448,7 +3069,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.11.0", + "bitflags", "bytes", "futures-util", "http 1.4.0", @@ -3478,7 +3099,6 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3510,45 +3130,84 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttp-core" +version = "0.1.0" +source = "git+https://github.com/Tensamin/TTP.git#e246d1af6a42c71514d0ccc06fef94d71e4ad167" +dependencies = [ + "base64 0.22.1", + "byteorder", + "rand 0.8.5", + "strum", + "strum_macros", +] + +[[package]] +name = "ttp-native" +version = "0.1.0" +source = "git+https://github.com/Tensamin/TTP.git#e246d1af6a42c71514d0ccc06fef94d71e4ad167" +dependencies = [ + "quinn", + "rustls", + "rustls-native-certs", + "thiserror 2.0.18", + "tokio", + "ttp-core", + "wtransport", +] + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 0.2.12", + "httparse", + "log", + "native-tls", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "native-tls", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -3589,6 +3248,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3597,9 +3262,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -3624,22 +3289,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -3673,17 +3322,11 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -3694,23 +3337,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3718,9 +3357,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -3731,9 +3370,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -3766,7 +3405,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags", "hashbrown 0.15.5", "indexmap", "semver", @@ -3774,9 +3413,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3792,25 +3431,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" -dependencies = [ - "libredox", - "wasite", -] - [[package]] name = "winapi" version = "0.3.9" @@ -3827,15 +3447,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3848,53 +3459,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3922,36 +3486,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -3985,18 +3519,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -4009,18 +3531,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -4033,18 +3543,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4069,18 +3567,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4093,18 +3579,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4117,18 +3591,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4141,18 +3603,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4181,7 +3631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "wit-parser", ] @@ -4192,7 +3642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "indexmap", "prettyplease", "syn", @@ -4223,7 +3673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags", "indexmap", "log", "serde", @@ -4352,9 +3802,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4363,9 +3813,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -4375,18 +3825,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -4395,18 +3845,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -4419,26 +3869,12 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -4447,9 +3883,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -4458,90 +3894,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "zip" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq", - "crc32fast", - "deflate64", - "flate2", - "getrandom 0.3.4", - "hmac", - "indexmap", - "lzma-rust2", - "memchr", - "pbkdf2", - "ppmd-rust", - "sha1", - "time", - "zeroize", - "zopfli", - "zstd", -] - -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml old mode 100755 new mode 100644 index 34673fb..3127b12 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,39 +1,37 @@ [package] -name = "Omega" +name = "Omikron" 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" } +ttp-core = { git = "https://github.com/Tensamin/TTP.git", package = "ttp-core" } +ttp-native = { git = "https://github.com/Tensamin/TTP.git", package = "ttp-native" } + +ansi_term = "*" +uuid = { version = "*", features = ["v4"] } -actix-web = { version = "4.12.1", features = ["rustls-0_23"] } -aes-gcm = "*" -ansi_term = "0.12.1" -anyhow = "1.0.101" base64 = "0.22.1" -dashmap = "6.1.0" -dotenv = "0.15.0" -hex = "0.4.3" -hkdf = "0.12.4" -json = "0.12.4" +dashmap = "*" +futures = "*" +hex = "*" once_cell = "1.21.3" rand = "0.8" rand_core = { version = "0.6", features = ["getrandom", "std"] } -reqwest = { version = "0.13.2" } rustls = { version = "0.23.37", default-features = false, features = [ "std", "tls12", "aws-lc-rs", "prefer-post-quantum", ] } -rustls-pemfile = "2.2.0" +aes-gcm = "0.10.3" sha2 = "0.10.9" -sqlx = { version = "0.8.6", features = ["mysql", "runtime-async-std"] } -strum = "0.27.2" -strum_macros = "0.27.2" tokio = { version = "*", features = ["full"] } -uuid = { version = "1.19.0", features = ["v4"] } -x448 = "0.6.0" -zip = "6.0.0" +x448 = { version = "*" } +log = "0.4" +dotenv = "0.15.0" +hkdf = "0.12.4" +strum = "0.28.0" +strum_macros = "0.28.0" +livekit-api = { version = "0.4.14", features = ["native-tls"] } +livekit-protocol = "0.7.1" thiserror = "2.0.18" diff --git a/README.md b/README.md index a2c50d5..06c1cdb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# Omega -A loadballancer and Omikron-manager for Tensamin +# ⚠️ Moved to [git.methanium.net](https://git.methanium.net/tensamin/omikron) ⚠️ diff --git a/src/anonymous_clients/anonymous_client_connection.rs b/src/anonymous_clients/anonymous_client_connection.rs new file mode 100644 index 0000000..8c57490 --- /dev/null +++ b/src/anonymous_clients/anonymous_client_connection.rs @@ -0,0 +1,519 @@ +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::RwLock; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use ttp_native::{Receiver, Sender}; +use uuid::Uuid; + +use crate::anonymous_clients::anonymous_manager::{self, generate_username}; +use crate::calls::call_manager; +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_cv_in, log_cv_out, log_out}; + +pub struct AnonymousClientConnection { + user_id: u64, + + pub sender: Arc, + pub receiver: Arc, + pub ping: Arc>, + pub interested_users: Arc>>, + is_open: Arc>, + pub user_name: Arc>, + pub display_name: Arc>, + pub avatar: Arc>, +} + +impl AnonymousClientConnection { + pub async fn from_general(general: Arc, user_id: u64) -> Arc { + let username: String = generate_username(); + Arc::new(Self { + 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; + } + self_clone.handle_close().await; + }); + } + + /// Get the user ID + pub fn get_user_id(&self) -> u64 { + self.user_id + } + + /// Get the user name + pub async fn get_user_name(&self) -> String { + self.user_name.read().await.clone() + } + + /// Get the display name + pub async fn get_display_name(&self) -> String { + self.display_name.read().await.clone() + } + + pub async fn set_display_name(&self, display: String) { + *self.display_name.write().await = display; + } + + /// Get the avatar + pub async fn get_avatar(&self) -> String { + self.avatar.read().await.clone() + } + + /// Send a CommunicationValue to the client + pub async fn send_message(self: Arc, cv: &CommunicationValue) { + if !*self.is_open.read().await { + log_out!( + self.user_id as i64, + PrintType::Client, + "Attempted to send message to a closed connection." + ); + return; + } + if !cv.is_type(CommunicationType::pong) { + log_cv_out!(PrintType::Client, &cv); + } + if let Err(e) = self.sender.send(&cv).await { + log_out!( + self.user_id as i64, + PrintType::Client, + "Send failed: {:?}", + e + ); + } + } + + /// Handle incoming message from client + pub async fn handle_message(self: Arc, cv: CommunicationValue) { + tokio::spawn(async move { + if cv.is_type(CommunicationType::ping) { + self.handle_ping(cv).await; + return; + } + log_cv_in!(PrintType::Client, &cv); + + if cv.is_type(CommunicationType::identification) { + 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 { + call + } else { + self.send_error_response( + &cv.get_id(), + CommunicationType::error_not_authenticated, + ) + .await; + return; + } + } else { + self.send_error_response( + &cv.get_id(), + CommunicationType::error_not_authenticated, + ) + .await; + return; + }; + + 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, + DataValue::Number(call_invitee.user_id as i64), + ), + Some(Duration::from_secs(2)), + ) + .await + .unwrap(); + 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(DataValue::Container(json_invitee)); + } + + let token = call.create_anonymous_token(self.get_user_id()).await; + + 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, DataValue::Number(self.user_id as i64)) + .add_data( + DataTypes::username, + DataValue::Str(self.clone().get_user_name().await), + ) + .add_data( + DataTypes::display, + DataValue::Str(self.get_display_name().await), + ) + .add_data(DataTypes::avatar, DataValue::Str(self.get_avatar().await)) + .add_data(DataTypes::call_state, DataValue::Container(serialized)), + ) + .await; + } + + // Handle ping + if cv.is_type(CommunicationType::ping) { + self.handle_ping(cv).await; + return; + } + // Handle client status changes + if cv.is_type(CommunicationType::client_changed) { + self.handle_client_changed(cv).await; + return; + } + + // Handle call invites + if cv.is_type(CommunicationType::call_invite) { + self.handle_call_invite(cv).await; + return; + } + + // Handle get call requests + if cv.is_type(CommunicationType::call_token) { + self.handle_get_call(cv).await; + return; + } + + if cv.is_type(CommunicationType::call_disconnect_user) { + self.handle_call_disconnect_user(cv).await; + return; + } + + if cv.is_type(CommunicationType::call_timeout_user) { + self.handle_call_timeout_user(cv).await; + return; + } + + if cv.is_type(CommunicationType::change_user_data) { + if let Some(display_name) = cv.get_data(DataTypes::display).as_str() { + let _ = self.set_display_name(display_name.to_string()).await; + } + + return; + } + + if cv.is_type(CommunicationType::get_user_data) { + if let Some(anonymous) = { + 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 + } + } { + let response = CommunicationValue::new(CommunicationType::get_user_data) + .with_id(cv.get_id()) + .add_data( + DataTypes::username, + DataValue::Str(anonymous.get_user_name().await), + ) + .add_data( + DataTypes::user_id, + DataValue::Number(anonymous.user_id as i64), + ) + .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; + + return; + } + } + + if cv.is_type(CommunicationType::get_user_data) + || cv.is_type(CommunicationType::get_iota_data) + || cv.is_type(CommunicationType::delete_user) + { + self.handle_omega_forward(cv).await; + return; + } + }); + } + async fn handle_omega_forward(self: Arc, cv: CommunicationValue) { + let client_for_closure = self.clone(); + tokio::spawn(async move { + let response_cv = get_omega_connection() + .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; + } + }); + } + + /// Handle ping message + async fn handle_ping(self: Arc, cv: CommunicationValue) { + // Update our ping if provided + 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; + } + } + + // Send pong response + let response = CommunicationValue::new(CommunicationType::pong).with_id(cv.get_id()); + + self.send_message(&response).await; + } + + /// 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) { + let user_status = UserStatus::online; + }*/ + } + + /// Handle call invite + async fn handle_call_invite(self: Arc, cv: CommunicationValue) { + 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; + return; + } + + let call_id = match cv.get_data(DataTypes::call_id) { + 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_invalid_call_id, + ) + .await; + return; + } + }, + _ => { + 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, receiver_id as u64).await; + if !invited { + self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id) + .await; + return; + } + + // Find target RhoConnection + 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) + .await; + return; + } + }; + + // Get sender user ID + let sender_id = self.get_user_id(); + + // Create and send call distribution message + let forward = CommunicationValue::new(CommunicationType::call_invite) + .with_receiver(receiver_id as u64) + .with_sender(sender_id) + .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; + + let response = CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); + self.send_message(&response).await; + } + + /// Handle get call request + async fn handle_get_call(self: Arc, cv: CommunicationValue) { + let user_id = self.get_user_id(); + + let call_id = match cv.get_data(DataTypes::call_id) { + 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) + .await; + return; + } + }, + _ => { + self.send_error_response(&cv.get_id(), CommunicationType::error) + .await; + return; + } + }; + + 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(DataTypes::call_token, DataValue::Str(token.to_string())); + self.send_message(&response).await; + } else { + 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).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 + .unwrap() + .has_admin() + { + call.get_caller(user_id as u64) + .await + .unwrap() + .set_timeout(untill) + .await; + } + } + } + async fn handle_call_disconnect_user(self: Arc, cv: CommunicationValue) { + 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 + .unwrap() + .has_admin() + { + call.remove_caller(user_id as u64).await; + } + } + } + + /// Send error response + 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; + } + /// Close the connection + pub async fn close(&self) { + let mut is_open_guard = self.is_open.write().await; + if !*is_open_guard { + return; + } + *is_open_guard = false; + + let _ = self.sender.close(); + } + + #[allow(dead_code)] + /// Set interested users list + pub async fn set_interested_users(self: Arc, interested_ids: Vec) { + let mut interested_guard = self.interested_users.write().await; + *interested_guard = interested_ids; + } + #[allow(dead_code)] + pub async fn get_interested_users(self: Arc) -> Vec { + let interested_guard = self.interested_users.read().await; + interested_guard.clone() + } + + #[allow(dead_code)] + /// Check if interested in a user and send notification + pub async fn are_you_interested(self: Arc, user_id: i64) { + let interested_guard = self.clone().get_interested_users().await; + if interested_guard.contains(&user_id) { + let notification = CommunicationValue::new(CommunicationType::client_changed) + .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; + } + } + + /// Handle connection close + pub async fn handle_close(&self) { + + // TODO delete temp user + } +} + +// Implement Clone to make it easier to work with Arc +impl Clone for AnonymousClientConnection { + fn clone(&self) -> Self { + Self { + sender: Arc::clone(&self.sender), + receiver: Arc::clone(&self.receiver), + user_id: self.user_id, + ping: Arc::clone(&self.ping), + interested_users: Arc::clone(&self.interested_users), + is_open: Arc::clone(&self.is_open), + user_name: Arc::clone(&self.user_name), + display_name: Arc::clone(&self.display_name), + avatar: Arc::clone(&self.avatar), + } + } +} diff --git a/src/anonymous_clients/anonymous_manager.rs b/src/anonymous_clients/anonymous_manager.rs new file mode 100644 index 0000000..edfefde --- /dev/null +++ b/src/anonymous_clients/anonymous_manager.rs @@ -0,0 +1,53 @@ +use dashmap::DashMap; +use once_cell::sync::Lazy; +use rand::Rng; +use rand::seq::SliceRandom; +use std::sync::Arc; + +use crate::anonymous_clients::anonymous_client_connection::AnonymousClientConnection; + +static ANONYMOUS_USERS: Lazy>> = + Lazy::new(|| DashMap::new()); + +#[allow(dead_code)] +pub async fn add_anonymous_user(connection: Arc) { + ANONYMOUS_USERS.insert(connection.get_user_id(), connection); +} + +#[allow(dead_code)] +pub async fn remove_anonymous_user(user_id: u64) { + ANONYMOUS_USERS.remove(&user_id); +} + +pub async fn get_anonymous_user(user_id: u64) -> Option> { + ANONYMOUS_USERS.get(&user_id).map(|c| c.clone()) +} + +pub async fn get_anonymous_user_by_name( + username: String, +) -> Option> { + for user_conn in ANONYMOUS_USERS + .iter() + .map(|ref_multi| ref_multi.value().clone()) + { + if user_conn.get_user_name().await == username { + return Some(user_conn); + } + } + + return None; +} + +// TODO: implement check if taken +pub fn generate_username() -> String { + let adjectives = ["Swift", "Clever", "Brave", "Sneaky", "Fierce"]; + let nouns = ["Tiger", "Eagle", "Shark", "Wolf", "Dragon"]; + + let mut rng = rand::thread_rng(); + let adj = adjectives.choose(&mut rng).unwrap(); + let noun = nouns.choose(&mut rng).unwrap(); + + let number: u16 = rng.gen_range(0..10000); + + format!("{}{}{}", adj, noun, number) +} diff --git a/src/anonymous_clients/mod.rs b/src/anonymous_clients/mod.rs new file mode 100644 index 0000000..3edd0b4 --- /dev/null +++ b/src/anonymous_clients/mod.rs @@ -0,0 +1,2 @@ +pub mod anonymous_client_connection; +pub mod anonymous_manager; diff --git a/src/calls/call_group.rs b/src/calls/call_group.rs new file mode 100644 index 0000000..91e85cf --- /dev/null +++ b/src/calls/call_group.rs @@ -0,0 +1,106 @@ +use ttp_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}, + omega::omega_connection::get_omega_connection, +}; + +pub struct CallGroup { + pub call_id: Uuid, + pub members: RwLock>>, + pub show: RwLock, + pub anonymous_joining: RwLock, + pub short_link: RwLock>, +} + +impl CallGroup { + pub fn new(call_id: Uuid, user: Arc) -> Self { + CallGroup { + call_id, + members: RwLock::new(vec![user]), + show: RwLock::new(true), + anonymous_joining: RwLock::new(false), + short_link: RwLock::new(None), + } + } + + pub async fn get_caller(&self, user_id: u64) -> Option> { + self.members + .read() + .await + .iter() + .find(|caller| caller.user_id == user_id) + .cloned() + } + + pub async fn is_anonymous(&self) -> bool { + *self.anonymous_joining.read().await + } + + pub async fn set_anonymous_joining(&self, enable: bool) { + *self.anonymous_joining.write().await = enable; + + let _ = call_util::set_room_metadata( + self.call_id, + format!("{{\"anonymous_joining\": \"{}\"}}", enable), + ) + .await; + + if self.short_link.read().await.is_none() { + let long_link = format!( + "https://app.tensamin.net/call/anonymous?call_id={}&omikron_id={}", + self.call_id, + env::var("ID") + .unwrap_or("0".to_string()) + .parse::() + .unwrap_or(0), + ); + let response_cv = get_omega_connection() + .await_response( + &CommunicationValue::new(CommunicationType::shorten_link) + .add_data(DataTypes::link, DataValue::Str(long_link)), + Some(Duration::from_secs(20)), + ) + .await; + if let Ok(response) = response_cv { + *self.short_link.write().await = Some( + response + .get_data(DataTypes::link) + .as_str() + .unwrap() + .to_string(), + ); + log::info!( + "Shortened link for call {} is {}", + self.call_id, + self.short_link.read().await.as_ref().unwrap() + ); + } + } + } + + 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); + } + } + None + } + + pub async fn remove_caller(&self, user_id: u64) { + let _ = call_util::remove_participant(self.call_id, user_id).await; + self.members + .write() + .await + .retain(|caller| caller.user_id != user_id); + } + + pub async fn get_short_link(self: Arc) -> Option { + self.short_link.read().await.clone() + } +} diff --git a/src/calls/call_manager.rs b/src/calls/call_manager.rs new file mode 100644 index 0000000..a17f347 --- /dev/null +++ b/src/calls/call_manager.rs @@ -0,0 +1,97 @@ +use dashmap::DashMap; +use once_cell::sync::Lazy; +use std::sync::Arc; +use uuid::Uuid; + +use crate::calls::{call_group::CallGroup, caller::Caller}; + +pub static CALL_GROUPS: Lazy>> = Lazy::new(|| DashMap::new()); +#[allow(dead_code)] +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; + for member in members.iter() { + if member.user_id == user_id { + callers.push(member.clone()); + } + } + } + callers +} + +pub async fn get_call(call_id: Uuid) -> Option> { + if let Some(b) = CALL_GROUPS.get(&call_id) { + Some(b.clone()) + } else { + None + } +} + +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 = { + let members = cg.members.read().await; + members.iter().any(|m| m.user_id == user_id) + }; + + if is_member { + call_groups.push(cg.clone()); + } + } + call_groups +} + +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; + + if let Some(member) = members.iter().find(|m| m.user_id == user_id) { + return Some(member.create_token()); + } + + let new_caller = Arc::new(Caller::new(user_id, call_id, false)); + let token = new_caller.create_token(); + + members.push(new_caller); + + return Some(token); + } + + if let Some(cg) = CALL_GROUPS.get(&call_id) { + let cg_clone = cg.clone(); + + let mut members = cg_clone.members.write().await; + if let Some(member) = members.iter().find(|m| m.user_id == user_id) { + return Some(member.create_token()); + } + let new_caller = Arc::new(Caller::new(user_id, call_id, false)); + let token = new_caller.create_token(); + members.push(new_caller); + return Some(token); + } + + let caller = Arc::new(Caller::new(user_id, call_id, true)); + let call_group = CallGroup::new(call_id, caller.clone()); + + CALL_GROUPS.insert(call_id, Arc::new(call_group)); + + Some(caller.create_token()) +} + +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; + + let is_inviter_member = members.iter().any(|m| m.user_id == inviter_id); + + if is_inviter_member { + if !members.iter().any(|m| m.user_id == invitee_id) { + members.push(Arc::new(Caller::new(invitee_id, call_id, false))); + } + return true; + } + } + false +} diff --git a/src/calls/call_util.rs b/src/calls/call_util.rs new file mode 100644 index 0000000..57df005 --- /dev/null +++ b/src/calls/call_util.rs @@ -0,0 +1,151 @@ +use livekit_api::{ + access_token::{self}, + services::room::RoomClient, +}; +use livekit_protocol::Room; +use std::env; +use std::str::FromStr; +use std::time::Duration; +use uuid::Uuid; + +use crate::{calls::call_manager::CALL_GROUPS, log, log_err, util::logger::PrintType}; + +pub fn get_livekit() -> Result<(String, String, String), ()> { + let hostname = match env::var("LIVEKI_HOSTNAME") { + Ok(secret) => secret, + Err(_) => { + log_err!(0, PrintType::General, "LIVEKI_HOSTNAME not set!"); + return Err(()); + } + }; + let api_key = match env::var("LIVEKIT_API_KEY") { + Ok(key) => key, + Err(_) => { + log_err!(0, PrintType::General, "LIVEKIT_API_KEY not set!"); + return Err(()); + } + }; + let api_secret = match env::var("LIVEKIT_API_SECRET") { + Ok(secret) => secret, + Err(_) => { + log_err!(0, PrintType::General, "LIVEKIT_API_SECRET not set!"); + return Err(()); + } + }; + Ok((hostname, api_key, api_secret)) +} + +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) + .with_identity(&user_id.to_string()) + .with_grants(access_token::VideoGrants { + room_join: true, + room_admin: has_admin, + room: call_id.to_string(), + ..Default::default() + }) + .with_metadata(&format!("{{\"isAdmin\":{}}}", has_admin)) + .to_jwt(); + if let Ok(token) = token { + Ok(token) + } else { + Err(()) + } +} +#[allow(dead_code)] +pub async fn get_room(call_id: Uuid) -> Result<(RoomClient, Room), ()> { + if let Ok((hostname, api_key, api_secret)) = get_livekit() { + let room_service = RoomClient::with_api_key(&hostname, &api_key, &api_secret); + let rooms = room_service.list_rooms(Vec::new()).await; + if let Ok(rooms) = rooms { + for room in rooms { + if room.name == call_id.to_string() { + return Ok((room_service, room)); + } + } + } + } + return Err(()); +} + +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 + .remove_participant(&call_id.to_string(), &user_id.to_string()) + .await + { + return Ok(()); + } + } + return Err(()); +} + +#[allow(dead_code)] +pub async fn get_room_metadata(call_id: Uuid) -> Result { + if let Ok((_, room)) = get_room(call_id).await { + Ok(room.metadata) + } else { + Err(()) + } +} + +pub async fn set_room_metadata(call_id: Uuid, metadata: String) -> 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 + .update_room_metadata(&call_id.to_string(), &metadata) + .await + { + return Ok(()); + } + } + return Err(()); +} + +pub fn garbage_collect_calls() { + tokio::spawn(async move { + loop { + if let Ok((hostname, api_key, api_secret)) = get_livekit() { + let room_service = RoomClient::with_api_key(&hostname, &api_key, &api_secret); + clean_calls(room_service).await; + } + tokio::time::sleep(Duration::from_secs(2)).await; + } + }); +} +pub async fn clean_calls(room_service: RoomClient) { + let rooms = room_service.list_rooms(Vec::new()).await.unwrap(); + let mut call_ids: Vec = Vec::new(); + let mut no_users: Vec = Vec::new(); + for room in rooms { + if let Ok(id) = Uuid::from_str(&room.name) { + if room.num_participants == 0 { + no_users.push(id); + } + call_ids.push(id); + } + } + let size_pre = CALL_GROUPS.len(); + for (id, _) in CALL_GROUPS.clone().into_iter() { + if !call_ids.contains(&id) { + CALL_GROUPS.remove(&id); + } + } + for (_, cg) in CALL_GROUPS.clone().into_iter() { + *cg.show.write().await = !no_users.contains(&cg.call_id); + } + + let size_post = CALL_GROUPS.len(); + if size_pre - size_post != 0 { + log!( + 0, + PrintType::Call, + "Cleaned {} calls, {} remaining", + size_pre - size_post, + size_post + ); + } +} diff --git a/src/calls/caller.rs b/src/calls/caller.rs new file mode 100644 index 0000000..74e0c3b --- /dev/null +++ b/src/calls/caller.rs @@ -0,0 +1,49 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use tokio::sync::RwLock; +use uuid::Uuid; + +use crate::calls::call_util; + +pub struct Caller { + pub user_id: u64, + pub call_id: Uuid, + pub has_admin: bool, + pub timeout: RwLock, +} + +impl Caller { + pub fn new(user_id: u64, call_id: Uuid, has_admin: bool) -> Self { + Caller { + user_id, + call_id, + has_admin, + timeout: RwLock::new(0), + } + } + #[allow(dead_code)] + pub fn set_admin(&mut self, has_admin: bool) { + self.has_admin = has_admin; + } + pub fn has_admin(&self) -> bool { + self.has_admin + } + #[allow(dead_code)] + pub async fn is_timeouted(&self) -> bool { + *self.timeout.read().await + > SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64 + } + pub async fn set_timeout(&self, timeout: i64) { + *self.timeout.write().await = timeout; + } + pub fn create_token(&self) -> String { + if let Ok(token) = call_util::create_token(self.user_id, self.call_id, self.has_admin()) { + token + } else { + String::new() + } + } +} diff --git a/src/calls/mod.rs b/src/calls/mod.rs new file mode 100644 index 0000000..8413354 --- /dev/null +++ b/src/calls/mod.rs @@ -0,0 +1,4 @@ +pub mod call_group; +pub mod call_manager; +pub mod call_util; +pub mod caller; diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/src/sql/connection_status.rs b/src/data/user.rs similarity index 100% rename from src/sql/connection_status.rs rename to src/data/user.rs diff --git a/src/main.rs b/src/main.rs index 8143c9f..fa511ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,25 @@ -mod server; -mod sql; -mod transport; +mod anonymous_clients; +mod calls; +mod data; +mod omega; +mod rho; mod util; -use crate::sql::sql::initialize_db; -use crate::sql::sql::print_users; -use crate::transport::omikron_connection; -use crate::util::crypto_helper::load_public_key; -use crate::util::crypto_helper::load_secret_key; -use crate::util::logger::PrintType; -use crate::util::logger::startup; +use std::env; + use dotenv::dotenv; use once_cell::sync::Lazy; use rustls::crypto::aws_lc_rs::default_provider; -use std::env; + +use crate::{ + calls::call_util::garbage_collect_calls, + omega::omega_connection::get_omega_connection, + rho::server::start, + util::{ + crypto_helper::{load_public_key, load_secret_key}, + logger::startup, + }, +}; static PRIVATE_KEY: Lazy = Lazy::new(|| env::var("PRIVATE_KEY").unwrap()); pub fn get_private_key() -> x448::Secret { @@ -32,34 +38,14 @@ async fn main() { } dotenv().ok(); startup(); - log_in!("Incoming messages"); - log_out!("Outgoing messages"); + get_omega_connection(); tokio::spawn(async move { - match omikron_connection::start(9187).await { - Err(e) => log_err!(0, PrintType::General, "{:?}", e), - _ => {} + if let Err(e) = start(959).await { + log_err!(0, util::logger::PrintType::General, "{}", e); } }); - - log!("Started"); - log!(" .env"); - if let Err(e) = initialize_db().await { - log!("[FATAL] Database initialization failed: {}", e); - log!( - "[FATAL] Please ensure the database is running and the .env file is configured correctly." - ); - return; - } else { - log!(" DB"); - } - if let Err(e) = print_users().await { - log!("[ERROR] Failed to print users: {}", e); - } else { - log!(" Users"); - } - - let _ = server::server::start(9188).await; + garbage_collect_calls(); tokio::signal::ctrl_c().await.unwrap(); } diff --git a/src/omega/mod.rs b/src/omega/mod.rs new file mode 100644 index 0000000..cbe77d9 --- /dev/null +++ b/src/omega/mod.rs @@ -0,0 +1 @@ +pub mod omega_connection; diff --git a/src/omega/omega_connection.rs b/src/omega/omega_connection.rs new file mode 100644 index 0000000..ab84eb2 --- /dev/null +++ b/src/omega/omega_connection.rs @@ -0,0 +1,741 @@ +use crate::{ + data::user::UserStatus, + get_private_key, log, log_cv_in, log_cv_out, log_err, log_in, + rho::rho_manager::{self, RHO_CONNECTIONS, connection_count}, + util::{ + crypto_helper::{decrypt_b64, secret_key_to_base64}, + file_util::load_file_vec, + logger::PrintType, + }, +}; +use dashmap::DashMap; +use once_cell::sync::Lazy; +use std::{collections::HashMap, env, sync::Arc, time::Duration}; +use tokio::{ + sync::{Mutex, RwLock, mpsc, watch}, + task::JoinHandle, + time::{Instant, sleep}, +}; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue, rand_u32}; +use ttp_native::{Receiver, Sender}; +use uuid::Uuid; + +// ============================================================================ +// Configuration +// ============================================================================ + +const OMEGA_HOST_DEFAULT: &str = "methanium.net"; +const OMEGA_PORT_DEFAULT: u16 = 9187; +const RECONNECT_DELAY: Duration = Duration::from_secs(5); +const MAX_RECONNECT_DELAY: Duration = Duration::from_secs(300); +const CONNECTION_TIMEOUT: Duration = Duration::from_secs(10); +const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); +const TASK_CLEANUP_INTERVAL: Duration = Duration::from_secs(60); +const TASK_MAX_AGE: Duration = Duration::from_secs(60); + +// ============================================================================ +// Waiting Task System +// ============================================================================ + +pub struct WaitingTask { + pub task: Box, CommunicationValue) -> bool + Send + Sync>, + pub inserted_at: Instant, +} + +pub static WAITING_TASKS: Lazy> = Lazy::new(DashMap::new); + +pub fn start_task_cleanup_loop() { + tokio::spawn(async { + loop { + sleep(TASK_CLEANUP_INTERVAL).await; + WAITING_TASKS.retain(|_, v| v.inserted_at.elapsed() < TASK_MAX_AGE); + } + }); +} + +// ============================================================================ +// Connection State +// ============================================================================ + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum ConnectionState { + Disconnected, + Connecting, + Connected { identified: bool }, +} + +#[allow(unused_variables)] +impl ConnectionState { + pub fn is_connected(&self) -> bool { + match self { + ConnectionState::Connected { identified } => true, + _ => false, + } + } + + #[allow(dead_code)] + pub fn is_identified(&self) -> bool { + match self { + ConnectionState::Connected { identified: true } => true, + _ => false, + } + } +} + +// ============================================================================ +// Omega Connection (Client-side with auto-reconnect) +// ============================================================================ +#[allow(dead_code)] +pub struct OmegaConnection { + state: Arc>, + sender: Arc>>>, + connection_loop_handle: Arc>>>, + host: String, + port: u16, + server_cert: Vec, + last_ping: Arc>, + heartbeat_handle: Arc>>>, + message_send_times: Arc>>, + pub connection_id: Uuid, + shutdown_tx: Arc>>>, + // Track if we should reconnect on close + reconnect_on_close: Arc>, +} + +impl OmegaConnection { + pub fn new() -> Self { + Self::with_host(OMEGA_HOST_DEFAULT, OMEGA_PORT_DEFAULT) + } + + pub fn with_host(host: &str, port: u16) -> Self { + // Load server certificate from default location + let server_cert = + load_file_vec("certs", "cert.pem").expect("Failed to load server certificate"); + + Self::with_host_and_cert(host, port, server_cert) + } + + // New constructor that accepts certificate directly + pub fn with_host_and_cert(host: &str, port: u16, server_cert: Vec) -> Self { + let (shutdown_tx, _) = watch::channel(false); + + OmegaConnection { + state: Arc::new(RwLock::new(ConnectionState::Disconnected)), + sender: Arc::new(RwLock::new(None)), + connection_loop_handle: Arc::new(Mutex::new(None)), + host: host.to_string(), + port, + server_cert, + last_ping: Arc::new(Mutex::new(-1)), + heartbeat_handle: Arc::new(Mutex::new(None)), + message_send_times: Arc::new(Mutex::new(HashMap::new())), + connection_id: Uuid::new_v4(), + shutdown_tx: Arc::new(Mutex::new(Some(shutdown_tx))), + reconnect_on_close: Arc::new(RwLock::new(true)), + } + } + + // ------------------------------------------------------------------------- + // Connection Management + // ------------------------------------------------------------------------- + + pub async fn start(self: Arc) { + // Cancel any existing connection loop + if let Some(handle) = self.connection_loop_handle.lock().await.take() { + handle.abort(); + } + + // Set reconnect flag + *self.reconnect_on_close.write().await = true; + + let self_clone = self.clone(); + let handle = tokio::spawn(async move { + self_clone.connection_loop().await; + }); + + *self.connection_loop_handle.lock().await = Some(handle); + } + + #[allow(dead_code)] + pub async fn stop(&self) { + // Disable reconnection + *self.reconnect_on_close.write().await = false; + + if let Some(tx) = self.shutdown_tx.lock().await.take() { + let _ = tx.send(true); + } + + if let Some(handle) = self.connection_loop_handle.lock().await.take() { + handle.abort(); + } + + if let Some(handle) = self.heartbeat_handle.lock().await.take() { + handle.abort(); + } + + // Close sender if connected + if let Some(sender) = self.sender.read().await.as_ref() { + sender.close(); + } + + *self.state.write().await = ConnectionState::Disconnected; + *self.sender.write().await = None; + } + + async fn connection_loop(self: Arc) { + let mut reconnect_delay = RECONNECT_DELAY; + let shutdown_rx = self.shutdown_tx.lock().await.as_ref().unwrap().subscribe(); + let mut shutdown_rx = shutdown_rx; + + loop { + if *shutdown_rx.borrow() { + log_in!(0, PrintType::Omega, "Connection loop shutting down"); + break; + } + + // Check if reconnection is enabled + if !*self.reconnect_on_close.read().await { + log_in!(0, PrintType::Omega, "Reconnection disabled, exiting loop"); + break; + } + + match self.clone().connect_once().await { + Ok(()) => { + // Connection closed gracefully, check if we should reconnect + if *self.reconnect_on_close.read().await { + log_err!( + 0, + PrintType::Omega, + "Connection lost, reconnecting in {:?}...", + reconnect_delay + ); + } else { + log_in!(0, PrintType::Omega, "Connection closed, not reconnecting"); + break; + } + } + Err(e) => { + log_err!( + 0, + PrintType::Omega, + "Connection failed: {}, retrying in {:?}...", + e, + reconnect_delay + ); + } + } + + tokio::select! { + _ = sleep(reconnect_delay) => {} + _ = shutdown_rx.changed() => { + if *shutdown_rx.borrow() { + break; + } + } + } + + reconnect_delay = std::cmp::min(reconnect_delay * 2, MAX_RECONNECT_DELAY); + } + } + + async fn connect_once(self: Arc) -> Result<(), String> { + *self.state.write().await = ConnectionState::Connecting; + + let addr_str = format!("https://{}:{}", self.host, self.port); + + let (sender, mut receiver) = ttp_native::client::connect(&addr_str, None) + .await + .map_err(|e| format!("Connection failed: {}", e))?; + + log_in!( + 0, + PrintType::Omega, + "QUIC connection established to {}", + addr_str + ); + + // Store sender + let sender_arc = Arc::new(sender); + *self.sender.write().await = Some(sender_arc.clone()); + *self.state.write().await = ConnectionState::Connected { identified: false }; + + // Get handle for close monitoring + let sender_handle = sender_arc.handle().clone(); + + // Start read loop + let read_self = self.clone(); + let read_handle = tokio::spawn(async move { + read_self.read_loop(&mut receiver, sender_handle).await; + }); + + // Send identification + self.send_identification().await; + + // Start heartbeat + let heartbeat_self = self.clone(); + let heartbeat_handle = tokio::spawn(async move { + heartbeat_self.heartbeat_loop().await; + }); + *self.heartbeat_handle.lock().await = Some(heartbeat_handle); + + // Wait for read loop to complete (connection closed) + let result = read_handle.await; + + // Cleanup + *self.sender.write().await = None; + *self.state.write().await = ConnectionState::Disconnected; + + if let Some(handle) = self.heartbeat_handle.lock().await.take() { + handle.abort(); + } + + match result { + Ok(()) => { + // Check if we should reconnect + if *self.reconnect_on_close.read().await { + Err("Connection closed, will reconnect".to_string()) + } else { + Ok(()) + } + } + Err(e) => Err(format!("Read loop error: {}", e)), + } + } + + // ------------------------------------------------------------------------- + // Identification Handshake + // ------------------------------------------------------------------------- + + async fn send_identification(&self) { + let id = rand_u32(); + + let omikron_id = env::var("ID") + .unwrap_or("0".to_string()) + .parse::() + .unwrap_or(0); + + let identify_msg = CommunicationValue::new(CommunicationType::identification) + .with_id(id) + .add_data(DataTypes::omikron_id, DataValue::Number(omikron_id)); + + 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" + ); + return false; + } + if !cv.is_type(CommunicationType::challenge) { + return false; + } + + tokio::spawn(async move { + if let Err(e) = selfc.handle_challenge(cv).await { + log_err!(0, PrintType::Omega, "Challenge handling failed: {}", e); + } + }); + true + }), + inserted_at: Instant::now(), + }, + ); + + self.send_message(&identify_msg).await; + } + + async fn handle_challenge(&self, cv: CommunicationValue) -> Result<(), String> { + let challenge = cv + .get_data(DataTypes::challenge) + .as_str() + .ok_or("Challenge not found")?; + + let server_pub_key = cv + .get_data(DataTypes::public_key) + .as_str() + .ok_or("Public key not found")?; + + let decrypted_challenge = decrypt_b64( + &secret_key_to_base64(&get_private_key()), + server_pub_key, + challenge, + ) + .map_err(|e| format!("Decryption failed: {:?}", e))?; + + let response_msg = CommunicationValue::new(CommunicationType::challenge_response) + .with_id(cv.get_id()) + .add_data(DataTypes::challenge, DataValue::Str(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"); + return false; + } + + let accepted = final_cv + .get_data(DataTypes::accepted) + .as_bool() + .unwrap_or(false); + + if !accepted { + log_err!(0, PrintType::Omega, "Omega did not accept identification"); + return false; + } + + tokio::spawn(async move { + let mut state = selfc.state.write().await; + if let ConnectionState::Connected { identified: _ } = *state { + *state = ConnectionState::Connected { identified: true }; + } + drop(state); + + selfc.sync_client_iota_status().await; + }); + + log!(0, PrintType::Omega, "Successfully identified with Omega"); + true + }), + inserted_at: Instant::now(), + }, + ); + + self.send_message(&response_msg).await; + Ok(()) + } + + async fn sync_client_iota_status(self: Arc) { + 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(DataValue::Number(*iota_id)); + } + + for rho in rho_connections_reader.values() { + for client_conn in rho.get_client_connections().await { + connected_user_ids.push(DataValue::Number(client_conn.get_user_id().await as i64)); + } + } + + drop(rho_connections_reader); + + let sync_msg = CommunicationValue::new(CommunicationType::sync_client_iota_status) + .add_data(DataTypes::iota_ids, DataValue::Array(connected_iota_ids)) + .add_data(DataTypes::user_ids, DataValue::Array(connected_user_ids)) + .add_data( + DataTypes::rho_connections, + DataValue::Number(connection_count().await as i64), + ); + + self.send_message(&sync_msg).await; + } + + // ------------------------------------------------------------------------- + // Read Loop & Heartbeat + // ------------------------------------------------------------------------- + + async fn read_loop( + self: Arc, + receiver: &mut Receiver, + sender_handle: Arc, + ) { + // Monitor both receiver and sender handle for close + let mut close_rx = sender_handle.subscribe_close(); + + loop { + tokio::select! { + result = receiver.receive() => { + match result { + Ok(cv) => { + if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) { + log_cv_in!(PrintType::Omega, &cv); + } + + if cv.is_type(CommunicationType::pong) || cv.is_type(CommunicationType::ping) { + self.handle_pong(&cv).await; + continue; + } + + let msg_id = cv.get_id(); + if let Some((_, task)) = WAITING_TASKS.remove(&msg_id) { + if (task.task)(self.clone(), cv.clone()) { + continue; + } + } + + if cv.is_type(CommunicationType::iota_user_data) { + if let DataValue::Array(users) = cv.get_data(DataTypes::user_ids) { + let mut user_ids: Vec = Vec::new(); + for value in users { + if let DataValue::Number(user_id) = value { + user_ids.push(*user_id as u64); + } + } + let connections = crate::rho::rho_manager::RHO_CONNECTIONS.read().await; + if let Some(iota_id) = cv.get_data(DataTypes::iota_id).as_number() { + if let Some(rho) = connections.get(&iota_id) { + rho.get_iota_connection().set_user_ids(user_ids).await; + } + } else { + for rho in connections.values() { + rho.get_iota_connection().set_user_ids(user_ids.clone()).await; + } + } + } + } + } + Err(e) => { + log_err!(0, PrintType::Omega, "Receive error: {}", e); + break; + } + } + } + _ = close_rx.changed() => { + // Connection was closed by either side + if let Some(reason) = close_rx.borrow().clone() { + log_err!(0, PrintType::Omega, "Connection closed: {:?}", reason); + } else { + log_in!(0, PrintType::Omega, "Connection closed cleanly"); + } + break; + } + } + } + } + + async fn heartbeat_loop(self: Arc) { + loop { + sleep(HEARTBEAT_INTERVAL).await; + + // Check if still connected + if !self.state.read().await.is_connected() { + break; + } + + // Check if sender is closed + if let Some(sender) = self.sender.read().await.as_ref() { + if sender.is_closed() { + log_err!(0, PrintType::Omega, "Sender closed, stopping heartbeat"); + break; + } + } else { + break; + } + + self.send_ping().await; + } + } + + async fn send_ping(&self) { + let ping = CommunicationValue::new(CommunicationType::ping).add_data( + DataTypes::send_time, + DataValue::Number( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i64, + ), + ); + self.send_message(&ping).await; + } + + async fn handle_pong(&self, cv: &CommunicationValue) { + let timestamp = cv + .get_data(DataTypes::send_time) + .as_number() + .unwrap_or_else(|| { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i64 + }); + + *self.last_ping.lock().await = timestamp; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + pub async fn send_message(&self, cv: &CommunicationValue) { + if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) { + log_cv_out!(PrintType::Omega, &cv); + } + + let sender_guard = self.sender.read().await; + if let Some(sender) = sender_guard.as_ref() { + // Check if closed before sending + if sender.is_closed() { + log_err!(0, PrintType::Omega, "Cannot send: connection closed"); + drop(sender_guard); + // Trigger reconnection by closing the connection state + if let Some(sender) = self.sender.write().await.take() { + sender.close(); + } + return; + } + + let sender_clone = Arc::clone(sender); + drop(sender_guard); + + if let Err(e) = sender_clone.send(cv).await { + log_err!(0, PrintType::Omega, "Send failed: {}", e); + } + } else { + log_err!(0, PrintType::Omega, "Cannot send: not connected"); + } + } + + pub async fn await_connection(&self, timeout_duration: Option) -> Result<(), String> { + if self.state.read().await.is_connected() { + return Ok(()); + } + + let timeout = timeout_duration.unwrap_or(CONNECTION_TIMEOUT); + let start = Instant::now(); + + loop { + if self.state.read().await.is_connected() { + return Ok(()); + } + + if start.elapsed() >= timeout { + return Err(format!( + "Connection not established within {} seconds", + timeout.as_secs() + )); + } + + sleep(Duration::from_millis(100)).await; + } + } + + 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(); + + WAITING_TASKS.insert( + msg_id, + WaitingTask { + task: Box::new(move |_, response_cv| { + let inner_tx = tx.clone(); + tokio::spawn(async move { + let _ = inner_tx.send(response_cv).await; + }); + 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("Channel closed".to_string()), + Err(_) => { + WAITING_TASKS.remove(&msg_id); + Err("Request timed out".to_string()) + } + } + } + + #[allow(dead_code)] + pub async fn is_connected(&self) -> bool { + self.state.read().await.is_connected() + } + #[allow(dead_code)] + pub async fn is_identified(&self) -> bool { + self.state.read().await.is_identified() + } + + #[allow(dead_code)] + pub async fn close_iota(iota_id: i64) { + let cv = CommunicationValue::new(CommunicationType::iota_disconnected) + .add_data(DataTypes::iota_id, DataValue::Number(iota_id)); + OMEGA_CONNECTION.send_message(&cv).await; + } + + pub async fn client_changed(_iota_id: i64, user_id: i64, state: UserStatus) { + let msg_type = match state { + UserStatus::iota_offline => CommunicationType::user_disconnected, + UserStatus::user_offline => CommunicationType::user_disconnected, + _ => CommunicationType::user_connected, + }; + + let cv = CommunicationValue::new(msg_type) + .add_data(DataTypes::user_id, DataValue::Number(user_id)); + OMEGA_CONNECTION.send_message(&cv).await; + } + + pub async fn user_states(user_id: i64, user_ids: Vec) { + let user_ids = user_ids.iter().map(|v| DataValue::Number(*v)).collect(); + + let cv = CommunicationValue::new(CommunicationType::get_states) + .add_data(DataTypes::user_ids, DataValue::Array(user_ids)); + let msg_id = cv.get_id(); + + WAITING_TASKS.insert( + msg_id, + WaitingTask { + task: Box::new( + move |_: Arc, response: CommunicationValue| { + tokio::spawn(async move { + let rho = rho_manager::get_rho_con_for_user(user_id).await; + if let Some(rho) = rho { + for client in rho.get_client_connections_for_user(user_id).await { + client.send_message(&response).await; + } + } + }); + true + }, + ), + inserted_at: Instant::now(), + }, + ); + + OMEGA_CONNECTION.send_message(&cv).await; + } +} + +// ============================================================================ +// Global Instance +// ============================================================================ + +static OMEGA_CONNECTION: Lazy> = Lazy::new(|| { + let conn = Arc::new(OmegaConnection::new()); + + // Start the connection manager immediately + let conn_clone = conn.clone(); + tokio::spawn(async move { + conn_clone.start().await; + }); + + start_task_cleanup_loop(); + + conn +}); + +pub fn get_omega_connection() -> Arc { + OMEGA_CONNECTION.clone() +} diff --git a/src/omega/ping_pong_task.rs b/src/omega/ping_pong_task.rs new file mode 100644 index 0000000..c7fc0b9 --- /dev/null +++ b/src/omega/ping_pong_task.rs @@ -0,0 +1,41 @@ +use std::time::Duration; +use tokio::time::Instant; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue, rand_u32}; + +use crate::omega::omega_connection::OmegaConnection; + +const PING_TIMEOUT: Duration = Duration::from_secs(30); + +impl OmegaConnection { + pub async fn send_ping(&self) { + 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(id as i64, send_time); + + self.send_ping_message(id).await; + } + + pub async fn send_ping_message(&self, id: u32) { + let ping_message = CommunicationValue::new(CommunicationType::ping) + .with_id(id) + .add_data( + DataTypes::last_ping, + DataValue::Number(self.last_ping.lock().await.unwrap()), + ); + + self.send_message(&ping_message).await; + } + + /// Handles incoming pong and calculates latency + 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 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 new file mode 100644 index 0000000..ebaabf2 --- /dev/null +++ b/src/rho/client_connection.rs @@ -0,0 +1,583 @@ +use crate::anonymous_clients::anonymous_manager; +use crate::calls::{call_manager, call_util}; +use crate::omega::omega_connection::get_omega_connection; +use crate::rho::connection::GeneralConnection; +use crate::rho::{rho_connection::RhoConnection, rho_manager}; +use crate::util::logger::PrintType; +use crate::{data::user::UserStatus, omega::omega_connection::OmegaConnection}; +use crate::{log_cv_in, log_cv_out, log_err, log_in, log_out}; +use std::str::FromStr; +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tokio::sync::RwLock; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use ttp_native::{Receiver, Sender}; +use uuid::Uuid; + +pub struct ClientConnection { + pub user_id: u64, + + pub sender: Arc, + pub receiver: Arc, + pub ping: Arc>, + pub_key: Arc>>>, + pub rho_connection: Arc>>>, + pub interested_users: Arc>>, + is_open: Arc>, +} + +impl ClientConnection { + pub async fn from_general(general: Arc, user_id: u64) -> Arc { + Arc::new(Self { + ping: Arc::new(RwLock::new(0)), + pub_key: Arc::new(RwLock::new(None)), + rho_connection: general.rho_connection.clone(), + 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; + } + self_clone.handle_close().await; + }); + + let self_clone2 = self.clone(); + tokio::spawn(async move { + tokio::time::sleep(Duration::from_millis(50)).await; + if self_clone2.get_rho_connection().await.is_none() { + self_clone2 + .send_error_response(0, CommunicationType::error) + .await; + } + }); + } + + /// Get the user ID + pub async fn get_user_id(&self) -> u64 { + self.user_id + } + + /// Get current ping + pub async fn get_ping(&self) -> i64 { + *self.ping.read().await + } + + /// Get RhoConnection if available + pub async fn get_rho_connection(&self) -> Option> { + self.rho_connection.read().await.clone() + } + + /// Send a CommunicationValue to the client + pub async fn send_message(self: Arc, cv: &CommunicationValue) { + if !*self.is_open.read().await { + log_out!( + 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_cv_out!(PrintType::Client, &cv); + } + let _ = self.sender.send(&cv).await; + } + + /// Handle incoming message from client + pub async fn handle_message(self: Arc, cv: CommunicationValue) { + tokio::spawn(async move { + if cv.is_type(CommunicationType::ping) { + self.handle_ping(cv).await; + return; + } + log_cv_in!(PrintType::Client, cv); + + // Handle client status changes + if cv.is_type(CommunicationType::client_changed) { + self.handle_client_changed(cv).await; + return; + } + + // Handle call invites + if cv.is_type(CommunicationType::call_invite) { + self.handle_call_invite(cv).await; + return; + } + + // Handle get call requests + if cv.is_type(CommunicationType::call_token) { + self.handle_get_call(cv).await; + return; + } + + if cv.is_type(CommunicationType::call_disconnect_user) { + self.handle_call_disconnect_user(cv).await; + return; + } + + if cv.is_type(CommunicationType::call_timeout_user) { + self.handle_call_timeout_user(cv).await; + return; + } + + if cv.is_type(CommunicationType::call_set_anonymous_joining) { + self.handle_call_set_anonymous_joining(cv).await; + return; + } + if cv.is_type(CommunicationType::get_user_data) { + if let Some(anonymous) = { + 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 + } + } { + let response = CommunicationValue::new(CommunicationType::get_user_data) + .with_id(cv.get_id()) + .add_data( + DataTypes::username, + DataValue::Str(anonymous.get_user_name().await), + ) + .add_data( + DataTypes::user_id, + DataValue::Number(anonymous.get_user_id() as i64), + ) + .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; + + return; + } + } + + if cv.is_type(CommunicationType::change_user_data) + || cv.is_type(CommunicationType::read_notification) + || cv.is_type(CommunicationType::get_notifications) + || cv.is_type(CommunicationType::get_user_data) + || cv.is_type(CommunicationType::get_iota_data) + || cv.is_type(CommunicationType::delete_user) + { + let sender = self.get_user_id().await; + self.handle_omega_forward(cv.with_sender(sender as u64)) + .await; + return; + } + // Forward other messages to Iota + self.forward_to_iota(cv).await; + }); + } + async fn handle_omega_forward(self: Arc, cv: CommunicationValue) { + let client_for_closure = self.clone(); + tokio::spawn(async move { + let response_cv = get_omega_connection() + .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; + } + }); + } + + /// Handle ping message + async fn handle_ping(self: Arc, cv: CommunicationValue) { + // Update our ping if provided + 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; + } + + // Get Iota ping from RhoConnection + let iota_ping = if let Some(rho_conn) = self.get_rho_connection().await { + rho_conn.get_iota_connection().get_ping().await + } else { + -1 + }; + + // Send pong response + let response = CommunicationValue::new(CommunicationType::pong) + .with_id(cv.get_id()) + .add_data(DataTypes::ping_iota, DataValue::Number(iota_ping)); + + self.send_message(&response).await; + } + + /// Handle client status change + async fn handle_client_changed(self: Arc, cv: CommunicationValue) { + let user_id = self.get_user_id().await; + 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 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).as_number().unwrap_or(0); + if receiver_id == 0 { + self.send_error_response(cv.get_id(), CommunicationType::error_no_user_id) + .await; + return; + } + + let call_id = match cv.get_data(DataTypes::call_id) { + 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; + return; + } + }, + _ => { + 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, receiver_id as u64).await; + if !invited { + self.send_error_response(cv.get_id(), CommunicationType::error_invalid_call_id) + .await; + return; + } + + // Find target RhoConnection + 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) + .await; + return; + } + }; + + // Get sender user ID + let sender_id = self.get_user_id().await; + + // Create and send call distribution message + let forward = CommunicationValue::new(CommunicationType::call_invite) + .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; + + let response = CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); + self.send_message(&response).await; + } + + /// Handle get call request + async fn handle_get_call(self: Arc, cv: CommunicationValue) { + let user_id = self.get_user_id().await; + + let call_id = match cv.get_data(DataTypes::call_id) { + 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) + .await; + return; + } + }, + _ => { + self.send_error_response(cv.get_id(), CommunicationType::error) + .await; + return; + } + }; + + 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 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) + .await; + return; + } + } + async fn handle_call_timeout_user(self: Arc, cv: CommunicationValue) { + 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) + .await + .unwrap() + .has_admin() + { + let _ = call_util::remove_participant(call_id, user_id as u64).await; + call.get_caller(user_id as u64) + .await + .unwrap() + .set_timeout(untill) + .await; + } + } + } + async fn handle_call_disconnect_user(self: Arc, cv: CommunicationValue) { + 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) + .await + .unwrap() + .has_admin() + { + 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).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; + + let mut short_link = None; + if let Some(call) = call { + if call + .get_caller(self.get_user_id().await) + .await + .unwrap() + .has_admin() + { + call.set_anonymous_joining(enable).await; + } + short_link = call.get_short_link().await; + } + let mut response_cv = + CommunicationValue::new(CommunicationType::call_set_anonymous_joining) + .with_id(cv.get_id()) + .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, DataValue::Str(short_link)); + } + self.send_message(&response_cv).await; + } + + /// Forward message to Iota + async fn forward_to_iota(self: Arc, cv: CommunicationValue) { + let sender_user_id = self.get_user_id().await; + let msg_id = cv.get_id(); + let msg_type = cv.get_type(); + + log_in!( + sender_user_id as i64, + PrintType::Client, + "Forwarding client->iota: sender={} type={:?} id={} receiver={}", + sender_user_id, + msg_type, + msg_id, + cv.get_receiver() + ); + + if cv.is_type(CommunicationType::add_conversation) + && cv + .get_data(DataTypes::chat_partner_id) + .as_number() + .is_none() + { + let chat_partner_name = cv + .get_data(DataTypes::chat_partner_name) + .as_str() + .unwrap_or("") + .to_string(); + + if anonymous_manager::get_anonymous_user_by_name(chat_partner_name.to_string()) + .await + .is_some() + { + self.send_error_response(cv.get_id(), CommunicationType::error_anonymous) + .await; + return; + } + + let load_uuid_response = get_omega_connection() + .await_response( + &CommunicationValue::new(CommunicationType::get_user_data) + .with_id(cv.clone().get_id()) + .add_data( + DataTypes::username, + 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).clone() + } else { + DataValue::Null + } + }; + + if let Some(rho_conn) = self.get_rho_connection().await { + let iota_id = rho_conn.get_iota_id().await; + log_in!( + sender_user_id as i64, + PrintType::Client, + "Resolved rho for add_conversation: sender={} -> iota_id={} id={}", + sender_user_id, + iota_id, + msg_id + ); + + let updated_cv = cv + .with_sender(sender_user_id as u64) + .add_data(DataTypes::chat_partner_id, chat_partner_id); + rho_conn.message_to_iota(updated_cv).await; + } else { + log_err!( + sender_user_id as i64, + PrintType::Client, + "No rho/iota mapping found for add_conversation sender={} type={:?} id={}", + sender_user_id, + msg_type, + msg_id + ); + } + return; + } + + if let Some(rho_conn) = self.get_rho_connection().await { + let iota_id = rho_conn.get_iota_id().await; + log_in!( + sender_user_id as i64, + PrintType::Client, + "Resolved rho for forward: sender={} -> iota_id={} type={:?} id={}", + sender_user_id, + iota_id, + msg_type, + msg_id + ); + + let updated_cv = cv.with_sender(sender_user_id as u64); + rho_conn.message_to_iota(updated_cv).await; + } else { + log_err!( + sender_user_id as i64, + PrintType::Client, + "No rho/iota mapping found for sender={} type={:?} id={}", + sender_user_id, + msg_type, + msg_id + ); + self.send_error_response(msg_id, CommunicationType::error) + .await; + } + } + + /// Send error response + 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; + } + + /// Close the connection + pub async fn close(&self) { + let mut is_open_guard = self.is_open.write().await; + if !*is_open_guard { + return; + } + *is_open_guard = false; + + let _ = self.sender.close(); + } + + /// Set interested users list + pub async fn set_interested_users(self: Arc, interested_ids: Vec) { + let mut interested_guard = self.interested_users.write().await; + *interested_guard = interested_ids; + } + #[allow(dead_code)] + pub async fn get_interested_users(self: Arc) -> Vec { + let interested_guard = self.interested_users.read().await; + interested_guard.clone() + } + + /// Check if interested in a user and send notification + #[allow(dead_code)] + pub async fn are_you_interested(self: Arc, user_id: i64) { + let interested_guard = self.clone().get_interested_users().await; + if interested_guard.contains(&user_id) { + let notification = CommunicationValue::new(CommunicationType::client_changed) + .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; + } + } + + /// Handle connection close + pub async fn handle_close(&self) { + 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; + } + } +} + +// Implement Clone to make it easier to work with Arc +impl Clone for ClientConnection { + fn clone(&self) -> Self { + Self { + sender: Arc::clone(&self.sender), + receiver: Arc::clone(&self.receiver), + user_id: self.user_id, + ping: Arc::clone(&self.ping), + pub_key: Arc::clone(&self.pub_key), + rho_connection: Arc::clone(&self.rho_connection), + interested_users: Arc::clone(&self.interested_users), + is_open: Arc::clone(&self.is_open), + } + } +} diff --git a/src/rho/connection.rs b/src/rho/connection.rs new file mode 100755 index 0000000..ca746f0 --- /dev/null +++ b/src/rho/connection.rs @@ -0,0 +1,358 @@ +use rand::{Rng, distributions::Alphanumeric}; +use std::{sync::Arc, time::Duration}; +use tokio::sync::RwLock; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; +use ttp_native::{Receiver, Sender}; + +use crate::{ + anonymous_clients::anonymous_client_connection::AnonymousClientConnection, + get_private_key, get_public_key, log_cv_in, log_cv_out, log_err, log_in, log_out, + omega::omega_connection::get_omega_connection, + rho::{ + client_connection::ClientConnection, iota_connection::IotaConnection, + rho_connection::RhoConnection, rho_manager, + }, + util::{ + crypto_helper::{load_public_key, public_key_to_base64}, + crypto_util::{DataFormat, SecurePayload}, + logger::PrintType, + }, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(dead_code)] +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>>, + pub rho_connection: 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)), + rho_connection: 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) { + log_in!(0, PrintType::General, "General connection handler started"); + + loop { + let cv = match self.receiver.receive().await { + Ok(v) => v, + Err(_) => { + break; + } + }; + + log_cv_in!(cv); + + if !*self.identified.read().await { + self.handle_identification(cv).await; + continue; + } + + if !*self.challenged.read().await { + self.handle_challenge_response(cv).await; + } + + if *self.challenged.read().await { + let self_clone = self.clone(); + tokio::spawn(async move { + self_clone.migrate().await; + }); + break; + } + } + + log_out!(0, PrintType::General, "General connection handler stopped"); + } + 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.as_bytes(), DataFormat::Raw, get_private_key()) + .unwrap() + .encrypt_x448(pub_key) + .unwrap() + .export(DataFormat::Base64); + + let response = CommunicationValue::new(CommunicationType::challenge) + .with_id(cv.get_id()) + .add_data( + DataTypes::public_key, + DataValue::Str(public_key_to_base64(&get_public_key())), + ) + .add_data(DataTypes::challenge, DataValue::Str(encrypted_challenge)); + + log_cv_out!(response); + let _ = self.sender.send(&response).await; + } else if let DataValue::Number(user_id) = cv.get_data(DataTypes::user_id) { + *self.id.write().await = *user_id as u64; + *self.connection_kind.write().await = Some(ConnectionKind::Client); + + let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_user_data) + .add_data(DataTypes::user_id, DataValue::Number(*user_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.as_bytes(), DataFormat::Raw, get_private_key()) + .unwrap() + .encrypt_x448(pub_key) + .unwrap() + .export(DataFormat::Base64); + + let response = CommunicationValue::new(CommunicationType::challenge) + .with_id(cv.get_id()) + .add_data( + DataTypes::public_key, + DataValue::Str(public_key_to_base64(&get_public_key())), + ) + .add_data(DataTypes::challenge, DataValue::Str(encrypted_challenge)); + + log_cv_out!(response); + let _ = self.sender.send(&response).await; + } + } + async fn handle_challenge_response(self: &Arc, cv: CommunicationValue) { + let id = *self.id.read().await as i64; + + if !cv.is_type(CommunicationType::challenge_response) { + return; + } + + if let DataValue::Str(response) = cv.get_data(DataTypes::challenge) { + let expected = self.challenge.read().await.clone(); + + if *response == expected { + *self.challenged.write().await = true; + + let response = CommunicationValue::new(CommunicationType::identification_response) + .with_id(cv.get_id()) + .add_data(DataTypes::accepted, DataValue::Bool(true)); + + log_cv_out!(response); + if let Err(_) = self.sender.send(&response).await { + return; + } + } else { + log_err!( + id, + PrintType::Iota, + "Challenge response mismatch expected={} actual={}", + expected, + response + ); + } + } else { + log_err!( + id, + PrintType::Iota, + "Challenge response missing challenge payload" + ); + } + } + + 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 notify = CommunicationValue::new(CommunicationType::user_connected) + .add_data(DataTypes::user_id, DataValue::Number(id as i64)); + get_omega_connection().send_message(¬ify).await; + + let user_id = id as i64; + + let client = ClientConnection::from_general(self.clone(), id).await; + + let mut rho = rho_manager::get_rho_con_for_user(user_id).await; + + if rho.is_none() { + let get_user_msg = CommunicationValue::new(CommunicationType::get_user_data) + .add_data(DataTypes::user_id, DataValue::Number(user_id)); + + if let Ok(user_data_cv) = get_omega_connection() + .await_response(&get_user_msg, Some(Duration::from_secs(20))) + .await + { + if let DataValue::Number(iota_id) = + user_data_cv.get_data(DataTypes::iota_id) + { + if let Some(bound_rho) = + rho_manager::bind_user_to_iota(user_id, *iota_id).await + { + bound_rho.bind_user_id(user_id).await; + rho = Some(bound_rho); + } + } + } + } + + *self.rho_connection.write().await = rho.clone(); + + if let Some(rho_conn) = rho { + rho_conn.bind_user_id(user_id).await; + rho_conn.add_client_connection(client.clone()).await; + } else { + log_err!( + user_id, + PrintType::Client, + "No RhoConnection found for user {}, client not attached to iota", + id + ); + } + + client.start(); + } + ConnectionKind::Iota => { + let notify = CommunicationValue::new(CommunicationType::iota_connected) + .add_data(DataTypes::iota_id, DataValue::Number(id as i64)); + get_omega_connection().send_message(¬ify).await; + + let iota = IotaConnection::from_general(self.clone(), id).await; + + let rho = Arc::new(RhoConnection::new(iota.clone(), Vec::new()).await); + + iota.set_rho_connection(rho.clone()).await; + + rho_manager::add_rho(rho).await; + + let get_iota_msg = CommunicationValue::new(CommunicationType::get_iota_data) + .add_data(DataTypes::iota_id, DataValue::Number(id as i64)); + + if let Ok(iota_data_cv) = get_omega_connection() + .await_response(&get_iota_msg, Some(Duration::from_secs(20))) + .await + { + if let DataValue::Array(users) = iota_data_cv.get_data(DataTypes::user_ids) { + let mut user_ids: Vec = Vec::new(); + for value in users { + if let DataValue::Number(user_id) = value { + user_ids.push(*user_id as u64); + } + } + iota.set_user_ids(user_ids).await; + } + } + + iota.clone().start(); + } + ConnectionKind::AnonymousClient => { + let client = AnonymousClientConnection::from_general(self.clone(), id).await; + client.start(); + } + ConnectionKind::Phi => { + let phi = ClientConnection::from_general(self.clone(), id).await; + phi.start(); + } + } + true + } +} diff --git a/src/rho/iota_connection.rs b/src/rho/iota_connection.rs new file mode 100755 index 0000000..41336ea --- /dev/null +++ b/src/rho/iota_connection.rs @@ -0,0 +1,461 @@ +use crate::calls::call_group::CallGroup; +use crate::calls::call_manager; +use crate::log_cv_in; +use crate::log_cv_out; +use crate::log_err; +use crate::log_in; +use crate::omega::omega_connection::get_omega_connection; +use crate::rho::connection::GeneralConnection; +use crate::util::logger::PrintType; +use dashmap::DashMap; +use std::collections::BTreeMap; +use std::{collections::HashMap, sync::Arc, time::Duration}; +use tokio::sync::RwLock; +use tokio::sync::mpsc; +use ttp_core::CommunicationType; +use ttp_core::CommunicationValue; +use ttp_core::DataTypes; +use ttp_core::DataValue; +use ttp_native::Receiver; +use ttp_native::Sender; +use x448::PublicKey; + +use super::{rho_connection::RhoConnection, rho_manager}; +use crate::omega::omega_connection::OmegaConnection; + +#[allow(dead_code)] +pub struct IotaConnection { + 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>>, + pub rho_connection: Arc>>>, +} + +impl IotaConnection { + pub async fn from_general(general: Arc, iota_id: u64) -> Arc { + Arc::new(Self { + ping: Arc::new(RwLock::new(0)), + pub_key: Arc::new(RwLock::new(None)), + rho_connection: general.rho_connection.clone(), + 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 { + loop { + match self_clone.receiver.receive().await { + Ok(cv) => { + self_clone.clone().handle_message(cv).await; + } + Err(_) => { + break; + } + } + } + self_clone.handle_close().await; + }); + } + + /// Get the Iota ID + pub async fn get_iota_id(&self) -> u64 { + self.iota_id + } + + #[allow(dead_code)] + pub async fn get_public_key(&self) -> Option { + if let Some(public_key) = self.pub_key.read().await.clone() { + PublicKey::from_bytes(&public_key) + } else { + None + } + } + + /// Get the user IDs + pub async fn get_user_ids(&self) -> Vec { + self.user_ids.read().await.clone() + } + + /// Replace all users linked to this iota and synchronize the attached rho mapping. + pub async fn set_user_ids(&self, user_ids: Vec) { + { + let mut guard = self.user_ids.write().await; + *guard = user_ids.clone(); + } + + if let Some(rho_conn) = self.get_rho_connection().await { + let user_ids_i64: Vec = user_ids.into_iter().map(|u| u as i64).collect(); + rho_conn.set_user_ids(user_ids_i64).await; + } + } + + pub async fn add_user_id(&self, user_id: u64) { + let mut should_sync = false; + { + let mut guard = self.user_ids.write().await; + if !guard.contains(&user_id) { + guard.push(user_id); + should_sync = true; + } + } + + if should_sync { + if let Some(rho_conn) = self.get_rho_connection().await { + rho_conn.add_user_id(user_id as i64).await; + } + } + } + + /// Get current ping + pub async fn get_ping(&self) -> i64 { + *self.ping.read().await + } + + /// Set the RhoConnection reference + pub async fn set_rho_connection(&self, rho_connection: Arc) { + let mut rho_ref = self.rho_connection.write().await; + *rho_ref = Some(rho_connection); + } + + /// Get RhoConnection if available + pub async fn get_rho_connection(&self) -> Option> { + let rho_ref = self.rho_connection.read().await; + if let Some(weak_ref) = rho_ref.as_ref() { + Some(weak_ref.clone()) + } else { + None + } + } + + /// Send a CommunicationValue to the Iota + pub async fn send_message(&self, cv: &CommunicationValue) { + if !cv.is_type(CommunicationType::pong) { + log_cv_out!(PrintType::Iota, cv); + } + if let Err(e) = self.sender.send(&cv).await { + log_err!( + self.iota_id as i64, + PrintType::Iota, + "Failed to send message: {:?}", + e + ); + } + } + + /// Handle incoming message from Iota + pub async fn handle_message(self: Arc, cv: CommunicationValue) { + let msg_id = cv.get_id(); + if let Some((_, task)) = self.waiting_tasks.remove(&msg_id) { + if (task)(self.clone(), cv.clone()) { + return; + } + } + + // Handle ping + if cv.is_type(CommunicationType::ping) || cv.is_type(CommunicationType::pong) { + self.handle_ping(cv).await; + return; + } + + log_cv_in!(PrintType::Iota, cv); + + // Handle GET_CHATS + if cv.is_type(CommunicationType::get_chats) { + self.handle_get_chats(cv).await; + return; + } + + // 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 as u64))) + || cv.is_type(CommunicationType::message_other_iota) + || cv.is_type(CommunicationType::send_chat) + { + self.handle_forward_message(cv).await; + return; + } + + if cv.is_type(CommunicationType::change_iota_data) + || cv.is_type(CommunicationType::push_notification) + || cv.is_type(CommunicationType::get_user_data) + || cv.is_type(CommunicationType::get_iota_data) + || cv.is_type(CommunicationType::get_register) + || cv.is_type(CommunicationType::complete_register_user) + || cv.is_type(CommunicationType::delete_iota) + { + let sender = self.get_iota_id().await; + self.handle_omega_forward(cv.with_sender(sender as u64)) + .await; + return; + } + self.forward_to_client(cv).await; + } + + #[allow(dead_code)] + 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; + } + + #[allow(dead_code)] + async fn close(&self) { + 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), Some(Duration::from_secs(20))) + .await; + if let Ok(response_cv) = response_cv { + iota_for_closure.send_message(&response_cv).await; + } + }); + } + /// Handle ping message + async fn handle_ping(&self, cv: CommunicationValue) { + 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; + } + } + + let client_pings = if let Some(rho_conn) = self.get_rho_connection().await { + rho_conn.get_client_pings().await + } else { + HashMap::new() + }; + + let pings: Vec<(DataTypes, DataValue)> = client_pings + .into_iter() + .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, DataValue::Container(pings)); + + self.send_message(&response).await; + } + + /// Handle message forwarding to other Iotas + async fn handle_forward_message(&self, cv: CommunicationValue) { + let receiver_id = cv.get_receiver(); + let sender_id = cv.get_sender(); + let my_user_ids = self.get_user_ids().await; + + log_in!( + self.iota_id as i64, + PrintType::Iota, + "Authority check: sender_id={} receiver_id={} iota_user_ids={:?} msg_type={:?} msg_id={}", + sender_id, + receiver_id, + my_user_ids, + cv.get_type(), + cv.get_id() + ); + + if my_user_ids.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) + .with_id(cv.get_id()) + .with_sender(cv.get_sender()); + self.send_message(&error).await; + } + } else { + log_err!( + self.iota_id as i64, + PrintType::Iota, + "Rejected client->iota forward: sender_id={} is not authorized for this iota. Known users={:?}", + sender_id, + my_user_ids + ); + + self.send_message( + &CommunicationValue::new(CommunicationType::error_invalid_user_id).add_data( + DataTypes::error_type, + DataValue::Str( + "You are sending to another User without authority.".to_string(), + ), + ), + ) + .await; + } + } + + /// Handle GET_CHATS message + async fn handle_get_chats(&self, cv: CommunicationValue) { + let receiver_id = cv.get_receiver(); + let mut interested_ids: Vec = Vec::new(); + + // ============================ + // Load Calls + // ============================ + let calls: Vec> = call_manager::get_call_groups(receiver_id).await; + + 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 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: BTreeMap = BTreeMap::new(); + + call_map.insert(DataTypes::call_id, DataValue::Str(call.call_id.to_string())); + + if timeout > 0 { + call_map.insert(DataTypes::timeout, DataValue::Number(timeout as i64)); + } + + 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); + } + } + + // ============================ + // 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 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: BTreeMap = + 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())); + } + } + + enriched.push(DataValue::container_from_map(&user_map)); + } + } + } + + DataValue::Array(enriched) + }; + + // ============================ + // Notify Omega + // ============================ + OmegaConnection::user_states(receiver_id as i64, interested_ids.clone()).await; + + // ============================ + // Notify Rho + // ============================ + if let Some(rho_conn) = self.get_rho_connection().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; + } + + /// Forward message to client + async fn forward_to_client(&self, cv: CommunicationValue) { + if let Some(rho_conn) = self.get_rho_connection().await { + let updated_cv = cv.with_sender(self.get_iota_id().await); + rho_conn.message_to_client(updated_cv).await; + } else { + } + } + + pub async fn handle_close(&self) { + if let Some(rho_conn) = self.get_rho_connection().await { + rho_conn.close_iota_connection().await; + } + } + + #[allow(dead_code)] + pub async fn await_response( + self: Arc, + cv: &CommunicationValue, + timeout_duration: Option, + ) -> Result { + let (tx, mut rx) = mpsc::channel(1); + let msg_id = cv.get_id(); + + let task_tx = tx.clone(); + self.waiting_tasks.insert( + msg_id, + Box::new(move |_, response_cv| { + let inner_tx = task_tx.clone(); + tokio::spawn(async move { + let _ = inner_tx.send(response_cv).await; + }); + true + }), + ); + + 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(_) => { + self.waiting_tasks.remove(&msg_id); + Err(format!( + "Request timed out after {} seconds.", + timeout.as_secs() + )) + } + } + } +} + +impl std::fmt::Debug for IotaConnection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("IotaConnection") + .field("iota_id", &"[async]") + .field("identified", &"[async]") + .field("ping", &"[async]") + .finish() + } +} diff --git a/src/rho/mod.rs b/src/rho/mod.rs new file mode 100644 index 0000000..ac498f3 --- /dev/null +++ b/src/rho/mod.rs @@ -0,0 +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 new file mode 100644 index 0000000..6a740ff --- /dev/null +++ b/src/rho/rho_connection.rs @@ -0,0 +1,199 @@ +use super::{client_connection::ClientConnection, iota_connection::IotaConnection, rho_manager}; + +use crate::data::user::UserStatus; +use crate::omega::omega_connection::OmegaConnection; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; +use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; + +pub struct RhoConnection { + iota_connection: Arc, + user_ids: Arc>>, + client_connections: Arc>>>, +} + +impl RhoConnection { + /// Create a new RhoConnection + pub async fn new(iota_connection: Arc, user_ids: Vec) -> Self { + let rho_connection = Self { + iota_connection, + user_ids: Arc::new(RwLock::new(user_ids.clone())), + client_connections: Arc::new(RwLock::new(Vec::new())), + }; + + rho_connection + } + + pub async fn get_iota_id(&self) -> u64 { + self.iota_connection.iota_id + } + + pub async fn get_user_ids(&self) -> Vec { + self.user_ids.read().await.clone() + } + + pub async fn set_user_ids(&self, user_ids: Vec) { + let mut guard = self.user_ids.write().await; + *guard = user_ids; + } + + pub async fn add_user_id(&self, user_id: i64) { + let mut guard = self.user_ids.write().await; + if !guard.contains(&user_id) { + guard.push(user_id); + } + } + + pub async fn bind_user_id(&self, user_id: i64) { + self.add_user_id(user_id).await; + self.iota_connection.add_user_id(user_id as u64).await; + } + + pub fn get_iota_connection(&self) -> &Arc { + &self.iota_connection + } + + pub async fn get_client_connections(&self) -> Vec> { + let connections = self.client_connections.read().await; + connections.clone() + } + + /// Get client connections for a specific user + pub async fn get_client_connections_for_user( + &self, + user_id: i64, + ) -> Vec> { + 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 as u64 { + collections.push(con.clone()); + } + } + collections + } + + /// Add a client connection + #[allow(dead_code)] + pub async fn add_client_connection(&self, connection: Arc) { + let notification = CommunicationValue::new(CommunicationType::client_connected).add_data( + DataTypes::user_id, + DataValue::Number(connection.get_user_id().await as i64), + ); + + self.iota_connection.send_message(¬ification).await; + + { + let mut connections = self.client_connections.write().await; + connections.push(Arc::clone(&connection)); + } + + OmegaConnection::client_changed( + self.get_iota_id().await as i64, + connection.get_user_id().await as i64, + UserStatus::user_online, + ) + .await; + } + + /// Remove a client connection + pub async fn close_client_connection(&self, connection: Arc) { + let target_user_id = connection.get_user_id().await; + { + let mut connections = self.client_connections.write().await; + connections.retain(|con| { + futures::executor::block_on(async { con.get_user_id().await != target_user_id }) + }); + } + + // Notify OmegaConnection + OmegaConnection::client_changed( + self.get_iota_id().await as i64, + connection.get_user_id().await as i64, + UserStatus::user_offline, + ) + .await; + } + + /// Close the Iota connection and all associated client connections + pub async fn close_iota_connection(&self) { + // Close all client connections + let connections = self.get_client_connections().await; + for connection in connections { + connection.close().await; + } + + // Remove from manager + rho_manager::remove_rho(self.get_iota_id().await as i64).await; + + // Notify OmegaConnection + OmegaConnection::close_iota(self.get_iota_id().await as i64).await; + } + + /// Send message from Iota to specific client + pub async fn message_to_client(&self, cv: CommunicationValue) { + let connections = self.client_connections.read().await; + let receiver_id = cv.get_receiver(); + for connection in connections.iter() { + if connection.get_user_id().await == receiver_id { + connection.clone().send_message(&cv).await; + } + } + } + + /// Send message to Iota + pub async fn message_to_iota(&self, cv: CommunicationValue) { + self.iota_connection.send_message(&cv).await; + } + + /// Set interested users for a specific client + pub async fn set_interested(&self, user_id: i64, interested_ids: Vec) { + 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 as u64 { + connection + .clone() + .set_interested_users(interested_ids.clone()) + .await; + break; + } + } + } + + /// Check if clients are interested in a user + #[allow(dead_code)] + pub async fn are_they_interested(&self, user_id: i64) { + let connections = self.client_connections.read().await; + for connection in connections.iter() { + connection.clone().are_you_interested(user_id).await; + } + } + + /// Get ping information for all clients + pub async fn get_client_pings(&self) -> HashMap { + let connections = self.client_connections.read().await; + let mut pings = HashMap::new(); + + for connection in connections.iter() { + let user_id = connection.get_user_id().await; + pings.insert(user_id.to_string(), connection.get_ping().await); + } + + pings + } + + /// Check if this RhoConnection contains a specific user ID + #[allow(dead_code)] + pub async fn contains_user(&self, user_id: &i64) -> bool { + self.user_ids.read().await.contains(user_id) + } + + /// Get count of active client connections + #[allow(dead_code)] + pub async fn client_count(&self) -> usize { + let connections = self.client_connections.read().await; + connections.len() + } +} diff --git a/src/rho/rho_manager.rs b/src/rho/rho_manager.rs new file mode 100644 index 0000000..e823a04 --- /dev/null +++ b/src/rho/rho_manager.rs @@ -0,0 +1,83 @@ +use super::rho_connection::RhoConnection; +use crate::log_in; +use crate::util::logger::PrintType; +use std::{ + collections::HashMap, + sync::{Arc, LazyLock}, +}; +use tokio::sync::RwLock; + +pub static RHO_CONNECTIONS: LazyLock>>>> = + LazyLock::new(|| Arc::new(RwLock::new(HashMap::new()))); + +pub async fn get_rho_con_for_user(user_id: i64) -> Option> { + let connections = RHO_CONNECTIONS.read().await; + for rho_connection in connections.values() { + let rho_user_ids = rho_connection.get_user_ids().await; + log_in!( + user_id, + PrintType::Client, + "Comparing user IDs: {:?}", + rho_user_ids + ); + if rho_user_ids.contains(&user_id) { + return Some(Arc::clone(rho_connection)); + } + } + None +} + +#[allow(dead_code)] +pub async fn contains_iota(iota_id: i64) -> bool { + let connections = RHO_CONNECTIONS.read().await; + connections.contains_key(&iota_id) +} + +/// Bind a user ID to an already tracked iota/rho connection. +pub async fn bind_user_to_iota(user_id: i64, iota_id: i64) -> Option> { + let connections = RHO_CONNECTIONS.read().await; + if let Some(rho_connection) = connections.get(&iota_id) { + let rho = Arc::clone(rho_connection); + drop(connections); + + rho.add_user_id(user_id).await; + + log_in!( + user_id, + PrintType::Client, + "Bound user {} to iota {}", + user_id, + iota_id + ); + + Some(rho) + } else { + None + } +} + +/// Remove a RhoConnection by Iota ID +pub async fn remove_rho(iota_id: i64) -> Option> { + let mut connections = RHO_CONNECTIONS.write().await; + connections.remove(&iota_id) +} + +/// Add a RhoConnection to the manager +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 as i64, rho_connection); +} + +/// Get a RhoConnection by Iota ID directly +#[allow(dead_code)] +pub async fn get_rho_by_iota(iota_id: i64) -> Option> { + let connections = RHO_CONNECTIONS.read().await; + connections.get(&iota_id).map(Arc::clone) +} + +/// Get the count of active connections +pub async fn connection_count() -> usize { + let connections = RHO_CONNECTIONS.read().await; + connections.len() +} diff --git a/src/rho/server.rs b/src/rho/server.rs new file mode 100644 index 0000000..9e3946d --- /dev/null +++ b/src/rho/server.rs @@ -0,0 +1,25 @@ +use crate::{ + log, + rho::connection::GeneralConnection, + util::{file_util::load_file_vec, logger::PrintType}, +}; +use ttp_native::Host; + +pub async fn start(port: u16) -> Result<(), Box> { + let cert_pem = load_file_vec("certs", "cert.pem").expect("Error loading Pemfile"); + + let key_pem = load_file_vec("certs", "key.pem").expect("Error loading Keyfile"); + + let mut host: Host = ttp_native::host(port, cert_pem, key_pem).await?; + log!(0, PrintType::General, "Server listening on port {}", port); + + while let Some((sender, receiver)) = host.next().await { + tokio::spawn(async move { + let conn = GeneralConnection::new(sender, receiver); + conn.handle().await; + }); + } + log!(0, PrintType::General, "Server stopped"); + + Ok(()) +} diff --git a/src/server/api.rs b/src/server/api.rs deleted file mode 100644 index eb6f9cf..0000000 --- a/src/server/api.rs +++ /dev/null @@ -1,275 +0,0 @@ -use crate::get_public_key; -use crate::sql::sql; -use crate::sql::user_online_tracker::get_iota_primary_omikron_connection; -use crate::transport::omikron_manager::get_random_omikron; -use crate::util::file_util::get_directory; -use crate::{ - sql::sql::{get_by_user_id, get_omikron_by_id}, - util::crypto_helper::public_key_to_base64, -}; -use actix_web::HttpResponse; -use actix_web::http::{StatusCode, header}; -use base64::Engine as _; -use json::JsonValue; - -pub async fn handle(path: &str, body_string: Option) -> HttpResponse { - if path == "OPTIONS" { - return HttpResponse::Ok() - .insert_header(("Access-Control-Allow-Origin", "*")) - .insert_header(("Access-Control-Allow-Methods", "GET, POST, OPTIONS")) - .insert_header(("Access-Control-Allow-Headers", "*")) - .finish(); - } - - let path_parts: Vec<&str> = path.split("/").filter(|s| !s.is_empty()).collect(); - - let _body: Option = if body_string.is_some() { - if let Ok(body_json) = json::parse(&body_string.unwrap()) { - Some(body_json) - } else { - None - } - } else { - None - }; - - let (status, body_text) = match path_parts.as_slice() { - // ================================================== - // DOWNLOAD IOTA FRONTEND - // ================================================== - ["api", "download", "iota_frontend"] => { - let file_path = format!("{}/downloads/iota_frontend.zip", get_directory()); - - match std::fs::read(file_path) { - Ok(file_bytes) => { - return HttpResponse::Ok() - .insert_header(("Access-Control-Allow-Origin", "*")) - .insert_header(("Content-Type", "application/zip")) - .insert_header(( - "Content-Disposition", - "attachment; filename=\"iota_frontend.zip\"", - )) - .body(file_bytes); - } - Err(_) => { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - return HttpResponse::NotFound() - .insert_header(("Access-Control-Allow-Origin", "*")) - .body(res.dump()); - } - } - } - - // ================================================== - // GET RANDOM OMIKRON - // ================================================== - ["api", "get", "omikron"] => { - if let Ok(omikron_conn) = get_random_omikron().await { - if let Some(id) = omikron_conn.get_omikron_id().await { - if let Ok((public_key, ip_address)) = sql::get_omikron_by_id(id).await { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["id"] = id.into(); - res["public_key"] = public_key.into(); - res["ip_address"] = ip_address.into(); - - (StatusCode::OK, res.dump()) - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error".into(); - (StatusCode::INTERNAL_SERVER_ERROR, res.dump()) - } - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error".into(); - (StatusCode::INTERNAL_SERVER_ERROR, res.dump()) - } - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::NOT_FOUND, res.dump()) - } - } - - // ================================================== - // GET OMIKRON BY ID - // ================================================== - ["api", "get", "omikron", id] => { - let id = id.parse::().unwrap_or(0); - - if id == 0 { - let mut res = JsonValue::new_object(); - res["status"] = "error_bad_request".into(); - (StatusCode::BAD_REQUEST, res.dump()) - } else if let Ok((public_key, ip_address)) = get_omikron_by_id(id).await { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["id"] = id.into(); - res["public_key"] = public_key.into(); - res["ip_address"] = ip_address.into(); - (StatusCode::OK, res.dump()) - } else if let Some(omikron_id) = get_iota_primary_omikron_connection(id) { - if let Ok((public_key, ip_address)) = get_omikron_by_id(omikron_id).await { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["id"] = omikron_id.into(); - res["public_key"] = public_key.into(); - res["ip_address"] = ip_address.into(); - (StatusCode::OK, res.dump()) - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::NOT_FOUND, res.dump()) - } - } else if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) = get_by_user_id(id).await - { - if let Some(omikron_id) = get_iota_primary_omikron_connection(iota_id) { - if let Ok((public_key, ip_address)) = get_omikron_by_id(omikron_id).await { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["id"] = omikron_id.into(); - res["public_key"] = public_key.into(); - res["ip_address"] = ip_address.into(); - (StatusCode::OK, res.dump()) - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::NOT_FOUND, res.dump()) - } - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::NOT_FOUND, res.dump()) - } - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::NOT_FOUND, res.dump()) - } - } - - // ================================================== - // GET ID BY USERNAME - // ================================================== - ["api", "get", "id", username] => { - if username.is_empty() { - let mut res = JsonValue::new_object(); - res["status"] = "error_bad_request".into(); - (StatusCode::BAD_REQUEST, res.dump()) - } else if let Ok(( - id, - iota_id, - username, - _, - _, - _, - _, - sub_level, - sub_end, - public_key, - _, - _, - )) = sql::get_by_username(username).await - { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["username"] = username.into(); - res["public_key"] = public_key.into(); - res["user_id"] = id.into(); - res["iota_id"] = iota_id.into(); - res["sub_level"] = sub_level.into(); - res["sub_end"] = sub_end.into(); - - (StatusCode::OK, res.dump()) - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::OK, res.dump()) - } - } - - // ================================================== - // GET SERVER PUBLIC KEY - // ================================================== - ["api", "get", "public_key"] => { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["public_key"] = public_key_to_base64(&get_public_key()).into(); - (StatusCode::OK, res.dump()) - } - - // ================================================== - // GET USER BY ID - // ================================================== - ["api", "get", "user", id] => { - let id: i64 = id.parse().unwrap_or(0); - - if id == 0 { - let mut res = JsonValue::new_object(); - res["status"] = "error_bad_request".into(); - (StatusCode::BAD_REQUEST, res.dump()) - } else if let Ok(( - id, - iota_id, - username, - display, - status_msg, - about, - avatar, - sub_level, - sub_end, - public_key, - _, - _, - )) = sql::get_by_user_id(id).await - { - let mut res = JsonValue::new_object(); - res["status"] = "success".into(); - res["username"] = username.into(); - res["public_key"] = public_key.into(); - res["user_id"] = id.into(); - res["iota_id"] = iota_id.into(); - res["sub_level"] = sub_level.into(); - res["sub_end"] = sub_end.into(); - - if let Some(display) = display { - res["display"] = display.into(); - } - if let Some(status_msg) = status_msg { - res["status_message"] = status_msg.into(); - } - if let Some(about) = about { - res["about"] = about.into(); - } - if let Some(avatar) = avatar { - res["avatar"] = base64::engine::general_purpose::STANDARD - .encode(avatar) - .into(); - } - - (StatusCode::OK, res.dump()) - } else { - let mut res = JsonValue::new_object(); - res["status"] = "error_not_found".into(); - (StatusCode::OK, res.dump()) - } - } - - // ================================================== - // DEFAULT - // ================================================== - _ => { - let mut res = JsonValue::new_object(); - res["status"] = "error".into(); - (StatusCode::INTERNAL_SERVER_ERROR, res.dump()) - } - }; - let body_bytes = body_text.into_bytes(); - - HttpResponse::build(status) - .insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")) - .insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, "*")) - .insert_header((header::ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS")) - .body(body_bytes) -} diff --git a/src/server/mod.rs b/src/server/mod.rs deleted file mode 100644 index 8645e6e..0000000 --- a/src/server/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod api; -pub mod server; -pub mod short_link; diff --git a/src/server/server.rs b/src/server/server.rs deleted file mode 100644 index 3618eb7..0000000 --- a/src/server/server.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::{ - log, - server::{api, short_link::get_short_link}, - util::file_util::load_file_buf, -}; - -use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, http::header, web}; - -use rustls::ServerConfig; -use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use rustls_pemfile::{certs, pkcs8_private_keys}; - -pub async fn start(port: u16) -> anyhow::Result<()> { - let mut cert_reader = load_file_buf("certs", "server_cert.pem")?; - - let mut key_reader = load_file_buf("certs", "server_key.pem")?; - - let cert_chain: Vec> = - certs(&mut cert_reader).collect::>()?; - - let mut keys: Vec> = pkcs8_private_keys(&mut key_reader) - .map(|res| res.map(Into::into)) - .collect::>()?; - - let key = keys.remove(0); - - let mut config = ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(cert_chain, key)?; - - config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - - let addr = format!("0.0.0.0:{port}"); - log!(" Server on {}", addr); - - HttpServer::new(move || { - App::new() - .route("/api/{path:.*}", web::to(api_handler)) - .route("/direct/{path:.*}", web::to(direct_handler)) - }) - .bind_rustls_0_23(addr, config)? - .run() - .await?; - - Ok(()) -} -async fn direct_handler(req: HttpRequest) -> impl Responder { - let path = req.uri().path().to_string(); - let short = path.replace("/direct/", ""); - - if let Ok(long) = get_short_link(&short).await { - HttpResponse::TemporaryRedirect() - .append_header((header::LOCATION, long)) - .finish() - } else { - HttpResponse::TemporaryRedirect() - .append_header((header::LOCATION, "https://tensamin.net")) - .finish() - } -} - -async fn api_handler(req: HttpRequest, body: web::Bytes) -> HttpResponse { - let path = req.uri().path().to_string(); - let body_string = String::from_utf8_lossy(&body).to_string(); - - api::handle(&path, Some(body_string)).await -} diff --git a/src/server/short_link.rs b/src/server/short_link.rs deleted file mode 100644 index c0ab51d..0000000 --- a/src/server/short_link.rs +++ /dev/null @@ -1,87 +0,0 @@ -use dashmap::DashMap; -use once_cell::sync::Lazy; -use rand::{Rng, thread_rng}; - -static LINKS: Lazy> = Lazy::new(DashMap::new); - -const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ1234567890"; - -pub async fn add_short_link(long: &str) -> Result { - let raw = generate_unique_short_link().await; - LINKS.insert(raw.clone(), long.to_string()); - - Ok(format!( - "https://omega.tensamin.net/direct/{}", - format_with_dashes(&raw) - )) -} - -async fn generate_unique_short_link() -> String { - loop { - let short = generate_short_link().await; - if !LINKS.contains_key(&short) { - return short; - } - } -} - -pub async fn generate_short_link() -> String { - let len = short_length(); - - let mut rng = thread_rng(); - (0..len) - .map(|_| { - let idx = rng.gen_range(0..CHARSET.len()); - CHARSET[idx] as char - }) - .collect() -} - -pub async fn get_short_link(short: &str) -> Result { - let key = if short.contains("/") { - short.split("/").nth(1).unwrap_or_default() - } else { - short - }; - let frag = short.replace(key, ""); - let normalized = normalize_short(&key); - - if let Ok(t) = LINKS.get(&normalized).map(|v| v.value().clone()).ok_or(()) { - Ok(format!("{}{}", t, frag)) - } else { - Err(()) - } -} - -/* ---------------- helpers ---------------- */ - -fn short_length() -> usize { - let count = LINKS.len(); - - match count { - 0..=1_999 => 4, - 2_000..=999_999 => 8, - _ => 12, - } -} - -fn format_with_dashes(s: &str) -> String { - s.chars() - .collect::>() - .chunks(4) - .map(|c| c.iter().collect::()) - .collect::>() - .join("-") -} - -fn normalize_short(input: &str) -> String { - input - .chars() - .filter(|c| *c != '-') - .map(|c| match c { - 'Q' | 'O' => '0', - 'I' => 'l', - _ => c, - }) - .collect() -} diff --git a/src/sql/mod.rs b/src/sql/mod.rs deleted file mode 100644 index 91ba841..0000000 --- a/src/sql/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod connection_status; -pub mod sql; -pub mod user_online_tracker; diff --git a/src/sql/sql.rs b/src/sql/sql.rs deleted file mode 100644 index 8445e26..0000000 --- a/src/sql/sql.rs +++ /dev/null @@ -1,765 +0,0 @@ -use crate::log; -use once_cell::sync::Lazy; -use sqlx::{MySql, Pool, Row, mysql::MySqlPoolOptions}; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::{ - env, - sync::Arc, - time::{SystemTime, UNIX_EPOCH}, -}; -use tokio::sync::RwLock; - -/* -use crate::sql::{ - iota_omikron_tracker::{get_omikron_for_iota, track_iota_omikron, untrack_iota}, - sql::{ - change_about, change_avatar, change_display_name, change_iota_id, change_iota_key, - change_keys, change_status, change_username, get_by_id, get_by_username, get_iota_by_id, - get_register_id, register_complete_iota, register_complete_user, - }, -}; - */ - -static SQL_DB: Lazy>>>> = Lazy::new(|| Arc::new(RwLock::new(None))); - -pub async fn connect() -> Result, sqlx::Error> { - let user = env::var("DB_USERNAME").expect("DB_USERNAME is not set"); - let passwd = env::var("DB_PASSWD").expect("DB_PASSWD is not set"); - let table = env::var("DB_TABLE").expect("DB_TABLE is not set"); - - MySqlPoolOptions::new() - .max_connections(200) - .connect(&format!( - "mysql://{}:{}@127.0.0.1:3306/{}", - user, passwd, table - )) - .await -} -// Omega -// - Omikron -// - Iota -// - User -// - User -// - Iota -// - User -// - User -// - Omikron -// - Iota -// - User -// - User -// - Iota -// - User -// - User -pub async fn initialize_db() -> Result<(), sqlx::Error> { - let pool = connect().await?; - let mut db_lock = SQL_DB.write().await; - // create tables - // with indexes - let _ = sqlx::query( - "CREATE TABLE IF NOT EXISTS - users ( - id BIGINT UNSIGNED NOT NULL PRIMARY KEY, - username VARCHAR(15) NOT NULL UNIQUE COLLATE utf8mb4_bin, - display VARCHAR(15) COLLATE utf8mb4_bin, - status VARCHAR(15) COLLATE utf8mb4_bin, - about VARCHAR(200) COLLATE utf8mb4_bin, - avatar MEDIUMBLOB, - sub_level INT(11) NOT NULL DEFAULT 0, - sub_end BIGINT(20) NOT NULL DEFAULT 0, - public_key TEXT NOT NULL COLLATE utf8mb4_bin, - private_key_hash TEXT NOT NULL COLLATE utf8mb4_bin DEFAULT '', - iota_id BIGINT UNSIGNED NOT NULL, - token VARCHAR(255) NOT NULL UNIQUE COLLATE utf8mb4_bin - )", - ) - .execute(&pool) - .await; - let _ = sqlx::query( - "CREATE TABLE IF NOT EXISTS - iotas ( - id BIGINT UNSIGNED NOT NULL PRIMARY KEY, - public_key VARCHAR(255) NOT NULL COLLATE utf8mb4_bin - )", - ) - .execute(&pool) - .await; - let _ = sqlx::query( - "CREATE TABLE IF NOT EXISTS - omikrons ( - id BIGINT UNSIGNED NOT NULL PRIMARY KEY, - public_key VARCHAR(255) NOT NULL COLLATE utf8mb4_bin, - location VARCHAR(255) NOT NULL COLLATE utf8mb4_bin, - ip_address VARCHAR(255) NOT NULL COLLATE utf8mb4_bin - )", - ) - .execute(&pool) - .await; - let _ = sqlx::query( - "CREATE TABLE IF NOT EXISTS - notifications ( - id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO INCREMENT, - sender_id BIGINT UNSIGNED NOT NULL, - receiver_id BIGINT UNSIGNED NOT NULL, - amount BIGINT UNSIGNED NOT NULL DEFAULT 0 - )", - ) - .execute(&pool) - .await; - *db_lock = Some(pool); - Ok(()) -} - -// ========================================================================================== -// REGISTER -// ========================================================================================== -pub static CURRENT_MILLI_USED: Lazy> = Lazy::new(|| Arc::new(AtomicU64::new(0))); -pub static CURRENT_REGISTER_PROCESS: Lazy>>> = - Lazy::new(|| Arc::new(RwLock::new(Vec::new()))); - -pub async fn get_register_id() -> u64 { - let mut current_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_millis() as u64; - - loop { - let current_locked = CURRENT_MILLI_USED.load(Ordering::SeqCst); - - if current_locked < current_time { - let result = CURRENT_MILLI_USED.compare_exchange( - current_locked, // expected value - current_time, // new value - Ordering::SeqCst, // acquire/release ordering - Ordering::SeqCst, // failure ordering - ); - - match result { - Ok(_) => { - CURRENT_REGISTER_PROCESS.write().await.push(current_time); - return current_time; - } - Err(_) => { - continue; - } - } - } else { - current_time = current_locked + 1; - } - } -} - -// ========================================================================================== -// USERS -// ========================================================================================== - -pub async fn get_by_username( - username: &str, -) -> Result< - ( - i64, - i64, - String, - Option, - Option, - Option, - Option>, - i32, - i64, - String, - String, - String, - ), - sqlx::Error, -> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - let row = sqlx::query( - "SELECT id, iota_id, username, display, status, about, avatar, sub_level, sub_end, public_key, private_key_hash, token FROM users WHERE username = ?", - ) - .bind(username) - .fetch_optional(&pool) - .await?; - - match row { - Some(row) => { - let id: i64 = row.get("id"); - let iota_id: i64 = row.get("iota_id"); - let username: String = row.get("username"); - let display: Option> = row.get("display"); - let status: Option> = row.get("status"); - let about: Option> = row.get("about"); - let avatar: Option> = row.get("avatar"); - let sub_level: i32 = row.get("sub_level"); - let sub_end: i64 = row.get("sub_end"); - let public_key: String = row.get("public_key"); - let private_key_hash: String = row.get("private_key_hash"); - let token: Vec = row.get("token"); - - Ok(( - id, - iota_id, - username, - display.map(|d| String::from_utf8_lossy(&d).to_string()), - status.map(|s| String::from_utf8_lossy(&s).to_string()), - about.map(|a| String::from_utf8_lossy(&a).to_string()), - avatar, - sub_level, - sub_end, - public_key, - private_key_hash, - String::from_utf8_lossy(&token).to_string(), - )) - } - _ => Err(sqlx::Error::RowNotFound), - } -} - -pub async fn get_by_user_id( - id: i64, -) -> Result< - ( - i64, - i64, - String, - Option, - Option, - Option, - Option>, - i32, - i64, - String, - String, - String, - ), - sqlx::Error, -> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - let row = sqlx::query( - "SELECT id, iota_id, username, display, status, about, avatar, sub_level, sub_end, public_key, private_key_hash, token FROM users WHERE id = CAST(? AS UNSIGNED)", - ) - .bind(id) - .fetch_optional(&pool) - .await?; - - match row { - Some(row) => { - let id: i64 = row.get("id"); - let iota_id: i64 = row.get("iota_id"); - let username: String = row.get("username"); - let display: Option> = row.get("display"); - let status: Option> = row.get("status"); - let about: Option> = row.get("about"); - let avatar: Option> = row.get("avatar"); - let sub_level: i32 = row.get("sub_level"); - let sub_end: i64 = row.get("sub_end"); - let public_key: String = row.get("public_key"); - let private_key_hash: String = row.get("private_key_hash"); - let token: Vec = row.get("token"); - - Ok(( - id, - iota_id, - username, - display.map(|d| String::from_utf8_lossy(&d).to_string()), - status.map(|s| String::from_utf8_lossy(&s).to_string()), - about.map(|a| String::from_utf8_lossy(&a).to_string()), - avatar, - sub_level, - sub_end, - public_key, - private_key_hash, - String::from_utf8_lossy(&token).to_string(), - )) - } - _ => Err(sqlx::Error::RowNotFound), - } -} - -pub async fn get_users_by_iota_id( - iota_id_param: i64, -) -> Result< - Vec<( - i64, - i64, - String, - Option, - Option, - Option, - Option>, - i32, - i64, - String, - String, - String, - )>, - sqlx::Error, -> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - let rows = sqlx::query( - "SELECT id, iota_id, username, display, status, about, avatar, sub_level, sub_end, public_key, private_key_hash, token FROM users WHERE iota_id = CAST(? AS UNSIGNED)", - ) - .bind(iota_id_param) - .fetch_all(&pool) - .await?; - - let mut users = Vec::new(); - for row in rows { - let id: i64 = row.get("id"); - let iota_id: i64 = row.get("iota_id"); - let username: String = row.get("username"); - let display: Option> = row.get("display"); - let status: Option> = row.get("status"); - let about: Option> = row.get("about"); - let avatar: Option> = row.get("avatar"); - let sub_level: i32 = row.get("sub_level"); - let sub_end: i64 = row.get("sub_end"); - let public_key: String = row.get("public_key"); - let private_key_hash: String = row.get("private_key_hash"); - let token: Vec = row.get("token"); - - users.push(( - id, - iota_id, - username, - display.map(|d| String::from_utf8_lossy(&d).to_string()), - status.map(|s| String::from_utf8_lossy(&s).to_string()), - about.map(|a| String::from_utf8_lossy(&a).to_string()), - avatar, - sub_level, - sub_end, - public_key, - private_key_hash, - String::from_utf8_lossy(&token).to_string(), - )); - } - - Ok(users) -} - -pub async fn change_username(id: i64, new_username: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET username = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_username) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn change_display_name(id: i64, new_display: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET display = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_display) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn change_avatar(id: i64, new_avatar: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET avatar = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_avatar) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn change_about(id: i64, new_about: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET about = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_about) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn change_status(id: i64, new_status: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET status = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_status) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn delete_user(id: i64) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("DELETE FROM users WHERE id = CAST(? AS UNSIGNED)") - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} -pub async fn change_iota_id(id: i64, new_iota_id: i64) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET iota_id = CAST(? AS UNSIGNED) WHERE id = CAST(? AS UNSIGNED)") - .bind(new_iota_id) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn change_keys( - id: i64, - new_public_key: String, - new_private_key_hash: String, -) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query( - "UPDATE users SET public_key = ?, private_key_hash = ? WHERE id = CAST(? AS UNSIGNED)", - ) - .bind(new_public_key) - .bind(new_private_key_hash) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} -pub async fn change_token(id: i64, new_token: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE users SET token = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_token) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} -pub async fn register_complete_user( - id: i64, - username: String, - public_key: String, - iota_id: i64, - token: String, -) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query( - "INSERT INTO users (id, username, public_key, iota_id, token) VALUES (?, ?, ?, ?, ?)", - ) - .bind(id) - .bind(username) - .bind(public_key) - .bind(iota_id) - .bind(token) - .execute(&pool) - .await?; - - Ok(()) -} -pub async fn print_users() -> Result<(), Box> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - log!("Printing users..."); - for row in sqlx::query( - "SELECT id, iota_id, username, display, status, about, sub_level, sub_end, public_key, private_key_hash, token FROM users", - ) - .fetch_all(&pool) - .await? - .iter() - { - let id: i64 = row.get("id"); - let iota_id: i64 = row.get("iota_id"); - let username: String = row.get("username"); - let display: Option> = row.get("display"); - let status: Option> = row.get("status"); - let about: Option> = row.get("about"); - let sub_level: i32 = row.get("sub_level"); - let sub_end: i64 = row.get("sub_end"); - - log!( - "User: {:?}", - ( - id, - iota_id, - username, - display.map_or("".to_string(), |d| String::from_utf8_lossy(&d).to_string()), - status.map_or("".to_string(), |s| String::from_utf8_lossy(&s).to_string()), - about.map_or("".to_string(), |a| String::from_utf8_lossy(&a).to_string()), - sub_level, - sub_end - ) - ); - } - - Ok(()) -} - -// ========================================================================================== -// IOTA -// ========================================================================================== -pub async fn create_new_iota(public_key: String) -> Result { - let new_id = get_register_id().await as i64; - register_complete_iota(new_id, public_key).await?; - Ok(new_id) -} - -pub async fn register_complete_iota(id: i64, public_key: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("INSERT INTO iotas (id, public_key) VALUES (?, ?)") - .bind(id) - .bind(public_key) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn get_iota_by_id(id: i64) -> Result<(i64, String), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - let result = sqlx::query_as::<_, (u64, Vec)>( - "SELECT id, public_key FROM iotas WHERE id = CAST(? AS UNSIGNED)", - ) - .bind(id) - .fetch_optional(&pool) - .await; - - match result { - Ok(optional_row) => match optional_row { - Some((id_u64, public_key)) => Ok(( - id_u64 as i64, - String::from_utf8_lossy(&public_key).to_string(), - )), - _ => Err(sqlx::Error::RowNotFound), - }, - Err(e) => Err(e), - } -} - -pub async fn change_iota_key(id: i64, new_key: String) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("UPDATE iotas SET public_key = ? WHERE id = CAST(? AS UNSIGNED)") - .bind(new_key) - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -pub async fn delete_iota(id: i64) -> Result<(), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - sqlx::query("DELETE FROM iotas WHERE id = CAST(? AS UNSIGNED)") - .bind(id) - .execute(&pool) - .await?; - - Ok(()) -} - -// ========================================================================================== -// OMIKRONS -// ========================================================================================== - -pub async fn get_omikron_by_id(id: i64) -> Result<(String, String), sqlx::Error> { - let pool = { - let db_lock = SQL_DB.read().await; - db_lock - .as_ref() - .cloned() - .expect("Database pool not initialized") - }; - - let row = sqlx::query_as::<_, (Vec, Vec)>( - "SELECT public_key, ip_address FROM omikrons WHERE id = CAST(? AS UNSIGNED)", - ) - .bind(id) - .fetch_optional(&pool) - .await?; - - match row { - Some((public_key, ip_address)) => Ok(( - String::from_utf8_lossy(&public_key).to_string(), - String::from_utf8_lossy(&ip_address).to_string(), - )), - _ => Err(sqlx::Error::RowNotFound), - } -} - -// ========================================================================================== -// PHI -// ========================================================================================== - -pub async fn add_notification(sender_id: i64, receiver_id: i64) -> Result<(), sqlx::Error> { - let db_lock = SQL_DB.read().await; - let pool = db_lock.as_ref().expect("Database pool is not initialized"); - - sqlx::query( - r#" - INSERT INTO notifications (sender_id, receiver_id, amount) - VALUES (?, ?, 1) - ON DUPLICATE KEY UPDATE amount = amount + 1 - "#, - ) - .bind(sender_id) - .bind(receiver_id) - .execute(pool) - .await?; - - Ok(()) -} -pub async fn read_notification(sender_id: i64, receiver_id: i64) -> Result<(), sqlx::Error> { - let db_lock = SQL_DB.read().await; - let pool = db_lock.as_ref().expect("Database pool is not initialized"); - - sqlx::query( - r#" - DELETE FROM notifications WHERE sender_id = ? AND receiver_id = ? - "#, - ) - .bind(sender_id) - .bind(receiver_id) - .execute(pool) - .await?; - - Ok(()) -} -pub async fn get_notifications(user_id: i64) -> Result, sqlx::Error> { - let db_lock = SQL_DB.read().await; - let pool = db_lock.as_ref().expect("Database pool is not initialized"); - - sqlx::query_as::<_, (i64, i64)>( - r#" - SELECT sender_id, amount FROM notifications WHERE receiver_id = ? - "#, - ) - .bind(user_id) - .fetch_all(pool) - .await -} diff --git a/src/sql/user_online_tracker.rs b/src/sql/user_online_tracker.rs deleted file mode 100644 index 42dfa22..0000000 --- a/src/sql/user_online_tracker.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::sql; -use crate::sql::connection_status::UserStatus; -use dashmap::DashMap; -use once_cell::sync::Lazy; - -#[derive(Debug, Clone)] -pub struct UserConnection { - pub connection_type: UserStatus, - pub omikron_id: i64, -} - -// IotaID -> Primary OmikronID -static IOTA_PRIMARY_OMIKRON_CONNECTION: Lazy> = Lazy::new(DashMap::new); - -// IotaID -> Vec -static IOTA_OMIKRON_CONNECTIONS: Lazy>> = Lazy::new(DashMap::new); - -// UserID -> UserStatus -static USER_STATUS_MAP: Lazy> = Lazy::new(DashMap::new); - -pub fn track_iota_connection(iota_id: i64, omikron_id: i64, primary: bool) { - let mut entry = IOTA_OMIKRON_CONNECTIONS - .entry(iota_id) - .or_insert_with(Vec::new); - - if !entry.contains(&omikron_id) { - entry.push(omikron_id); - } - - if primary { - IOTA_PRIMARY_OMIKRON_CONNECTION.insert(iota_id, omikron_id); - } -} - -pub fn untrack_iota_connection(iota_id: i64, omikron_id: i64) -> bool { - let connections_empty = if let Some(r) = IOTA_OMIKRON_CONNECTIONS.get(&iota_id) { - let mut vec = r.value().clone(); - vec.retain(|&id| id != omikron_id); - let empty = vec.is_empty(); - drop(r); - IOTA_OMIKRON_CONNECTIONS.insert(iota_id, vec); - empty - } else { - false - }; - - if let Some(primary_ref) = IOTA_PRIMARY_OMIKRON_CONNECTION.get(&iota_id) { - let primary_id = *primary_ref.value(); - drop(primary_ref); - if primary_id == omikron_id { - IOTA_PRIMARY_OMIKRON_CONNECTION.remove(&iota_id); - } - } - - if connections_empty { - IOTA_OMIKRON_CONNECTIONS.remove(&iota_id); - } - - connections_empty -} - -pub fn get_iota_primary_omikron_connection(iota_id: i64) -> Option { - IOTA_PRIMARY_OMIKRON_CONNECTION.get(&iota_id).map(|v| *v) -} - -pub fn get_iota_omikron_connections(iota_id: i64) -> Option> { - IOTA_OMIKRON_CONNECTIONS.get(&iota_id).map(|v| v.clone()) -} - -pub fn track_user_status(user_id: i64, status: UserStatus, omikron_id: i64) { - USER_STATUS_MAP.insert( - user_id, - UserConnection { - connection_type: status, - omikron_id, - }, - ); -} - -pub fn get_user_status(user_id: i64) -> Option { - USER_STATUS_MAP.get(&user_id).map(|v| v.clone()) -} - -pub fn untrack_many_users(user_ids: &[i64]) { - for user_id in user_ids { - USER_STATUS_MAP.remove(user_id); - } -} - -pub async fn untrack_omikron(omikron_id: i64) { - let primary_keys_to_remove: Vec = IOTA_PRIMARY_OMIKRON_CONNECTION - .iter() - .filter(|entry| *entry.value() == omikron_id) - .map(|entry| *entry.key()) - .collect(); - - for key in primary_keys_to_remove { - IOTA_PRIMARY_OMIKRON_CONNECTION.remove(&key); - } - - let mut offline_iotas = Vec::new(); - let mut primary_to_remove = Vec::new(); - - // Collect iotas and primary info first - for r in IOTA_OMIKRON_CONNECTIONS.iter() { - let iota_id = *r.key(); - let mut connections = r.value().clone(); - connections.retain(|&id| id != omikron_id); - - if connections.is_empty() { - offline_iotas.push(iota_id); - } - - if IOTA_PRIMARY_OMIKRON_CONNECTION - .get(&iota_id) - .map(|p| *p == omikron_id) - .unwrap_or(false) - { - primary_to_remove.push(iota_id); - } - - // Update the connections vector after filtering - IOTA_OMIKRON_CONNECTIONS.insert(iota_id, connections); - } - - // Step 2: Remove primary connections safely - for iota_id in primary_to_remove { - IOTA_PRIMARY_OMIKRON_CONNECTION.remove(&iota_id); - } - - // Step 3: Remove users that were on this omikron - USER_STATUS_MAP.retain(|_, status| status.omikron_id != omikron_id); - - // Step 4: For offline iotas, remove associated users from USER_STATUS_MAP - for iota_id in offline_iotas { - if let Ok(users) = sql::sql::get_users_by_iota_id(iota_id).await { - for user in users { - USER_STATUS_MAP.remove(&user.0); - } - } - // Finally remove the empty connections vector - IOTA_OMIKRON_CONNECTIONS.remove(&iota_id); - } -} diff --git a/src/transport/mod.rs b/src/transport/mod.rs deleted file mode 100644 index 4f3953a..0000000 --- a/src/transport/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod omikron_connection; -pub mod omikron_manager; diff --git a/src/transport/omikron_connection.rs b/src/transport/omikron_connection.rs deleted file mode 100644 index 504d723..0000000 --- a/src/transport/omikron_connection.rs +++ /dev/null @@ -1,1139 +0,0 @@ -use crate::{ - get_private_key, get_public_key, log, log_cv_in, log_cv_out, log_err, log_in, - server::short_link::add_short_link, - sql::{ - connection_status::UserStatus, - sql::{self, get_by_user_id, get_by_username, get_iota_by_id, get_omikron_by_id}, - user_online_tracker::{self}, - }, - transport::omikron_manager, - util::{crypto_helper::encrypt, file_util::load_file_vec, logger::PrintType}, -}; -use base64::{Engine as _, engine::general_purpose::STANDARD}; -use dashmap::DashMap; -use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; -use epsilon_native::{Host, Receiver, Sender}; -use rand::{Rng, distributions::Alphanumeric}; -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; -use tokio::{ - sync::{Mutex, RwLock}, - time::interval, -}; -use x448::PublicKey; - -// ============================================================================ -// Configuration -// ============================================================================ - -const CLEANUP_INTERVAL: Duration = Duration::from_secs(30); -const MAX_WAITING_AGE: Duration = Duration::from_secs(60); - -// ============================================================================ -// Error Types -// ============================================================================ - -#[derive(Debug, thiserror::Error)] -pub enum OmikronError { - #[error("Not connected")] - NotConnected, - #[error("Not authenticated")] - NotAuthenticated, - #[error("Invalid response")] - InvalidResponse, - #[error("Authentication failed")] - AuthenticationFailed, - #[error("SQL error: {0}")] - Sql(String), - #[error("Send error: {0}")] - Send(String), -} - -pub type OmikronResult = Result; - -// ============================================================================ -// Waiting Task System (Preserved from original) -// ============================================================================ - -pub struct WaitingTask { - pub task: Box, CommunicationValue) -> bool + Send + Sync>, - pub inserted_at: Instant, -} - -// ============================================================================ -// Connection State -// ============================================================================ - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum AuthState { - Unauthenticated, - Identified { omikron_id: i64 }, - Authenticated { omikron_id: i64 }, -} - -impl AuthState { - fn is_authenticated(&self) -> bool { - match self { - AuthState::Authenticated { omikron_id } => true, - _ => false, - } - } - - fn omikron_id(&self) -> Option { - match self { - AuthState::Identified { omikron_id } | AuthState::Authenticated { omikron_id } => { - Some(*omikron_id) - } - _ => None, - } - } -} - -// ============================================================================ -// Omikron Connection (Epsilon/QUIC-based) -// ============================================================================ - -pub struct OmikronConnection { - id: u64, - sender: Mutex>, - state: RwLock, - challenge: RwLock, - pub_key: RwLock>>, - pub ping: RwLock, - waiting_tasks: DashMap, - cleanup_handle: Mutex>>, -} - -impl OmikronConnection { - // ------------------------------------------------------------------------- - // Construction - // ------------------------------------------------------------------------- - - pub fn new(sender: Sender) -> Arc { - let conn = Arc::new(Self { - id: rand::random(), - sender: Mutex::new(Some(sender)), - state: RwLock::new(AuthState::Unauthenticated), - challenge: RwLock::new(String::new()), - pub_key: RwLock::new(None), - ping: RwLock::new(-1), - waiting_tasks: DashMap::new(), - cleanup_handle: Mutex::new(None), - }); - - conn - } - - // ------------------------------------------------------------------------- - // Main Handler Loop - // ------------------------------------------------------------------------- - - pub async fn handle(self: Arc, receiver: &mut Receiver) { - log_in!( - self.id as i64, - PrintType::Omega, - "Omikron connection started" - ); - - // Start cleanup task - let cleanup_conn = self.clone(); - let cleanup_handle = tokio::spawn(async move { - let mut ticker = interval(CLEANUP_INTERVAL); - loop { - ticker.tick().await; - cleanup_conn - .waiting_tasks - .retain(|_, v| v.inserted_at.elapsed() < MAX_WAITING_AGE); - } - }); - *self.cleanup_handle.lock().await = Some(cleanup_handle); - - while let Ok(cv) = receiver.receive().await { - if let Err(e) = self.clone().process_message(cv).await { - log_err!(0, PrintType::Omega, "Error processing message: {}", e); - if matches!(e, OmikronError::NotConnected) { - break; - } - } - } - - self.clone().cleanup().await; - log_in!( - self.id as i64, - PrintType::Omega, - "Omikron connection closed" - ); - } - - // ------------------------------------------------------------------------- - // Message Processing - // ------------------------------------------------------------------------- - - async fn process_message(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) { - log_cv_in!(PrintType::Omikron, &cv); - } - - let msg_id = cv.get_id(); - - // Check waiting tasks first (response to previous request) - if let Some((_, task)) = self.waiting_tasks.remove(&msg_id) { - let _ = (task.task)(self.clone(), cv); - return Ok(()); - } - - // Handle ping regardless of auth state - if cv.is_type(CommunicationType::ping) { - return self.handle_ping(cv).await; - } - - let current_state = *self.state.read().await; - // Route based on authentication state - match current_state { - AuthState::Unauthenticated => self.clone().handle_unauthenticated(cv).await, - AuthState::Identified { .. } => self.clone().handle_identified(cv).await, - AuthState::Authenticated { omikron_id } => { - self.clone().handle_authenticated(cv, omikron_id).await - } - } - } - - // ------------------------------------------------------------------------- - // Authentication Handlers - // ------------------------------------------------------------------------- - - async fn handle_unauthenticated(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - if !cv.is_type(CommunicationType::identification) { - let _ = self - .send_error_response(cv.get_id(), CommunicationType::error_not_authenticated) - .await; - return Err(OmikronError::NotAuthenticated); - } - - // Extract omikron ID - let omikron_id = cv - .get_data(DataTypes::omikron_id) - .as_number() - .ok_or(OmikronError::InvalidResponse)?; - log!("Omikron {:?} connected", omikron_id); - - // Lookup omikron in database - let (public_key, _) = get_omikron_by_id(omikron_id) - .await - .map_err(|e| OmikronError::Sql(e.to_string()))?; - - log!("Got public Key"); - - let pub_key_bytes = STANDARD - .decode(&public_key) - .map_err(|_| OmikronError::AuthenticationFailed)?; - - let pub_key_bytes_clone = pub_key_bytes.clone(); - let omikron_pub_key = PublicKey::from_bytes(&pub_key_bytes_clone) - .ok_or(OmikronError::AuthenticationFailed)?; - - log!("Decoded public Key"); - - // Generate challenge - let challenge: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(32) - .map(char::from) - .collect(); - - log!("Generated Challenge"); - - *self.challenge.write().await = challenge.clone(); - - log!("Stored Challenge"); - - *self.pub_key.write().await = Some(pub_key_bytes); - - log!("Stored Pubkey"); - - *self.state.write().await = AuthState::Identified { omikron_id }; - - log!("Stored State"); - - let challenge_clone = challenge.clone(); - let private_key = get_private_key(); - let public_key_for_encrypt = omikron_pub_key; - - let encrypted = tokio::task::spawn_blocking(move || { - encrypt(private_key, public_key_for_encrypt, &challenge_clone) - .map_err(|_| OmikronError::AuthenticationFailed) - }) - .await - .map_err(|_| OmikronError::AuthenticationFailed)??; - - log!("Encrypted Challenge"); - - // Send challenge response - let response = CommunicationValue::new(CommunicationType::challenge) - .with_id(cv.get_id()) - .add_data( - DataTypes::public_key, - DataValue::Str(STANDARD.encode(get_public_key().as_bytes())), - ) - .add_data(DataTypes::challenge, DataValue::Str(encrypted)); - - log!("Sending Challenge"); - self.send(&response).await - } - - async fn handle_identified(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - if !cv.is_type(CommunicationType::challenge_response) { - let _ = self - .send_error_response(cv.get_id(), CommunicationType::error_not_authenticated) - .await; - return Err(OmikronError::NotAuthenticated); - } - - let client_response = cv - .get_data(DataTypes::challenge) - .as_str() - .ok_or(OmikronError::InvalidResponse)?; - - let expected_challenge = self.challenge.read().await.clone(); - - if client_response == expected_challenge { - let omikron_id = self.state.read().await.omikron_id().unwrap_or(0); - *self.state.write().await = AuthState::Authenticated { omikron_id }; - - omikron_manager::add_omikron(self.clone()).await; - - let response = CommunicationValue::new(CommunicationType::identification_response) - .with_id(cv.get_id()) - .add_data(DataTypes::accepted, DataValue::Bool(true)); - - self.clone().send(&response).await?; - log_in!(omikron_id, PrintType::Omega, "Omikron authenticated"); - Ok(()) - } else { - let _ = self - .send_error_response(cv.get_id(), CommunicationType::error_invalid_challenge) - .await; - Err(OmikronError::AuthenticationFailed) - } - } - - // ------------------------------------------------------------------------- - // Authenticated Message Handlers - // ------------------------------------------------------------------------- - - async fn handle_authenticated( - self: Arc, - cv: CommunicationValue, - omikron_id: i64, - ) -> OmikronResult<()> { - match cv.get_type() { - // Link shortening - CommunicationType::shorten_link => self.handle_shorten_link(cv).await, - - // Online status tracking - CommunicationType::user_connected => { - self.handle_user_connected(cv, omikron_id).await; - Ok(()) - } - CommunicationType::user_disconnected => { - self.handle_user_disconnected(cv, omikron_id).await; - Ok(()) - } - CommunicationType::iota_connected => { - self.handle_iota_connected(cv, omikron_id).await; - Ok(()) - } - CommunicationType::iota_disconnected => { - self.handle_iota_disconnected(cv, omikron_id).await; - Ok(()) - } - CommunicationType::sync_client_iota_status => { - self.handle_sync_status(cv, omikron_id).await; - Ok(()) - } - - CommunicationType::get_user_data => self.handle_get_user_data(cv).await, - CommunicationType::get_iota_data => self.handle_get_iota_data(cv).await, - - CommunicationType::get_register => self.handle_get_register(cv).await, - CommunicationType::complete_register_iota => { - self.handle_complete_register_iota(cv).await - } - CommunicationType::complete_register_user => { - self.handle_complete_register_user(cv).await - } - - CommunicationType::change_user_data => self.handle_change_user_data(cv).await, - CommunicationType::change_iota_data => self.handle_change_iota_data(cv).await, - CommunicationType::delete_user => self.handle_delete_user(cv).await, - CommunicationType::delete_iota => self.handle_delete_iota(cv).await, - - CommunicationType::get_notifications => self.handle_get_notifications(cv).await, - CommunicationType::read_notification => self.handle_read_notification(cv).await, - CommunicationType::push_notification => self.handle_push_notification(cv).await, - - _ => { - log_err!( - 0, - PrintType::Omega, - "Unknown message type: {:?}", - cv.get_type() - ); - Ok(()) - } - } - } - - // ------------------------------------------------------------------------- - // Specific Handlers (ported from original WebSocket implementation) - // ------------------------------------------------------------------------- - - async fn handle_shorten_link(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - let link = cv - .get_data(DataTypes::link) - .as_str() - .ok_or(OmikronError::InvalidResponse)?; - - let short = add_short_link(link) - .await - .map_err(|_| OmikronError::Sql("Shortend link Error".to_string()))?; - - let response = CommunicationValue::new(CommunicationType::shorten_link) - .with_id(cv.get_id()) - .add_data(DataTypes::link, DataValue::Str(short)); - - self.send(&response).await - } - - async fn handle_user_connected(self: Arc, cv: CommunicationValue, omikron_id: i64) { - log_in!(PrintType::Omega, "User connected"); - if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { - user_online_tracker::track_user_status( - user_id as i64, - UserStatus::user_online, - omikron_id, - ); - } - } - - async fn handle_user_disconnected(self: Arc, cv: CommunicationValue, _omikron_id: i64) { - log_in!(PrintType::Omega, "User disconnected"); - if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { - if let Some(status) = user_online_tracker::get_user_status(user_id as i64) { - user_online_tracker::track_user_status( - user_id as i64, - UserStatus::user_offline, - status.omikron_id, - ); - } - } - } - - async fn handle_iota_connected(self: Arc, cv: CommunicationValue, omikron_id: i64) { - log_in!(PrintType::Omega, "IOTA connected"); - if let Some(iota_id) = cv.get_data(DataTypes::iota_id).as_number() { - let iota_id = iota_id as i64; - user_online_tracker::track_iota_connection(iota_id, omikron_id, true); - - let mut user_ids = Vec::new(); - if let Ok(users) = sql::get_users_by_iota_id(iota_id).await { - for (user_id, _, _, _, _, _, _, _, _, _, _, _) in users { - user_ids.push(DataValue::Number(user_id)); - user_online_tracker::track_user_status( - user_id, - UserStatus::user_offline, - omikron_id, - ); - } - } else { - log_in!(PrintType::General, "SQL error loading users for IOTA"); - } - - let response = CommunicationValue::new(CommunicationType::iota_user_data) - .with_id(cv.get_id()) - .add_data(DataTypes::user_ids, DataValue::Array(user_ids)); - - let _ = self.send(&response).await; - } else { - log_in!(PrintType::General, "No IOTA ID found"); - } - } - - async fn handle_iota_disconnected(self: Arc, cv: CommunicationValue, omikron_id: i64) { - log_in!(PrintType::Omega, "IOTA disconnected"); - if let Some(iota_id) = cv.get_data(DataTypes::iota_id).as_number() { - let iota_id = iota_id as i64; - let iota_offline = user_online_tracker::untrack_iota_connection(iota_id, omikron_id); - if iota_offline { - if let Ok(users) = sql::get_users_by_iota_id(iota_id).await { - let user_ids: Vec = users.iter().map(|u| u.0).collect(); - user_online_tracker::untrack_many_users(&user_ids); - } - } - } - } - - async fn handle_sync_status(self: Arc, cv: CommunicationValue, omikron_id: i64) { - if let DataValue::Array(user_ids) = cv.get_data(DataTypes::user_ids) { - for user_id_val in user_ids { - if let DataValue::Number(user_id) = user_id_val { - user_online_tracker::track_user_status( - *user_id, - UserStatus::user_online, - omikron_id, - ); - } - } - } - - if let DataValue::Array(iota_ids) = cv.get_data(DataTypes::iota_ids) { - for iota_id_val in iota_ids { - if let DataValue::Number(iota_id) = iota_id_val { - user_online_tracker::track_iota_connection(*iota_id, omikron_id, true); - } - } - } - } - - async fn handle_get_user_data(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - // Try by user_id first - if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { - if let Ok(user_data) = get_by_user_id(user_id as i64).await { - let response = self - .clone() - .build_user_data_response(cv.get_id(), user_data) - .await; - return self.send(&response).await; - } - } - - // Try by username - if let Some(username) = cv.get_data(DataTypes::username).as_str() { - if let Ok(user_data) = get_by_username(username).await { - let response = self - .clone() - .build_user_data_response(cv.get_id(), user_data) - .await; - return self.send(&response).await; - } - } - - // Not found - let response = - CommunicationValue::new(CommunicationType::error_not_found).with_id(cv.get_id()); - self.send(&response).await - } - - async fn build_user_data_response( - self: Arc, - msg_id: u32, - user: ( - i64, - i64, - String, - Option, - Option, - Option, - Option>, - i32, - i64, - String, - String, - String, - ), - ) -> CommunicationValue { - let ( - id, - iota_id, - username, - display, - status, - about, - avatar, - sub_level, - sub_end, - public_key, - _, - _, - ) = user; - - let mut response = CommunicationValue::new(CommunicationType::get_user_data) - .with_id(msg_id) - .add_data(DataTypes::username, DataValue::Str(username.clone())) - .add_data(DataTypes::public_key, DataValue::Str(public_key)) - .add_data(DataTypes::user_id, DataValue::Number(id)) - .add_data(DataTypes::iota_id, DataValue::Number(iota_id)) - .add_data(DataTypes::sub_level, DataValue::Number(sub_level as i64)) - .add_data(DataTypes::sub_end, DataValue::Number(sub_end)); - - // Display name (fallback to username) - let display_name = display.filter(|d| !d.is_empty()).unwrap_or(username); - response = response.add_data(DataTypes::display, DataValue::Str(display_name)); - - // Optional fields - if let Some(s) = status.filter(|s| !s.is_empty()) { - response = response.add_data(DataTypes::status, DataValue::Str(s)); - } - if let Some(a) = about.filter(|a| !a.is_empty()) { - response = response.add_data(DataTypes::about, DataValue::Str(a)); - } - if let Some(av) = avatar { - response = response.add_data(DataTypes::avatar, DataValue::Str(STANDARD.encode(av))); - } - - // Online status - let user_status = user_online_tracker::get_user_status(id); - let iota_connections = - user_online_tracker::get_iota_omikron_connections(iota_id).unwrap_or_default(); - - if let Some(us) = user_status { - response = response.add_data( - DataTypes::online_status, - DataValue::Str(us.connection_type.to_string()), - ); - response = response.add_data(DataTypes::omikron_id, DataValue::Number(us.omikron_id)); - } else { - response = response.add_data( - DataTypes::online_status, - DataValue::Str(UserStatus::iota_offline.to_string()), - ); - } - - response = response.add_data( - DataTypes::omikron_connections, - DataValue::Array( - iota_connections - .into_iter() - .map(DataValue::Number) - .collect(), - ), - ); - - response - } - - async fn handle_get_iota_data(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - // Try by iota_id - if let Some(iota_id) = cv.get_data(DataTypes::iota_id).as_number() { - if let Ok((iota_id, public_key)) = get_iota_by_id(iota_id as i64).await { - let response = self - .clone() - .build_iota_data_response(cv.get_id(), iota_id, public_key, None, None) - .await; - return self.send(&response).await; - } - } - - // Try by user_id - if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() { - if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) = - get_by_user_id(user_id as i64).await - { - if let Ok((iota_id, public_key)) = get_iota_by_id(iota_id).await { - let response = self - .clone() - .build_iota_data_response( - cv.get_id(), - iota_id, - public_key, - Some(user_id as i64), - None, - ) - .await; - return self.send(&response).await; - } - } - } - - // Try by username - if let Some(username) = cv.get_data(DataTypes::username).as_str() { - if let Ok((user_id, iota_id, _, _, _, _, _, _, _, _, _, _)) = - get_by_username(username).await - { - if let Ok((iota_id, public_key)) = get_iota_by_id(iota_id).await { - let response = self - .clone() - .build_iota_data_response( - cv.get_id(), - iota_id, - public_key, - Some(user_id), - Some(username.to_string()), - ) - .await; - return self.send(&response).await; - } - } - } - - let response = - CommunicationValue::new(CommunicationType::error_not_found).with_id(cv.get_id()); - self.send(&response).await - } - - async fn build_iota_data_response( - self: Arc, - msg_id: u32, - iota_id: i64, - public_key: String, - user_id: Option, - username: Option, - ) -> CommunicationValue { - let mut response = CommunicationValue::new(CommunicationType::get_iota_data) - .with_id(msg_id) - .add_data(DataTypes::public_key, DataValue::Str(public_key)) - .add_data(DataTypes::iota_id, DataValue::Number(iota_id)); - - if let Some(uid) = user_id { - response = response.add_data(DataTypes::user_id, DataValue::Number(uid)); - } - if let Some(uname) = username { - response = response.add_data(DataTypes::username, DataValue::Str(uname)); - } - - let iota_connections = - user_online_tracker::get_iota_omikron_connections(iota_id).unwrap_or_default(); - - response.add_data( - DataTypes::omikron_connections, - DataValue::Array( - iota_connections - .into_iter() - .map(DataValue::Number) - .collect(), - ), - ) - } - - async fn handle_get_register(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - let register_id = sql::get_register_id().await; - let response = CommunicationValue::new(CommunicationType::get_register) - .with_id(cv.get_id()) - .add_data(DataTypes::user_id, DataValue::Number(register_id as i64)); - self.send(&response).await - } - - async fn handle_complete_register_iota( - self: Arc, - cv: CommunicationValue, - ) -> OmikronResult<()> { - let iota_id_opt = cv - .get_data(DataTypes::iota_id) - .as_number() - .map(|n| n as i64); - - if let Some(public_key) = cv.get_data(DataTypes::public_key).as_str() { - if let Some(iota_id) = iota_id_opt { - // Register existing IOTA - match sql::register_complete_iota(iota_id, public_key.to_string()).await { - Ok(_) => { - let response = CommunicationValue::new(CommunicationType::success) - .with_id(cv.get_id()); - self.send(&response).await - } - Err(e) => { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(e.to_string())); - self.send(&response).await - } - } - } else { - // Create new IOTA - match sql::create_new_iota(public_key.to_string()).await { - Ok(new_iota_id) => { - let response = - CommunicationValue::new(CommunicationType::complete_register_iota) - .with_id(cv.get_id()) - .add_data(DataTypes::iota_id, DataValue::Number(new_iota_id)); - self.send(&response).await - } - Err(e) => { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(e.to_string())); - self.send(&response).await - } - } - } - } else { - self.send_error_response(cv.get_id(), CommunicationType::error_invalid_data) - .await - } - } - - async fn handle_complete_register_user( - self: Arc, - cv: CommunicationValue, - ) -> OmikronResult<()> { - let user_id = cv - .get_data(DataTypes::user_id) - .as_number() - .map(|n| n as i64); - let username = cv - .get_data(DataTypes::username) - .as_str() - .map(|s| s.to_string()); - let public_key = cv - .get_data(DataTypes::public_key) - .as_str() - .map(|s| s.to_string()); - let iota_id = cv - .get_data(DataTypes::iota_id) - .as_number() - .map(|n| n as i64); - let reset_token = cv - .get_data(DataTypes::reset_token) - .as_str() - .map(|s| s.to_string()); - - if let (Some(uid), Some(uname), Some(pk), Some(iid), Some(rt)) = - (user_id, username, public_key, iota_id, reset_token) - { - match sql::register_complete_user(uid, uname, pk, iid, rt).await { - Ok(_) => { - let response = - CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); - self.send(&response).await - } - Err(e) => { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(e.to_string())); - self.send(&response).await - } - } - } else { - self.send_error_response(cv.get_id(), CommunicationType::error_invalid_data) - .await - } - } - - async fn handle_change_user_data(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - let mut success = true; - let mut error_message = String::new(); - - // Process each field - if let Some(username) = cv.get_data(DataTypes::username).as_str() { - if let Err(e) = sql::change_username(user_id, username.to_string()).await { - success = false; - error_message = e.to_string(); - } - } - if let Some(display) = cv.get_data(DataTypes::display).as_str() { - if let Err(e) = sql::change_display_name(user_id, display.to_string()).await { - success = false; - error_message = e.to_string(); - } - } - if let Some(avatar) = cv.get_data(DataTypes::avatar).as_str() { - if let Err(e) = sql::change_avatar(user_id, avatar.to_string()).await { - success = false; - error_message = e.to_string(); - } - } - if let Some(about) = cv.get_data(DataTypes::about).as_str() { - if let Err(e) = sql::change_about(user_id, about.to_string()).await { - success = false; - error_message = e.to_string(); - } - } - if let Some(status) = cv.get_data(DataTypes::status).as_str() { - if let Err(e) = sql::change_status(user_id, status.to_string()).await { - success = false; - error_message = e.to_string(); - } - } - if let (Some(public_key), Some(private_key_hash)) = ( - cv.get_data(DataTypes::public_key).as_str(), - cv.get_data(DataTypes::private_key_hash).as_str(), - ) { - if let Err(e) = sql::change_keys( - user_id, - public_key.to_string(), - private_key_hash.to_string(), - ) - .await - { - success = false; - error_message = e.to_string(); - } - } - - if success { - let response = CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); - self.send(&response).await - } else { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(error_message)); - self.send(&response).await - } - } - - async fn handle_change_iota_data(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - - if let (Some(iota_id), Some(reset_token), Some(new_token)) = ( - cv.get_data(DataTypes::iota_id) - .as_number() - .map(|n| n as i64), - cv.get_data(DataTypes::reset_token).as_str(), - cv.get_data(DataTypes::new_token).as_str(), - ) { - match sql::get_by_user_id(user_id).await { - Ok(user) => { - let current_token = user.11; // reset_token field - if current_token == reset_token { - let mut success = true; - let mut error_message = String::new(); - - if let Err(e) = sql::change_iota_id(user_id, iota_id).await { - success = false; - error_message = e.to_string(); - } - if success { - if let Err(e) = sql::change_token(user_id, new_token.to_string()).await - { - success = false; - error_message = e.to_string(); - } - } - - if success { - let response = CommunicationValue::new(CommunicationType::success) - .with_id(cv.get_id()); - self.send(&response).await - } else { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(error_message)); - self.send(&response).await - } - } else { - self.send_error_response( - cv.get_id(), - CommunicationType::error_invalid_challenge, - ) - .await - } - } - Err(_) => { - self.send_error_response(cv.get_id(), CommunicationType::error_not_found) - .await - } - } - } else { - self.send_error_response(cv.get_id(), CommunicationType::error_invalid_data) - .await - } - } - - async fn handle_delete_user(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - match sql::delete_user(user_id).await { - Ok(_) => { - let response = - CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); - self.send(&response).await - } - Err(e) => { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(e.to_string())); - self.send(&response).await - } - } - } - - async fn handle_delete_iota(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - if let Some(iota_id) = cv - .get_data(DataTypes::iota_id) - .as_number() - .map(|n| n as i64) - { - match sql::delete_iota(iota_id).await { - Ok(_) => { - let response = - CommunicationValue::new(CommunicationType::success).with_id(cv.get_id()); - self.send(&response).await - } - Err(e) => { - let response = CommunicationValue::new(CommunicationType::error) - .with_id(cv.get_id()) - .add_data(DataTypes::error_type, DataValue::Str(e.to_string())); - self.send(&response).await - } - } - } else { - self.send_error_response(cv.get_id(), CommunicationType::error_invalid_data) - .await - } - } - - async fn handle_get_notifications( - self: Arc, - cv: CommunicationValue, - ) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - if let Ok(notifications) = sql::get_notifications(user_id).await { - let json_array: Vec = notifications - .into_iter() - .map(|(sender, amount)| { - DataValue::Container(vec![ - (DataTypes::sender_id, DataValue::Number(sender)), - (DataTypes::amount, DataValue::Number(amount)), - ]) - }) - .collect(); - - let response = CommunicationValue::new(CommunicationType::get_notifications) - .with_id(cv.get_id()) - .add_data(DataTypes::notifications, DataValue::Array(json_array)); - self.send(&response).await - } else { - Ok(()) - } - } - - async fn handle_read_notification( - self: Arc, - cv: CommunicationValue, - ) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - if let Some(other_id) = cv - .get_data(DataTypes::sender_id) - .as_number() - .map(|n| n as i64) - { - if sql::read_notification(user_id, other_id).await.is_ok() { - let response = CommunicationValue::new(CommunicationType::read_notification) - .with_id(cv.get_id()); - self.send(&response).await - } else { - Ok(()) - } - } else { - Ok(()) - } - } - - async fn handle_push_notification( - self: Arc, - cv: CommunicationValue, - ) -> OmikronResult<()> { - let user_id = cv.get_sender() as i64; - if let Some(other_id) = cv - .get_data(DataTypes::sender_id) - .as_number() - .map(|n| n as i64) - { - if sql::add_notification(user_id, other_id).await.is_ok() { - let response = CommunicationValue::new(CommunicationType::push_notification) - .with_id(cv.get_id()); - self.send(&response).await - } else { - Ok(()) - } - } else { - Ok(()) - } - } - - async fn handle_ping(self: Arc, cv: CommunicationValue) -> OmikronResult<()> { - if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) { - *self.ping.write().await = *last_ping; - } - - let response = CommunicationValue::new(CommunicationType::pong).with_id(cv.get_id()); - self.send(&response).await - } - - // ------------------------------------------------------------------------- - // Utilities - // ------------------------------------------------------------------------- - - async fn send(self: Arc, cv: &CommunicationValue) -> OmikronResult<()> { - if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) { - log_cv_out!(PrintType::Omikron, cv); - } - - let guard = self.sender.lock().await; - let sender = guard.as_ref().ok_or(OmikronError::NotConnected)?; - - sender - .send(cv) - .await - .map_err(|e| OmikronError::Send(e.to_string())) - } - - async fn send_error_response( - self: Arc, - message_id: u32, - error_type: CommunicationType, - ) -> OmikronResult<()> { - let error = CommunicationValue::new(error_type).with_id(message_id); - self.send(&error).await - } - - pub async fn close(self: Arc) { - log_in!( - self.get_omikron_id().await.unwrap_or(0), - PrintType::Omega, - "Omikron connection Closed" - ); - } - - async fn cleanup(self: Arc) { - if let Some(omikron_id) = self.state.read().await.omikron_id() { - if omikron_id != 0 { - log_in!(omikron_id, PrintType::Omega, "Omikron disconnected"); - omikron_manager::remove_omikron(omikron_id).await; - user_online_tracker::untrack_omikron(omikron_id).await; - } - } - - // Cancel cleanup task - if let Some(handle) = self.cleanup_handle.lock().await.take() { - handle.abort(); - } - } - - // Public API for external use - pub async fn is_authenticated(self: Arc) -> bool { - self.state.read().await.is_authenticated() - } - - pub async fn get_omikron_id(self: Arc) -> Option { - self.state.read().await.omikron_id() - } - - pub async fn send_message(self: Arc, cv: &CommunicationValue) -> OmikronResult<()> { - self.send(cv).await - } -} - -// ============================================================================ -// Server Startup -// ============================================================================ - -pub async fn start(port: u16) -> Result<(), Box> { - let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile"); - - let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile"); - - let mut host: Host = epsilon_native::host(port, cert_pem, key_pem).await?; - log!("OmikronServer listening on port {}", port); - - while let Some((sender, mut receiver)) = host.next().await { - tokio::spawn(async move { - let conn = OmikronConnection::new(sender); - conn.handle(&mut receiver).await; - }); - } - - Ok(()) -} diff --git a/src/transport/omikron_manager.rs b/src/transport/omikron_manager.rs deleted file mode 100644 index fbee4a9..0000000 --- a/src/transport/omikron_manager.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::transport::omikron_connection::OmikronConnection; -use dashmap::DashMap; -use once_cell::sync::Lazy; -use rand::prelude::IteratorRandom; -use std::sync::Arc; - -pub static OMIKRON_CONNECTIONS: Lazy>> = - Lazy::new(|| DashMap::new()); - -pub async fn add_omikron(conn: Arc) { - let id = match conn.clone().get_omikron_id().await { - Some(id) => id, - _ => { - conn.close().await; - return; - } - }; - - if let Some(old) = OMIKRON_CONNECTIONS.insert(id, conn.clone()) { - old.close().await; - } -} - -pub async fn remove_omikron(omikron_id: i64) { - OMIKRON_CONNECTIONS.remove(&omikron_id); -} -pub async fn get_random_omikron() -> Result, ()> { - let mut rng = rand::thread_rng(); - - let keys: Vec<_> = OMIKRON_CONNECTIONS.iter().map(|e| *e.key()).collect(); - - if let Some(key) = keys.into_iter().choose(&mut rng) { - if let Some(entry) = OMIKRON_CONNECTIONS.get(&key) { - return Ok(entry.clone()); - } - } - - Err(()) -} diff --git a/src/util/crypto_helper.rs b/src/util/crypto_helper.rs old mode 100644 new mode 100755 index 68e3855..cb3ae6a --- a/src/util/crypto_helper.rs +++ b/src/util/crypto_helper.rs @@ -8,27 +8,36 @@ use sha2::{Digest, Sha256}; use x448::{PublicKey, Secret, SharedSecret}; /// Errors for crypto operations +#[allow(dead_code)] #[derive(Debug)] pub enum CryptoError { - Base64Decode, + Base64Decode(base64::DecodeError), + InvalidKey, AgreementError, EncryptionError(aes_gcm::Error), DecryptionError(aes_gcm::Error), } impl From for CryptoError { - fn from(_: base64::DecodeError) -> Self { - CryptoError::Base64Decode + fn from(err: base64::DecodeError) -> Self { + CryptoError::Base64Decode(err) } } -pub fn generate_keypair() -> (Secret, PublicKey) { +#[allow(dead_code)] +pub struct KeyPair { + pub secret: Secret, + pub public: PublicKey, +} + +#[allow(dead_code)] +pub fn generate_keypair() -> KeyPair { let mut buf = [0u8; 56]; let mut rng = OsRng; rng.fill_bytes(&mut buf); let secret = Secret::from_bytes(&buf).unwrap(); let public = PublicKey::from(&secret); - (secret, public) + KeyPair { secret, public } } pub fn public_key_to_base64(pubkey: &PublicKey) -> String { @@ -58,6 +67,7 @@ fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] { key } +#[allow(dead_code)] pub fn encrypt_b64( base64_secret: &str, base64_peer_pub: &str, @@ -125,12 +135,14 @@ pub fn decrypt( Ok(plaintext) } +#[allow(dead_code)] pub fn hash_it(input: &str) -> Vec { let mut hasher = Sha256::new(); hasher.update(input.as_bytes()); hasher.finalize().to_vec() } +#[allow(dead_code)] pub fn hex_hash(input: &str) -> String { let digest = hash_it(input); digest.iter().map(|b| format!("{:02x}", b)).collect() diff --git a/src/util/crypto_util.rs b/src/util/crypto_util.rs index 97acab4..bb784e8 100644 --- a/src/util/crypto_util.rs +++ b/src/util/crypto_util.rs @@ -4,11 +4,12 @@ use aes_gcm::{ }; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STD}; use hkdf::Hkdf; -use sha2::{Digest, Sha256}; +type HkdfSha256 = sha2::Sha256; +use sha2::{Digest, Sha256 as HashSha256}; use x448::{PublicKey, Secret}; -// --- Custom Errors --- #[derive(Debug)] +#[allow(dead_code)] pub enum SecurePayloadError { InvalidBase64, InvalidHex, @@ -17,19 +18,16 @@ pub enum SecurePayloadError { InvalidKeyLength, } -// --- Data Format Enum --- #[derive(Clone, Copy, Debug)] +#[allow(dead_code)] pub enum DataFormat { Raw, Base64, Hex, } -// --- Main Class Structure --- pub struct SecurePayload { - /// The internal canonical representation is always raw bytes. inner_data: Vec, - /// The private key of the user associated with this payload instance. private_key: Secret, } @@ -42,8 +40,8 @@ impl Clone for SecurePayload { } } +#[allow(dead_code)] impl SecurePayload { - /// Clear Constructor: Takes data in any format and the user's private key. pub fn new>( data: T, format: DataFormat, @@ -68,12 +66,10 @@ impl SecurePayload { }) } - /// Helper to get the public key associated with this instance's private key. pub fn get_public_key(&self) -> [u8; 56] { *PublicKey::from(&self.private_key).as_bytes() } - /// Exports the internal data to the requested format pub fn export(&self, format: DataFormat) -> String { match format.into() { DataFormat::Raw => String::from_utf8_lossy(&self.inner_data).to_string(), @@ -82,14 +78,12 @@ impl SecurePayload { } } - /// Access raw bytes directly pub fn get_bytes(&self) -> &[u8] { &self.inner_data } - /// Returns the SHA-256 Hash of the data in the requested format pub fn get_hash(&self, format: DataFormat) -> String { - let mut hasher = Sha256::new(); + let mut hasher = HashSha256::new(); hasher.update(&self.inner_data); let result = hasher.finalize(); @@ -100,8 +94,6 @@ impl SecurePayload { } } - /// Encrypts the held data for a specific recipient using AES-256-GCM. - /// The message will contain ONLY the ciphertext. pub fn encrypt_x448(&self, public_key: S) -> Result where S: Into, @@ -109,22 +101,15 @@ impl SecurePayload { let peer_pub = public_key.into(); let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap(); - println!( - "Encryption Shared Secret (Hex): {}", - hex::encode(shared_secret.as_bytes()) - ); + let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); + let mut okm = [0u8; 44]; - // 3. Key & Nonce Derivation (HKDF) - // We derive 32 bytes for the key and 12 bytes for a deterministic nonce. - let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); - let mut okm = [0u8; 44]; // 32 (Key) + 12 (Nonce) hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm) .map_err(|_| SecurePayloadError::EncryptionError)?; let key = &okm[..32]; let nonce_bytes = &okm[32..]; - // 4. Encrypt with AES-256-GCM let cipher = Aes256Gcm::new(key.into()); let nonce = Nonce::from_slice(nonce_bytes); @@ -138,40 +123,33 @@ impl SecurePayload { ) .map_err(|_| SecurePayloadError::EncryptionError)?; - // 5. Result is ONLY the ciphertext. No key or nonce is packed. Ok(SecurePayload { inner_data: ciphertext, private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), }) } - /// Decrypts the held data providing the sender's public key manually. pub fn decrypt_to_format( &self, peer_public_key_bytes: &[u8; 56], output_format: DataFormat, ) -> Result { - let decrypted_instance = self.decrypt_x448(peer_public_key_bytes)?; + let decrypted_instance = + self.decrypt_x448(PublicKey::from_bytes(peer_public_key_bytes).unwrap())?; Ok(decrypted_instance.export(output_format)) } - /// Decrypts the held data using the internal Private Key and the provided Peer Public Key. - pub fn decrypt_x448( + pub fn decrypt_x448( &self, - peer_public_key_bytes: &[u8; 56], - ) -> Result { - // 1. Perform Exchange - let peer_pub = PublicKey::from_bytes(peer_public_key_bytes).unwrap(); + peer_public_key_bytes: S, + ) -> Result + where + S: Into, + { + let peer_pub = peer_public_key_bytes.into(); let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap(); - // LOGGING: Shared Secret - println!( - "Decryption Shared Secret (Hex): {}", - hex::encode(shared_secret.as_bytes()) - ); - - // 2. Key & Nonce Derivation (Must match encryption exactly) - let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); + let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); let mut okm = [0u8; 44]; hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm) .map_err(|_| SecurePayloadError::DecryptionError)?; @@ -179,7 +157,6 @@ impl SecurePayload { let key = &okm[..32]; let nonce_bytes = &okm[32..]; - // 3. Decrypt with AES-256-GCM let cipher = Aes256Gcm::new(key.into()); let nonce = Nonce::from_slice(nonce_bytes); diff --git a/src/util/file_util.rs b/src/util/file_util.rs index b108810..cd214b1 100644 --- a/src/util/file_util.rs +++ b/src/util/file_util.rs @@ -1,20 +1,9 @@ use std::fs::{self, File}; use std::io::{self, BufReader, Read}; use std::path::{Path, PathBuf}; -use tokio::io::AsyncWriteExt; -use uuid::Uuid; -use zip::ZipArchive; use crate::log; - -pub fn delete_file(path: &str, name: &str) -> bool { - let dir = Path::new(&get_directory()).join(path); - let file = dir.join(name); - if !file.exists() { - return false; - } - fs::remove_file(file).is_ok() -} +use crate::util::logger::PrintType; #[allow(dead_code)] pub fn delete_directory(path: &str) -> bool { @@ -29,9 +18,11 @@ fn delete_dir_recursive(directory: &Path) -> bool { } if let Err(e) = fs::remove_dir_all(directory) { log!( + 0, + PrintType::General, "[IMPORTANT] Couldn't delete directory {}: {}", directory.display(), - e + e, ); return false; } @@ -39,13 +30,14 @@ fn delete_dir_recursive(directory: &Path) -> bool { } #[allow(dead_code)] -pub fn delete_user_directory(user_id: Uuid) { +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); } +#[allow(dead_code)] 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); @@ -72,6 +64,7 @@ pub fn load_file_buf(path: &str, name: &str) -> io::Result> { let file = File::open(&file_path)?; Ok(BufReader::new(file)) } +#[allow(dead_code)] pub fn has_file(path: &str, name: &str) -> bool { let dir = Path::new(&get_directory()).join(path); let file_path = dir.join(name); @@ -86,6 +79,7 @@ pub fn has_file(path: &str, name: &str) -> bool { true } +#[allow(dead_code)] pub fn has_dir(path: &str) -> bool { let dir = Path::new(&get_directory()).join(path); @@ -96,13 +90,19 @@ pub fn has_dir(path: &str) -> bool { true } +#[allow(dead_code)] 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!("[IMPORTANT] Couldn't create directories: {}", e); + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't create directories: {}", + e + ); return String::new(); } return String::new(); @@ -110,7 +110,12 @@ pub fn load_file(path: &str, name: &str) -> String { if !file_path.exists() { if let Err(e) = File::create(&file_path) { - log!("[IMPORTANT] Couldn't create file: {}", e); + log!( + 0, + PrintType::General, + "[IMPORTANT] Couldn't create file: {}", + e + ); } return String::new(); } @@ -129,19 +134,27 @@ pub fn load_file_vec(path: &str, name: &str) -> Result, std::io::Error> std::fs::read(file_path) } +#[allow(dead_code)] 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!("[IMPORTANT] Couldn't create directories: {}", e); + 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 @@ -149,6 +162,7 @@ pub fn save_file(path: &str, name: &str, value: &str) { } } +#[allow(dead_code)] pub fn get_children(path: &str) -> Vec { let dir = Path::new(&get_directory()).join(path); let mut children = Vec::new(); @@ -169,121 +183,3 @@ pub fn get_directory() -> String { .to_string_lossy() .to_string() } - -// Helper to download the zip file content to a file on disk -#[allow(dead_code)] -async fn download_zip(url: &str, as_name: &Path) -> Result<(), Box> { - let response = reqwest::get(url).await?; - - // Check for successful response status - if !response.status().is_success() { - return Err(format!("Failed to download file: Status {}", response.status()).into()); - } - - let mut zip_file = tokio::fs::File::create(as_name).await?; - let body = response.bytes().await?; - zip_file.write_all(&body).await?; - - Ok(()) -} - -#[allow(dead_code, deprecated)] -fn extract_zip_contents_to_folder( - zip_path: &Path, - target_dir: &Path, -) -> Result<(), Box> { - let file = File::open(zip_path)?; - let mut archive = ZipArchive::new(file)?; - - let staging_dir = target_dir.with_extension("staging"); - - let _ = fs::remove_dir_all(&staging_dir); - fs::create_dir_all(&staging_dir)?; - - let mut first_item_name: Option = None; - - for i in 0..archive.len() { - let mut file = archive.by_index(i)?; - let entry_path = staging_dir.join(file.sanitized_name()); - - if i == 0 { - if file.name().ends_with('/') || file.sanitized_name().components().count() == 1 { - first_item_name = Some(file.sanitized_name()); - } - } - - if file.name().ends_with('/') { - fs::create_dir_all(&entry_path)?; - } else { - if let Some(parent) = entry_path.parent() { - fs::create_dir_all(parent)?; - } - let mut out_file = File::create(entry_path)?; - io::copy(&mut file, &mut out_file)?; - } - } - - if let Some(root_path) = first_item_name { - let root_dir = staging_dir.join(&root_path); - - if root_dir.is_dir() { - let root_contents_count = fs::read_dir(&staging_dir)?.count(); - - if root_contents_count == 1 - || (root_contents_count > 1 && fs::metadata(&root_dir).is_ok()) - { - let _ = fs::remove_dir_all(target_dir); - fs::create_dir_all(target_dir)?; - - for entry in fs::read_dir(root_dir)? { - let entry = entry?; - let src = entry.path(); - let dest = target_dir.join(entry.file_name()); - - if let Err(_) = fs::rename(&src, &dest) { - if src.is_file() { - fs::copy(&src, &dest)?; - } else { - if entry.path().is_dir() { - fs::rename(&src, &dest)?; - } - } - } - } - - let _ = fs::remove_dir_all(&staging_dir); - return Ok(()); - } - } - } - - log!("Extracting directly (no single root folder detected)."); - let _ = fs::remove_dir_all(target_dir); - fs::rename(&staging_dir, target_dir)?; - - Ok(()) -} - -#[allow(dead_code)] -pub async fn download_and_extract_zip(url: &str, as_name: &str) { - let base_dir = PathBuf::from(get_directory()); - let zip_filename = format!("{}.zip", Uuid::new_v4()); - let zip_path = base_dir.join(&zip_filename); - let target_dir = base_dir.join(as_name); - - if let Err(e) = download_zip(url, &zip_path).await { - log!("Error downloading file: {}", e); - return; - } - - let zip_path_clone = zip_path.clone(); - let target_dir_clone = target_dir.clone(); - let extract_result = extract_zip_contents_to_folder(&zip_path_clone, &target_dir_clone); - if let Err(e) = extract_result { - log!("Panic during ZIP extraction: {}", e); - } - - if let Err(e) = tokio::fs::remove_file(&zip_path).await { - log!("Error cleaning up ZIP file {}: {}", zip_path.display(), e); - } -} diff --git a/src/util/logger.rs b/src/util/logger.rs index d1a7764..9e9a085 100644 --- a/src/util/logger.rs +++ b/src/util/logger.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, fs::{self, OpenOptions}, io::Write, path::Path, @@ -9,13 +9,12 @@ use std::{ }; use ansi_term::Color; -use epsilon_core::{CommunicationValue, DataTypes, DataValue}; -use json::JsonValue; +use ttp_core::{CommunicationValue, DataTypes, DataValue}; static LOGGER: OnceLock> = OnceLock::new(); +#[allow(dead_code)] #[derive(Clone, Copy)] -#[allow(unused)] pub enum PrintType { Call, Client, @@ -96,12 +95,14 @@ fn fixed_box(content: &str, width: usize) -> String { } pub fn log_internal( - sender: Option, + sender: i64, kind: PrintType, prefix: &'static str, is_error: bool, message: String, ) { + let sender = if sender == 0 { None } else { Some(sender) }; + if let Some(tx) = LOGGER.get() { let _ = tx.send(LogMessage { timestamp_ms: SystemTime::now() @@ -116,102 +117,28 @@ pub fn log_internal( }); } } - #[macro_export] macro_rules! log { - // plain - ($($arg:tt)*) => { - $crate::util::logger::log_internal( - None, - $crate::util::logger::PrintType::General, - "", - false, - format!($($arg)*) - ) - }; - - // sender + actor - ($sender:expr, $kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(Some($sender), $kind, "", false, format!($($arg)*)) - }; - - // actor only - ($kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(None, $kind, "", false, format!($($arg)*)) + ($sender: expr, $kind:expr, $($arg:tt)*) => { + $crate::util::logger::log_internal($sender, $kind, "", false, format!($($arg)*)) }; } - #[macro_export] macro_rules! log_in { - // sender + actor - ($sender:expr, $kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(Some($sender), $kind, ">", false, format!($($arg)*)) - }; - - // actor only - ($kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(None, $kind, ">", false, format!($($arg)*)) - }; - - // plain - ($($arg:tt)*) => { - $crate::util::logger::log_internal( - None, - $crate::util::logger::PrintType::General, - ">", - false, - format!($($arg)*) - ) + ($sender: expr, $kind:expr, $($arg:tt)*) => { + $crate::util::logger::log_internal($sender, $kind, ">", false, format!($($arg)*)) }; } - #[macro_export] macro_rules! log_out { - - // sender + actor ($sender:expr, $kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(Some($sender), $kind, "<", false, format!($($arg)*)) - }; - - // actor only - ($kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(None, $kind, "<", false, format!($($arg)*)) - }; - - // plain - ($($arg:tt)*) => { - $crate::util::logger::log_internal( - None, - $crate::util::logger::PrintType::General, - "<", - false, - format!($($arg)*) - ) + $crate::util::logger::log_internal($sender, $kind, "<", false, format!($($arg)*)) }; } - #[macro_export] macro_rules! log_err { - - // sender + actor ($sender:expr, $kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(Some($sender), $kind, ">>", true, format!($($arg)*)) - }; - - // actor only - ($kind:expr, $($arg:tt)*) => { - $crate::util::logger::log_internal(None, $kind, ">>", true, format!($($arg)*)) - }; - - // plain - ($($arg:tt)*) => { - $crate::util::logger::log_internal( - None, - $crate::util::logger::PrintType::General, - ">>", - true, - format!($($arg)*) - ) + $crate::util::logger::log_internal($sender, $kind, ">>", true, format!($($arg)*)) }; } @@ -224,7 +151,7 @@ pub fn log_cv_internal( let formatted = format_cv(cv); log_internal( - Some(cv.get_sender() as i64), + cv.get_sender() as i64, print_type.unwrap_or(PrintType::General), prefix, false, @@ -322,6 +249,7 @@ fn format_array(arr: Vec) -> String { parts.join(", ") } + #[macro_export] macro_rules! log_cv { ($kind:expr, $cv:expr) => {