This commit is contained in:
Paul Zinselmeyer 2024-06-20 16:57:42 +02:00
commit 0d69c204bb
Signed by: pfzetto
GPG key ID: B471A1AF06C895FD
8 changed files with 2460 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

981
Cargo.lock generated Normal file
View file

@ -0,0 +1,981 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "alsa"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce34de545ad29bcc00cb1b87a94c132256dcf83aa7eeb9674482568405a6ff0a"
dependencies = [
"alsa-sys",
"bitflags 2.4.2",
"libc",
"nix",
]
[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "audiopus_sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62314a1546a2064e033665d658e88c620a62904be945f8147e6b16c3db9f8651"
dependencies = [
"cmake",
"log",
"pkg-config",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bindgen"
version = "0.66.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
dependencies = [
"bitflags 2.4.2",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-expr"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cookie-factory"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libloading"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "libspa"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0434617020ddca18b86067912970c55410ca654cdafd775480322f50b857a8c4"
dependencies = [
"bitflags 2.4.2",
"cc",
"convert_case",
"cookie-factory",
"libc",
"libspa-sys",
"nix",
"nom",
"system-deps",
]
[[package]]
name = "libspa-sys"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e70ca3f3e70f858ef363046d06178c427b4e0b63d210c95fd87d752679d345"
dependencies = [
"bindgen",
"cc",
"system-deps",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "multimap"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "mumble-foo"
version = "0.1.0"
dependencies = [
"alsa",
"anyhow",
"opus",
"pipewire",
"prost",
"prost-build",
"rustls",
]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opus"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6526409b274a7e98e55ff59d96aafd38e6cd34d46b7dbbc32ce126dffcd75e8e"
dependencies = [
"audiopus_sys",
"libc",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pipewire"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2d009c8dd65e890b515a71950f7e4c801523b8894ff33863a40830bf762e9e9"
dependencies = [
"anyhow",
"bitflags 2.4.2",
"libc",
"libspa",
"libspa-sys",
"nix",
"once_cell",
"pipewire-sys",
"thiserror",
]
[[package]]
name = "pipewire-sys"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "890c084e7b737246cb4799c86b71a0e4da536031ff7473dd639eba9f95039f64"
dependencies = [
"bindgen",
"libspa-sys",
"system-deps",
]
[[package]]
name = "pkg-config"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
[[package]]
name = "prettyplease"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-build"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2"
dependencies = [
"bytes",
"heck",
"itertools",
"log",
"multimap",
"once_cell",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
"syn",
"tempfile",
"which",
]
[[package]]
name = "prost-derive"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
dependencies = [
"prost",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ring"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
dependencies = [
"bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a"
[[package]]
name = "rustls-webpki"
version = "0.102.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "system-deps"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
[[package]]
name = "tempfile"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
dependencies = [
"memchr",
]
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "mumble-foo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
alsa = "0.8.1"
pipewire = "0.7.2"
opus = "0.3.0"
anyhow = "1.0.79"
rustls = "0.22.2"
prost = "0.12.3"
[build-dependencies]
prost-build = "0.12.3"

5
build.rs Normal file
View file

@ -0,0 +1,5 @@
use std::io::Result;
fn main() -> Result<()> {
prost_build::compile_protos(&["proto/Mumble.proto"], &["proto/"])?;
Ok(())
}

621
proto/Mumble.proto Normal file
View file

@ -0,0 +1,621 @@
// Copyright 2009-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
syntax = "proto2";
package MumbleProto;
option optimize_for = SPEED;
message Version {
// Legacy version number format.
optional uint32 version_v1 = 1;
// New version number format.
// Necessary since patch level may exceed 255. (See https://github.com/mumble-voip/mumble/issues/5827)
optional uint64 version_v2 = 5;
// Client release name.
optional string release = 2;
// Client OS name.
optional string os = 3;
// Client OS version.
optional string os_version = 4;
}
// Not used. Not even for tunneling UDP through TCP.
message UDPTunnel {
// Not used.
required bytes packet = 1;
}
// Used by the client to send the authentication credentials to the server.
message Authenticate {
// UTF-8 encoded username.
optional string username = 1;
// Server or user password.
optional string password = 2;
// Additional access tokens for server ACL groups.
repeated string tokens = 3;
// A list of CELT bitstream version constants supported by the client.
repeated int32 celt_versions = 4;
optional bool opus = 5 [default = false];
// 0 = REGULAR, 1 = BOT
optional int32 client_type = 6 [default = 0];
}
// Sent by the client to notify the server that the client is still alive.
// Server must reply to the packet with the same timestamp and its own
// good/late/lost/resync numbers. None of the fields is strictly required.
message Ping {
// Client timestamp. Server should not attempt to decode.
optional uint64 timestamp = 1;
// The amount of good packets received.
optional uint32 good = 2;
// The amount of late packets received.
optional uint32 late = 3;
// The amount of packets never received.
optional uint32 lost = 4;
// The amount of nonce resyncs.
optional uint32 resync = 5;
// The total amount of UDP packets received.
optional uint32 udp_packets = 6;
// The total amount of TCP packets received.
optional uint32 tcp_packets = 7;
// UDP ping average.
optional float udp_ping_avg = 8;
// UDP ping variance.
optional float udp_ping_var = 9;
// TCP ping average.
optional float tcp_ping_avg = 10;
// TCP ping variance.
optional float tcp_ping_var = 11;
}
// Sent by the server when it rejects the user connection.
message Reject {
enum RejectType {
// The rejection reason is unknown (details should be available
// in Reject.reason).
None = 0;
// The client attempted to connect with an incompatible version.
WrongVersion = 1;
// The user name supplied by the client was invalid.
InvalidUsername = 2;
// The client attempted to authenticate as a user with a password but it
// was wrong.
WrongUserPW = 3;
// The client attempted to connect to a passworded server but the password
// was wrong.
WrongServerPW = 4;
// Supplied username is already in use.
UsernameInUse = 5;
// Server is currently full and cannot accept more users.
ServerFull = 6;
// The user did not provide a certificate but one is required.
NoCertificate = 7;
AuthenticatorFail = 8;
}
// Rejection type.
optional RejectType type = 1;
// Human readable rejection reason.
optional string reason = 2;
}
// ServerSync message is sent by the server when it has authenticated the user
// and finished synchronizing the server state.
message ServerSync {
// The session of the current user.
optional uint32 session = 1;
// Maximum bandwidth that the user should use.
optional uint32 max_bandwidth = 2;
// Server welcome text.
optional string welcome_text = 3;
// Current user permissions in the root channel.
// Note: The permissions data type usually is uin32 (e.g. in PermissionQuery and PermissionDenied messages). Here
// it is uint64 because of an oversight in the past. Nonetheless it should never exceed the uin32 range.
// See also: https://github.com/mumble-voip/mumble/issues/5139
optional uint64 permissions = 4;
}
// Sent by the client when it wants a channel removed. Sent by the server when
// a channel has been removed and clients should be notified.
message ChannelRemove {
required uint32 channel_id = 1;
}
// Used to communicate channel properties between the client and the server.
// Sent by the server during the login process or when channel properties are
// updated. Client may use this message to update said channel properties.
message ChannelState {
// Unique ID for the channel within the server.
optional uint32 channel_id = 1;
// channel_id of the parent channel.
optional uint32 parent = 2;
// UTF-8 encoded channel name.
optional string name = 3;
// A collection of channel id values of the linked channels. Absent during
// the first channel listing.
repeated uint32 links = 4;
// UTF-8 encoded channel description. Only if the description is less than
// 128 bytes
optional string description = 5;
// A collection of channel_id values that should be added to links.
repeated uint32 links_add = 6;
// A collection of channel_id values that should be removed from links.
repeated uint32 links_remove = 7;
// True if the channel is temporary.
optional bool temporary = 8 [default = false];
// Position weight to tweak the channel position in the channel list.
optional int32 position = 9 [default = 0];
// SHA1 hash of the description if the description is 128 bytes or more.
optional bytes description_hash = 10;
// Maximum number of users allowed in the channel. If this value is zero,
// the maximum number of users allowed in the channel is given by the
// server's "usersperchannel" setting.
optional uint32 max_users = 11;
// Whether this channel has enter restrictions (ACL denying ENTER) set
optional bool is_enter_restricted = 12;
// Whether the receiver of this msg is considered to be able to enter this channel
optional bool can_enter = 13;
}
// Used to communicate user leaving or being kicked. May be sent by the client
// when it attempts to kick a user. Sent by the server when it informs the
// clients that a user is not present anymore.
message UserRemove {
// The user who is being kicked, identified by their session, not present
// when no one is being kicked.
required uint32 session = 1;
// The user who initiated the removal. Either the user who performs the kick
// or the user who is currently leaving.
optional uint32 actor = 2;
// Reason for the kick, stored as the ban reason if the user is banned.
optional string reason = 3;
// True if the kick should result in a ban.
optional bool ban = 4;
}
// Sent by the server when it communicates new and changed users to client.
// First seen during login procedure. May be sent by the client when it wishes
// to alter its state.
message UserState {
message VolumeAdjustment {
optional uint32 listening_channel = 1;
optional float volume_adjustment = 2;
}
// Unique user session ID of the user whose state this is, may change on
// reconnect.
optional uint32 session = 1;
// The session of the user who is updating this user.
optional uint32 actor = 2;
// User name, UTF-8 encoded.
optional string name = 3;
// Registered user ID if the user is registered.
optional uint32 user_id = 4;
// Channel on which the user is.
optional uint32 channel_id = 5;
// True if the user is muted by admin.
optional bool mute = 6;
// True if the user is deafened by admin.
optional bool deaf = 7;
// True if the user has been suppressed from talking by a reason other than
// being muted.
optional bool suppress = 8;
// True if the user has muted self.
optional bool self_mute = 9;
// True if the user has deafened self.
optional bool self_deaf = 10;
// User image if it is less than 128 bytes.
optional bytes texture = 11;
// The positional audio plugin identifier.
// Positional audio information is only sent to users who share
// identical plugin contexts.
//
// This value is not transmitted to clients.
optional bytes plugin_context = 12;
// The user's plugin-specific identity.
// This value is not transmitted to clients.
optional string plugin_identity = 13;
// User comment if it is less than 128 bytes.
optional string comment = 14;
// The hash of the user certificate.
optional string hash = 15;
// SHA1 hash of the user comment if it 128 bytes or more.
optional bytes comment_hash = 16;
// SHA1 hash of the user picture if it 128 bytes or more.
optional bytes texture_hash = 17;
// True if the user is a priority speaker.
optional bool priority_speaker = 18;
// True if the user is currently recording.
optional bool recording = 19;
// A list of temporary access tokens to be respected when processing this request.
repeated string temporary_access_tokens = 20;
// A list of channels the user wants to start listening to.
repeated uint32 listening_channel_add = 21;
// a list of channels the user does no longer want to listen to.
repeated uint32 listening_channel_remove = 22;
// A list of volume adjustments the user has applied to listeners
repeated VolumeAdjustment listening_volume_adjustment = 23;
}
// Relays information on the bans. The client may send the BanList message to
// either modify the list of bans or query them from the server. The server
// sends this list only after a client queries for it.
message BanList {
message BanEntry {
// Banned IP address.
required bytes address = 1;
// The length of the subnet mask for the ban.
required uint32 mask = 2;
// User name for identification purposes (does not affect the ban).
optional string name = 3;
// The certificate hash of the banned user.
optional string hash = 4;
// Reason for the ban (does not affect the ban).
optional string reason = 5;
// Ban start time.
optional string start = 6;
// Ban duration in seconds.
optional uint32 duration = 7;
}
// List of ban entries currently in place.
repeated BanEntry bans = 1;
// True if the server should return the list, false if it should replace old
// ban list with the one provided.
optional bool query = 2 [default = false];
}
// Used to send and broadcast text messages.
message TextMessage {
// The message sender, identified by its session.
optional uint32 actor = 1;
// Target users for the message, identified by their session.
repeated uint32 session = 2;
// The channels to which the message is sent, identified by their
// channel_ids.
repeated uint32 channel_id = 3;
// The root channels when sending message recursively to several channels,
// identified by their channel_ids.
repeated uint32 tree_id = 4;
// The UTF-8 encoded message. May be HTML if the server allows.
required string message = 5;
}
message PermissionDenied {
enum DenyType {
// Operation denied for other reason, see reason field.
Text = 0;
// Permissions were denied.
Permission = 1;
// Cannot modify SuperUser.
SuperUser = 2;
// Invalid channel name.
ChannelName = 3;
// Text message too long.
TextTooLong = 4;
// The flux capacitor was spelled wrong.
H9K = 5;
// Operation not permitted in temporary channel.
TemporaryChannel = 6;
// Operation requires certificate.
MissingCertificate = 7;
// Invalid username.
UserName = 8;
// Channel is full.
ChannelFull = 9;
// Channels are nested too deeply.
NestingLimit = 10;
// Maximum channel count reached.
ChannelCountLimit = 11;
// Amount of listener objects for this channel has been reached
ChannelListenerLimit = 12;
// Amount of listener proxies for the user has been reached
UserListenerLimit = 13;
}
// The denied permission when type is Permission.
optional uint32 permission = 1;
// channel_id for the channel where the permission was denied when type is
// Permission.
optional uint32 channel_id = 2;
// The user who was denied permissions, identified by session.
optional uint32 session = 3;
// Textual reason for the denial.
optional string reason = 4;
// Type of the denial.
optional DenyType type = 5;
// The name that is invalid when type is UserName.
optional string name = 6;
}
message ACL {
message ChanGroup {
// Name of the channel group, UTF-8 encoded.
required string name = 1;
// True if the group has been inherited from the parent (Read only).
optional bool inherited = 2 [default = true];
// True if the group members are inherited.
optional bool inherit = 3 [default = true];
// True if the group can be inherited by sub channels.
optional bool inheritable = 4 [default = true];
// Users explicitly included in this group, identified by user_id.
repeated uint32 add = 5;
// Users explicitly removed from this group in this channel if the group
// has been inherited, identified by user_id.
repeated uint32 remove = 6;
// Users inherited, identified by user_id.
repeated uint32 inherited_members = 7;
}
message ChanACL {
// True if this ACL applies to the current channel.
optional bool apply_here = 1 [default = true];
// True if this ACL applies to the sub channels.
optional bool apply_subs = 2 [default = true];
// True if the ACL has been inherited from the parent.
optional bool inherited = 3 [default = true];
// ID of the user that is affected by this ACL.
optional uint32 user_id = 4;
// ID of the group that is affected by this ACL.
optional string group = 5;
// Bit flag field of the permissions granted by this ACL.
optional uint32 grant = 6;
// Bit flag field of the permissions denied by this ACL.
optional uint32 deny = 7;
}
// Channel ID of the channel this message affects.
required uint32 channel_id = 1;
// True if the channel inherits its parent's ACLs.
optional bool inherit_acls = 2 [default = true];
// User group specifications.
repeated ChanGroup groups = 3;
// ACL specifications.
repeated ChanACL acls = 4;
// True if the message is a query for ACLs instead of setting them.
optional bool query = 5 [default = false];
}
// Client may use this message to refresh its registered user information. The
// client should fill the IDs or Names of the users it wants to refresh. The
// server fills the missing parts and sends the message back.
message QueryUsers {
// user_ids.
repeated uint32 ids = 1;
// User names in the same order as ids.
repeated string names = 2;
}
// Used to initialize and resync the UDP encryption. Either side may request a
// resync by sending the message without any values filled. The resync is
// performed by sending the message with only the client or server nonce
// filled.
message CryptSetup {
// Encryption key.
optional bytes key = 1;
// Client nonce.
optional bytes client_nonce = 2;
// Server nonce.
optional bytes server_nonce = 3;
}
// Used to add or remove custom context menu item on client-side.
message ContextActionModify {
enum Context {
// Action is applicable to the server.
Server = 0x01;
// Action can target a Channel.
Channel = 0x02;
// Action can target a User.
User = 0x04;
}
enum Operation {
Add = 0;
Remove = 1;
}
// The action identifier. Used later to initiate an action.
required string action = 1;
// The display name of the action.
optional string text = 2;
// Context bit flags defining where the action should be displayed.
// Flags can be OR-ed to combine different types.
optional uint32 context = 3;
// Choose either to add or to remove the context action.
// Note: This field only exists after Mumble 1.2.4-beta1 release.
// The message will be recognized as Add regardless of this field
// before said release.
optional Operation operation = 4;
}
// Sent by the client when it wants to initiate a Context action.
message ContextAction {
// The target User for the action, identified by session.
optional uint32 session = 1;
// The target Channel for the action, identified by channel_id.
optional uint32 channel_id = 2;
// The action that should be executed.
required string action = 3;
}
// Lists the registered users.
message UserList {
message User {
// Registered user ID.
required uint32 user_id = 1;
// Registered user name.
optional string name = 2;
optional string last_seen = 3;
optional uint32 last_channel = 4;
}
// A list of registered users.
repeated User users = 1;
}
// Sent by the client when it wants to register or clear whisper targets.
//
// Note: The first available target ID is 1 as 0 is reserved for normal
// talking. Maximum target ID is 30.
message VoiceTarget {
message Target {
// Users that are included as targets.
repeated uint32 session = 1;
// Channel that is included as a target.
optional uint32 channel_id = 2;
// ACL group that is included as a target.
optional string group = 3;
// True if the voice should follow links from the specified channel.
optional bool links = 4 [default = false];
// True if the voice should also be sent to children of the specific
// channel.
optional bool children = 5 [default = false];
}
// Voice target ID.
optional uint32 id = 1;
// The receivers that this voice target includes.
repeated Target targets = 2;
}
// Sent by the client when it wants permissions for a certain channel. Sent by
// the server when it replies to the query or wants the user to resync all
// channel permissions.
message PermissionQuery {
// channel_id of the channel for which the permissions are queried.
optional uint32 channel_id = 1;
// Channel permissions.
optional uint32 permissions = 2;
// True if the client should drop its current permission information for all
// channels.
optional bool flush = 3 [default = false];
}
// Sent by the server to notify the users of the version of the CELT codec they
// should use. This may change during the connection when new users join.
message CodecVersion {
// The version of the CELT Alpha codec.
required int32 alpha = 1;
// The version of the CELT Beta codec.
required int32 beta = 2;
// True if the user should prefer Alpha over Beta.
required bool prefer_alpha = 3 [default = true];
optional bool opus = 4 [default = false];
}
// Used to communicate user stats between the server and clients.
message UserStats {
message Stats {
// The amount of good packets received.
optional uint32 good = 1;
// The amount of late packets received.
optional uint32 late = 2;
// The amount of packets never received.
optional uint32 lost = 3;
// The amount of nonce resyncs.
optional uint32 resync = 4;
}
// User whose stats these are.
optional uint32 session = 1;
// True if the message contains only mutable stats (packets, ping).
optional bool stats_only = 2 [default = false];
// Full user certificate chain of the user certificate in DER format.
repeated bytes certificates = 3;
// Packet statistics for packets received from the client.
optional Stats from_client = 4;
// Packet statistics for packets sent by the server.
optional Stats from_server = 5;
// Amount of UDP packets sent.
optional uint32 udp_packets = 6;
// Amount of TCP packets sent.
optional uint32 tcp_packets = 7;
// UDP ping average.
optional float udp_ping_avg = 8;
// UDP ping variance.
optional float udp_ping_var = 9;
// TCP ping average.
optional float tcp_ping_avg = 10;
// TCP ping variance.
optional float tcp_ping_var = 11;
// Client version.
optional Version version = 12;
// A list of CELT bitstream version constants supported by the client of this
// user.
repeated int32 celt_versions = 13;
// Client IP address.
optional bytes address = 14;
// Bandwidth used by this client.
optional uint32 bandwidth = 15;
// Connection duration.
optional uint32 onlinesecs = 16;
// Duration since last activity.
optional uint32 idlesecs = 17;
// True if the user has a strong certificate.
optional bool strong_certificate = 18 [default = false];
optional bool opus = 19 [default = false];
}
// Used by the client to request binary data from the server. By default large
// comments or textures are not sent within standard messages but instead the
// hash is. If the client does not recognize the hash it may request the
// resource when it needs it. The client does so by sending a RequestBlob
// message with the correct fields filled with the user sessions or channel_ids
// it wants to receive. The server replies to this by sending a new
// UserState/ChannelState message with the resources filled even if they would
// normally be transmitted as hashes.
message RequestBlob {
// sessions of the requested UserState textures.
repeated uint32 session_texture = 1;
// sessions of the requested UserState comments.
repeated uint32 session_comment = 2;
// channel_ids of the requested ChannelState descriptions.
repeated uint32 channel_description = 3;
}
// Sent by the server when it informs the clients on server configuration
// details.
message ServerConfig {
// The maximum bandwidth the clients should use.
optional uint32 max_bandwidth = 1;
// Server welcome text.
optional string welcome_text = 2;
// True if the server allows HTML.
optional bool allow_html = 3;
// Maximum text message length.
optional uint32 message_length = 4;
// Maximum image message length.
optional uint32 image_message_length = 5;
// The maximum number of users allowed on the server.
optional uint32 max_users = 6;
// Whether using Mumble's recording feature is allowed on the server
optional bool recording_allowed = 7;
}
// Sent by the server to inform the clients of suggested client configuration
// specified by the server administrator.
message SuggestConfig {
// Suggested client version in the legacy format.
optional uint32 version_v1 = 1;
// Suggested client version in the new format.
// Necessary since patch level may exceed 255. (See https://github.com/mumble-voip/mumble/issues/5827)
optional uint64 version_v2 = 4;
// True if the administrator suggests positional audio to be used on this
// server.
optional bool positional = 2;
// True if the administrator suggests push to talk to be used on this server.
optional bool push_to_talk = 3;
}
// Used to send plugin messages between clients
message PluginDataTransmission {
// The session ID of the client this message was sent from
optional uint32 senderSession = 1;
// The session IDs of the clients that should receive this message
repeated uint32 receiverSessions = 2 [packed = true];
// The data that is sent
optional bytes data = 3;
// The ID of the sent data. This will be used by plugins to check whether they will
// process it or not
optional string dataID = 4;
}

22
src/audio_io.rs Normal file
View file

@ -0,0 +1,22 @@
use std::collections::HashMap;
use alsa::PCM;
pub trait AudioIO {
fn play(&self, sender_id: u32, buf: &[i16]);
fn capture(&self, buf: &mut [i16]);
}
pub struct Alsa {
output_streams: HashMap<u32, PCM>,
}
impl AudioIO for Alsa {
fn play(&self, sender_id: u32, buf: &[i16]) {
todo!()
}
fn capture(&self, buf: &mut [i16]) {
todo!()
}
}

473
src/main.rs Normal file
View file

@ -0,0 +1,473 @@
use std::{
collections::HashMap,
io::{Cursor, Read, Write},
net::TcpStream,
sync::Arc,
time::{Duration, Instant},
};
use alsa::{
pcm::{Access, Format, HwParams, State},
Direction, ValueOr, PCM,
};
use audio_io::AudioIO;
use opus::{Channels, Decoder};
use prost::Message;
use rustls::{
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
internal::msgs::codec::Codec,
ClientConfig, ClientConnection, RootCertStore, SignatureScheme, Stream,
};
use crate::mumble::{voice::VoiceData, ControlMessage};
mod audio_io;
mod mumble;
struct Client {
users: HashMap<u32, MumbleUser>,
user_audio: HashMap<u32, PCM>,
channels: HashMap<u32, MumbleChannel>,
conn: ClientConnection,
sock: TcpStream,
decoder: Decoder,
}
#[derive(Clone, Debug)]
struct MumbleUser {
name: String,
channel_id: u32,
mute: bool,
deaf: bool,
suppress: bool,
self_mute: bool,
self_deaf: bool,
priority_speaker: bool,
}
#[derive(Clone, Debug)]
struct MumbleChannel {
name: String,
parent: Option<u32>,
max_users: u32,
is_enter_restricted: bool,
can_enter: bool,
}
impl Client {
pub fn connect(server: &str) -> anyhow::Result<Self> {
let mut root_store = RootCertStore::empty();
let mut config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
config
.dangerous()
.set_certificate_verifier(Arc::new(Foo {}));
let mut conn = ClientConnection::new(Arc::new(config), "localhost".try_into()?)?;
let mut sock = TcpStream::connect(server)?;
let mut tls = Stream::new(&mut conn, &mut sock);
let msg = ControlMessage::Version(mumble::control::Version {
version_v1: Some(66815),
version_v2: Some(281492175388672),
release: Some("0.1.0".to_string()),
os: Some("RustMumble".to_string()),
os_version: Some("RustMumble".to_string()),
})
.encode();
tls.write_all(&msg)?;
let msg = ControlMessage::Authenticate(mumble::control::Authenticate {
username: Some("rust".to_string()),
password: None,
tokens: vec![],
celt_versions: vec![],
opus: Some(true),
client_type: Some(0),
})
.encode();
tls.write_all(&msg)?;
Ok(Self {
users: HashMap::new(),
user_audio: HashMap::new(),
channels: HashMap::new(),
conn,
sock,
decoder: Decoder::new(48000, Channels::Mono)?,
})
}
pub fn handle_incoming(&mut self) -> anyhow::Result<()> {
let mut tls = Stream::new(&mut self.conn, &mut self.sock);
match ControlMessage::decode(&mut tls)? {
ControlMessage::UserRemove(u) => {
self.users.remove(&u.session);
self.user_audio.remove(&u.session);
}
ControlMessage::UserState(u) => {
if let Some(user) = self.users.get_mut(&u.session()) {
if let Some(name) = &u.name {
user.name = name.clone();
}
if let Some(channel_id) = &u.channel_id {
user.channel_id = *channel_id;
}
if let Some(mute) = &u.mute {
user.mute = *mute;
}
if let Some(deaf) = &u.deaf {
user.deaf = *deaf;
}
if let Some(suppress) = &u.suppress {
user.suppress = *suppress;
}
if let Some(self_mute) = &u.self_mute {
user.self_mute = *self_mute;
}
if let Some(self_deaf) = &u.self_deaf {
user.self_deaf = *self_deaf;
}
if let Some(priority_speaker) = &u.priority_speaker {
user.priority_speaker = *priority_speaker;
}
} else {
let user = MumbleUser {
name: u.name().to_string(),
channel_id: u.channel_id(),
mute: u.mute(),
deaf: u.deaf(),
suppress: u.suppress(),
self_mute: u.self_mute(),
self_deaf: u.self_deaf(),
priority_speaker: u.priority_speaker(),
};
self.users.insert(u.session(), user);
};
}
ControlMessage::ChannelRemove(c) => {
self.channels.remove(&c.channel_id);
}
ControlMessage::ChannelState(c) => {
if let Some(channel) = self.channels.get_mut(&c.channel_id()) {
if let Some(name) = c.name {
channel.name = name.to_string();
}
if let Some(parent) = c.parent {
channel.parent = Some(parent);
}
if let Some(max_users) = c.max_users {
channel.max_users = max_users;
}
if let Some(is_enter_restricted) = c.is_enter_restricted {
channel.is_enter_restricted = is_enter_restricted;
}
if let Some(can_enter) = c.can_enter {
channel.can_enter = can_enter;
}
} else {
let channel = MumbleChannel {
name: c.name().to_string(),
parent: c.parent,
max_users: c.max_users(),
is_enter_restricted: c.is_enter_restricted(),
can_enter: c.can_enter(),
};
self.channels.insert(c.channel_id(), channel);
};
}
ControlMessage::UdpTunnel(packet) => match packet {
VoiceData::Opus { target, packet } => {
let mut buf: Vec<i16> = vec![0_i16; 16 * packet.payload.data.len()];
let len =
self.decoder
.decode(&packet.payload.data, buf.as_mut_slice(), false)?;
let buf = &buf[..len];
let pcm = match self.user_audio.get(&(packet.session_id.0 as u32)) {
Some(pcm) => pcm,
None => {
let pcm = PCM::new("default", Direction::Playback, true).unwrap();
{
// Set hardware parameters: 44100 Hz / Mono / 16 bit
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(1).unwrap();
hwp.set_rate(48000, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::RWInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
}
{
let hwp = pcm.hw_params_current().unwrap();
let swp = pcm.sw_params_current().unwrap();
swp.set_start_threshold(960 * 10).unwrap();
dbg!(hwp.get_buffer_size_min());
pcm.sw_params(&swp).unwrap();
}
println!("START");
self.user_audio.insert(packet.session_id.0 as u32, pcm);
self.user_audio.get(&(packet.session_id.0 as u32)).unwrap()
}
};
let io = pcm.io_i16()?;
dbg!(&io.writei(&buf));
println!("wrote");
if pcm.state() == State::Paused {}
if packet.payload.last {
println!("STOP");
pcm.wait(None)?;
}
}
_ => {}
},
x => {
dbg!(x);
}
}
Ok(())
}
}
pub trait endecode {
fn encode(&self) -> Vec<u8>;
fn decode(stream: &mut impl Read) -> anyhow::Result<Self>
where
Self: Sized;
}
fn main2() -> anyhow::Result<()> {
// Open default playback device
let pcm = PCM::new("default", Direction::Playback, false).unwrap();
{
// Set hardware parameters: 44100 Hz / Mono / 16 bit
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(1).unwrap();
hwp.set_rate(48000, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::RWInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
}
let io = pcm.io_i16().unwrap();
let hwp = pcm.hw_params_current().unwrap();
let swp = pcm.sw_params_current().unwrap();
swp.set_start_threshold(hwp.get_buffer_size().unwrap())
.unwrap();
pcm.sw_params(&swp).unwrap();
let pcm2 = PCM::new("default", Direction::Capture, false).unwrap();
{
// Set hardware parameters: 44100 Hz / Mono / 16 bit
let hwp = HwParams::any(&pcm2).unwrap();
hwp.set_channels(1).unwrap();
hwp.set_rate(48000, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::RWInterleaved).unwrap();
pcm2.hw_params(&hwp).unwrap();
}
let io2 = pcm2.io_i16().unwrap();
// // Make a sine wave
// let mut buf = [0i16; 512];
// for (i, a) in buf.iter_mut().enumerate() {
// *a = ((i as f32 * 8.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16
// }
//
// // Play it back for 2 seconds.
// for _ in 0..2 * 8000 / 512 {
// assert_eq!(io.writei(&buf[..]).unwrap(), 512);
// }
//
pcm2.start()?;
let mut buf = [0i16; 512];
loop {
let x = io2.readi(&mut buf)?;
buf.iter_mut().for_each(|x| *x = 2 * *x);
io.writei(&buf)?;
if pcm.state() != State::Running {
pcm.start().unwrap()
}
}
}
fn main() -> anyhow::Result<()> {
let mut client = Client::connect("127.0.0.1:64738")?;
loop {
client.handle_incoming();
}
let mut root_store = RootCertStore::empty();
let mut config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
config
.dangerous()
.set_certificate_verifier(Arc::new(Foo {}));
let mut conn = ClientConnection::new(Arc::new(config), "localhost".try_into()?)?;
let mut sock = TcpStream::connect("127.0.0.1:64738")?;
let mut tls = Stream::new(&mut conn, &mut sock);
println!("1");
dbg!(ControlMessage::decode(&mut tls));
println!("2");
let msg = ControlMessage::Version(mumble::control::Version {
version_v1: Some(66815),
version_v2: Some(281492175388672),
release: Some("0.1.0".to_string()),
os: Some("RustMumble".to_string()),
os_version: Some("RustMumble".to_string()),
})
.encode();
tls.write_all(&msg)?;
let msg = ControlMessage::Authenticate(mumble::control::Authenticate {
username: Some("rust".to_string()),
password: None,
tokens: vec![],
celt_versions: vec![],
opus: Some(true),
client_type: Some(0),
})
.encode();
tls.write_all(&msg)?;
let msg = ControlMessage::UserState(mumble::control::UserState {
channel_id: Some(1),
self_mute: Some(true),
..Default::default()
})
.encode();
tls.write_all(&msg)?;
let mut decoder = Decoder::new(48000, Channels::Mono)?;
let pcm = PCM::new("default", Direction::Playback, false).unwrap();
{
// Set hardware parameters: 44100 Hz / Mono / 16 bit
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(1).unwrap();
hwp.set_rate(48000, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::RWInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
}
let io = pcm.io_i16().unwrap();
let hwp = pcm.hw_params_current().unwrap();
let swp = pcm.sw_params_current().unwrap();
swp.set_start_threshold(hwp.get_buffer_size().unwrap())
.unwrap();
pcm.sw_params(&swp).unwrap();
let mut running = false;
let mut last_ping = Instant::now();
loop {
//TODO: non blocking
let cm = ControlMessage::decode(&mut tls)?;
match cm {
ControlMessage::UdpTunnel(x) => match x {
VoiceData::Opus { target, packet } => {
let mut buf: Vec<i16> = vec![0_i16; 16 * packet.payload.data.len()];
let len = decoder.decode(&packet.payload.data, buf.as_mut_slice(), false)?;
let buf = &buf[..len];
io.writei(&buf)?;
match pcm.state() {
State::Prepared if running => pcm.start()?,
State::Paused => pcm.pause(false)?,
_ => {}
}
running = true;
if packet.payload.last {
running = false;
pcm.pause(true)?;
}
}
_ => {}
},
x => {
dbg!(x);
}
}
if last_ping.elapsed() > Duration::from_secs(20) {
println!("TEST");
let msg = ControlMessage::Ping(mumble::control::Ping {
..Default::default()
})
.encode();
tls.write_all(&msg)?;
last_ping = Instant::now();
}
}
Ok(())
}
#[derive(Debug)]
struct Foo {}
impl ServerCertVerifier for Foo {
fn verify_server_cert(
&self,
end_entity: &rustls::pki_types::CertificateDer<'_>,
intermediates: &[rustls::pki_types::CertificateDer<'_>],
server_name: &rustls::pki_types::ServerName<'_>,
ocsp_response: &[u8],
now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
vec![
SignatureScheme::RSA_PKCS1_SHA1,
SignatureScheme::ECDSA_SHA1_Legacy,
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::ECDSA_NISTP521_SHA512,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::ED25519,
SignatureScheme::ED448,
]
}
}

340
src/mumble.rs Normal file
View file

@ -0,0 +1,340 @@
use std::io::{Cursor, Read};
use prost::Message;
use crate::endecode;
use self::voice::VoiceData;
pub mod control {
include!(concat!(env!("OUT_DIR"), "/mumble_proto.rs"));
}
pub mod voice {
use std::io::Read;
use crate::endecode;
#[derive(Debug)]
pub enum VoiceData {
CeltAlpha {
target: VoiceTarget,
packet: AudioDataPacket<CeltAlphaAudioPacket>,
},
Ping(u64),
Speex,
CeltBeta,
Opus {
target: VoiceTarget,
packet: AudioDataPacket<OpusAudioPacket>,
},
}
impl endecode for VoiceData {
fn encode(&self) -> Vec<u8> {
todo!()
}
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
let mut header = [0_u8; 1];
stream.read_exact(&mut header)?;
let header = header[0];
let target = VoiceTarget::from(header & 0b00011111);
let kind = (header >> 5) & 0b00000111;
Ok(match kind {
0 => Self::CeltAlpha {
target,
packet: AudioDataPacket::<CeltAlphaAudioPacket>::decode(stream)?,
},
1 => {
let mut timestamp = [0_u8; 8];
stream.read_exact(&mut timestamp)?;
Self::Ping(u64::from_be_bytes(timestamp))
}
2 => Self::Speex,
3 => Self::CeltBeta,
4 => Self::Opus {
target,
packet: AudioDataPacket::<OpusAudioPacket>::decode(stream)?,
},
_ => todo!(),
})
}
}
#[derive(Debug)]
pub enum VoiceTarget {
Normal,
Whisper(u8),
ServerLoopback,
}
impl From<u8> for VoiceTarget {
fn from(value: u8) -> Self {
match value {
0 => Self::Normal,
31 => Self::ServerLoopback,
x if x < 32 => Self::Whisper(x),
_ => todo!(),
}
}
}
#[derive(Debug)]
pub struct AudioDataPacket<T: endecode> {
pub session_id: VarI64,
pub seq_nr: VarI64,
pub payload: T,
//position_info: [f32; 3],
}
impl<T: endecode> endecode for AudioDataPacket<T> {
fn encode(&self) -> Vec<u8> {
todo!()
}
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
Ok(Self {
session_id: VarI64::decode(stream)?,
seq_nr: VarI64::decode(stream)?,
payload: T::decode(stream)?,
// position_info: {
// let mut f1 = [0_u8; 4];
// //stream.read_exact(&mut f1)?;
// let mut f2 = [0_u8; 4];
// //stream.read_exact(&mut f2)?;
// let mut f3 = [0_u8; 4];
// //stream.read_exact(&mut f3)?;
//
// [
// f32::from_be_bytes(f1),
// f32::from_be_bytes(f2),
// f32::from_be_bytes(f3),
// ]
// },
})
}
}
#[derive(Debug)]
pub struct OpusAudioPacket {
pub last: bool,
pub data: Vec<u8>,
}
impl endecode for OpusAudioPacket {
fn encode(&self) -> Vec<u8> {
todo!()
}
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
let length = VarI64::decode(stream)?.0;
let last = (length & 0x2000) == 0x2000;
let length = length & 0x1FFF;
let mut data = vec![0_u8; length as usize];
stream.read_exact(&mut data)?;
Ok(Self { last, data })
}
}
#[derive(Debug)]
pub struct CeltAlphaAudioPacket(pub Vec<u8>);
impl endecode for CeltAlphaAudioPacket {
fn encode(&self) -> Vec<u8> {
todo!()
}
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
let mut length = [0_u8; 1];
stream.read_exact(&mut length)?;
let length = length[0];
let continuation = ((length >> 7) & 0b1) == 1;
let length = length & 0b01111111;
let mut data = vec![0_u8; length as usize];
stream.read_exact(&mut data)?;
Ok(Self(data))
}
}
#[derive(Debug)]
pub struct VarI64(pub i64);
impl endecode for VarI64 {
fn encode(&self) -> Vec<u8> {
let oct = self.0.to_be_bytes();
match self.0 {
x if x < (1 << 7) && x >= 0 => vec![oct[7] & 0b01111111 + 0b00000000],
x if x < (1 << 14) && x >= 0 => vec![oct[6] & 0b00111111 + 0b10000000, oct[7]],
x if x < (1 << 21) && x >= 0 => {
vec![oct[5] & 0b00011111 + 0b11000000, oct[6], oct[7]]
}
x if x < (1 << 28) && x >= 0 => {
vec![oct[4] & 0b00001111 + 0b11100000, oct[5], oct[6], oct[7]]
}
x if x < (1 << 32) && x >= 0 => vec![0b11110000, oct[4], oct[5], oct[6], oct[7]],
x if x >= 0 => vec![
0b11110100, oct[0], oct[1], oct[2], oct[3], oct[4], oct[5], oct[6], oct[7],
],
x if x < 0 && x > -4 => vec![0b11111100 + ((!(-(oct[7] as i8) as u8)) & 0b11)],
x => [&[0b11111100], VarI64(-self.0).encode().as_slice()].concat(),
}
}
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
let mut first = [0_u8; 1];
stream.read_exact(&mut first)?;
let first = first[0];
Ok(Self(match first {
x if ((x >> 7) & 0b00000001) == 0 => (x as i64) & 0b01111111,
x if ((x >> 6) & 0b00000011) == 0b10 => {
let mut next = [0_u8; 1];
stream.read_exact(&mut next)?;
i64::from_be_bytes([0, 0, 0, 0, 0, 0, x & 0b00111111, next[0]])
}
x if ((x >> 5) & 0b00000111) == 0b110 => {
let mut next = [0_u8; 2];
stream.read_exact(&mut next)?;
i64::from_be_bytes([0, 0, 0, 0, 0, x & 0b00011111, next[0], next[1]])
}
x if ((x >> 4) & 0b00001111) == 0b1110 => {
let mut next = [0_u8; 3];
stream.read_exact(&mut next)?;
i64::from_be_bytes([0, 0, 0, 0, x & 0b00001111, next[0], next[1], next[2]])
}
x if ((x >> 2) & 0b00111111) == 0b111100 => {
let mut next = [0_u8; 4];
stream.read_exact(&mut next)?;
i64::from_be_bytes([0, 0, 0, 0, next[0], next[1], next[2], next[3]])
}
x if ((x >> 2) & 0b00111111) == 0b111101 => {
let mut next = [0_u8; 8];
stream.read_exact(&mut next)?;
i64::from_be_bytes(next)
}
x if ((x >> 2) & 0b00111111) == 0b111110 => -(Self::decode(stream)?.0),
x if ((x >> 2) & 0b00111111) == 0b111111 => -!((x as i64) & 0b1111111),
_ => todo!(),
}))
}
}
}
#[derive(Debug)]
pub enum ControlMessage {
Version(control::Version),
UdpTunnel(VoiceData),
Authenticate(control::Authenticate),
Ping(control::Ping),
Reject(control::Reject),
ServerSync(control::ServerSync),
ChannelRemove(control::ChannelRemove),
ChannelState(control::ChannelState),
UserRemove(control::UserRemove),
UserState(control::UserState),
BanList(control::BanList),
TextMessage(control::TextMessage),
PermissionDenied(control::PermissionDenied),
Acl(control::Acl),
QueryUsers(control::QueryUsers),
CryptSetup(control::CryptSetup),
ContextActionModify(control::ContextActionModify),
ContextAction(control::ContextAction),
UserList(control::UserList),
VoiceTarget(control::VoiceTarget),
PermissionQuery(control::PermissionQuery),
CodecVersion(control::CodecVersion),
UserStats(control::UserStats),
RequestBlob(control::RequestBlob),
ServerConfig(control::ServerConfig),
SuggestConfig(control::SuggestConfig),
}
impl endecode for ControlMessage {
fn decode(stream: &mut impl Read) -> anyhow::Result<Self> {
let mut kind = [0_u8; 2];
stream.read_exact(&mut kind)?;
let kind = u16::from_be_bytes(kind);
let mut length = [0_u8; 4];
stream.read_exact(&mut length)?;
let length = u32::from_be_bytes(length);
let mut data = vec![0_u8; length as usize];
stream.read_exact(&mut data)?;
let mut data = Cursor::new(data);
macro_rules! cm {
($x:ident) => {
Self::$x(control::$x::decode(data.get_ref().as_slice())?)
};
}
Ok(match kind {
0 => cm!(Version),
1 => Self::UdpTunnel(VoiceData::decode(&mut data)?),
2 => cm!(Authenticate),
3 => cm!(Ping),
4 => cm!(Reject),
5 => cm!(ServerSync),
6 => cm!(ChannelRemove),
7 => cm!(ChannelState),
8 => cm!(UserRemove),
9 => cm!(UserState),
10 => cm!(BanList),
11 => cm!(TextMessage),
12 => cm!(PermissionDenied),
13 => cm!(Acl),
14 => cm!(QueryUsers),
15 => cm!(CryptSetup),
16 => cm!(ContextActionModify),
17 => cm!(ContextAction),
18 => cm!(UserList),
19 => cm!(VoiceTarget),
20 => cm!(PermissionQuery),
21 => cm!(CodecVersion),
22 => cm!(UserStats),
23 => cm!(RequestBlob),
24 => cm!(ServerConfig),
25 => cm!(SuggestConfig),
_ => todo!(),
})
}
fn encode(&self) -> Vec<u8> {
let (kind, data): (u16, Vec<u8>) = match self {
Self::Version(x) => (0, x.encode_to_vec()),
Self::UdpTunnel(x) => (1, x.encode()),
Self::Authenticate(x) => (2, x.encode_to_vec()),
Self::Ping(x) => (3, x.encode_to_vec()),
Self::Reject(x) => (4, x.encode_to_vec()),
Self::ServerSync(x) => (5, x.encode_to_vec()),
Self::ChannelRemove(x) => (6, x.encode_to_vec()),
Self::ChannelState(x) => (7, x.encode_to_vec()),
Self::UserRemove(x) => (8, x.encode_to_vec()),
Self::UserState(x) => (9, x.encode_to_vec()),
Self::BanList(x) => (10, x.encode_to_vec()),
Self::TextMessage(x) => (11, x.encode_to_vec()),
Self::PermissionDenied(x) => (12, x.encode_to_vec()),
Self::Acl(x) => (13, x.encode_to_vec()),
Self::QueryUsers(x) => (14, x.encode_to_vec()),
Self::CryptSetup(x) => (15, x.encode_to_vec()),
Self::ContextActionModify(x) => (16, x.encode_to_vec()),
Self::ContextAction(x) => (17, x.encode_to_vec()),
Self::UserList(x) => (18, x.encode_to_vec()),
Self::VoiceTarget(x) => (19, x.encode_to_vec()),
Self::PermissionQuery(x) => (20, x.encode_to_vec()),
Self::CodecVersion(x) => (21, x.encode_to_vec()),
Self::UserStats(x) => (22, x.encode_to_vec()),
Self::RequestBlob(x) => (23, x.encode_to_vec()),
Self::ServerConfig(x) => (24, x.encode_to_vec()),
Self::SuggestConfig(x) => (25, x.encode_to_vec()),
};
let length = (data.len() as u32).to_be_bytes();
let kind = kind.to_be_bytes();
[kind.as_slice(), length.as_slice(), &data].concat()
}
}