Message States, Tauri, Global and Local Settings

This commit is contained in:
Alex Emmet 2026-06-03 20:55:15 +02:00
commit a4ec766351
2 changed files with 376 additions and 153 deletions

162
Cargo.lock generated
View file

@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b"
dependencies = [ dependencies = [
"actix-rt", "actix-rt",
"bitflags 2.11.1", "bitflags 2.12.1",
"bytes", "bytes",
"crossbeam-channel", "crossbeam-channel",
"futures-core", "futures-core",
@ -31,7 +31,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"bytes", "bytes",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -54,7 +54,7 @@ dependencies = [
"actix-tls", "actix-tls",
"actix-utils", "actix-utils",
"base64", "base64",
"bitflags 2.11.1", "bitflags 2.12.1",
"brotli", "brotli",
"bytes", "bytes",
"bytestring", "bytestring",
@ -211,7 +211,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"smallvec", "smallvec",
"socket2 0.6.3", "socket2 0.6.4",
"time", "time",
"tracing", "tracing",
"url", "url",
@ -474,9 +474,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.1" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -498,9 +498,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "8.0.2" version = "8.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@ -509,9 +509,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli-decompressor" name = "brotli-decompressor"
version = "5.0.0" version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@ -570,9 +570,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.62" version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@ -699,9 +699,9 @@ dependencies = [
[[package]] [[package]]
name = "compact_str" name = "compact_str"
version = "0.9.0" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab"
dependencies = [ dependencies = [
"castaway", "castaway",
"cfg-if", "cfg-if",
@ -832,7 +832,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"crossterm_winapi", "crossterm_winapi",
"derive_more", "derive_more",
"document-features", "document-features",
@ -1039,9 +1039,9 @@ dependencies = [
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1421,7 +1421,7 @@ dependencies = [
"fnv", "fnv",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.4.0", "http 1.4.1",
"indexmap", "indexmap",
"slab", "slab",
"tokio", "tokio",
@ -1479,7 +1479,7 @@ dependencies = [
"base64", "base64",
"bytes", "bytes",
"headers-core", "headers-core",
"http 1.4.0", "http 1.4.1",
"httpdate", "httpdate",
"mime", "mime",
"sha1 0.10.6", "sha1 0.10.6",
@ -1491,7 +1491,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
dependencies = [ dependencies = [
"http 1.4.0", "http 1.4.1",
] ]
[[package]] [[package]]
@ -1543,9 +1543,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0"
dependencies = [ dependencies = [
"bytes", "bytes",
"itoa", "itoa",
@ -1558,7 +1558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [ dependencies = [
"bytes", "bytes",
"http 1.4.0", "http 1.4.1",
] ]
[[package]] [[package]]
@ -1569,7 +1569,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"pin-project-lite", "pin-project-lite",
] ]
@ -1597,16 +1597,16 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.9.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2 0.4.14", "h2 0.4.14",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"httparse", "httparse",
"httpdate", "httpdate",
@ -1623,7 +1623,7 @@ version = "0.27.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
dependencies = [ dependencies = [
"http 1.4.0", "http 1.4.1",
"hyper", "hyper",
"hyper-util", "hyper-util",
"rustls", "rustls",
@ -1642,14 +1642,14 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"hyper", "hyper",
"ipnet", "ipnet",
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.6.3", "socket2 0.6.4",
"system-configuration", "system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
@ -2276,7 +2276,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
] ]
[[package]] [[package]]
@ -2325,9 +2325,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.29" version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f"
[[package]] [[package]]
name = "lru" name = "lru"
@ -2366,9 +2366,9 @@ dependencies = [
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.0" version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]] [[package]]
name = "memmem" name = "memmem"
@ -2419,9 +2419,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.2.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -2452,7 +2452,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -2544,7 +2544,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
] ]
[[package]] [[package]]
@ -2622,7 +2622,7 @@ version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@ -3073,7 +3073,7 @@ dependencies = [
"quinn-udp", "quinn-udp",
"rustc-hash", "rustc-hash",
"rustls", "rustls",
"socket2 0.6.3", "socket2 0.6.4",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tracing", "tracing",
@ -3111,7 +3111,7 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
"once_cell", "once_cell",
"socket2 0.6.3", "socket2 0.6.4",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
@ -3239,7 +3239,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"compact_str", "compact_str",
"hashbrown 0.16.1", "hashbrown 0.16.1",
"indoc", "indoc",
@ -3291,7 +3291,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"hashbrown 0.16.1", "hashbrown 0.16.1",
"indoc", "indoc",
"instability", "instability",
@ -3323,7 +3323,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
] ]
[[package]] [[package]]
@ -3363,16 +3363,16 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.13.3" version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"h2 0.4.14", "h2 0.4.14",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
@ -3429,7 +3429,7 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"fallible-iterator", "fallible-iterator",
"fallible-streaming-iterator", "fallible-streaming-iterator",
"hashlink", "hashlink",
@ -3468,7 +3468,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -3493,9 +3493,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-native-certs" name = "rustls-native-certs"
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 = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
dependencies = [ dependencies = [
"openssl-probe", "openssl-probe",
"rustls-pki-types", "rustls-pki-types",
@ -3609,7 +3609,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"core-foundation 0.10.1", "core-foundation 0.10.1",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -3744,9 +3744,9 @@ dependencies = [
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.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 = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
@ -3831,9 +3831,9 @@ dependencies = [
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.6.3" 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 = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@ -3841,9 +3841,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlite-wasm-rs" name = "sqlite-wasm-rs"
version = "0.5.4" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdd578e94101503d97e2b286bbf8db2135035ca24b2ce4cbf3f9e2fb2bbf1eee" checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
dependencies = [ dependencies = [
"cc", "cc",
"js-sys", "js-sys",
@ -3976,7 +3976,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"core-foundation 0.9.4", "core-foundation 0.9.4",
"system-configuration-sys", "system-configuration-sys",
] ]
@ -4033,7 +4033,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
"bitflags 2.11.1", "bitflags 2.12.1",
"fancy-regex", "fancy-regex",
"filedescriptor", "filedescriptor",
"finl_unicode", "finl_unicode",
@ -4177,7 +4177,7 @@ dependencies = [
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2 0.6.3", "socket2 0.6.4",
"tokio-macros", "tokio-macros",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
@ -4261,10 +4261,10 @@ version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"bytes", "bytes",
"futures-util", "futures-util",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"pin-project-lite", "pin-project-lite",
"tower", "tower",
@ -4326,7 +4326,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "ttp-core" name = "ttp-core"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.methanium.net/Tensamin/TTP.git#886edbf51d26636e46745c3637b80610df82f5dc" source = "git+https://git.methanium.net/Tensamin/TTP.git#23438fa8f884e6ad0d32ca1004c0dedcce0cc8d2"
dependencies = [ dependencies = [
"base64", "base64",
"byteorder", "byteorder",
@ -4339,7 +4339,7 @@ dependencies = [
[[package]] [[package]]
name = "ttp-native" name = "ttp-native"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.methanium.net/Tensamin/TTP.git#886edbf51d26636e46745c3637b80610df82f5dc" source = "git+https://git.methanium.net/Tensamin/TTP.git#23438fa8f884e6ad0d32ca1004c0dedcce0cc8d2"
dependencies = [ dependencies = [
"quinn", "quinn",
"rustls", "rustls",
@ -4358,7 +4358,7 @@ checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
dependencies = [ dependencies = [
"bytes", "bytes",
"data-encoding", "data-encoding",
"http 1.4.0", "http 1.4.1",
"httparse", "httparse",
"log", "log",
"native-tls", "native-tls",
@ -4369,9 +4369,9 @@ dependencies = [
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.20.0" version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]] [[package]]
name = "ucd-trie" name = "ucd-trie"
@ -4393,9 +4393,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.13.2" version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]] [[package]]
name = "unicode-truncate" name = "unicode-truncate"
@ -4468,9 +4468,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.23.1" version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
dependencies = [ dependencies = [
"atomic", "atomic",
"getrandom 0.4.2", "getrandom 0.4.2",
@ -4527,7 +4527,7 @@ dependencies = [
"bytes", "bytes",
"futures-util", "futures-util",
"headers", "headers",
"http 1.4.0", "http 1.4.1",
"http-body", "http-body",
"http-body-util", "http-body-util",
"log", "log",
@ -4652,7 +4652,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.12.1",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap", "indexmap",
"semver", "semver",
@ -5191,7 +5191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.11.1", "bitflags 2.12.1",
"indexmap", "indexmap",
"log", "log",
"serde", "serde",
@ -5241,7 +5241,7 @@ dependencies = [
"rustls-native-certs", "rustls-native-certs",
"rustls-pki-types", "rustls-pki-types",
"sha2 0.11.0", "sha2 0.11.0",
"socket2 0.6.3", "socket2 0.6.4",
"thiserror 2.0.18", "thiserror 2.0.18",
"time", "time",
"tokio", "tokio",
@ -5327,18 +5327,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.48" version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.48" version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -2,14 +2,13 @@ use dashmap::DashMap;
use iota_logger::{log, log_cv_in, log_cv_out, log_t}; use iota_logger::{log, log_cv_in, log_cv_out, log_t};
use iota_state::{ACTIVE_TASKS, SHUTDOWN}; use iota_state::{ACTIVE_TASKS, SHUTDOWN};
use iota_storage::users::contact::Contact; use iota_storage::users::contact::Contact;
use iota_storage::util::chat_files::{MessageState, change_message_state}; use iota_storage::util::chat_files::{self, MessageState, change_message_state};
use iota_storage::util::chats_util::{get_user, mod_user}; use iota_storage::util::chats_util::{self, get_user, mod_user};
use iota_storage::util::communities_util::CommunitiesUtil; use iota_storage::util::communities_util::CommunitiesUtil;
use iota_storage::util::config_util::CONFIG; use iota_storage::util::config_util::CONFIG;
use iota_storage::util::{chat_files, chats_util};
use iota_util::crypto_helper; use iota_util::crypto_helper;
use iota_util::crypto_util::{DataFormat, SecurePayload}; use iota_util::crypto_util::{DataFormat, SecurePayload};
use iota_util::file_util::{get_children, load_file, save_file}; use iota_util::file_util::{get_children, has_file, load_file, save_file};
use json::JsonValue; use json::JsonValue;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
@ -21,6 +20,18 @@ use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use ttp_native::{Policy, Receiver, SendMode, Sender}; use ttp_native::{Policy, Receiver, SendMode, Sender};
use uuid::Uuid; use uuid::Uuid;
// Helper function to check if read receipts are enabled globally
async fn is_read_receipts_enabled() -> bool {
// Check global config for read receipts setting
// Default to true if not set
let conf = CONFIG.read().await;
let value = conf.get("read_receipts_enabled");
match value {
JsonValue::Boolean(b) => *b,
_ => true,
}
}
// ============================================================================ // ============================================================================
// Configuration // Configuration
// ============================================================================ // ============================================================================
@ -933,7 +944,7 @@ impl OmikronConnection {
// Attempt delivery and await a response from the local client // Attempt delivery and await a response from the local client
let user_resp = self let user_resp = self
.clone() .clone()
.await_response(&user_forward, Some(Duration::from_secs(10))) .await_response(&user_forward, Some(Duration::from_secs(3)))
.await; .await;
if let Ok(user_resp) = user_resp { if let Ok(user_resp) = user_resp {
@ -959,23 +970,25 @@ impl OmikronConnection {
ms.clone(), ms.clone(),
); );
// notify original sender about the delivered/read state // notify original sender about the delivered/read state (if read receipts are enabled)
self.send_message( if is_read_receipts_enabled().await {
&CommunicationValue::new(CommunicationType::message_state) self.send_message(
.with_id(cv.get_id()) &CommunicationValue::new(CommunicationType::message_state)
.with_receiver(sender_id as u64) .with_id(cv.get_id())
.with_sender(receiver_id as u64) .with_receiver(sender_id as u64)
.add_data( .with_sender(receiver_id as u64)
DataTypes::chat_partner_id, .add_data(
DataValue::Number(receiver_id as i64), DataTypes::chat_partner_id,
) DataValue::Number(receiver_id as i64),
.add_data(DataTypes::send_time, DataValue::Number(timestamp_i64)) )
.add_data( .add_data(DataTypes::send_time, DataValue::Number(timestamp_i64))
DataTypes::message_state, .add_data(
DataValue::Str(ms.as_str().to_string()), DataTypes::message_state,
), DataValue::Str(ms.as_str().to_string()),
) ),
.await; )
.await;
}
} else { } else {
// Delivery failed or timed out; mark as Sent // Delivery failed or timed out; mark as Sent
let _ = chat_files::change_message_state( let _ = chat_files::change_message_state(
@ -1072,7 +1085,7 @@ impl OmikronConnection {
let user_resp = self let user_resp = self
.clone() .clone()
.await_response(&user_forward, Some(Duration::from_secs(10))) .await_response(&user_forward, Some(Duration::from_secs(3)))
.await; .await;
if let Ok(user_resp) = user_resp { if let Ok(user_resp) = user_resp {
@ -1089,22 +1102,25 @@ impl OmikronConnection {
ms.clone(), ms.clone(),
); );
self.send_message( // notify original sender about the delivered/read state (if read receipts are enabled)
&CommunicationValue::new(CommunicationType::message_state) if is_read_receipts_enabled().await {
.with_id(cv.get_id()) self.send_message(
.with_receiver(*sender_id) &CommunicationValue::new(CommunicationType::message_state)
.with_sender(*receiver_id) .with_id(cv.get_id())
.add_data(DataTypes::send_time, DataValue::Number(timestamp)) .with_receiver(*sender_id)
.add_data( .with_sender(*receiver_id)
DataTypes::chat_partner_id, .add_data(DataTypes::send_time, DataValue::Number(timestamp))
DataValue::Number(*sender_id as i64), .add_data(
) DataTypes::chat_partner_id,
.add_data( DataValue::Number(*sender_id as i64),
DataTypes::message_state, )
DataValue::Str(ms.as_str().to_string()), .add_data(
), DataTypes::message_state,
) DataValue::Str(ms.as_str().to_string()),
.await; ),
)
.await;
}
} else { } else {
// Delivery timed out/failed — update stored state and notify sender with numeric timestamp // Delivery timed out/failed — update stored state and notify sender with numeric timestamp
let _ = chat_files::change_message_state( let _ = chat_files::change_message_state(
@ -1306,20 +1322,145 @@ impl OmikronConnection {
return; return;
} }
if cv.is_type(CommunicationType::settings_save) { if cv.is_type(CommunicationType::global_settings_save) {
let my_id = cv.get_sender(); let my_id = cv.get_sender();
let settings_name = cv.get_data(DataTypes::settings_name).as_str().unwrap(); let Some(settings_value) = cv.get_data(DataTypes::payload).as_str() else {
let settings_value = cv.get_data(DataTypes::payload).as_str().unwrap(); let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing settings payload".to_string()),
);
self.send_message(&response).await;
return;
};
save_file( save_file(
&format!("users/{}/settings/", my_id), &format!("users/{}", my_id),
"global.settings",
settings_value,
);
let mut response = CommunicationValue::new(CommunicationType::global_settings_save)
.with_receiver(my_id)
.with_id(cv.get_id());
if let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() {
response = response.add_data(DataTypes::session_id, DataValue::Number(session_id));
}
self.send_message(&response).await;
return;
}
if cv.is_type(CommunicationType::global_settings_load) {
let my_id = cv.get_sender();
let path = format!("users/{}", my_id);
let name = "global.settings";
if !has_file(&path, name) {
let mut response = CommunicationValue::new(CommunicationType::error_not_found)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(DataTypes::path, DataValue::Str(name.to_string()));
if let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() {
response =
response.add_data(DataTypes::session_id, DataValue::Number(session_id));
}
self.send_message(&response).await;
return;
}
let settings_value_str = load_file(&path, name);
let mut response = CommunicationValue::new(CommunicationType::global_settings_load)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(DataTypes::payload, DataValue::Str(settings_value_str));
if let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() {
response = response.add_data(DataTypes::session_id, DataValue::Number(session_id));
}
self.send_message(&response).await;
return;
}
if cv.is_type(CommunicationType::settings_save) {
let my_id = cv.get_sender();
let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() else {
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing session_id".to_string()),
);
self.send_message(&response).await;
return;
};
let Some(settings_name) = cv.get_data(DataTypes::settings_name).as_str() else {
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing settings_name".to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
};
let Some(settings_value) = cv.get_data(DataTypes::payload).as_str() else {
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing settings payload".to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
};
if !settings_name
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '.')
|| settings_name.contains("..")
{
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Invalid settings_name".to_string()),
)
.add_data(
DataTypes::settings_name,
DataValue::Str(settings_name.to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
}
save_file(
&format!("users/{}/settings/{}/", my_id, session_id),
&format!("{}.settings", settings_name), &format!("{}.settings", settings_name),
&settings_value, settings_value,
); );
let response = CommunicationValue::new(CommunicationType::settings_save) let response = CommunicationValue::new(CommunicationType::settings_save)
.with_receiver(my_id) .with_receiver(my_id)
.with_id(cv.get_id()); .with_id(cv.get_id())
.add_data(
DataTypes::settings_name,
DataValue::Str(settings_name.to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await; self.send_message(&response).await;
return; return;
@ -1327,16 +1468,76 @@ impl OmikronConnection {
if cv.is_type(CommunicationType::settings_load) { if cv.is_type(CommunicationType::settings_load) {
let my_id = cv.get_sender(); let my_id = cv.get_sender();
let settings_name = cv.get_data(DataTypes::settings_name).as_string().unwrap(); let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() else {
let settings_value_str = load_file( let response = CommunicationValue::new(CommunicationType::error_invalid_data)
&format!("users/{}/settings/", my_id), .with_id(cv.get_id())
&format!("{}.settings", settings_name), .with_receiver(my_id)
); .add_data(
DataTypes::message,
DataValue::Str("Missing session_id".to_string()),
);
self.send_message(&response).await;
return;
};
let Some(settings_name) = cv.get_data(DataTypes::settings_name).as_str() else {
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing settings_name".to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
};
if !settings_name
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '.')
|| settings_name.contains("..")
{
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Invalid settings_name".to_string()),
)
.add_data(
DataTypes::settings_name,
DataValue::Str(settings_name.to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
}
let settings_file = format!("{}.settings", settings_name);
let settings_path = format!("users/{}/settings/{}/", my_id, session_id);
if !has_file(&settings_path, &settings_file) {
let response = CommunicationValue::new(CommunicationType::error_not_found)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::settings_name,
DataValue::Str(settings_name.to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await;
return;
}
let settings_value_str = load_file(&settings_path, &settings_file);
let response = CommunicationValue::new(CommunicationType::settings_load) let response = CommunicationValue::new(CommunicationType::settings_load)
.with_id(cv.get_id()) .with_id(cv.get_id())
.with_receiver(my_id) .with_receiver(my_id)
.add_data(DataTypes::payload, DataValue::Str(settings_value_str)) .add_data(DataTypes::payload, DataValue::Str(settings_value_str))
.add_data(DataTypes::settings_name, DataValue::Str(settings_name)); .add_data(
DataTypes::settings_name,
DataValue::Str(settings_name.to_string()),
)
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await; self.send_message(&response).await;
return; return;
@ -1344,7 +1545,19 @@ impl OmikronConnection {
if cv.is_type(CommunicationType::settings_list) { if cv.is_type(CommunicationType::settings_list) {
let my_id = cv.get_sender(); let my_id = cv.get_sender();
let settings = get_children(&format!("users/{}/settings/", my_id)); let Some(session_id) = cv.get_data(DataTypes::session_id).as_number() else {
let response = CommunicationValue::new(CommunicationType::error_invalid_data)
.with_id(cv.get_id())
.with_receiver(my_id)
.add_data(
DataTypes::message,
DataValue::Str("Missing session_id".to_string()),
);
self.send_message(&response).await;
return;
};
let settings = get_children(&format!("users/{}/settings/{}/", my_id, session_id));
let mut settings_json = Vec::new(); let mut settings_json = Vec::new();
for s in settings { for s in settings {
let s = s.replace(".settings", ""); let s = s.replace(".settings", "");
@ -1356,7 +1569,8 @@ impl OmikronConnection {
let response = CommunicationValue::new(CommunicationType::settings_list) let response = CommunicationValue::new(CommunicationType::settings_list)
.with_id(cv.get_id()) .with_id(cv.get_id())
.with_receiver(my_id) .with_receiver(my_id)
.add_data(DataTypes::settings, DataValue::Array(settings_json)); .add_data(DataTypes::settings, DataValue::Array(settings_json))
.add_data(DataTypes::session_id, DataValue::Number(session_id));
self.send_message(&response).await; self.send_message(&response).await;
return; return;
@ -1365,29 +1579,38 @@ impl OmikronConnection {
async fn handle_challenge(&self, cv: &CommunicationValue) { async fn handle_challenge(&self, cv: &CommunicationValue) {
let conf = CONFIG.read().await; let conf = CONFIG.read().await;
let private_key = conf.get_private_key().unwrap(); let Some(private_key) = conf.get_private_key() else {
drop(conf);
log_t!("omikron_challenge_decryption_failed");
*self.auth_failure.write().await = Some(
"Challenge decryption failed: no private key configured on this Iota.".to_string(),
);
return;
};
drop(conf); drop(conf);
let omikron_public_key = cv.get_data(DataTypes::public_key).as_str().unwrap(); let Some(omikron_public_key) = cv.get_data(DataTypes::public_key).as_str() else {
let encrypted_challenge = cv.get_data(DataTypes::challenge).as_str().unwrap(); log_t!("omikron_challenge_decryption_failed");
return;
let solved_challenge = {
if let Ok(decrypted) = SecurePayload::new(
encrypted_challenge,
DataFormat::Base64,
crypto_helper::load_secret_key(&private_key).unwrap(),
) {
if let Ok(decrypted) = decrypted
.decrypt_x448(crypto_helper::load_public_key(omikron_public_key).unwrap())
{
Some(decrypted)
} else {
None
}
} else {
None
}
}; };
let Some(encrypted_challenge) = cv.get_data(DataTypes::challenge).as_str() else {
log_t!("omikron_challenge_decryption_failed");
return;
};
let Some(secret_key) = crypto_helper::load_secret_key(&private_key) else {
log_t!("omikron_challenge_decryption_failed");
return;
};
let Some(pub_key) = crypto_helper::load_public_key(omikron_public_key) else {
log_t!("omikron_challenge_decryption_failed");
return;
};
let solved_challenge =
SecurePayload::new(encrypted_challenge, DataFormat::Base64, secret_key)
.ok()
.and_then(|decrypted| decrypted.decrypt_x448(pub_key).ok());
if let Some(decrypted) = solved_challenge { if let Some(decrypted) = solved_challenge {
let solved = decrypted.export(DataFormat::Raw); let solved = decrypted.export(DataFormat::Raw);