Compare commits

...

8 Commits

Author SHA1 Message Date
JustTestingV 9582160d2c xmpp: logopass auth (#19)
Reviewed-on: lavina/lavina#19
Co-authored-by: JustTestingV <JustTestingV@gmail.com>
Co-committed-by: JustTestingV <JustTestingV@gmail.com>
2023-10-08 13:53:39 +00:00
Nikita Vilunov 3ff8c98f14 update dependencies 2023-10-07 18:08:27 +02:00
Nikita Vilunov 1a1136187e sanitize IRC parsing (#23) 2023-10-04 18:27:43 +00:00
Nikita Vilunov 0b98102580 add author id in messages schema 2023-10-04 18:32:08 +02:00
Nikita Vilunov 1373767d7f xmpp: add muc proto tests 2023-10-04 15:55:34 +02:00
Nikita Vilunov 887fd95194 xmpp: fix parsing of unknown elements in messages 2023-10-03 00:17:48 +02:00
Nikita Vilunov 4621470bde fix graceful shutdown 2023-10-02 23:35:23 +02:00
Nikita Vilunov 9fca913430 xmpp: fix parsing of unknown elements in messages (#20)
Reviewed-on: lavina/lavina#20
2023-10-01 23:16:25 +00:00
19 changed files with 607 additions and 314 deletions

346
Cargo.lock generated
View File

@ -31,9 +31,9 @@ dependencies = [
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.0.4" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.5.0" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -60,15 +60,15 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
@ -84,9 +84,9 @@ dependencies = [
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "2.1.0" version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys", "windows-sys",
@ -115,9 +115,12 @@ dependencies = [
[[package]] [[package]]
name = "atomic" name = "atomic"
version = "0.5.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
@ -142,9 +145,9 @@ dependencies = [
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.3" version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@ -178,21 +181,27 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.13.0" version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]] [[package]]
name = "cc" name = "cc"
@ -211,9 +220,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.4" version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -221,9 +230,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.4" version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -240,7 +249,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -267,6 +276,22 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.9" version = "0.2.9"
@ -388,9 +413,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.2" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480"
dependencies = [ dependencies = [
"errno-dragonfly", "errno-dragonfly",
"libc", "libc",
@ -426,15 +451,15 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.0" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]] [[package]]
name = "figment" name = "figment"
version = "0.10.10" version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5" checksum = "a014ac935975a70ad13a3bff2463b1c1b083b35ae4cb6309cfc59476aa7a181f"
dependencies = [ dependencies = [
"atomic", "atomic",
"pear", "pear",
@ -445,14 +470,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "flume" name = "finl_unicode"
version = "0.10.14" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
[[package]]
name = "flume"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"pin-project",
"spin 0.9.8", "spin 0.9.8",
] ]
@ -523,7 +553,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -609,9 +639,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.0" version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
dependencies = [ dependencies = [
"ahash", "ahash",
"allocator-api2", "allocator-api2",
@ -619,11 +649,11 @@ dependencies = [
[[package]] [[package]]
name = "hashlink" name = "hashlink"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [ dependencies = [
"hashbrown 0.14.0", "hashbrown 0.14.1",
] ]
[[package]] [[package]]
@ -637,9 +667,9 @@ dependencies = [
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.2" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]] [[package]]
name = "hex" name = "hex"
@ -798,12 +828,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.0", "hashbrown 0.14.1",
] ]
[[package]] [[package]]
@ -820,9 +850,9 @@ checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [ dependencies = [
"either", "either",
] ]
@ -892,15 +922,15 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]] [[package]]
name = "libm" name = "libm"
version = "0.2.7" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
@ -915,9 +945,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.5" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -937,18 +967,19 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]] [[package]]
name = "md-5" name = "md-5"
version = "0.10.5" version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [ dependencies = [
"cfg-if",
"digest", "digest",
] ]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]] [[package]]
name = "mgmt-api" name = "mgmt-api"
@ -1075,9 +1106,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.32.0" version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -1143,7 +1174,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -1161,26 +1192,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pin-project"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.13" version = "0.2.13"
@ -1228,9 +1239,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.66" version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1243,7 +1254,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
"version_check", "version_check",
"yansi", "yansi",
] ]
@ -1313,6 +1324,8 @@ name = "proto-xmpp"
version = "0.0.1-dev" version = "0.0.1-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_matches",
"base64",
"derive_more", "derive_more",
"lazy_static", "lazy_static",
"quick-xml", "quick-xml",
@ -1380,9 +1393,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.9.4" version = "1.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -1392,9 +1405,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.3.7" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -1409,9 +1422,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.20" version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -1432,6 +1445,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"url", "url",
@ -1495,9 +1509,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.9" version = "0.38.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7"
dependencies = [ dependencies = [
"bitflags 2.4.0", "bitflags 2.4.0",
"errno", "errno",
@ -1508,9 +1522,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.21.6" version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
dependencies = [ dependencies = [
"log", "log",
"ring", "ring",
@ -1529,9 +1543,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.101.4" version = "0.101.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe"
dependencies = [ dependencies = [
"ring", "ring",
"untrusted", "untrusted",
@ -1561,9 +1575,9 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.18" version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
[[package]] [[package]]
name = "serde" name = "serde"
@ -1582,14 +1596,14 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.105" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1619,9 +1633,9 @@ dependencies = [
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.5" version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@ -1630,9 +1644,9 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.7" version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@ -1641,9 +1655,9 @@ dependencies = [
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.4" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
] ]
@ -1678,9 +1692,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.0" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]] [[package]]
name = "socket2" name = "socket2"
@ -1694,9 +1708,9 @@ dependencies = [
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.3" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys",
@ -1729,9 +1743,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlformat" name = "sqlformat"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
dependencies = [ dependencies = [
"itertools", "itertools",
"nom", "nom",
@ -1740,9 +1754,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e58421b6bc416714d5115a2ca953718f6c621a51b68e4f4922aea5a4391a721" checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
dependencies = [ dependencies = [
"sqlx-core", "sqlx-core",
"sqlx-macros", "sqlx-macros",
@ -1753,9 +1767,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-core" name = "sqlx-core"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4cef4251aabbae751a3710927945901ee1d97ee96d757f6880ebb9a79bfd53" checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
dependencies = [ dependencies = [
"ahash", "ahash",
"atoi", "atoi",
@ -1773,7 +1787,7 @@ dependencies = [
"futures-util", "futures-util",
"hashlink", "hashlink",
"hex", "hex",
"indexmap 2.0.0", "indexmap 2.0.2",
"log", "log",
"memchr", "memchr",
"once_cell", "once_cell",
@ -1791,9 +1805,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros" name = "sqlx-macros"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "208e3165167afd7f3881b16c1ef3f2af69fa75980897aac8874a0696516d12c2" checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1804,9 +1818,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros-core" name = "sqlx-macros-core"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a4a8336d278c62231d87f24e8a7a74898156e34c1c18942857be2acb29c7dfc" checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"either", "either",
@ -1828,9 +1842,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-mysql" name = "sqlx-mysql"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482" checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64",
@ -1870,9 +1884,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-postgres" name = "sqlx-postgres"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e" checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64",
@ -1909,9 +1923,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-sqlite" name = "sqlx-sqlite"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2" checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f"
dependencies = [ dependencies = [
"atoi", "atoi",
"flume", "flume",
@ -1931,10 +1945,11 @@ dependencies = [
[[package]] [[package]]
name = "stringprep" name = "stringprep"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6"
dependencies = [ dependencies = [
"finl_unicode",
"unicode-bidi", "unicode-bidi",
"unicode-normalization", "unicode-normalization",
] ]
@ -1964,15 +1979,36 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.29" version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.8.0" version = "3.8.0"
@ -1988,22 +2024,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.47" version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.47" version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -2045,7 +2081,7 @@ dependencies = [
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2 0.5.3", "socket2 0.5.4",
"tokio-macros", "tokio-macros",
"windows-sys", "windows-sys",
] ]
@ -2058,7 +2094,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -2073,9 +2109,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.8" version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@ -2087,9 +2123,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.7.6" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
@ -2108,11 +2144,11 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.19.14" version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [ dependencies = [
"indexmap 2.0.0", "indexmap 2.0.2",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
@ -2146,7 +2182,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
] ]
[[package]] [[package]]
@ -2192,9 +2228,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.16.0" version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "uncased" name = "uncased"
@ -2213,9 +2249,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.11" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@ -2324,7 +2360,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2358,7 +2394,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.38",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2475,9 +2511,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.15" version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View File

@ -26,6 +26,7 @@ clap = { version = "4.4.4", features = ["derive"] }
serde = { version = "1.0.152", features = ["rc", "serde_derive"] } serde = { version = "1.0.152", features = ["rc", "serde_derive"] }
tracing = "0.1.37" # logging & tracing api tracing = "0.1.37" # logging & tracing api
prometheus = { version = "0.13.3", default-features = false } prometheus = { version = "0.13.3", default-features = false }
base64 = "0.21.3"
lavina-core = { path = "crates/lavina-core" } lavina-core = { path = "crates/lavina-core" }
[package] [package]

View File

@ -0,0 +1 @@
alter table messages add author_id integer null references users(id);

View File

@ -155,6 +155,7 @@ enum PlayerCommand {
GetRooms(Promise<Vec<RoomInfo>>), GetRooms(Promise<Vec<RoomInfo>>),
/** Events from rooms */ /** Events from rooms */
Update(Updates), Update(Updates),
Stop,
} }
pub enum Cmd { pub enum Cmd {
@ -241,9 +242,8 @@ impl PlayerRegistry {
pub async fn shutdown_all(&mut self) -> Result<()> { pub async fn shutdown_all(&mut self) -> Result<()> {
let mut inner = self.0.write().unwrap(); let mut inner = self.0.write().unwrap();
let mut players = HashMap::new();
std::mem::swap(&mut players, &mut inner.players);
for (i, (k, j)) in inner.players.drain() { for (i, (k, j)) in inner.players.drain() {
k.send(PlayerCommand::Stop).await;
drop(k); drop(k);
j.await?; j.await?;
log::debug!("Player stopped #{i:?}") log::debug!("Player stopped #{i:?}")
@ -325,6 +325,7 @@ impl Player {
} }
} }
PlayerCommand::Cmd(cmd, connection_id) => self.handle_cmd(cmd, connection_id).await, PlayerCommand::Cmd(cmd, connection_id) => self.handle_cmd(cmd, connection_id).await,
PlayerCommand::Stop => break,
} }
} }
log::debug!("Shutting down player actor #{:?}", self.player_id); log::debug!("Shutting down player actor #{:?}", self.player_id);

View File

@ -3,6 +3,7 @@
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use anyhow::anyhow;
use serde::Deserialize; use serde::Deserialize;
use sqlx::sqlite::SqliteConnectOptions; use sqlx::sqlite::SqliteConnectOptions;
use sqlx::{ConnectOptions, Connection, FromRow, Sqlite, SqliteConnection, Transaction}; use sqlx::{ConnectOptions, Connection, FromRow, Sqlite, SqliteConnection, Transaction};
@ -76,16 +77,24 @@ impl Storage {
Ok(id) Ok(id)
} }
pub async fn insert_message(&mut self, room_id: u32, id: u32, content: &str) -> Result<()> { pub async fn insert_message(&mut self, room_id: u32, id: u32, content: &str, author_id: &str) -> Result<()> {
let mut executor = self.conn.lock().await; let mut executor = self.conn.lock().await;
let res: Option<(u32,)> = sqlx::query_as("select id from users where name = ?;")
.bind(author_id)
.fetch_optional(&mut *executor)
.await?;
let Some((author_id,)) = res else {
return Err(anyhow!("No such user"));
};
sqlx::query( sqlx::query(
"insert into messages(room_id, id, content) "insert into messages(room_id, id, content, author_id)
values (?, ?, ?); values (?, ?, ?, ?);
update rooms set message_count = message_count + 1 where id = ?;", update rooms set message_count = message_count + 1 where id = ?;",
) )
.bind(room_id) .bind(room_id)
.bind(id) .bind(id)
.bind(content) .bind(content)
.bind(author_id)
.bind(room_id) .bind(room_id)
.execute(&mut *executor) .execute(&mut *executor)
.await?; .await?;

View File

@ -181,7 +181,7 @@ impl Room {
async fn send_message(&mut self, author_id: PlayerId, body: Str) -> Result<()> { async fn send_message(&mut self, author_id: PlayerId, body: Str) -> Result<()> {
tracing::info!("Adding a message to room"); tracing::info!("Adding a message to room");
self.storage self.storage
.insert_message(self.storage_id, self.message_count, &body) .insert_message(self.storage_id, self.message_count, &body, &*author_id.as_inner())
.await?; .await?;
self.message_count += 1; self.message_count += 1;
let update = Updates::NewMessage { let update = Updates::NewMessage {

View File

@ -7,6 +7,7 @@ use nonempty::nonempty;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use prometheus::{IntCounter, IntGauge, Registry as MetricsRegistry}; use prometheus::{IntCounter, IntGauge, Registry as MetricsRegistry};
use serde::Deserialize; use serde::Deserialize;
use tokio::io::AsyncReadExt;
use tokio::io::{AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}; use tokio::io::{AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter};
use tokio::net::tcp::{ReadHalf, WriteHalf}; use tokio::net::tcp::{ReadHalf, WriteHalf};
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
@ -96,14 +97,14 @@ async fn handle_registration<'a>(
let mut pass: Option<Str> = None; let mut pass: Option<Str> = None;
let user = loop { let user = loop {
let res = reader.read_until(b'\n', &mut buffer).await; let res = read_irc_message(reader, &mut buffer).await;
let res = match res { let res = match res {
Ok(len) => { Ok(len) => {
if len == 0 { if len == 0 {
log::info!("Terminating socket"); log::info!("Terminating socket");
break Err(anyhow::Error::msg("EOF")); break Err(anyhow::Error::msg("EOF"));
} }
match std::str::from_utf8(&buffer[..len]) { match std::str::from_utf8(&buffer[..len-2]) {
Ok(res) => res, Ok(res) => res,
Err(e) => break Err(e.into()), Err(e) => break Err(e.into()),
} }
@ -116,7 +117,7 @@ async fn handle_registration<'a>(
log::debug!("Incoming raw IRC message: '{res}'"); log::debug!("Incoming raw IRC message: '{res}'");
let parsed = client_message(res); let parsed = client_message(res);
match parsed { match parsed {
Ok((_, msg)) => { Ok(msg) => {
log::debug!("Incoming IRC message: {msg:?}"); log::debug!("Incoming IRC message: {msg:?}");
match msg { match msg {
ClientMessage::Pass { password } => { ClientMessage::Pass { password } => {
@ -252,7 +253,7 @@ async fn handle_registered_socket<'a>(
loop { loop {
select! { select! {
biased; biased;
len = reader.read_until(b'\n', &mut buffer) => { len = read_irc_message(reader, &mut buffer) => {
let len = len?; let len = len?;
let len = if len == 0 { let len = if len == 0 {
log::info!("EOF, Terminating socket"); log::info!("EOF, Terminating socket");
@ -260,7 +261,7 @@ async fn handle_registered_socket<'a>(
} else { } else {
len len
}; };
let incoming = std::str::from_utf8(&buffer[0..len])?; let incoming = std::str::from_utf8(&buffer[0..len-2])?;
if let HandleResult::Leave = handle_incoming_message(incoming, &config, &user, &rooms, &mut connection, writer).await? { if let HandleResult::Leave = handle_incoming_message(incoming, &config, &user, &rooms, &mut connection, writer).await? {
break; break;
} }
@ -291,6 +292,21 @@ async fn handle_registered_socket<'a>(
Ok(()) Ok(())
} }
async fn read_irc_message(reader: &mut BufReader<ReadHalf<'_>>, buf: &mut Vec<u8>) -> Result<usize> {
let mut size = 0;
'outer: loop {
let res = reader.read_until(b'\r', buf).await?;
size += res;
let next = reader.read_u8().await?;
buf.push(next);
size += 1;
if next != b'\n' {
continue 'outer;
}
return Ok(size);
}
}
async fn handle_update( async fn handle_update(
config: &ServerConfig, config: &ServerConfig,
user: &RegisteredUser, user: &RegisteredUser,
@ -398,7 +414,7 @@ async fn handle_incoming_message(
let parsed = client_message(buffer); let parsed = client_message(buffer);
log::debug!("Incoming IRC message: {parsed:?}"); log::debug!("Incoming IRC message: {parsed:?}");
match parsed { match parsed {
Ok((_, msg)) => match msg { Ok(msg) => match msg {
ClientMessage::Ping { token } => { ClientMessage::Ping { token } => {
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],

View File

@ -26,10 +26,12 @@ use lavina_core::player::{PlayerConnection, PlayerId, PlayerRegistry};
use lavina_core::prelude::*; use lavina_core::prelude::*;
use lavina_core::room::{RoomId, RoomRegistry}; use lavina_core::room::{RoomId, RoomRegistry};
use lavina_core::terminator::Terminator; use lavina_core::terminator::Terminator;
use lavina_core::repo::Storage;
use proto_xmpp::bind::{BindResponse, Jid, Name, Resource, Server}; use proto_xmpp::bind::{BindResponse, Jid, Name, Resource, Server};
use proto_xmpp::client::{Iq, Message, MessageType, Presence}; use proto_xmpp::client::{Iq, Message, MessageType, Presence};
use proto_xmpp::disco::*; use proto_xmpp::disco::*;
use proto_xmpp::roster::RosterQuery; use proto_xmpp::roster::RosterQuery;
use proto_xmpp::sasl::AuthBody;
use proto_xmpp::session::Session; use proto_xmpp::session::Session;
use proto_xmpp::stream::*; use proto_xmpp::stream::*;
use proto_xmpp::xml::{Continuation, FromXml, Parser, ToXml}; use proto_xmpp::xml::{Continuation, FromXml, Parser, ToXml};
@ -60,6 +62,7 @@ pub async fn launch(
players: PlayerRegistry, players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
metrics: MetricsRegistry, metrics: MetricsRegistry,
storage: Storage,
) -> Result<Terminator> { ) -> Result<Terminator> {
log::info!("Starting XMPP projection"); log::info!("Starting XMPP projection");
@ -99,11 +102,12 @@ pub async fn launch(
} }
let players = players.clone(); let players = players.clone();
let rooms = rooms.clone(); let rooms = rooms.clone();
let storage = storage.clone();
let terminator = Terminator::spawn(|termination| { let terminator = Terminator::spawn(|termination| {
let stopped_tx = stopped_tx.clone(); let stopped_tx = stopped_tx.clone();
let loaded_config = loaded_config.clone(); let loaded_config = loaded_config.clone();
async move { async move {
match handle_socket(loaded_config, stream, &socket_addr, players, rooms, termination).await { match handle_socket(loaded_config, stream, &socket_addr, players, rooms, storage, termination).await {
Ok(_) => log::info!("Connection terminated"), Ok(_) => log::info!("Connection terminated"),
Err(err) => log::warn!("Connection failed: {err}"), Err(err) => log::warn!("Connection failed: {err}"),
} }
@ -143,6 +147,7 @@ async fn handle_socket(
socket_addr: &SocketAddr, socket_addr: &SocketAddr,
mut players: PlayerRegistry, mut players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
mut storage: Storage,
termination: Deferred<()>, // TODO use it to stop the connection gracefully termination: Deferred<()>, // TODO use it to stop the connection gracefully
) -> Result<()> { ) -> Result<()> {
log::info!("Received an XMPP connection from {socket_addr}"); log::info!("Received an XMPP connection from {socket_addr}");
@ -167,7 +172,7 @@ async fn handle_socket(
let mut xml_reader = NsReader::from_reader(BufReader::new(a)); let mut xml_reader = NsReader::from_reader(BufReader::new(a));
let mut xml_writer = Writer::new(b); let mut xml_writer = Writer::new(b);
let authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf).await?; let authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf, &mut storage).await?;
log::debug!("User authenticated"); log::debug!("User authenticated");
let mut connection = players.connect_to_player(authenticated.player_id.clone()).await; let mut connection = players.connect_to_player(authenticated.player_id.clone()).await;
socket_final( socket_final(
@ -224,6 +229,7 @@ async fn socket_auth(
xml_reader: &mut NsReader<(impl AsyncBufRead + Unpin)>, xml_reader: &mut NsReader<(impl AsyncBufRead + Unpin)>,
xml_writer: &mut Writer<(impl AsyncWrite + Unpin)>, xml_writer: &mut Writer<(impl AsyncWrite + Unpin)>,
reader_buf: &mut Vec<u8>, reader_buf: &mut Vec<u8>,
storage: &mut Storage,
) -> Result<Authenticated> { ) -> Result<Authenticated> {
read_xml_header(xml_reader, reader_buf).await?; read_xml_header(xml_reader, reader_buf).await?;
let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?; let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?;
@ -248,16 +254,41 @@ async fn socket_auth(
.await?; .await?;
xml_writer.get_mut().flush().await?; xml_writer.get_mut().flush().await?;
let _ = proto_xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?; let auth: proto_xmpp::sasl::Auth = proto_xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?;
proto_xmpp::sasl::Success.write_xml(xml_writer).await?; proto_xmpp::sasl::Success.write_xml(xml_writer).await?;
let name: Str = "darova".into(); match AuthBody::from_str(&auth.body) {
Ok(Authenticated { Ok(logopass) => {
player_id: PlayerId::from("darova")?, let name = &logopass.login;
xmpp_name: Name(name.clone()), let stored_user = storage.retrieve_user_by_name(name).await?;
xmpp_resource: Resource(name.clone()),
xmpp_muc_name: Resource(name), let stored_user = match stored_user {
}) Some(u) => u,
None => {
log::info!("User '{}' not found", name);
return Err(fail("no user found"));
}
};
// TODO return proper XML errors to the client
if stored_user.password.is_none() {
log::info!("Password not defined for user '{}'", name);
return Err(fail("password is not defined"));
}
if stored_user.password.as_deref() != Some(&logopass.password) {
log::info!("Incorrect password supplied for user '{}'", name);
return Err(fail("passwords do not match"));
}
Ok(Authenticated {
player_id: PlayerId::from(name.as_str())?,
xmpp_name: Name(name.to_string().into()),
xmpp_resource: Resource(name.to_string().into()),
xmpp_muc_name: Resource(name.to_string().into()),
})
},
Err(e) => return Err(e)
}
} }
async fn socket_final( async fn socket_final(
@ -328,7 +359,7 @@ async fn socket_final(
if let Some(update) = update { if let Some(update) = update {
match update { match update {
lavina_core::player::Updates::NewMessage { room_id, author_id, body } => { lavina_core::player::Updates::NewMessage { room_id, author_id, body } => {
Message { Message::<()> {
to: Some(Jid { to: Some(Jid {
name: Some(authenticated.xmpp_name.clone()), name: Some(authenticated.xmpp_name.clone()),
server: Server("localhost".into()), server: Server("localhost".into()),
@ -344,6 +375,7 @@ async fn socket_final(
lang: None, lang: None,
subject: None, subject: None,
body: body.into(), body: body.into(),
custom: vec![],
} }
.serialize(&mut events); .serialize(&mut events);
} }
@ -393,7 +425,7 @@ async fn handle_packet(
user_handle user_handle
.send_message(RoomId::from(name.0.clone())?, m.body.clone().into()) .send_message(RoomId::from(name.0.clone())?, m.body.clone().into())
.await?; .await?;
Message { Message::<()> {
to: Some(Jid { to: Some(Jid {
name: Some(user.xmpp_name.clone()), name: Some(user.xmpp_name.clone()),
server: Server("localhost".into()), server: Server("localhost".into()),
@ -409,6 +441,7 @@ async fn handle_packet(
lang: None, lang: None,
subject: None, subject: None,
body: m.body.clone(), body: m.body.clone(),
custom: vec![],
} }
.serialize(output); .serialize(output);
false false

View File

@ -49,7 +49,7 @@ impl FromXml for IqClientBody {
#[derive(PartialEq, Eq, Debug, From)] #[derive(PartialEq, Eq, Debug, From)]
pub enum ClientPacket { pub enum ClientPacket {
Iq(Iq<IqClientBody>), Iq(Iq<IqClientBody>),
Message(Message), Message(Message<Ignore>),
Presence(Presence<Ignore>), Presence(Presence<Ignore>),
StreamEnd, StreamEnd,
} }
@ -65,7 +65,7 @@ impl FromXml for ClientPacket {
match_parser!(name, namespace, event; match_parser!(name, namespace, event;
Iq::<IqClientBody>, Iq::<IqClientBody>,
Presence::<Ignore>, Presence::<Ignore>,
Message, Message::<Ignore>,
{ {
Err(anyhow!( Err(anyhow!(
"Unexpected XML event of name {:?} in namespace {:?}", "Unexpected XML event of name {:?} in namespace {:?}",

View File

@ -1,6 +1,7 @@
use super::*; use super::*;
use nom::combinator::opt; use anyhow::{anyhow, Result};
use nom::combinator::{all_consuming, opt};
/// Client-to-server command. /// Client-to-server command.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -60,8 +61,8 @@ pub enum ClientMessage {
}, },
} }
pub fn client_message(input: &str) -> IResult<&str, ClientMessage> { pub fn client_message(input: &str) -> Result<ClientMessage> {
alt(( let res = all_consuming(alt((
client_message_capability, client_message_capability,
client_message_ping, client_message_ping,
client_message_pong, client_message_pong,
@ -75,7 +76,11 @@ pub fn client_message(input: &str) -> IResult<&str, ClientMessage> {
client_message_part, client_message_part,
client_message_privmsg, client_message_privmsg,
client_message_quit, client_message_quit,
))(input) )))(input);
match res {
Ok((_, e)) => Ok(e),
Err(e) => Err(anyhow!("Parsing failed: {e}")),
}
} }
fn client_message_capability(input: &str) -> IResult<&str, ClientMessage> { fn client_message_capability(input: &str) -> IResult<&str, ClientMessage> {
@ -89,24 +94,14 @@ fn client_message_ping(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PING ")(input)?; let (input, _) = tag("PING ")(input)?;
let (input, token) = token(input)?; let (input, token) = token(input)?;
Ok(( Ok((input, ClientMessage::Ping { token: token.into() }))
input,
ClientMessage::Ping {
token: token.into(),
},
))
} }
fn client_message_pong(input: &str) -> IResult<&str, ClientMessage> { fn client_message_pong(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PONG ")(input)?; let (input, _) = tag("PONG ")(input)?;
let (input, token) = token(input)?; let (input, token) = token(input)?;
Ok(( Ok((input, ClientMessage::Pong { token: token.into() }))
input,
ClientMessage::Pong {
token: token.into(),
},
))
} }
fn client_message_nick(input: &str) -> IResult<&str, ClientMessage> { fn client_message_nick(input: &str) -> IResult<&str, ClientMessage> {
@ -225,12 +220,7 @@ fn client_message_quit(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("QUIT :")(input)?; let (input, _) = tag("QUIT :")(input)?;
let (input, reason) = token(input)?; let (input, reason) = token(input)?;
Ok(( Ok((input, ClientMessage::Quit { reason: reason.into() }))
input,
ClientMessage::Quit {
reason: reason.into(),
},
))
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -275,7 +265,7 @@ mod test {
}; };
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
@ -286,28 +276,24 @@ mod test {
}; };
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
fn test_client_message_ping() { fn test_client_message_ping() {
let input = "PING 1337"; let input = "PING 1337";
let expected = ClientMessage::Ping { let expected = ClientMessage::Ping { token: "1337".into() };
token: "1337".into(),
};
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
fn test_client_message_pong() { fn test_client_message_pong() {
let input = "PONG 1337"; let input = "PONG 1337";
let expected = ClientMessage::Pong { let expected = ClientMessage::Pong { token: "1337".into() };
token: "1337".into(),
};
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
fn test_client_message_nick() { fn test_client_message_nick() {
@ -317,7 +303,7 @@ mod test {
}; };
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
fn test_client_message_user() { fn test_client_message_user() {
@ -328,7 +314,7 @@ mod test {
}; };
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[test]
fn test_client_message_part() { fn test_client_message_part() {
@ -339,6 +325,6 @@ mod test {
}; };
let result = client_message(input); let result = client_message(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
} }

View File

@ -7,7 +7,6 @@ mod testkit;
pub mod user; pub mod user;
use crate::prelude::Str; use crate::prelude::Str;
use std::io::Result;
use nom::{ use nom::{
branch::alt, branch::alt,
@ -39,7 +38,7 @@ pub enum Chan {
Local(Str), Local(Str),
} }
impl Chan { impl Chan {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> { pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
match self { match self {
Chan::Global(name) => { Chan::Global(name) => {
writer.write_all(b"#").await?; writer.write_all(b"#").await?;
@ -76,7 +75,7 @@ pub enum Recipient {
Chan(Chan), Chan(Chan),
} }
impl Recipient { impl Recipient {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> { pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
match self { match self {
Recipient::Nick(nick) => writer.write_all(nick.as_bytes()).await?, Recipient::Nick(nick) => writer.write_all(nick.as_bytes()).await?,
Recipient::Chan(chan) => chan.write_async(writer).await?, Recipient::Chan(chan) => chan.write_async(writer).await?,

View File

@ -10,3 +10,7 @@ regex.workspace = true
anyhow.workspace = true anyhow.workspace = true
tokio.workspace = true tokio.workspace = true
derive_more.workspace = true derive_more.workspace = true
base64.workspace = true
[dev-dependencies]
assert_matches.workspace = true

View File

@ -3,8 +3,7 @@ use quick_xml::events::attributes::Attribute;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::name::{QName, ResolveResult}; use quick_xml::name::{QName, ResolveResult};
use anyhow::{Result, anyhow as ffail}; use anyhow::{anyhow as ffail, Result};
use crate::prelude::*; use crate::prelude::*;
use crate::xml::*; use crate::xml::*;
@ -14,44 +13,45 @@ use super::bind::Jid;
pub const XMLNS: &'static str = "jabber:client"; pub const XMLNS: &'static str = "jabber:client";
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct Message { pub struct Message<T> {
pub from: Option<Jid>, pub from: Option<Jid>,
pub id: Option<String>, pub id: Option<String>,
pub to: Option<Jid>, pub to: Option<Jid>,
// default is Normal // default is Normal
pub r#type: MessageType, pub r#type: MessageType,
pub lang: Option<Str>, pub lang: Option<Str>,
pub subject: Option<Str>, pub subject: Option<Str>,
pub body: Str, pub body: Str,
pub custom: Vec<T>,
} }
impl FromXmlTag for Message { impl<T: FromXml> FromXmlTag for Message<T> {
const NS: &'static str = XMLNS; const NS: &'static str = XMLNS;
const NAME: &'static str = "message"; const NAME: &'static str = "message";
} }
impl FromXml for Message { impl<T: FromXml> FromXml for Message<T> {
type P = MessageParser; type P = impl Parser<Output = Result<Self>>;
fn parse() -> Self::P { fn parse() -> Self::P {
MessageParserInner::Init.into() MessageParser(MessageParserInner::Init)
} }
} }
#[derive(From)] #[derive(From)]
pub struct MessageParser(MessageParserInner); struct MessageParser<T: FromXml>(MessageParserInner<T>);
#[derive(Default)] #[derive(Default)]
enum MessageParserInner { enum MessageParserInner<T: FromXml> {
#[default] #[default]
Init, Init,
Outer(MessageParserState), Outer(MessageParserState<T>),
InSubject(MessageParserState), InSubject(MessageParserState<T>),
InBody(MessageParserState), InBody(MessageParserState<T>),
InCustom(MessageParserState<T>, T::P),
} }
#[derive(Default)] #[derive(Default)]
struct MessageParserState { struct MessageParserState<T> {
from: Option<Jid>, from: Option<Jid>,
id: Option<String>, id: Option<String>,
to: Option<Jid>, to: Option<Jid>,
@ -59,21 +59,27 @@ struct MessageParserState {
lang: Option<Str>, lang: Option<Str>,
subject: Option<Str>, subject: Option<Str>,
body: Option<Str>, body: Option<Str>,
custom: Vec<T>,
} }
impl Parser for MessageParser { impl<T: FromXml> Parser for MessageParser<T> {
type Output = Result<Message>; type Output = Result<Message<T>>;
fn consume<'a>( fn consume<'a>(self: Self, namespace: ResolveResult, event: &Event<'a>) -> Continuation<Self, Self::Output> {
self: Self,
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output> {
// TODO validate tag name and namespace at each stage // TODO validate tag name and namespace at each stage
use MessageParserInner::*; use MessageParserInner::*;
match self.0 { match self.0 {
Init => { Init => {
if let Event::Start(ref bytes) = event { if let Event::Start(ref bytes) = event {
let mut state: MessageParserState = Default::default(); let mut state: MessageParserState<T> = MessageParserState {
from: None,
id: None,
to: None,
r#type: MessageType::Normal,
lang: None,
subject: None,
body: None,
custom: vec![],
};
for attr in bytes.attributes() { for attr in bytes.attributes() {
let attr = fail_fast!(attr); let attr = fail_fast!(attr);
if attr.key.0 == b"from" { if attr.key.0 == b"from" {
@ -97,14 +103,22 @@ impl Parser for MessageParser {
Continuation::Final(Err(ffail!("Expected start"))) Continuation::Final(Err(ffail!("Expected start")))
} }
} }
Outer(state) => match event { Outer(mut state) => match event {
Event::Start(ref bytes) => { Event::Start(ref bytes) => {
if bytes.name().0 == b"subject" { if bytes.name().0 == b"subject" {
Continuation::Continue(InSubject(state).into()) Continuation::Continue(InSubject(state).into())
} else if bytes.name().0 == b"body" { } else if bytes.name().0 == b"body" {
Continuation::Continue(InBody(state).into()) Continuation::Continue(InBody(state).into())
} else { } else {
Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))) let parser = T::parse();
match parser.consume(namespace, event) {
Continuation::Final(Ok(e)) => {
state.custom.push(e);
Continuation::Continue(Outer(state).into())
}
Continuation::Final(Err(e)) => Continuation::Final(Err(e)),
Continuation::Continue(p) => Continuation::Continue(InCustom(state, p).into()),
}
} }
} }
Event::End(_) => { Event::End(_) => {
@ -117,11 +131,23 @@ impl Parser for MessageParser {
lang: state.lang, lang: state.lang,
subject: state.subject, subject: state.subject,
body, body,
custom: state.custom,
})) }))
} else { } else {
Continuation::Final(Err(ffail!("Body not found"))) Continuation::Final(Err(ffail!("Body not found")))
} }
} }
Event::Empty(_) => {
let parser = T::parse();
match parser.consume(namespace, event) {
Continuation::Final(Ok(e)) => {
state.custom.push(e);
Continuation::Continue(Outer(state).into())
}
Continuation::Final(Err(e)) => Continuation::Final(Err(e)),
Continuation::Continue(p) => Continuation::Continue(InCustom(state, p).into()),
}
}
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
}, },
InSubject(mut state) => match event { InSubject(mut state) => match event {
@ -144,11 +170,19 @@ impl Parser for MessageParser {
Event::End(_) => Continuation::Continue(Outer(state).into()), Event::End(_) => Continuation::Continue(Outer(state).into()),
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
}, },
InCustom(mut state, custom) => match custom.consume(namespace, event) {
Continuation::Final(Ok(e)) => {
state.custom.push(e);
Continuation::Continue(Outer(state).into())
}
Continuation::Final(Err(e)) => Continuation::Final(Err(e)),
Continuation::Continue(c) => Continuation::Continue(InCustom(state, c).into()),
},
} }
} }
} }
impl ToXml for Message { impl<T: ToXml> ToXml for Message<T> {
fn serialize(&self, events: &mut Vec<Event<'static>>) { fn serialize(&self, events: &mut Vec<Event<'static>>) {
let mut bytes = BytesStart::new(format!(r#"message xmlns="{}""#, XMLNS)); let mut bytes = BytesStart::new(format!(r#"message xmlns="{}""#, XMLNS));
if let Some(from) = &self.from { if let Some(from) = &self.from {
@ -261,11 +295,7 @@ struct IqParserState<T> {
impl<T: FromXml> Parser for IqParser<T> { impl<T: FromXml> Parser for IqParser<T> {
type Output = Result<Iq<T>>; type Output = Result<Iq<T>>;
fn consume<'a>( fn consume<'a>(self: Self, namespace: ResolveResult, event: &Event<'a>) -> Continuation<Self, Self::Output> {
self: Self,
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output> {
match self.0 { match self.0 {
IqParserInner::Init => { IqParserInner::Init => {
if let Event::Start(ref bytes) = event { if let Event::Start(ref bytes) = event {
@ -297,18 +327,16 @@ impl<T: FromXml> Parser for IqParser<T> {
Continuation::Final(Err(ffail!("Expected start"))) Continuation::Final(Err(ffail!("Expected start")))
} }
} }
IqParserInner::ParsingBody(mut state, parser) => { IqParserInner::ParsingBody(mut state, parser) => match parser.consume(namespace, event) {
match parser.consume(namespace, event) { Continuation::Final(f) => {
Continuation::Final(f) => { let body = fail_fast!(f);
let body = fail_fast!(f); state.body = Some(body);
state.body = Some(body); Continuation::Continue(IqParser(IqParserInner::Final(state)))
Continuation::Continue(IqParser(IqParserInner::Final(state)))
}
Continuation::Continue(parser) => {
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, parser)))
}
} }
} Continuation::Continue(parser) => {
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, parser)))
}
},
IqParserInner::Final(state) => { IqParserInner::Final(state) => {
if let Event::End(ref bytes) = event { if let Event::End(ref bytes) = event {
let id = fail_fast!(state.id.ok_or_else(|| ffail!("No id provided"))); let id = fail_fast!(state.id.ok_or_else(|| ffail!("No id provided")));
@ -585,34 +613,14 @@ mod tests {
use crate::bind::{BindRequest, Name, Resource, Server}; use crate::bind::{BindRequest, Name, Resource, Server};
use super::*; use super::*;
use quick_xml::NsReader;
#[tokio::test] #[tokio::test]
async fn parse_message() { async fn parse_message() {
let input = r#"<message id="aacea" type="chat" to="nikita@vlnv.dev"><subject>daa</subject><body>bbb</body></message>"#; let input = r#"<message id="aacea" type="chat" to="nikita@vlnv.dev"><subject>daa</subject><body>bbb</body><unknown-stuff></unknown-stuff></message>"#;
let mut reader = NsReader::from_reader(input.as_bytes()); let result: Message<Ignore> = crate::xml::parse(input).unwrap();
let mut buf = vec![];
let (ns, event) = reader
.read_resolved_event_into_async(&mut buf)
.await
.unwrap();
let mut parser = Message::parse().consume(ns, &event);
let result = loop {
match parser {
Continuation::Final(res) => break res,
Continuation::Continue(next) => {
let (ns, event) = reader
.read_resolved_event_into_async(&mut buf)
.await
.unwrap();
parser = next.consume(ns, &event);
}
}
}
.unwrap();
assert_eq!( assert_eq!(
result, result,
Message { Message::<Ignore> {
from: None, from: None,
id: Some("aacea".to_string()), id: Some("aacea".to_string()),
to: Some(Jid { to: Some(Jid {
@ -624,6 +632,30 @@ mod tests {
lang: None, lang: None,
subject: Some("daa".into()), subject: Some("daa".into()),
body: "bbb".into(), body: "bbb".into(),
custom: vec![Ignore],
}
)
}
#[tokio::test]
async fn parse_message_empty_custom() {
let input = r#"<message id="aacea" type="chat" to="nikita@vlnv.dev"><subject>daa</subject><body>bbb</body><unknown-stuff/></message>"#;
let result: Message<Ignore> = crate::xml::parse(input).unwrap();
assert_eq!(
result,
Message::<Ignore> {
from: None,
id: Some("aacea".to_string()),
to: Some(Jid {
name: Some(Name("nikita".into())),
server: Server("vlnv.dev".into()),
resource: None
}),
r#type: MessageType::Chat,
lang: None,
subject: Some("daa".into()),
body: "bbb".into(),
custom: vec![Ignore],
} }
) )
} }
@ -631,26 +663,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn parse_iq() { async fn parse_iq() {
let input = r#"<iq id="bind_1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>mobile</resource></bind></iq>"#; let input = r#"<iq id="bind_1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>mobile</resource></bind></iq>"#;
let mut reader = NsReader::from_reader(input.as_bytes()); let result: Iq<BindRequest> = crate::xml::parse(input).unwrap();
let mut buf = vec![];
let (ns, event) = reader
.read_resolved_event_into_async(&mut buf)
.await
.unwrap();
let mut parser = Iq::<BindRequest>::parse().consume(ns, &event);
let result = loop {
match parser {
Continuation::Final(res) => break res,
Continuation::Continue(next) => {
let (ns, event) = reader
.read_resolved_event_into_async(&mut buf)
.await
.unwrap();
parser = next.consume(ns, &event);
}
}
}
.unwrap();
assert_eq!( assert_eq!(
result, result,
Iq { Iq {

View File

@ -1,8 +1,10 @@
#![allow(unused_variables)]
use quick_xml::events::Event; use quick_xml::events::Event;
use quick_xml::name::ResolveResult; use quick_xml::name::ResolveResult;
use anyhow::{anyhow, Result};
use crate::xml::*; use crate::xml::*;
use anyhow::{anyhow, Result};
pub const XMLNS: &'static str = "http://jabber.org/protocol/muc"; pub const XMLNS: &'static str = "http://jabber.org/protocol/muc";
@ -20,8 +22,8 @@ impl FromXml for History {
|(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
let mut history = History::default(); let mut history = History::default();
let (bytes, end) = match event { let (bytes, end) = match event {
Event::Start(bytes) => (bytes, false), Event::Start(bytes) if bytes.name().0 == Self::NAME.as_bytes() => (bytes, false),
Event::Empty(bytes) => (bytes, true), Event::Empty(bytes) if bytes.name().0 == Self::NAME.as_bytes() => (bytes, true),
_ => return Err(anyhow!("Unexpected XML event: {event:?}")), _ => return Err(anyhow!("Unexpected XML event: {event:?}")),
}; };
for attr in bytes.attributes() { for attr in bytes.attributes() {
@ -72,14 +74,16 @@ impl FromXml for Password {
fn parse() -> Self::P { fn parse() -> Self::P {
|(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
let Event::Start(bytes) = event else { let bytes = match event {
return Err(anyhow!("Unexpected XML event: {event:?}")); Event::Start(bytes) if bytes.name().0 == Self::NAME.as_bytes() => bytes,
_ => return Err(anyhow!("Unexpected XML event: {event:?}")),
}; };
let (namespace, event) = yield; let (namespace, event) = yield;
let Event::Text(bytes) = event else { let Event::Text(bytes) = event else {
return Err(anyhow!("Unexpected XML event: {event:?}")); return Err(anyhow!("Unexpected XML event: {event:?}"));
}; };
let s = std::str::from_utf8(bytes)?.to_string(); let s = std::str::from_utf8(bytes)?.to_string();
let (namespace, event) = yield;
let Event::End(bytes) = event else { let Event::End(bytes) = event else {
return Err(anyhow!("Unexpected XML event: {event:?}")); return Err(anyhow!("Unexpected XML event: {event:?}"));
}; };
@ -106,7 +110,7 @@ impl FromXml for X {
fn parse() -> Self::P { fn parse() -> Self::P {
|(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
let mut res = X::default(); let mut res = X::default();
let (bytes, end) = match event { let (_, end) = match event {
Event::Start(bytes) => (bytes, false), Event::Start(bytes) => (bytes, false),
Event::Empty(bytes) => (bytes, true), Event::Empty(bytes) => (bytes, true),
_ => return Err(anyhow!("Unexpected XML event: {event:?}")), _ => return Err(anyhow!("Unexpected XML event: {event:?}")),
@ -138,3 +142,90 @@ impl FromXml for X {
} }
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_history_success_empty() {
let input = "<history/>";
let res: History = parse(input).unwrap();
let expected = History {
maxchars: None,
maxstanzas: None,
seconds: None,
};
assert_eq!(res, expected);
}
#[test]
fn test_history_success_empty_attrs() {
let input = r#"<history maxchars="1" maxstanzas="2" seconds="4"/>"#;
let res: History = parse(input).unwrap();
let expected = History {
maxchars: Some(1),
maxstanzas: Some(2),
seconds: Some(4),
};
assert_eq!(res, expected);
}
#[test]
fn test_history_success_start_end() {
let input = r#"<history></history>"#;
let res: History = parse(input).unwrap();
let expected = History {
maxchars: None,
maxstanzas: None,
seconds: None,
};
assert_eq!(res, expected);
}
#[test]
fn test_history_incorrect_empty() {
let input = r#"<iq/>"#;
parse::<History>(input).err().unwrap();
}
#[test]
fn test_password_success() {
let input = "<password>olala</password>";
let res: Password = parse(input).unwrap();
let expected = Password("olala".into());
assert_eq!(res, expected);
}
#[test]
fn test_password_incorrect() {
let input = r#"<iq>asdsd</iq>"#;
parse::<Password>(input).err().unwrap();
}
#[test]
fn test_x_success_empty() {
let input = "<x/>";
let res: X = parse(input).unwrap();
let expected = X {
history: None,
password: None,
};
assert_eq!(res, expected);
}
#[test]
fn test_x_success_full() {
let input = r#"<x><password>ololo</password><history maxchars="1"/></x>"#;
let res: X = parse(input).unwrap();
let expected = X {
history: Some(History {
maxchars: Some(1),
maxstanzas: None,
seconds: None,
}),
password: Some(Password("ololo".into())),
};
assert_eq!(res, expected);
}
}

View File

@ -5,6 +5,7 @@ use quick_xml::{
NsReader, Writer, NsReader, Writer,
}; };
use tokio::io::{AsyncBufRead, AsyncWrite}; use tokio::io::{AsyncBufRead, AsyncWrite};
use base64::{Engine as _, engine::general_purpose};
use super::skip_text; use super::skip_text;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -12,6 +13,7 @@ use anyhow::{anyhow, Result};
pub enum Mechanism { pub enum Mechanism {
Plain, Plain,
} }
impl Mechanism { impl Mechanism {
pub fn to_str(&self) -> &'static str { pub fn to_str(&self) -> &'static str {
match self { match self {
@ -27,10 +29,93 @@ impl Mechanism {
} }
} }
#[derive(PartialEq, Debug)]
pub struct AuthBody {
pub login: String,
pub password: String,
}
impl AuthBody {
pub fn from_str(input: &[u8]) -> Result<AuthBody> {
match general_purpose::STANDARD.decode(input){
Ok(decoded_body) => {
match String::from_utf8(decoded_body) {
Ok(parsed_to_string) => {
let separated_words: Vec<&str> = parsed_to_string.split("\x00").collect::<Vec<_>>().clone();
if separated_words.len() == 3 {
// first segment ignored (might be needed in the future)
Ok(AuthBody { login: separated_words[1].to_string(), password: separated_words[2].to_string() })
} else { return Err(anyhow!("Incorrect auth format")) }
},
Err(e) => return Err(anyhow!(e))
}
},
Err(e) => return Err(anyhow!(e))
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_returning_auth_body() {
let orig = b"\x00login\x00pass";
let encoded = general_purpose::STANDARD.encode(orig);
let expected = AuthBody {login: "login".to_string(), password: "pass".to_string()};
let result = AuthBody::from_str(encoded.as_bytes()).unwrap();
assert_eq!(expected, result);
}
#[test]
fn test_ignoring_first_segment() {
let orig = b"ignored\x00login\x00pass";
let encoded = general_purpose::STANDARD.encode(orig);
let expected = AuthBody {login: "login".to_string(), password: "pass".to_string()};
let result = AuthBody::from_str(encoded.as_bytes()).unwrap();
assert_eq!(expected, result);
}
#[test]
fn test_returning_auth_body_with_empty_strings() {
let orig = b"\x00\x00";
let encoded = general_purpose::STANDARD.encode(orig);
let expected = AuthBody {login: "".to_string(), password: "".to_string()};
let result = AuthBody::from_str(encoded.as_bytes()).unwrap();
assert_eq!(expected, result);
}
#[test]
fn test_fail_if_size_less_then_3() {
let orig = b"login\x00pass";
let encoded = general_purpose::STANDARD.encode(orig);
let expected = AuthBody {login: "login".to_string(), password: "pass".to_string()};
let result = AuthBody::from_str(encoded.as_bytes());
assert!(result.is_err());
}
#[test]
fn test_fail_if_size_greater_then_3() {
let orig = b"first\x00login\x00pass\x00other";
let encoded = general_purpose::STANDARD.encode(orig);
let expected = AuthBody {login: "login".to_string(), password: "pass".to_string()};
let result = AuthBody::from_str(encoded.as_bytes());
assert!(result.is_err());
}
}
pub struct Auth { pub struct Auth {
pub mechanism: Mechanism, pub mechanism: Mechanism,
pub body: Vec<u8>, pub body: Vec<u8>,
} }
impl Auth { impl Auth {
pub async fn parse( pub async fn parse(
reader: &mut NsReader<impl AsyncBufRead + Unpin>, reader: &mut NsReader<impl AsyncBufRead + Unpin>,
@ -69,6 +154,7 @@ impl Auth {
} }
pub struct Success; pub struct Success;
impl Success { impl Success {
pub async fn write_xml(&self, writer: &mut Writer<impl AsyncWrite + Unpin>) -> Result<()> { pub async fn write_xml(&self, writer: &mut Writer<impl AsyncWrite + Unpin>) -> Result<()> {
let event = BytesStart::new(r#"success xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#); let event = BytesStart::new(r#"success xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#);

View File

@ -17,7 +17,7 @@ impl Parser for IgnoreParser {
fn consume<'a>( fn consume<'a>(
self: Self, self: Self,
namespace: ResolveResult, _: ResolveResult,
event: &Event<'a>, event: &Event<'a>,
) -> Continuation<Self, Self::Output> { ) -> Continuation<Self, Self::Output> {
match self.0 { match self.0 {
@ -58,5 +58,5 @@ impl FromXml for Ignore {
} }
impl ToXml for () { impl ToXml for () {
fn serialize(&self, events: &mut Vec<Event<'static>>) {} fn serialize(&self, _: &mut Vec<Event<'static>>) {}
} }

View File

@ -1,10 +1,11 @@
use std::ops::Generator; use std::ops::Generator;
use std::pin::Pin; use std::pin::Pin;
use quick_xml::NsReader;
use quick_xml::events::Event; use quick_xml::events::Event;
use quick_xml::name::ResolveResult; use quick_xml::name::ResolveResult;
use anyhow::{anyhow, Result}; use anyhow::Result;
mod ignore; mod ignore;
pub use ignore::Ignore; pub use ignore::Ignore;
@ -63,6 +64,22 @@ pub enum Continuation<Parser, Res> {
Continue(Parser), Continue(Parser),
} }
pub fn parse<T: FromXml>(input: &str) -> Result<T> {
let mut reader = NsReader::from_reader(input.as_bytes());
let mut buf = vec![];
let (ns, event) = reader.read_resolved_event_into(&mut buf)?;
let mut parser: Continuation<_, std::result::Result<T, anyhow::Error>> = T::parse().consume(ns, &event);
loop {
match parser {
Continuation::Final(res) => break res,
Continuation::Continue(next) => {
let (ns, event) = reader.read_resolved_event_into(&mut buf)?;
parser = next.consume(ns, &event);
}
}
}
}
macro_rules! fail_fast { macro_rules! fail_fast {
($errorable: expr) => { ($errorable: expr) => {
match $errorable { match $errorable {

View File

@ -1 +1 @@
nightly-2023-08-15 nightly-2023-10-06

View File

@ -62,7 +62,7 @@ async fn main() -> Result<()> {
storage.clone(), storage.clone(),
) )
.await?; .await?;
let xmpp = projection_xmpp::launch(xmpp_config, players.clone(), rooms.clone(), metrics.clone()).await?; let xmpp = projection_xmpp::launch(xmpp_config, players.clone(), rooms.clone(), metrics.clone(), storage.clone()).await?;
tracing::info!("Started"); tracing::info!("Started");
sleep.await; sleep.await;