From 50ca1077de6ed4b0f112363868846678cccef2c5 Mon Sep 17 00:00:00 2001 From: Alois Date: Thu, 2 Apr 2026 22:28:41 +0200 Subject: [PATCH] Sync --- .gitignore | 29 + Cargo.lock | 4547 +++++++++++++++++++++++++++ Cargo.toml | 39 + LICENSE | 16 + README.md | 1 + src/main.rs | 65 + src/server/api.rs | 275 ++ src/server/mod.rs | 3 + src/server/server.rs | 67 + src/server/short_link.rs | 87 + src/sql/connection_status.rs | 30 + 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 | 137 + src/util/crypto_util.rs | 201 ++ src/util/file_util.rs | 289 ++ src/util/logger.rs | 352 +++ src/util/mod.rs | 4 + 22 files changed, 8234 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100755 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/main.rs create mode 100644 src/server/api.rs create mode 100644 src/server/mod.rs create mode 100644 src/server/server.rs create mode 100644 src/server/short_link.rs create mode 100644 src/sql/connection_status.rs create mode 100644 src/sql/mod.rs create mode 100644 src/sql/sql.rs create mode 100644 src/sql/user_online_tracker.rs create mode 100644 src/transport/mod.rs create mode 100644 src/transport/omikron_connection.rs create mode 100644 src/transport/omikron_manager.rs create mode 100644 src/util/crypto_helper.rs create mode 100644 src/util/crypto_util.rs create mode 100644 src/util/file_util.rs create mode 100644 src/util/logger.rs create mode 100644 src/util/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3d273c --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# 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 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1444c40 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4547 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "Omega" +version = "0.1.0" +dependencies = [ + "actix-web", + "aes-gcm", + "ansi_term", + "anyhow", + "base64", + "dashmap", + "dotenv", + "epsilon-core", + "epsilon-native", + "hex", + "hkdf", + "json", + "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", + "thiserror 2.0.18", + "tokio", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +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" +dependencies = [ + "alloc-no-stdlib", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.3.0", + "futures-lite 2.6.1", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io 2.6.0", + "async-lock 3.4.2", + "blocking", + "futures-lite 2.6.1", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.6.1", + "parking", + "polling 3.11.0", + "rustix 1.1.4", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 2.6.0", + "async-lock 3.4.2", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 2.6.1", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +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" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite 2.6.1", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +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-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenv" +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 = "ed448-goldilocks" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b5fa9e9e3dd5fe1369f380acd3dcdfa766dbd0a1cd5b048fb40e38a6a78e79" +dependencies = [ + "fiat-crypto", + "hex", + "subtle", +] + +[[package]] +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +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", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "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 = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +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", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "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", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +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", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "heck" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9fcbcc408c5526c3ab80d534e5c86e7967c1fb7aa0a8c76abd1edc27deb877" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.4.0", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body", + "hyper", + "ipnet", + "libc", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.17" +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" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libm" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru-slab" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "octets" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8311fa8ab7a57759b4ff1f851a3048d9ef0effaa0130726426b742d26d8a88e7" + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand 2.3.0", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.3", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rcgen" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e" +dependencies = [ + "aws-lc-rs", + "rustls-pki-types", + "time", + "x509-parser 0.18.1", + "yasna", +] + +[[package]] +name = "redox_syscall" +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", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2 0.4.13", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +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", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[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", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +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" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "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", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +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", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http 1.4.0", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +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", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "wtransport" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56b611f195638f3790e4e5a41e9d777643a6c324ae4ffd1f0f53f2738e7678c" +dependencies = [ + "bytes", + "pem", + "quinn", + "rcgen", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "sha2", + "socket2 0.5.10", + "thiserror 2.0.18", + "time", + "tokio", + "tracing", + "url", + "wtransport-proto", + "x509-parser 0.17.0", +] + +[[package]] +name = "wtransport-proto" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1627c5b59450278e9771aab35275d72bfa2788128c197c5af1e4a820c8737ef4" +dependencies = [ + "httlib-huffman", + "octets", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "x448" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd07d4fae29e07089dbcacf7077cd52dce7760125ca9a4dd5a35ca603ffebb" +dependencies = [ + "ed448-goldilocks", + "hex", + "rand_core 0.5.1", +] + +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs", + "aws-lc-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +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 new file mode 100755 index 0000000..34673fb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "Omega" +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" } + +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" +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" +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" +thiserror = "2.0.18" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d218eff --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +Copyright (c) [2025] [Methanium] +All rights reserved. + +This software is protected by copyright. Copying, editing, +distributing, publicly performing, or any other use of this software +or its components, in source or binary form, is strictly prohibited without the express +written permission of the copyright holder. + +FUTURE LICENSE ACCEPTANCE: +It is the copyright holder's intention to release this software in the future +under a license yet to be defined, which will, among other things, +allow private, non-commercial use. This statement does not constitute +a current license grant and does not alter the above +prohibition on use, copying, or modification. Until the formal +publication of such a future license, all rights remain +reserved. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e210a3 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# ⚠️ Moved to [git.methanium.net](https://git.methanium.net/tensamin/omega) ⚠️ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8143c9f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,65 @@ +mod server; +mod sql; +mod transport; +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 dotenv::dotenv; +use once_cell::sync::Lazy; +use rustls::crypto::aws_lc_rs::default_provider; +use std::env; + +static PRIVATE_KEY: Lazy = Lazy::new(|| env::var("PRIVATE_KEY").unwrap()); +pub fn get_private_key() -> x448::Secret { + load_secret_key(&*PRIVATE_KEY).unwrap() +} +static PUBLIC_KEY: Lazy = Lazy::new(|| env::var("PUBLIC_KEY").unwrap()); +pub fn get_public_key() -> x448::PublicKey { + load_public_key(&*PUBLIC_KEY).unwrap() +} + +#[tokio::main] +async fn main() { + if let Err(_) = default_provider().install_default() { + println!("Error loading Provider"); + return; + } + dotenv().ok(); + startup(); + log_in!("Incoming messages"); + log_out!("Outgoing messages"); + + tokio::spawn(async move { + match omikron_connection::start(9187).await { + Err(e) => log_err!(0, 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; + + tokio::signal::ctrl_c().await.unwrap(); +} diff --git a/src/server/api.rs b/src/server/api.rs new file mode 100644 index 0000000..eb6f9cf --- /dev/null +++ b/src/server/api.rs @@ -0,0 +1,275 @@ +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 new file mode 100644 index 0000000..8645e6e --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,3 @@ +pub mod api; +pub mod server; +pub mod short_link; diff --git a/src/server/server.rs b/src/server/server.rs new file mode 100644 index 0000000..3618eb7 --- /dev/null +++ b/src/server/server.rs @@ -0,0 +1,67 @@ +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 new file mode 100644 index 0000000..c0ab51d --- /dev/null +++ b/src/server/short_link.rs @@ -0,0 +1,87 @@ +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/connection_status.rs b/src/sql/connection_status.rs new file mode 100644 index 0000000..a175e6d --- /dev/null +++ b/src/sql/connection_status.rs @@ -0,0 +1,30 @@ +use strum::IntoEnumIterator; +use strum_macros::EnumIter; + +#[derive(Debug, Clone, PartialEq, EnumIter, Eq)] +#[allow(unused, non_camel_case_types)] +pub enum UserStatus { + user_offline, + user_online, + user_dnd, + user_idle, + user_wc, + user_borked, + iota_offline, + iota_online, + iota_borked, +} +#[allow(unused)] +impl UserStatus { + pub fn to_string(&self) -> String { + format!("{:?}", self) + } + pub fn from_str(s: &str) -> Option { + for sel in UserStatus::iter() { + if &sel.to_string() == s { + return Some(sel); + } + } + None + } +} diff --git a/src/sql/mod.rs b/src/sql/mod.rs new file mode 100644 index 0000000..91ba841 --- /dev/null +++ b/src/sql/mod.rs @@ -0,0 +1,3 @@ +pub mod connection_status; +pub mod sql; +pub mod user_online_tracker; diff --git a/src/sql/sql.rs b/src/sql/sql.rs new file mode 100644 index 0000000..8445e26 --- /dev/null +++ b/src/sql/sql.rs @@ -0,0 +1,765 @@ +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 new file mode 100644 index 0000000..42dfa22 --- /dev/null +++ b/src/sql/user_online_tracker.rs @@ -0,0 +1,144 @@ +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 new file mode 100644 index 0000000..4f3953a --- /dev/null +++ b/src/transport/mod.rs @@ -0,0 +1,2 @@ +pub mod omikron_connection; +pub mod omikron_manager; diff --git a/src/transport/omikron_connection.rs b/src/transport/omikron_connection.rs new file mode 100644 index 0000000..504d723 --- /dev/null +++ b/src/transport/omikron_connection.rs @@ -0,0 +1,1139 @@ +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 new file mode 100644 index 0000000..fbee4a9 --- /dev/null +++ b/src/transport/omikron_manager.rs @@ -0,0 +1,39 @@ +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 new file mode 100644 index 0000000..68e3855 --- /dev/null +++ b/src/util/crypto_helper.rs @@ -0,0 +1,137 @@ +use aes_gcm::{ + Aes256Gcm, Nonce, + aead::{Aead, KeyInit, OsRng}, +}; +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use rand_core::RngCore; +use sha2::{Digest, Sha256}; +use x448::{PublicKey, Secret, SharedSecret}; + +/// Errors for crypto operations +#[derive(Debug)] +pub enum CryptoError { + Base64Decode, + AgreementError, + EncryptionError(aes_gcm::Error), + DecryptionError(aes_gcm::Error), +} + +impl From for CryptoError { + fn from(_: base64::DecodeError) -> Self { + CryptoError::Base64Decode + } +} + +pub fn generate_keypair() -> (Secret, PublicKey) { + 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) +} + +pub fn public_key_to_base64(pubkey: &PublicKey) -> String { + STANDARD.encode(pubkey.as_bytes().as_ref()) +} + +pub fn secret_key_to_base64(secret: &Secret) -> String { + STANDARD.encode(secret.as_bytes().as_ref()) +} + +pub fn load_public_key(base64_pub: &str) -> Option { + let bytes = STANDARD.decode(base64_pub).unwrap(); + PublicKey::from_bytes(&bytes) +} + +pub fn load_secret_key(base64_secret: &str) -> Option { + let bytes = STANDARD.decode(base64_secret).unwrap(); + Secret::from_bytes(&bytes) +} + +fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(shared.as_bytes()); + let result = hasher.finalize(); + let mut key = [0u8; 32]; + key.copy_from_slice(&result[..32]); + key +} + +pub fn encrypt_b64( + base64_secret: &str, + base64_peer_pub: &str, + plaintext: &str, +) -> Result { + let secret = load_secret_key(base64_secret).unwrap(); + let peer_pub = load_public_key(base64_peer_pub).unwrap(); + encrypt(secret, peer_pub, plaintext) +} +pub fn encrypt( + secret: Secret, + peer_pub: PublicKey, + plaintext: &str, +) -> Result { + let shared = secret + .to_diffie_hellman(&peer_pub) + .ok_or(CryptoError::AgreementError)?; + let key_bytes = derive_aes_key(&shared); + let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); + let mut nonce_bytes = [0u8; 12]; + OsRng.fill_bytes(&mut nonce_bytes); + let nonce = Nonce::from_slice(&nonce_bytes); + let ciphertext = cipher + .encrypt(nonce, plaintext.as_bytes()) + .map_err(CryptoError::EncryptionError)?; + // prefix nonce to ciphertext + let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len()); + out.extend_from_slice(&nonce_bytes); + out.extend_from_slice(&ciphertext); + Ok(STANDARD.encode(&out)) +} + +pub fn decrypt_b64( + base64_secret: &str, + base64_peer_pub: &str, + encrypted_base64: &str, +) -> Result { + let secret = load_secret_key(base64_secret).unwrap(); + let peer_pub = load_public_key(base64_peer_pub).unwrap(); + decrypt(secret, peer_pub, encrypted_base64) +} +pub fn decrypt( + secret: Secret, + peer_pub: PublicKey, + encrypted_base64: &str, +) -> Result { + let shared = secret + .to_diffie_hellman(&peer_pub) + .ok_or(CryptoError::AgreementError)?; + let key_bytes = derive_aes_key(&shared); + let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); + + let encrypted = STANDARD.decode(encrypted_base64)?; + if encrypted.len() < 12 { + return Err(CryptoError::DecryptionError(aes_gcm::Error)); + } + let nonce_bytes = &encrypted[..12]; + let ciphertext = &encrypted[12..]; + let nonce = Nonce::from_slice(nonce_bytes); + let plaintext_bytes = cipher + .decrypt(nonce, ciphertext) + .map_err(CryptoError::DecryptionError)?; + let plaintext = String::from_utf8(plaintext_bytes) + .map_err(|_| CryptoError::DecryptionError(aes_gcm::Error))?; + Ok(plaintext) +} + +pub fn hash_it(input: &str) -> Vec { + let mut hasher = Sha256::new(); + hasher.update(input.as_bytes()); + hasher.finalize().to_vec() +} + +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 new file mode 100644 index 0000000..97acab4 --- /dev/null +++ b/src/util/crypto_util.rs @@ -0,0 +1,201 @@ +use aes_gcm::{ + Aes256Gcm, Nonce, + aead::{Aead, KeyInit, Payload}, +}; +use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STD}; +use hkdf::Hkdf; +use sha2::{Digest, Sha256}; +use x448::{PublicKey, Secret}; + +// --- Custom Errors --- +#[derive(Debug)] +pub enum SecurePayloadError { + InvalidBase64, + InvalidHex, + EncryptionError, + DecryptionError, + InvalidKeyLength, +} + +// --- Data Format Enum --- +#[derive(Clone, Copy, Debug)] +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, +} + +impl Clone for SecurePayload { + fn clone(&self) -> Self { + Self { + inner_data: self.inner_data.clone(), + private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), + } + } +} + +impl SecurePayload { + /// Clear Constructor: Takes data in any format and the user's private key. + pub fn new>( + data: T, + format: DataFormat, + private_key: S, + ) -> Result + where + S: Into, + { + let raw_data = match format { + DataFormat::Raw => data.as_ref().to_vec(), + DataFormat::Base64 => BASE64_STD + .decode(data.as_ref()) + .map_err(|_| SecurePayloadError::InvalidBase64)?, + DataFormat::Hex => { + hex::decode(data.as_ref()).map_err(|_| SecurePayloadError::InvalidHex)? + } + }; + + Ok(Self { + inner_data: raw_data, + private_key: private_key.into(), + }) + } + + /// 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(), + DataFormat::Base64 => BASE64_STD.encode(&self.inner_data), + DataFormat::Hex => hex::encode(&self.inner_data), + } + } + + /// 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(); + hasher.update(&self.inner_data); + let result = hasher.finalize(); + + match format { + DataFormat::Raw => String::from_utf8_lossy(&result).to_string(), + DataFormat::Base64 => BASE64_STD.encode(result), + DataFormat::Hex => hex::encode(result), + } + } + + /// 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, + { + 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()) + ); + + // 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); + + let ciphertext = cipher + .encrypt( + nonce, + Payload { + msg: &self.inner_data, + aad: &[], + }, + ) + .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)?; + 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( + &self, + peer_public_key_bytes: &[u8; 56], + ) -> Result { + // 1. Perform Exchange + let peer_pub = PublicKey::from_bytes(peer_public_key_bytes).unwrap(); + 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 mut okm = [0u8; 44]; + hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm) + .map_err(|_| SecurePayloadError::DecryptionError)?; + + 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); + + let plaintext = cipher + .decrypt( + nonce, + Payload { + msg: &self.inner_data, + aad: &[], + }, + ) + .map_err(|_| SecurePayloadError::DecryptionError)?; + + Ok(SecurePayload { + inner_data: plaintext, + private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), + }) + } +} diff --git a/src/util/file_util.rs b/src/util/file_util.rs new file mode 100644 index 0000000..b108810 --- /dev/null +++ b/src/util/file_util.rs @@ -0,0 +1,289 @@ +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() +} + +#[allow(dead_code)] +pub fn delete_directory(path: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + delete_dir_recursive(&dir) +} + +#[allow(dead_code)] +fn delete_dir_recursive(directory: &Path) -> bool { + if !directory.exists() { + return false; + } + if let Err(e) = fs::remove_dir_all(directory) { + log!( + "[IMPORTANT] Couldn't delete directory {}: {}", + directory.display(), + e + ); + return false; + } + true +} + +#[allow(dead_code)] +pub fn delete_user_directory(user_id: Uuid) { + let user_dir = Path::new(&get_directory()) + .join("users") + .join(user_id.to_string()); + let _ = delete_dir_recursive(&user_dir); +} + +pub fn load_file_buf(path: &str, name: &str) -> io::Result> { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + // Ensure the directory exists, create if necessary + if !dir.exists() { + if let Err(_) = fs::create_dir_all(&dir) { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Directory creation failed", + )); + } + } + + // Create the file if it doesn't exist + if !file_path.exists() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "File creation failed", + )); + } + + // Open the file and return a BufReader for efficient reading + let file = File::open(&file_path)?; + Ok(BufReader::new(file)) +} +pub fn has_file(path: &str, name: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + return false; + } + + if !file_path.exists() { + return false; + } + + true +} +pub fn has_dir(path: &str) -> bool { + let dir = Path::new(&get_directory()).join(path); + + if !dir.exists() { + return false; + } + + true +} + +pub fn load_file(path: &str, name: &str) -> String { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + log!("[IMPORTANT] Couldn't create directories: {}", e); + return String::new(); + } + return String::new(); + } + + if !file_path.exists() { + if let Err(e) = File::create(&file_path) { + log!("[IMPORTANT] Couldn't create file: {}", e); + } + return String::new(); + } + + let mut content = String::new(); + if let Ok(mut f) = File::open(&file_path) { + let _ = f.read_to_string(&mut content); + } + content +} + +pub fn load_file_vec(path: &str, name: &str) -> Result, std::io::Error> { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + std::fs::read(file_path) +} + +pub fn save_file(path: &str, name: &str, value: &str) { + let dir = Path::new(&get_directory()).join(path); + let file_path = dir.join(name); + + if !dir.exists() { + if let Err(e) = fs::create_dir_all(&dir) { + log!("[IMPORTANT] Couldn't create directories: {}", e); + return; + } + } + + if let Err(e) = fs::write(&file_path, value) { + log!( + "[IMPORTANT] Couldn't write file {}: {}", + file_path.display(), + e + ); + } +} + +pub fn get_children(path: &str) -> Vec { + let dir = Path::new(&get_directory()).join(path); + let mut children = Vec::new(); + if let Ok(entries) = fs::read_dir(&dir) { + for entry in entries { + if let Ok(entry) = entry { + children.push(entry.file_name().to_string_lossy().to_string()); + } + } + } + children +} + +pub fn get_directory() -> String { + let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); + exe.parent() + .unwrap_or(Path::new(".")) + .to_string_lossy() + .to_string() +} + +// 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 new file mode 100644 index 0000000..d1a7764 --- /dev/null +++ b/src/util/logger.rs @@ -0,0 +1,352 @@ +use std::{ + collections::{BTreeMap, HashMap}, + fs::{self, OpenOptions}, + io::Write, + path::Path, + sync::{OnceLock, mpsc}, + thread, + time::{SystemTime, UNIX_EPOCH}, +}; + +use ansi_term::Color; +use epsilon_core::{CommunicationValue, DataTypes, DataValue}; +use json::JsonValue; + +static LOGGER: OnceLock> = OnceLock::new(); + +#[derive(Clone, Copy)] +#[allow(unused)] +pub enum PrintType { + Call, + Client, + Iota, + Omikron, + Omega, + General, +} + +struct LogMessage { + timestamp_ms: u128, + sender: Option, + prefix: &'static str, + kind: PrintType, + is_error: bool, + message: String, +} + +pub fn startup() { + let (tx, rx) = mpsc::channel::(); + LOGGER.set(tx).expect("Logger already initialized"); + + thread::spawn(move || { + let log_dir = Path::new("logs"); + fs::create_dir_all(log_dir).expect("Failed to create log directory"); + + let start_ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + let path = log_dir.join(format!("log_{}.txt", start_ts)); + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .expect("Failed to open log file"); + + for msg in rx { + let ts = fixed_box(&msg.timestamp_ms.to_string(), 13); + let sender = match msg.sender { + Some(id) => fixed_box(&id.to_string(), 19), + _ => fixed_box("", 19), + }; + + let line = format!("{} {} {} {}", ts, sender, msg.prefix, msg.message); + + println!("{}", colorize(msg.kind, msg.is_error).paint(&line)); + + let _ = writeln!(file, "{}", line); + } + }); +} + +fn colorize(kind: PrintType, is_error: bool) -> Color { + if is_error { + return Color::Red; + } + + match kind { + PrintType::Call => Color::Purple, + PrintType::Client => Color::Green, + PrintType::Iota => Color::Yellow, + PrintType::Omikron => Color::Blue, + PrintType::Omega => Color::Cyan, + PrintType::General => Color::White, + } +} + +fn fixed_box(content: &str, width: usize) -> String { + let s: String = content.chars().take(width).collect(); + let len = s.chars().count(); + if len < width { + format!("[{}{}]", " ".repeat(width - len), s) + } else { + s + } +} + +pub fn log_internal( + sender: Option, + kind: PrintType, + prefix: &'static str, + is_error: bool, + message: String, +) { + if let Some(tx) = LOGGER.get() { + let _ = tx.send(LogMessage { + timestamp_ms: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(), + sender, + prefix, + kind, + is_error, + message, + }); + } +} + +#[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)*)) + }; +} + +#[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)*) + ) + }; +} + +#[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)*) + ) + }; +} + +#[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)*) + ) + }; +} + +// ******** COMMUNICATION VALUES ******** +pub fn log_cv_internal( + prefix: &'static str, + cv: &CommunicationValue, + print_type: Option, +) { + let formatted = format_cv(cv); + + log_internal( + Some(cv.get_sender() as i64), + print_type.unwrap_or(PrintType::General), + prefix, + false, + formatted, + ); +} + +pub fn format_cv(cv: &CommunicationValue) -> String { + let mut parts = Vec::new(); + + let sender = cv.get_sender(); + let receiver = cv.get_receiver(); + + if sender > 0 && receiver > 0 { + parts.push(format!("{} > {}", sender, receiver)); + } else if sender > 0 { + parts.push(format!("{}", sender)); + } else if receiver > 0 { + parts.push(format!("> {}", receiver)); + } + + let comm_type = cv.get_type().to_string(); + parts.push(format!("{}", comm_type)); + + let data: &BTreeMap = cv.get_data_container(); + + let formated_data = + format_data_container(data.iter().map(|(k, v)| (k.clone(), v.clone())).collect()); + + parts.push(format!("{}", formated_data)); + + parts.join(": ") +} + +fn format_data_container(data: Vec<(DataTypes, DataValue)>) -> String { + let parts: Vec = data + .into_iter() + .map(|(key, value)| { + let key_str = key.to_string(); + + match value { + DataValue::Str(s) => format!("{}=\"{}\"", key_str, s), + + DataValue::Container(inner) => { + let inner_formatted = format_data_container(inner); + format!("{}={{ {} }}", key_str, inner_formatted) + } + + DataValue::Array(arr) => { + let arr_formatted = format_array(arr); + format!("{}=[{}]", key_str, arr_formatted) + } + + DataValue::Bool(b) => format!("{}={}", key_str, b), + + DataValue::BoolTrue => format!("{}=true", key_str), + DataValue::BoolFalse => format!("{}=false", key_str), + + DataValue::Number(num) => format!("{}={}", key_str, num), + + _ => "".to_string(), + } + }) + .collect(); + + parts.join(", ") +} + +fn format_array(arr: Vec) -> String { + let parts: Vec = arr + .into_iter() + .map(|value| match value { + DataValue::Str(s) => format!("\"{}\"", s), + + DataValue::Container(inner) => { + let inner_formatted = format_data_container(inner); + format!("{{ {} }}", inner_formatted) + } + + DataValue::Array(inner_arr) => { + let formatted = format_array(inner_arr); + format!("[{}]", formatted) + } + + DataValue::Bool(b) => b.to_string(), + + DataValue::BoolTrue => "true".to_string(), + DataValue::BoolFalse => "false".to_string(), + + DataValue::Number(num) => num.to_string(), + + _ => String::new(), + }) + .collect(); + + parts.join(", ") +} +#[macro_export] +macro_rules! log_cv { + ($kind:expr, $cv:expr) => { + $crate::util::logger::log_cv_internal("", &$cv, Some($kind)) + }; + ($cv:expr) => { + $crate::util::logger::log_cv_internal("", &$cv, None) + }; +} + +#[macro_export] +macro_rules! log_cv_in { + ($kind:expr, $cv:expr) => { + $crate::util::logger::log_cv_internal("> ", &$cv, Some($kind)) + }; + ($cv:expr) => { + $crate::util::logger::log_cv_internal("> ", &$cv, None) + }; +} +#[macro_export] +macro_rules! log_cv_out { + ($kind:expr, $cv:expr) => { + $crate::util::logger::log_cv_internal("< ", &$cv, Some($kind)) + }; + ($cv:expr) => { + $crate::util::logger::log_cv_internal("< ", &$cv, None) + }; +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..1897223 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,4 @@ +pub mod crypto_helper; +pub mod crypto_util; +pub mod file_util; +pub mod logger;