diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b92839d..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#65c89b", - "activityBar.background": "#65c89b", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#945bc4", - "activityBarBadge.foreground": "#e7e7e7", - "commandCenter.border": "#15202b99", - "sash.hoverBorder": "#65c89b", - "statusBar.background": "#42b883", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#359268", - "statusBarItem.remoteBackground": "#42b883", - "statusBarItem.remoteForeground": "#15202b", - "titleBar.activeBackground": "#42b883", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#42b88399", - "titleBar.inactiveForeground": "#15202b99" - }, - "peacock.color": "#42b883" -} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c42765c..48022a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.18" @@ -117,154 +132,26 @@ dependencies = [ ] [[package]] -name = "async-broadcast" -version = "0.7.2" +name = "argon2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", + "base64ct", + "blake2", + "cpufeatures", + "password-hash", ] [[package]] -name = "async-channel" -version = "2.3.1" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", + "num-traits", ] -[[package]] -name = "async-executor" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix 0.38.44", - "slab", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 0.38.44", - "tracing", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-signal" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.44", - "signal-hook-registry", - "slab", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[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.4.0" @@ -298,39 +185,67 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" +dependencies = [ + "base64 0.22.1", + "blowfish", + "getrandom 0.3.1", + "subtle", + "zeroize", +] + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2", + "serde", ] [[package]] -name = "blocking" -version = "1.6.1" +name = "blake2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", + "digest", +] + +[[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 = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", ] [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -361,9 +276,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "shlex", ] @@ -375,33 +290,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chatclient" +name = "chatserver" version = "0.1.0" dependencies = [ "aes-gcm", + "argon2", "base64 0.21.7", + "bcrypt", + "chrono", "colog", - "crossterm 0.27.0", + "crossterm", "crypto", "log", - "notify-rust", "rand", "rand_core", "ratatui", - "reqwest", "serde", + "sqlx", + "thiserror", "tokio", - "tokio-util", "toml", "x25519-dalek", ] +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "cipher" version = "0.4.4" @@ -462,6 +387,12 @@ 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 = "core-foundation" version = "0.9.4" @@ -487,28 +418,36 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +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 = "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 = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags", - "crossterm_winapi", - "libc", - "mio 0.8.11", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - [[package]] name = "crossterm" version = "0.28.1" @@ -517,9 +456,9 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags", "crossterm_winapi", - "mio 1.0.2", + "mio", "parking_lot", - "rustix 0.38.44", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -591,9 +530,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -601,9 +540,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -615,9 +554,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -625,22 +564,26 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.4.0" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "powerfmt", + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] -name = "dispatch2" -version = "0.3.0" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "bitflags", - "objc2", + "block-buffer", + "const-oid", + "crypto-common", + "subtle", ] [[package]] @@ -654,48 +597,21 @@ dependencies = [ "syn", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" -version = "1.15.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" dependencies = [ - "cfg-if", -] - -[[package]] -name = "endi" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" - -[[package]] -name = "enumflags2" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" -dependencies = [ - "enumflags2_derive", "serde", ] -[[package]] -name = "enumflags2_derive" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_filter" version = "0.1.2" @@ -727,14 +643,25 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.59.0", ] +[[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 = "5.4.0" @@ -746,16 +673,6 @@ dependencies = [ "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", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -768,6 +685,17 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[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" @@ -776,9 +704,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -811,6 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -819,25 +748,34 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +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.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-sink" version = "0.3.31" @@ -857,9 +795,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -885,14 +827,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -911,25 +853,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "h2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.15.2" @@ -941,6 +864,15 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + [[package]] name = "heck" version = "0.5.0" @@ -953,12 +885,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - [[package]] name = "hex" version = "0.4.3" @@ -966,45 +892,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "http" -version = "1.3.1" +name = "hkdf" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "bytes", - "fnv", - "itoa", + "hmac", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "bytes", - "http", + "digest", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "home" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", + "windows-sys 0.59.0", ] -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - [[package]] name = "humantime" version = "2.1.0" @@ -1012,76 +925,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "hyper" -version = "1.6.0" +name = "iana-time-zone" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "hyper-rustls" -version = "0.27.5" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "libc", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", + "cc", ] [[package]] @@ -1125,9 +988,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" @@ -1149,9 +1012,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" @@ -1170,9 +1033,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" @@ -1231,9 +1094,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -1241,9 +1104,9 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "inout" @@ -1267,12 +1130,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1290,9 +1147,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" @@ -1309,12 +1166,32 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" -version = "0.2.172" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" @@ -1322,17 +1199,11 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - [[package]] name = "litemap" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -1360,15 +1231,13 @@ dependencies = [ ] [[package]] -name = "mac-notification-sys" -version = "0.6.4" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b95dfb34071d1592b45622bf93e315e3a72d414b6782aca9a015c12bec367ef" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cc", - "objc2", - "objc2-foundation", - "time", + "cfg-if", + "digest", ] [[package]] @@ -1377,21 +1246,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1401,25 +1255,13 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1428,9 +1270,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1444,84 +1286,50 @@ dependencies = [ ] [[package]] -name = "nix" -version = "0.29.0" +name = "num-bigint-dig" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", ] [[package]] -name = "notify-rust" -version = "4.11.7" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "futures-lite", - "log", - "mac-notification-sys", - "serde", - "tauri-winrt-notification", - "zbus", + "num-traits", ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num-iter" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "libc", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "objc2" -version = "0.6.1" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" -dependencies = [ - "bitflags", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", - "objc2-core-foundation", + "autocfg", + "libm", ] [[package]] @@ -1535,9 +1343,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -1547,9 +1355,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ "bitflags", "cfg-if", @@ -1579,9 +1387,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -1589,16 +1397,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "parking" version = "2.2.1" @@ -1628,12 +1426,32 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[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.1" @@ -1653,36 +1471,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "piper" -version = "0.2.4" +name = "pkcs1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", + "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" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "polling" -version = "3.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.4.0", - "pin-project-lite", - "rustix 0.38.44", - "tracing", - "windows-sys 0.59.0", -] +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polyval" @@ -1696,12 +1509,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1711,15 +1518,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.92" @@ -1729,15 +1527,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.37.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.37" @@ -1747,12 +1536,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - [[package]] name = "rand" version = "0.8.5" @@ -1792,14 +1575,13 @@ dependencies = [ "bitflags", "cassowary", "compact_str", - "crossterm 0.28.1", + "crossterm", "indoc", "instability", "itertools", "lru", "paste", "strum", - "time", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -1844,61 +1626,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "reqwest" -version = "0.12.15" +name = "rsa" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", -] - -[[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.15", - "libc", - "untrusted", - "windows-sys 0.52.0", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] @@ -1925,73 +1669,21 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.4.15", + "linux-raw-sys", "windows-sys 0.59.0", ] -[[package]] -name = "rustix" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls" -version = "0.23.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[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.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" - -[[package]] -name = "rustls-webpki" -version = "0.103.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" @@ -2059,9 +1751,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -2069,17 +1761,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "serde_spanned" version = "0.6.8" @@ -2101,6 +1782,28 @@ dependencies = [ "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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2124,8 +1827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 0.8.11", - "mio 1.0.2", + "mio", "signal-hook", ] @@ -2138,6 +1840,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "slab" version = "0.4.9" @@ -2152,17 +1864,227 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +dependencies = [ + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "indexmap", + "log", + "memchr", + "native-tls", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "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", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2175,6 +2097,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[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 = "strsim" version = "0.11.1" @@ -2211,24 +2144,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 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.1" @@ -2240,49 +2164,17 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tauri-winrt-notification" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" -dependencies = [ - "quick-xml", - "thiserror", - "windows", - "windows-version", -] - [[package]] name = "tempfile" -version = "3.19.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ + "cfg-if", "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.1", "once_cell", - "rustix 1.0.5", + "rustix", "windows-sys 0.59.0", ] @@ -2306,27 +2198,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - [[package]] name = "tinystr" version = "0.7.6" @@ -2337,6 +2208,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +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.41.1" @@ -2346,7 +2232,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -2367,34 +2253,12 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-stream" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", "futures-core", - "futures-sink", "pin-project-lite", "tokio", ] @@ -2430,42 +2294,16 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow", ] -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "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.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2491,12 +2329,6 @@ 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.17.0" @@ -2504,15 +2336,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] -name = "uds_windows" -version = "1.1.0" +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" @@ -2520,6 +2347,21 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -2559,12 +2401,6 @@ dependencies = [ "subtle", ] -[[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.4" @@ -2606,15 +2442,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[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.0+wasi-snapshot-preview1" @@ -2623,13 +2450,19 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" dependencies = [ "wit-bindgen-rt", ] +[[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.100" @@ -2656,19 +2489,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -2702,13 +2522,13 @@ dependencies = [ ] [[package]] -name = "web-sys" -version = "0.3.77" +name = "whoami" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "js-sys", - "wasm-bindgen", + "redox_syscall", + "wasite", ] [[package]] @@ -2733,125 +2553,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core", -] - [[package]] name = "windows-core" -version = "0.61.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings 0.4.0", -] - -[[package]] -name = "windows-future" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" -dependencies = [ - "windows-core", - "windows-link", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core", - "windows-link", -] - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" -dependencies = [ - "windows-link", + "windows-targets 0.52.6", ] [[package]] @@ -2905,38 +2613,13 @@ 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_gnullvm", "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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows-version" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" -dependencies = [ - "windows-link", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -2949,12 +2632,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -2967,12 +2644,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -2985,24 +2656,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3015,12 +2674,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3033,12 +2686,6 @@ 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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3051,12 +2698,6 @@ 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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3069,12 +2710,6 @@ 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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" version = "0.6.20" @@ -3084,20 +2719,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen-rt" -version = "0.39.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ "bitflags", ] @@ -3126,16 +2752,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "yoke" version = "0.7.5" @@ -3160,69 +2776,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "5.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "nix", - "ordered-stream", - "serde", - "serde_repr", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.59.0", - "winnow 0.7.6", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" -dependencies = [ - "serde", - "static_assertions", - "winnow 0.7.6", - "zvariant", -] - [[package]] name = "zerocopy" version = "0.7.35" @@ -3246,18 +2799,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", @@ -3306,45 +2859,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zvariant" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "winnow 0.7.6", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "static_assertions", - "syn", - "winnow 0.7.6", -] diff --git a/Cargo.toml b/Cargo.toml index 83a197f..f4e8af5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chatclient" +name = "chatserver" version = "0.1.0" edition = "2021" @@ -7,16 +7,18 @@ edition = "2021" colog = "1.3.0" log = "0.4.22" tokio = { version = "1.41.1", features = ["full"] } -tokio-util = { version = "0.7.12", features = ["codec"] } -serde = { version = "1.0", features = ["derive"] } -toml = "0.8.19" x25519-dalek = "2.0.0-rc.3" aes-gcm = "0.10.3" rand = "0.8.5" rand_core = "0.6.4" crypto = "0.5.1" base64 = "0.21" -ratatui = { version = "0.29.0", features = ["all-widgets"] } -crossterm = "0.27" -reqwest = "0.12.15" -notify-rust = "4" +serde = { version = "1.0", features = ["derive"] } +toml = "0.8.19" +ratatui = "0.29.0" +crossterm = "0.28.1" +chrono = "0.4.39" +sqlx = { version = "0.8", features = [ "sqlite", "runtime-tokio", "tls-native-tls" ] } +bcrypt = "0.17.0" +argon2 = "0.5.3" +thiserror = "2.0.12" diff --git a/README.md b/README.md new file mode 100644 index 0000000..5db8b6d --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# SRC (Simple Rust Chat) + +Simple Rust Chat è una chat Client/Server TCP + +La chat è basata molto sull'idea di una chat IRC (inizialmente il progetto aveva come scopo la creazione di un server IRC da utilizzare con dei clienti IRC come Halloy o mIRC) + + +## Linguaggio + +Ho utilizzato Rust come linguaggio per questo programma per la sua velocità e leggerezza che permette di farlo runnare anche su sistemi con componenti poco potenti. Anche il client è scritto in Rust per compatibilità tra librerie utilizzate nel client e nel server. Permette anche di essere dockerizzato come immagine il server che permette di scalare il server utilizzando Kubernets o altri sistemi di scalability. + +## Librerie + +Le librerie utilizzate in particolare sono Tokio, un framework per applicazioni async e che offre anche connessioni Socket. Per la gestione del database viene usato SQLx, una libreria che offre una connessione standard per vari tipi di DBMS. +serde: Per serializzazione/deserializzazione strutturata dei pacchetti +log + env_logger: Per logging strutturato + + +## Funzioni +### Funzioni Fondamentali: +- Chattare con altri utenti in canali per topic +- Chattare con una persona sola (DMs) +- Inviare i file tra utenti +- Possibilità di amministrare la chat con comandi di /kick o /ban +- Usa un Db SQLite per tenere le informazioni degli utenti registrati + - È possibile registarsi usando /register password che viene salvata usando SHA-256 e usare dal prossimo login il comando /login + +### Funzionalità opzionali che si potrebbero aggiungere: +- Lista utenti online per canale +- Cronologia messaggi +- Sistema di ruoli più granulare +- Notifiche di menzione (@user) + +## Protocolli e Sicurezza + +Il server utilizza TCP/IP come protocollo per la trasmissione dei dati in rete. I pacchetti sono composti da un pacchetto prestabilito + +``` + /* + Specifications of the packet + 32 bytes - Command name + 512 bytes - Command argument + if command is empty then it is a message + */ +``` + +La chat è sicura usando x25519-dalek e AES-128 per criptare i messaggi e i dati dei file che vengono inviati. Lo scambio di chiavi viene effettuato con Diffie Hellman + +Il Db è SQLite che permette di usarlo da un singolo file senza nessun problema. Per lo sviluppo sono utilizzate le mitigations cosi da aggiornare il db anche con versione vecchie del server + +```mermaid +sequenceDiagram + participant C as Client + participant S as Server + participant DB as SQLite DB + + C->>S: Connessione TCP + S->>C: Challenge DH + C->>S: Risposta DH + Note over C,S: Generazione chiavi AES-128 + + alt Registrazione + C->>S: /register [password] + Note over S: Hash SHA-256 + S->>DB: Salva utente + hash + S->>C: Conferma registrazione + else Login + C->>S: /login [password] + S->>DB: Verifica hash + S->>C: Conferma login + end + + rect rgb(200, 220, 255) + Note over C,S: Comunicazione crittografata + C->>S: Messaggi/Comandi (AES-128) + S->>C: Risposte/Broadcast (AES-128) + end +``` \ No newline at end of file diff --git a/config.toml b/config.toml index e6c4020..41ae0ce 100644 --- a/config.toml +++ b/config.toml @@ -1,2 +1,2 @@ -ip = '127.0.0.1' -port = 25565 \ No newline at end of file +address = "127.0.0.1" +port = "25565" \ No newline at end of file diff --git a/db.sqlite b/db.sqlite new file mode 100644 index 0000000..988613f Binary files /dev/null and b/db.sqlite differ diff --git a/migrations/001_create_users_table.sql b/migrations/001_create_users_table.sql new file mode 100644 index 0000000..6c6aeb2 --- /dev/null +++ b/migrations/001_create_users_table.sql @@ -0,0 +1,9 @@ +-- Create the users table +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, -- Unique ID for each user + username TEXT NOT NULL UNIQUE, -- Username, must be unique + password_hash TEXT NOT NULL -- Hashed password for security +); + +-- Create an index on the username and email columns for faster lookups +CREATE INDEX IF NOT EXISTS idx_users_username ON users (username); diff --git a/migrations/002_create_admin_flag.sql b/migrations/002_create_admin_flag.sql new file mode 100644 index 0000000..ec70f0e --- /dev/null +++ b/migrations/002_create_admin_flag.sql @@ -0,0 +1,2 @@ +ALTER TABLE users +ADD COLUMN is_admin BOOLEAN DEFAULT FALSE; diff --git a/migrations/003_create_ban_flag.sql b/migrations/003_create_ban_flag.sql new file mode 100644 index 0000000..2459b6a --- /dev/null +++ b/migrations/003_create_ban_flag.sql @@ -0,0 +1,5 @@ +ALTER TABLE users +ADD COLUMN is_banned BOOLEAN DEFAULT FALSE; + +ALTER TABLE users +ADD COLUMN ban_reason VARCHAR(255); diff --git a/migrations/004_create_kick_table.sql b/migrations/004_create_kick_table.sql new file mode 100644 index 0000000..09693ef --- /dev/null +++ b/migrations/004_create_kick_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS kick ( + id INTEGER PRIMARY KEY AUTOINCREMENT, -- Unique ID for each kick + user_name VARCHAR(255) NOT NULL -- ID of the user who made the kick +); + +-- -- Create an index on the user_name column for faster lookups +CREATE INDEX IF NOT EXISTS idx_kick_user_name ON kick (user_name); diff --git a/migrations/005_create_files_table.sql b/migrations/005_create_files_table.sql new file mode 100644 index 0000000..8dea5ac --- /dev/null +++ b/migrations/005_create_files_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS files ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + path VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_files_name ON files (name); diff --git a/migrations/006_add_admin_verified_to_files.sql b/migrations/006_add_admin_verified_to_files.sql new file mode 100644 index 0000000..e5ad31e --- /dev/null +++ b/migrations/006_add_admin_verified_to_files.sql @@ -0,0 +1,2 @@ +ALTER TABLE files +ADD COLUMN admin_verified BOOLEAN DEFAULT FALSE; diff --git a/src/client/mod.rs b/src/client/mod.rs new file mode 100644 index 0000000..cf663aa --- /dev/null +++ b/src/client/mod.rs @@ -0,0 +1,781 @@ +pub(crate) mod handlers { + use aes_gcm::{ + aead::{Aead, KeyInit, OsRng}, + Aes256Gcm, Key, Nonce, + }; + + use crate::db::users::{ + add_kick, add_new_file, add_verified_flag_to_file, ban_user, change_password, check_ban, + check_file_verified, check_for_account, check_kick, create_user, get_ban_reason, + hash_password, remove_kick, request_file, unban_user, verify_admin, verify_password, + }; + use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; + use log::{debug, error, info}; + use serde::Deserialize; + use std::sync::Arc; + use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; + use tokio::net::TcpStream; + use tokio::sync::broadcast; + use x25519_dalek::{EphemeralSecret, PublicKey}; + + /* + Specifications of the packet + 32 bytes - Command name + 512 bytes - Command argument + if command is empty then it is a message + */ + #[derive(Deserialize, Debug)] + struct Message { + command: Vec, + argument: Vec, // Changed from Vec to Vec + } + + fn parse_message(message: &str) -> Message { + let mut iter = message.split_whitespace(); + + let command: Vec = if let Some(cmd) = iter.next() { + if cmd.starts_with("/") { + vec![cmd.to_string()] + } else { + Vec::new() // Empty command means it's a regular message + } + } else { + Vec::new() + }; + + let argument: Vec = iter.map(String::from).collect(); + + Message { command, argument } + } + + pub async fn handle_client( + socket: TcpStream, + tx: broadcast::Sender, + mut rx: broadcast::Receiver, + ) -> Result<(), Box> { + let (reader, mut writer) = socket.into_split(); + let mut reader = BufReader::new(reader); + let mut line = String::new(); + + let server_secret = EphemeralSecret::random_from_rng(OsRng); + let server_public = PublicKey::from(&server_secret); + + // Send the server's public key to the client + writer.write_all(server_public.as_bytes()).await?; + + // Receive the client's public key + let mut client_public_bytes = [0u8; 32]; + reader.read_exact(&mut client_public_bytes).await?; + let client_public = PublicKey::from(client_public_bytes); + + // Compute the shared secret + let shared_secret = server_secret.diffie_hellman(&client_public); + + let key = Key::::from_slice(shared_secret.as_bytes()); + + let cipher_reader = Aes256Gcm::new(&key); + let cipher_writer = Aes256Gcm::new(&key); + let nonce_reader = Nonce::from_slice(b"unique nonce"); // 96-bits; fixed nonce + let nonce_writer = nonce_reader.clone(); + + debug!("Reciving Username"); + + // Read the username from the client + line.clear(); + reader.read_line(&mut line).await?; + let decoded = BASE64.decode(line.trim().as_bytes())?; + let decrypted = cipher_reader + .decrypt(&nonce_reader, decoded.as_ref()) + .unwrap(); + let username = Arc::new(String::from_utf8(decrypted)?); + let username_read = Arc::clone(&username); // Clone the Arc for read task + let username_write = Arc::clone(&username); // Clone the Arc for write task + info!("Username received: {}", username); + + // Check if the user already exists in the database + if check_for_account(&username).await? { + // Check if the user is banned + if check_ban(&username).await? == true { + let ban_reason_result = get_ban_reason(&username).await; + + let message: String = match ban_reason_result { + Ok(Some(reason)) => { + info!("User {} is banned, Reason: {}", username, reason); + format!("User {} is banned, Reason: {}", username, reason).to_string() + } + Ok(None) => { + info!("User {} is banned, but no reason provided", username); + format!("User {} is banned, but no reason provided", username).to_string() + } + Err(e) => { + error!("Error fetching ban reason: {}", e); + format!("You are banned").to_string(); + return Ok(()); + } + }; + + let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {}", e); + return Ok(()); + } + }; + let message = format!("{}\n", BASE64.encode(&encrypted)); + writer.write_all(message.as_bytes()).await?; + return Ok(()); + } + + info!("User {} already exists", username); + // Send a message to the client + let message = format!("User {} is registered, input your password", username); + let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {}", e); + return Ok(()); + } + }; + let message = format!("{}\n", BASE64.encode(&encrypted)); + writer.write_all(message.as_bytes()).await?; + + // Read the password from the client + line.clear(); + reader.read_line(&mut line).await?; + let decoded = BASE64.decode(line.trim().as_bytes())?; + let decrypted = cipher_reader + .decrypt(&nonce_reader, decoded.as_ref()) + .unwrap(); + // verifiy password + let password = String::from_utf8(decrypted)?; + if verify_password(&username, &password).await? == true { + info!("Password verified successfully"); + } else { + info!("Password verification failed"); + // Send an error message to the client + let message = format!("Invalid password for user {}", username); + let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {}", e); + return Ok(()); + } + }; + let message = format!("{}\n", BASE64.encode(&encrypted)); + writer.write_all(message.as_bytes()).await?; + return Ok(()); + } + } else { + // User does not exist, create a new account + // Send a message to the client + let message = format!("User {} is not registered, input your password", username); + let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {}", e); + return Ok(()); + } + }; + let message = format!("{}\n", BASE64.encode(&encrypted)); + writer.write_all(message.as_bytes()).await?; + // Read the password from the client + line.clear(); + reader.read_line(&mut line).await?; + let decoded = BASE64.decode(line.trim().as_bytes())?; + let decrypted = cipher_reader + .decrypt(&nonce_reader, decoded.as_ref()) + .unwrap(); + let password = String::from_utf8(decrypted)?; + info!("Password received"); + // Hash the password + let password_hash = hash_password(&password).await; + let password_hash = password_hash.as_str(); + info!("Password hashed successfully"); + debug!("Hash: {}", password_hash); + // Create the user in the database + create_user(&username, password_hash).await?; + } + // Send a success message to the client + let message = format!("Welcome, {}!", username); + let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {}", e); + return Ok(()); + } + }; + let message = format!("{}\n", BASE64.encode(&encrypted)); + writer.write_all(message.as_bytes()).await?; + + // Read task for receiving messages from the client + let read_task = tokio::spawn(async move { + loop { + line.clear(); + match reader.read_line(&mut line).await { + Ok(bytes_read) => { + if bytes_read == 0 { + info!("Client disconnected"); + break; + } + + let decoded = match BASE64.decode(line.trim().as_bytes()) { + Ok(decoded) => decoded, + Err(e) => { + error!("Base64 decode error: {:?}", e); + continue; + } + }; + + let decrypted = match cipher_reader.decrypt(&nonce_reader, decoded.as_ref()) + { + Ok(decrypted) => decrypted, + Err(e) => { + error!("Decryption error: {:?}", e); + continue; + } + }; + + let message = match String::from_utf8(decrypted) { + Ok(msg) => msg, + Err(e) => { + error!("UTF-8 conversion error: {:?}", e); + continue; + } + }; + + info!("Parsing message"); + + let parsed_message = parse_message(message.as_str()); + + if check_kick(&username).await.unwrap() == true { + info!("User {} is kicked", username); + let message = format!("User {} is kicked", username); + let _ = tx.send(message); + remove_kick(&username).await.unwrap(); + break; + } + + if check_ban(&username).await.unwrap() == true { + info!("User {} is banned", username); + let message = format!("User {} is banned", username); + let _ = tx.send(message); + break; + } + + // Handle commands + if !parsed_message.command.is_empty() { + match parsed_message.command[0].as_str() { + "/msg" => { + if parsed_message.argument.len() < 2 { + match tx.send("Error! Invalid /msg format".to_string()) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + let target_user = &parsed_message.argument[0]; + let msg_content = parsed_message.argument[1..].join(" "); + info!("Private message to {}: {}", target_user, msg_content); + // dm format sender|target_user message + let formatted_message = format!( + "{}|{} {}", + username_read, target_user, msg_content + ); + match tx.send(formatted_message) { + Ok(_) => info!("Private message sent successfully"), + Err(e) => { + error!("Failed to send private message: {:?}", e); + break; + } + } + } + + "/quit" => { + info!("Client requested to quit"); + break; + } + + "/kick" => { + if parsed_message.argument.is_empty() { + error!("Invalid /kick format. Usage: /kick username"); + match tx.send( + format!("Error! Invalid /kick format").to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + match verify_admin(&username_read).await { + Ok(true) => { + info!("User {} is admin", username); + let target_user = &parsed_message.argument[0]; + info!("Kicking user: {}", target_user); + add_kick(&target_user).await.unwrap(); + match tx.send(format!( + "User {} has been kicked", + target_user + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + Ok(false) => { + error!("User {} is not admin", username); + continue; + } + Err(e) => { + error!("Error verifying admin: {:?}", e); + continue; + } + } + } + + "/addfile" => { + if parsed_message.argument.is_empty() { + error!("Invalid /addfile format. Usage: /addfile filename link"); + match tx.send( + format!("Invalid /addfile format. Usage: /addfile filename link").to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + let file_name = &parsed_message.argument[0]; + let file_link = &parsed_message.argument[1]; + info!("Adding file: {}", file_name); + info!("File link: {}", file_link); + + add_new_file(&file_name, &file_link).await.unwrap(); + + match tx.send(format!("File {} has been added", file_name)) { + Ok(_) => { + info!("Error message sent to client {}", username_write) + } + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + + "/verifylink" => { + if parsed_message.argument.is_empty() { + error!("Invalid /verifylink format. Usage: /verifylink filename"); + match tx.send( + format!("Invalid /verifylink format. Usage: /verifylink filename").to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + let file_name = &parsed_message.argument[0]; + info!("Verifying link for file: {}", file_name); + + match verify_admin(&username).await { + Ok(true) => { + info!("User {} is admin", username); + add_verified_flag_to_file(file_name).await.unwrap(); + match tx.send(format!( + "File {} has been verified", + file_name + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + Ok(false) => { + error!("User {} is not admin", username); + continue; + } + Err(e) => { + error!("Error verifying admin: {:?}", e); + continue; + } + } + } + + "/requestfile" => { + if parsed_message.argument.is_empty() { + error!("Invalid /requestfile format. Usage: /requestfile filename"); + match tx.send( + format!("Invalid /requestfile format. Usage: /requestfile filename").to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + let file_name = &parsed_message.argument[0]; + info!("Requesting file: {}", file_name); + + let file_link = request_file(file_name).await.unwrap(); + + match tx.send(format!("Link for {}: {}", file_name, file_link)) + { + Ok(_) => { + info!("message sent to client {}", username_write) + } + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + + if check_file_verified(file_name).await.unwrap() == true { + match tx.send(format!("dl! {}", file_link)) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + } + + "/ban" => { + if parsed_message.argument.is_empty() { + error!("Invalid /ban format. Usage: /ban username"); + match tx + .send(format!("Error! Invalid /ban format").to_string()) + { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + match verify_admin(&username_read).await { + Ok(true) => { + info!("User {} is admin", username); + let target_user = &parsed_message.argument[0]; + info!("Banning user: {}", target_user); + match check_ban(target_user).await { + Ok(true) => { + info!("User {} is already banned", target_user); + match tx.send(format!( + "User {} is already banned", + target_user + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + Ok(false) => { + ban_user( + target_user, + "You're banned from this server.", + ) + .await + .unwrap(); + info!("User {} has been banned", target_user); + match tx.send( + format!( + "User {} has been banned", + target_user, + ) + .to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + Err(e) => { + error!("Error checking ban status: {:?}", e); + } + } + } + Ok(false) => { + error!("User {} is not admin", username); + continue; + } + Err(e) => { + error!("Error verifying admin: {:?}", e); + continue; + } + } + } + + "/unban" => { + if parsed_message.argument.is_empty() { + error!("Invalid /unban format. Usage: /unban username"); + } + + match verify_admin(&username_read).await { + Ok(true) => { + info!("User {} is admin", username); + let target_user = &parsed_message.argument[0]; + info!("Unbanning user: {}", target_user); + match check_ban(target_user).await { + Ok(true) => { + info!("User {} is banned", target_user); + unban_user(target_user).await.unwrap(); + info!("User {} has been unbanned", target_user); + match tx.send( + format!( + "User {} has been unbanned", + target_user, + ) + .to_string(), + ) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + Ok(false) => { + info!("User {} is not banned", target_user); + match tx.send(format!( + "User {} is not banned", + target_user + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + Err(e) => { + error!("Error checking ban status: {:?}", e); + } + } + } + Ok(false) => { + error!("User {} is not admin", username); + continue; + } + Err(e) => { + error!("Error verifying admin: {:?}", e); + continue; + } + } + } + + "/changepassword" => { + if parsed_message.argument.len() < 2 { + error!("Invalid /changepassword format. Usage: /changepassword old_password new_password"); + match tx + .send(format!("Invalid /changepassword format. Usage: /changepassword old_password new_password")) + { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + + let old_password = &parsed_message.argument[0]; + let new_password = &parsed_message.argument[1]; + + info!("Changing password for user {}", username); + info!("new password: {}", new_password); + info!("old password: {}", old_password); + + if verify_password(old_password, &username).await.is_ok() { + match change_password(&username, new_password).await { + Ok(_) => { + info!("Password changed successfully"); + let _ = tx.send( + "Password changed successfully".to_string(), + ); + } + Err(e) => { + error!("Error changing password: {:?}", e); + match tx.send(format!( + "Error changing password: {:?}", + e + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!( + "Failed to send error message: {:?}", + e + ); + break; + } + } + continue; + } + } + } else { + info!("Old password verification failed"); + match tx.send(format!( + "Invalid old password for user {}", + username + )) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + } + + _ => { + error!("Unknown command: {}", parsed_message.command[0]); + match tx.send("Error! Unknown command".to_string()) { + Ok(_) => { + info!("Error message sent to client {}", username_write) + } + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + } + } + } else { + // Regular message handling + info!( + "Received message from {}: {}", + username_read, + parsed_message.argument.join(" ") + ); + + let formatted_message = + format!("{}: {}", username_read, message.trim()); + + // Broadcast the message to all clients + match tx.send(formatted_message) { + Ok(_) => info!("Message broadcast successfully"), + Err(e) => { + error!("Failed to broadcast message: {:?}", e); + break; + } + } + } + } + Err(e) => { + error!("Error reading from client: {:?}", e); + break; + } + } + } + }); + + // Write task for sending messages to the client + let write_task = tokio::spawn(async move { + while let Ok(msg) = rx.recv().await { + if !msg.is_empty() { + // Encrypt the message with error handling + let encrypted = match cipher_writer.encrypt(&nonce_writer, msg.as_bytes()) { + Ok(encrypted) => encrypted, + Err(e) => { + error!("Encryption error: {:?}", e); + continue; + } + }; + + // Base64 encode and format with newline + let message = format!("{}\n", BASE64.encode(&encrypted)); + + // Write with proper error handling + if let Err(e) = writer.write_all(message.as_bytes()).await { + error!("Failed to send message: {:?}", e); + break; + } + } + } + }); + + // Wait for both tasks to complete + tokio::select! { + _ = read_task => (), + _ = write_task => (), + } + + info!("Client handling completed"); + Ok(()) + } +} diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..79f4646 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,392 @@ +pub(crate) mod users { + use argon2::{ + password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Argon2, + }; + use log::info; + use sqlx::{sqlite::SqlitePool, Row}; + use thiserror::Error; + + #[derive(Error, Debug)] + pub enum DbError { + #[error("Database error: {0}")] + Database(#[from] sqlx::Error), + #[error("Password hashing error: {0}")] + Hashing(argon2::password_hash::Error), + #[error("User not found")] + UserNotFound, + } + + pub async fn connect_to_db() -> Result { + let pool = SqlitePool::connect("sqlite:./db.sqlite").await?; + Ok(pool) + } + + pub async fn create_db_pool() -> Result { + let pool = connect_to_db().await?; + sqlx::migrate!("./migrations").run(&pool).await?; + Ok(pool) + } + + pub async fn check_for_account(username: &str) -> Result { + // Fixed error type + let pool = create_db_pool().await?; + + let exists = sqlx::query( + r#" + SELECT EXISTS( + SELECT 1 + FROM users + WHERE username = ? + ) + "#, + ) + .bind(username) + .fetch_one(&pool) + .await? + .get::(0); + + Ok(exists == 1) + } + + pub async fn create_user(username: &str, password_hash: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + sqlx::query( + r#" + INSERT INTO users (username, password_hash) + VALUES (?, ?) + "#, + ) + .bind(username) + .bind(password_hash) + .execute(&pool) + .await?; + Ok(()) + } + + pub async fn hash_password(password: &str) -> String { + let salt = SaltString::generate(&mut rand::thread_rng()); + let argon2 = Argon2::default(); + let password_hash = argon2 + .hash_password(password.as_bytes(), &salt) + .expect("Failed to hash password"); + password_hash.to_string() + } + + pub async fn check_ban(username: &str) -> Result { + let pool = create_db_pool().await?; + + let is_banned = sqlx::query( + r#" + SELECT EXISTS( + SELECT 1 + FROM users + WHERE username = ? AND is_banned = 1 + ) + "#, + ) + .bind(username) + .fetch_one(&pool) + .await? + .get::(0); + + // Check if the user is banned + if is_banned == 1 { + info!("User {} is banned", username); + } else { + info!("User {} is not banned", username); + } + + Ok(is_banned == 1) + } + + pub async fn get_ban_reason(username: &str) -> Result, sqlx::Error> { + let pool = create_db_pool().await?; + info!("Attempting to fetch ban reason for user: {}", username); + + let row_option = sqlx::query( + r#" + SELECT ban_reason + FROM users + WHERE username = ? + "#, + ) + .bind(username) + .fetch_optional(&pool) + .await?; + + // Process the result + match row_option { + Some(row) => { + // Row found, now get the ban_reason (which might be NULL) + let reason: Option = row.get(0); // Type annotation clarifies intent + if let Some(ref r) = reason { + info!("User {} found. Ban reason: {}", username, r); + } else { + // User exists, but ban_reason is NULL in the database + info!( + "User {} found, but ban_reason is NULL (not banned)", + username + ); + } + Ok(reason) + } + None => { + // No row found for the username + info!("User {} not found in the database", username); + // Return Ok(None) as per the function signature, indicating no ban reason found + // because the user doesn't exist. + Ok(None) + } + } + } + + pub async fn ban_user(username: &str, ban_reason: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + // Use a single query to update the user + sqlx::query( + r#" + UPDATE users + SET is_banned = 1, ban_reason = ? + WHERE username = ? + "#, + ) + .bind(ban_reason) + .bind(username) + .execute(&pool) + .await?; + + Ok(()) + } + + pub async fn unban_user(username: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + // Use a single query to update the user + sqlx::query( + r#" + UPDATE users + SET is_banned = 0, ban_reason = NULL + WHERE username = ? + "#, + ) + .bind(username) + .execute(&pool) + .await?; + + Ok(()) + } + + pub async fn change_password(username: &str, new_password: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + // Hash the new password + let new_password_hash = hash_password(new_password).await; + + // Update the password in the database + sqlx::query( + r#" + UPDATE users + SET password_hash = ? + WHERE username = ? + "#, + ) + .bind(new_password_hash) + .bind(username) + .execute(&pool) + .await?; + + Ok(()) + } + + pub async fn verify_password(username: &str, provided_password: &str) -> Result { + let pool = create_db_pool().await?; // Propagates sqlx::Error + + // Fetch the stored hash for the user + let user_row = sqlx::query( + r#" + SELECT password_hash + FROM users + WHERE username = ? + "#, + ) + .bind(username) + .fetch_optional(&pool) // Use fetch_optional to handle not found case + .await?; + + // Get the stored hash string or return error if user not found + let stored_hash_str = match user_row { + Some(row) => row.get::(0), + None => return Err(DbError::UserNotFound), + }; + + // Parse the stored hash + let parsed_hash = PasswordHash::new(&stored_hash_str).map_err(DbError::Hashing)?; + let argon2 = Argon2::default(); + + let verification_result = + argon2.verify_password(provided_password.as_bytes(), &parsed_hash); + + // Check the result and return true/false accordingly + match verification_result { + Ok(()) => { + info!("Password check successful for user: {}", username); + Ok(true) + } + Err(argon2::password_hash::Error::Password) => { + info!("Password check failed (mismatch) for user: {}", username); + Ok(false) + } + Err(e) => { + // Handle other potential argon2 errors (e.g., invalid hash format) + info!( + "Password check failed for user {} with error: {}", + username, e + ); + Err(DbError::Hashing(e)) + } + } + } + + pub async fn verify_admin(username: &str) -> Result { + let pool = create_db_pool().await?; + + let is_admin = sqlx::query( + r#" + SELECT EXISTS( + SELECT 1 + FROM users + WHERE username = ? AND is_admin = 1 + ) + "#, + ) + .bind(username) + .fetch_one(&pool) + .await? + .get::(0); + + Ok(is_admin == 1) + } + + pub async fn add_kick(username: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + sqlx::query( + r#" + INSERT INTO kick (user_name) + VALUES (?) + "#, + ) + .bind(username) + .execute(&pool) + .await?; + Ok(()) + } + + pub async fn remove_kick(username: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + sqlx::query( + r#" + DELETE FROM kick + WHERE user_name = ? + "#, + ) + .bind(username) + .execute(&pool) + .await?; + Ok(()) + } + + pub async fn check_kick(username: &str) -> Result { + let pool = create_db_pool().await?; + + let exists = sqlx::query( + r#" + SELECT EXISTS( + SELECT 1 + FROM kick + WHERE user_name = ? + ) + "#, + ) + .bind(username) + .fetch_one(&pool) + .await? + .get::(0); + + Ok(exists == 1) + } + + pub async fn add_new_file(name: &str, link: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + sqlx::query( + r#" + INSERT INTO files (name, path) + VALUES (?, ?) + "#, + ) + .bind(name) + .bind(link) + .execute(&pool) + .await?; + Ok(()) + } + + pub async fn request_file(name: &str) -> Result { + let pool = create_db_pool().await?; + + let file_path = sqlx::query( + r#" + SELECT path + FROM files + WHERE name = ? + "#, + ) + .bind(name) + .fetch_one(&pool) + .await? + .get::(0); + + Ok(file_path) + } + + pub async fn add_verified_flag_to_file(name: &str) -> Result<(), sqlx::Error> { + let pool = create_db_pool().await?; + + sqlx::query( + r#" + UPDATE files + SET admin_verified = 1 + WHERE name = ? + "#, + ) + .bind(name) + .execute(&pool) + .await?; + Ok(()) + } + + pub async fn check_file_verified(name: &str) -> Result { + let pool = create_db_pool().await?; + + let is_verified = sqlx::query( + r#" + SELECT EXISTS( + SELECT 1 + FROM files + WHERE name = ? AND admin_verified = 1 + ) + "#, + ) + .bind(name) + .fetch_one(&pool) + .await? + .get::(0); + + Ok(is_verified == 1) + } +} diff --git a/src/main.rs b/src/main.rs index badf434..1605e11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,510 +1,108 @@ -use aes_gcm::{ - aead::{Aead, KeyInit, OsRng}, - Aes256Gcm, Key, Nonce, -}; -use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; -use colog; -use log::{debug, error, info, warn}; -use notify_rust::Notification; +mod client; +mod db; +mod tui; + +use client::handlers::handle_client; +use db::users::create_db_pool; +use log::{error, info, Level, Log, Metadata, Record}; use serde::Deserialize; -use std::{ - fs, - fs::File, - io, - sync::{Arc, Mutex}, -}; -use tokio::{ - io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}, - net::TcpStream, - sync::mpsc, - time::{sleep, Duration}, -}; -use x25519_dalek::{EphemeralSecret, PublicKey}; -// Ratatui imports -use crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; -use ratatui::{ - backend::CrosstermBackend, - layout::{Constraint, Direction, Layout, Position}, - style::{Color, Modifier, Style}, - text::{Line, Span}, - widgets::{Block, Borders, List, ListItem, Paragraph}, - Terminal, -}; +use std::thread; +use std::{process::exit, sync::mpsc}; +use tokio::net::TcpListener; +use tokio::sync::broadcast; +use tui::{run_app, LogEntry}; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct Config { - ip: String, - port: Option, + address: String, + port: String, } -// UI structs and enums -enum InputMode { - Normal, - Editing, +struct CustomLogger { + tx: mpsc::Sender, } -struct ChatState { - input: String, - messages: Vec<(String, String)>, // (username, message) - input_mode: InputMode, - username: String, - should_quit: bool, -} +impl Log for CustomLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= Level::Info + } -impl ChatState { - fn new(username: String) -> Self { - ChatState { - input: String::new(), - messages: Vec::new(), - input_mode: InputMode::Editing, - username, - should_quit: false, + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let now = chrono::Local::now(); + let log_entry = LogEntry { + timestamp: now.format("%H:%M:%S").to_string(), + level: record.level().to_string(), + message: record.args().to_string(), + }; + + let _ = self.tx.send(log_entry); } } + + fn flush(&self) {} } #[tokio::main] -async fn main() -> io::Result<()> { - let original_hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |panic_info| { - // Restore terminal - let _ = disable_raw_mode(); - let mut stdout = io::stdout(); - let _ = execute!(stdout, LeaveAlternateScreen, DisableMouseCapture); +async fn main() { + create_db_pool().await.unwrap(); - // Call the original hook - original_hook(panic_info); - })); + // Create a channel for logging + let (tx, rx) = mpsc::channel(); - colog::init(); + // Create and set the custom logger + let logger = Box::new(CustomLogger { tx }); + log::set_boxed_logger(logger).unwrap(); + log::set_max_level(log::LevelFilter::Info); - let contents = - fs::read_to_string("config.toml").expect("Should have been able to read the file"); - let config: Config = - toml::from_str(&contents).expect("Should have been able to parse the file"); + // Start the TUI in a separate thread + let _tui_handle = thread::spawn(move || { + if let Err(e) = run_app(rx) { + eprintln!("Error running TUI: {:?}", e); + } - info!("Enter your username (or press Enter to use a random one): "); - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let username = if input.trim().is_empty() { - format!("User{}", rand::random::()) - } else { - input.trim().to_string() + // Exit the process when the TUI closes + exit(0); + }); + + // Load the configuration from config file + let config = match std::fs::read_to_string("config.toml") { + Ok(config) => match toml::from_str::(&config) { + Ok(config) => config, + Err(e) => { + error!("Failed to parse config file: {:?}", e); + std::process::exit(1); + } + }, + Err(e) => { + error!("Failed to read config file: {:?}", e); + std::process::exit(1); + } }; - info!("Username: {}", username); + info!("Configuration loaded: {:?}", config); - let port = config.port.unwrap_or(8080); - info!("Connecting to server at {}:{}", config.ip, port); - - // Connect to the server - let stream = TcpStream::connect(format!("{}:{}", config.ip, port)) + // Bind a TCP listener to accept incoming connections + let listener = TcpListener::bind(config.address + ":" + config.port.as_str()) .await .unwrap(); + info!("Server running on port {}", config.port); - let (reader, mut writer) = stream.into_split(); - let mut reader = BufReader::new(reader); + // Create a broadcast channel for sharing messages + let (tx, _) = broadcast::channel(100); + loop { + // Accept a new client + let (socket, addr) = listener.accept().await.unwrap(); + info!("Client connected: {}", addr); - info!("Generating the Keys"); + let tx = tx.clone(); + let rx = tx.subscribe(); - let client_secret = EphemeralSecret::random_from_rng(OsRng); - let client_public = PublicKey::from(&client_secret); - - writer.write_all(client_public.as_bytes()).await.unwrap(); - - let mut server_public_bytes = [0u8; 32]; - reader.read_exact(&mut server_public_bytes).await.unwrap(); - - let server_public = PublicKey::from(server_public_bytes); - let shared_secret = client_secret.diffie_hellman(&server_public); - - info!("Shared Secret: {:?}", shared_secret.as_bytes()); - info!("Server public key: {:?}", server_public.as_bytes()); - - let key = Key::::from_slice(shared_secret.as_bytes()); - let cipher_reader = Aes256Gcm::new(&key); - let cipher_writer = Aes256Gcm::new(&key); - let nonce_reader = Nonce::from_slice(b"unique nonce"); // 96-bits; fixed nonce - let nonce_writer = nonce_reader.clone(); - - warn!("Nonce: {:?}", nonce_reader); - - debug!("Sending Username"); - - let encrypted = match cipher_writer.encrypt(&nonce_writer, username.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - - let encoded = BASE64.encode(&encrypted); - - if let Err(e) = writer.write_all((encoded + "\n").as_bytes()).await { - error!("Failed to send username: {}", e); - return Ok(()); + // Handle the client in a new task + tokio::spawn(async move { + if let Err(e) = handle_client(socket, tx, rx).await { + error!("Error handling client: {}", e); + } + }); } - - info!("Starting the chat"); - - // Setup UI - enable_raw_mode()?; - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - - // Setup channels for communication - let (tx_ui, mut rx_ui) = mpsc::channel::<(String, String)>(100); - let (tx_net, mut rx_net) = mpsc::channel::(100); - - // Create shared state - let chat_state = Arc::new(Mutex::new(ChatState::new(username.clone()))); - let chat_state_ui = Arc::clone(&chat_state); - - // Task for UI handling - let ui_task = tokio::spawn(async move { - let mut chat_state = chat_state_ui; - - loop { - let should_quit = { - let state = chat_state.lock().unwrap(); - state.should_quit - }; - - if should_quit { - break; - } - - // Check for new messages from network - if let Ok(msg) = rx_ui.try_recv() { - let mut state = chat_state.lock().unwrap(); - state.messages.push(msg); - } - - // Handle input events - if let Ok(should_break) = ui_loop(&mut terminal, &mut chat_state, &tx_net) { - if should_break { - break; - } - } - - sleep(Duration::from_millis(16)).await; // ~60 fps refresh rate - } - if let Err(e) = disable_raw_mode() { - error!("Failed to disable raw mode: {}", e); - } - - if let Err(e) = execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - ) { - error!("Failed to leave alternate screen: {}", e); - } - - if let Err(e) = terminal.show_cursor() { - error!("Failed to show cursor: {}", e); - } - }); - - // Task for sending messages to the server - let send_task = tokio::spawn(async move { - while let Some(input) = rx_net.recv().await { - // Encrypt the input - let encrypted = match cipher_writer.encrypt(&nonce_writer, input.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - continue; - } - }; - - let encoded = BASE64.encode(&encrypted); - - if let Err(e) = writer.write_all((encoded + "\n").as_bytes()).await { - error!("Failed to send message: {}", e); - break; - } - } - }); - - // Task for receiving messages from the server - let receive_task = tokio::spawn(async move { - let mut line = String::new(); - loop { - line.clear(); - match reader.read_line(&mut line).await { - Ok(0) => { - // Server closed connection - info!("\nServer disconnected"); - tx_ui - .send(("System".to_string(), "Server disconnected".to_string())) - .await - .ok(); - break; - } - Ok(_) => { - let decoded = match BASE64.decode(line.trim()) { - Ok(decoded) => decoded, - Err(e) => { - error!("Base64 decode error: {}", e); - continue; - } - }; - - let decrypted = match cipher_reader.decrypt(&nonce_reader, &*decoded) { - Ok(decrypted) => decrypted, - Err(e) => { - error!("Decryption error: {}", e); - continue; - } - }; - - let message = match String::from_utf8(decrypted) { - Ok(msg) => msg, - Err(e) => { - error!("UTF-8 conversion error: {}", e); - continue; - } - }; - - if message.contains('|') { - // Handle DM format - let parts: Vec<&str> = message.splitn(2, '|').collect(); - if parts.len() == 2 { - let sender = parts[0].trim(); - // The second part contains both receiver and message - let receiver_and_message = parts[1].trim(); - // Split at the first space to separate receiver from message - if let Some(space_pos) = receiver_and_message.find(' ') { - let (receiver, content) = receiver_and_message.split_at(space_pos); - if receiver != username { - // If the receiver is the same as the client, ignore - continue; - } - - let content = content.trim_start(); - - // Style as DM - let dm_label = if sender == &username { - format!("DM to {}: ", receiver) - } else { - format!("DM from {}: ", sender) - }; - - tx_ui - .send(("DM".to_string(), format!("{}{}", dm_label, content))) - .await - .ok(); - } - } - } else if message.contains("dl!") { - // Handle file download request - let parts: Vec<&str> = message.splitn(2, ' ').collect(); - if parts.len() == 2 { - let filename = parts[1].trim(); - tx_ui - .send(( - "System".to_string(), - format!("Download request for file: {}", filename), - )) - .await - .ok(); - let resp = reqwest::get(filename).await.expect("request failed"); - let body = resp.bytes().await.expect("body invalid"); - // get the file name from the end of the link - let filename = filename.split('/').last().unwrap_or("file"); - // Create the file - let mut out = File::create(filename).expect("failed to create file"); - let body_bytes = body.to_vec(); - io::copy(&mut &body_bytes[..], &mut out) - .expect("failed to copy content"); - tx_ui - .send(( - "System".to_string(), - format!("Download completed, {}", filename), - )) - .await - .ok(); - } - } else if let Some(pos) = message.find(':') { - let (sender, content) = message.split_at(pos); - if sender == username { - // If the sender is the same as the client, ignore - continue; - } - - // if the message contains a @username, highlight it - if content.contains(&username) { - // send the message in chat - - Notification::new() - .summary("You got tagged in a message") - .body(&format!("{}{}", sender, content)) - .show() - .unwrap(); - } - - // Skip the colon and any space - let content = content.trim_start_matches(|c| c == ':' || c == ' '); - tx_ui - .send((sender.to_string(), content.to_string())) - .await - .ok(); - } else { - // If message format is different, treat as system message - tx_ui.send(("System".to_string(), message)).await.ok(); - } - } - Err(e) => { - error!("Error reading from server: {}", e); - tx_ui - .send(("System".to_string(), format!("Error: {}", e))) - .await - .ok(); - break; - } - } - } - }); - - // Wait for tasks to complete - tokio::select! { - _ = ui_task => (), - _ = send_task => (), - _ = receive_task => (), - } - - info!("Client exiting"); - Ok(()) -} - -// UI rendering function -fn ui_loop( - terminal: &mut Terminal, - chat_state: &mut Arc>, - tx_net: &mpsc::Sender, -) -> io::Result { - terminal.draw(|f| { - let size = f.area(); - - // Create layout with chat messages on top and input at bottom - let chunks = Layout::default() - .direction(Direction::Vertical) - .margin(1) - .constraints([Constraint::Min(3), Constraint::Length(3)]) - .split(size); - - let state = chat_state.lock().unwrap(); - - // Create messages list - let messages: Vec = state - .messages - .iter() - .map(|(username, message)| { - let username_style = if username == &state.username { - Style::default().fg(Color::Green) - } else if username == "System" { - Style::default().fg(Color::Yellow) - } else if username == "DM" { - Style::default() - .fg(Color::Magenta) - .add_modifier(Modifier::BOLD) - } else { - Style::default().fg(Color::Blue) - }; - ListItem::new(Line::from(vec![ - Span::styled(format!("{}: ", username), username_style), - Span::raw(message), - ])) - }) - .collect(); - - let messages = - List::new(messages).block(Block::default().borders(Borders::ALL).title("Messages")); - - // Input box - let input = Paragraph::new(state.input.as_str()) - .style(match state.input_mode { - InputMode::Normal => Style::default(), - InputMode::Editing => Style::default().fg(Color::Yellow), - }) - .block(Block::default().borders(Borders::ALL).title("Input")); - - f.render_widget(messages, chunks[0]); - f.render_widget(input, chunks[1]); - - // Set cursor position - match state.input_mode { - InputMode::Normal => {} - InputMode::Editing => { - f.set_cursor_position(Position::new( - chunks[1].x + 1 + state.input.len() as u16, - chunks[1].y + 1, - )); - } - } - })?; - - // Handle events - if event::poll(Duration::from_millis(10))? { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - let mut state = chat_state.lock().unwrap(); - - match state.input_mode { - InputMode::Normal => match key.code { - KeyCode::Char('e') => { - state.input_mode = InputMode::Editing; - } - KeyCode::Char('q') => { - state.should_quit = true; - tx_net.try_send("/quit".to_string()).ok(); - return Ok(true); - } - _ => {} - }, - InputMode::Editing => match key.code { - KeyCode::Enter => { - let message = state.input.drain(..).collect::(); - if !message.is_empty() { - drop(state); // Release mutex before async operation - - // Add message to UI - let username_clone = { - let state = chat_state.lock().unwrap(); - state.username.clone() - }; - let mut state = chat_state.lock().unwrap(); - state - .messages - .push((username_clone.clone(), message.clone())); - - // Send to network - tx_net.try_send(message).ok(); - } - } - KeyCode::Char(c) => { - state.input.push(c); - } - KeyCode::Backspace => { - state.input.pop(); - } - KeyCode::Esc => { - state.input_mode = InputMode::Normal; - } - _ => {} - }, - } - } - } - } - - Ok(false) } diff --git a/src/tui/mod.rs b/src/tui/mod.rs new file mode 100644 index 0000000..64823c1 --- /dev/null +++ b/src/tui/mod.rs @@ -0,0 +1,138 @@ +use crossterm::{ + event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; +use ratatui::{ + backend::CrosstermBackend, + layout::{Constraint, Direction, Layout}, + style::{Color, Modifier, Style}, + text::{Line, Span}, + widgets::{Block, Borders, List, ListItem}, + Terminal, +}; +use std::{ + collections::VecDeque, + sync::mpsc, + time::{Duration, Instant}, +}; + +pub struct LogEntry { + pub timestamp: String, + pub level: String, + pub message: String, +} + +pub struct App { + logs: VecDeque, + should_quit: bool, +} + +impl App { + pub fn new() -> App { + App { + logs: VecDeque::with_capacity(100), + should_quit: false, + } + } + + pub fn add_log(&mut self, entry: LogEntry) { + if self.logs.len() >= 100 { + self.logs.pop_front(); + } + self.logs.push_back(entry); + } +} + +pub fn run_app(rx: mpsc::Receiver) -> Result<(), Box> { + // Terminal initialization + enable_raw_mode()?; + let mut stdout = std::io::stdout(); + execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + let mut app = App::new(); + let mut last_tick = Instant::now(); + let tick_rate = Duration::from_millis(250); + + loop { + terminal.draw(|f| { + let chunks = Layout::default() + .direction(Direction::Vertical) + .margin(1) + .constraints( + [ + Constraint::Min(0), // Logs + ] + .as_ref(), + ) + .split(f.area()); + + // Logs + let logs: Vec = app + .logs + .iter() + .map(|log| { + let color = match log.level.as_str() { + "ERROR" => Color::Red, + "WARN" => Color::Yellow, + "INFO" => Color::Blue, + "DEBUG" => Color::Green, + _ => Color::White, + }; + + ListItem::new(Line::from(vec![ + Span::styled(&log.timestamp, Style::default().fg(Color::DarkGray)), + Span::raw(" "), + Span::styled( + format!("[{}]", log.level), + Style::default().fg(color).add_modifier(Modifier::BOLD), + ), + Span::raw(" "), + Span::raw(&log.message), + ])) + }) + .collect(); + + let logs = + List::new(logs).block(Block::default().borders(Borders::ALL).title("Server Logs")); + f.render_widget(logs, chunks[0]); + })?; + + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or_else(|| Duration::from_secs(0)); + + if crossterm::event::poll(timeout)? { + if let Event::Key(key) = event::read()? { + if let KeyCode::Char('q') = key.code { + app.should_quit = true; + } + } + } + + if last_tick.elapsed() >= tick_rate { + // Check for new log entries + if let Ok(log_entry) = rx.try_recv() { + app.add_log(log_entry); + } + last_tick = Instant::now(); + } + + if app.should_quit { + break; + } + } + + // Restore terminal + disable_raw_mode()?; + execute!( + terminal.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + )?; + terminal.show_cursor()?; + + Ok(()) +}