diff --git a/src/Cargo.lock b/src/Cargo.lock index bf1adc3..020c388 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -10,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -169,13 +169,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.92" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.2", ] [[package]] @@ -288,7 +288,7 @@ dependencies = [ [[package]] name = "bos-settings" -version = "0.8.2" +version = "0.8.0" dependencies = [ "anyhow", "bread-theme", @@ -307,8 +307,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.7.4" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.4#fcba3760387e2523edb71350f8efea3bc851b21e" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "dirs 5.0.1", "serde", @@ -410,9 +410,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.5" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" dependencies = [ "serde_core", ] @@ -437,7 +437,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.20", + "thiserror 2.0.19", ] [[package]] @@ -452,9 +452,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" dependencies = [ "find-msvc-tools", "shlex", @@ -526,9 +526,9 @@ dependencies = [ [[package]] name = "cookie" -version = "0.18.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "time", "version_check", @@ -701,37 +701,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.20", -] - [[package]] name = "deranged" version = "0.5.8" @@ -828,13 +797,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", ] [[package]] @@ -935,7 +904,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.3+spec-1.1.0", "vswhom", "winreg", ] @@ -1002,10 +971,11 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.2" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ + "concurrent-queue", "parking", "pin-project-lite", ] @@ -1057,9 +1027,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" @@ -1095,13 +1065,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", ] [[package]] @@ -1130,24 +1100,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -1156,9 +1126,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-lite" @@ -1175,32 +1145,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-core", "futures-io", @@ -1436,9 +1406,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gobject-sys" @@ -1551,9 +1521,9 @@ dependencies = [ [[package]] name = "http" -version = "1.5.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1571,9 +1541,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -1673,9 +1643,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", @@ -1687,9 +1657,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1700,9 +1670,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1714,17 +1684,16 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1735,15 +1704,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1844,9 +1813,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "is-docker" @@ -1896,59 +1865,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "jiff" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "jiff-tzdb-platform", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", - "windows-link 0.2.1", -] - -[[package]] -name = "jiff-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" -dependencies = [ - "defmt", -] - -[[package]] -name = "jiff-static" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" -dependencies = [ - "jiff-tzdb", -] - [[package]] name = "jni" version = "0.21.1" @@ -1995,9 +1911,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -2039,9 +1955,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -2083,9 +1999,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.189" +version = "0.2.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "22053b6a34f84abc97f9129e61334f40174659a1b9bd18c970b83db6a9a6348b" [[package]] name = "libdbus-sys" @@ -2108,9 +2024,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.20" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -2123,9 +2039,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lock_api" @@ -2213,7 +2129,7 @@ dependencies = [ "once_cell", "png 0.18.1", "serde", - "thiserror 2.0.20", + "thiserror 2.0.19", "windows-sys 0.61.2", ] @@ -2516,9 +2432,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "open" -version = "5.4.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cfef937e9c486488c7e3d949ae31c0f1d06bdacd75b99c086cb35356e30408" +checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5" dependencies = [ "dunce", "is-wsl", @@ -2673,9 +2589,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plist" @@ -2730,26 +2646,11 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "portable-atomic" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" - -[[package]] -name = "portable-atomic-util" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" -dependencies = [ - "portable-atomic", -] - [[package]] name = "potential_utf" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2891,7 +2792,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.20", + "thiserror 2.0.19", ] [[package]] @@ -2911,7 +2812,7 @@ checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.2", ] [[package]] @@ -2928,9 +2829,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.18" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -3073,9 +2974,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -3169,7 +3070,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.2", ] [[package]] @@ -3204,7 +3105,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.2", ] [[package]] @@ -3227,9 +3128,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.22.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", "bs58", @@ -3237,9 +3138,8 @@ dependencies = [ "hex", "indexmap 1.9.3", "indexmap 2.14.0", - "jiff", "schemars 0.9.0", - "schemars 1.2.2", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -3248,9 +3148,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.22.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling", "proc-macro2", @@ -3468,9 +3368,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" dependencies = [ "proc-macro2", "quote", @@ -3552,9 +3452,9 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", @@ -3609,7 +3509,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.20", + "thiserror 2.0.19", "tokio", "tray-icon", "url", @@ -3660,7 +3560,7 @@ dependencies = [ "sha2", "syn 2.0.119", "tauri-utils", - "thiserror 2.0.20", + "thiserror 2.0.19", "time", "url", "uuid", @@ -3711,7 +3611,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 2.0.20", + "thiserror 2.0.19", "url", ] @@ -3734,8 +3634,8 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.20", - "toml 1.1.4+spec-1.1.0", + "thiserror 2.0.19", + "toml 1.1.3+spec-1.1.0", "url", ] @@ -3755,7 +3655,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.20", + "thiserror 2.0.19", "url", "windows", "zbus", @@ -3779,7 +3679,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror 2.0.20", + "thiserror 2.0.19", "url", "webkit2gtk", "webview2-com", @@ -3842,8 +3742,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.20", - "toml 1.1.4+spec-1.1.0", + "thiserror 2.0.19", + "toml 1.1.3+spec-1.1.0", "url", "urlpattern", "uuid", @@ -3858,7 +3758,7 @@ checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" dependencies = [ "dunce", "embed-resource", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.3+spec-1.1.0", ] [[package]] @@ -3894,11 +3794,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.20", + "thiserror-impl 2.0.19", ] [[package]] @@ -3914,20 +3814,20 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.2", ] [[package]] name = "time" -version = "0.3.55" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", "num-conv", @@ -3955,9 +3855,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3996,13 +3896,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", ] [[package]] @@ -4047,9 +3947,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.4+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -4137,9 +4037,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.3+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow 1.0.4", ] @@ -4234,9 +4134,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.24.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" dependencies = [ "crossbeam-channel", "dirs 6.0.0", @@ -4250,7 +4150,7 @@ dependencies = [ "once_cell", "png 0.18.1", "serde", - "thiserror 2.0.20", + "thiserror 2.0.19", "windows-sys 0.61.2", ] @@ -4369,9 +4269,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.24.1" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4447,9 +4347,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -4460,9 +4360,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.77" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -4470,9 +4370,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4480,9 +4380,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -4493,9 +4393,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -4515,9 +4415,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.104" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -4525,9 +4425,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" dependencies = [ "phf", "phf_codegen", @@ -4610,7 +4510,7 @@ version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ - "thiserror 2.0.20", + "thiserror 2.0.19", "windows", "windows-core 0.61.2", ] @@ -5160,9 +5060,9 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wry" @@ -5197,7 +5097,7 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 2.0.20", + "thiserror 2.0.19", "url", "webkit2gtk", "webkit2gtk-sys", @@ -5254,9 +5154,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.19.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" dependencies = [ "async-broadcast", "async-executor", @@ -5289,14 +5189,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.19.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", "zbus_names", "zvariant", "zvariant_utils", @@ -5313,15 +5213,6 @@ dependencies = [ "zvariant", ] -[[package]] -name = "zcheapstr" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" -dependencies = [ - "serde", -] - [[package]] name = "zerofrom" version = "0.1.8" @@ -5345,9 +5236,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -5356,9 +5247,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.7" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -5367,13 +5258,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.4" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47402523226a02bfe5230160dc3ccc089aa6f6f19e7fcbb4e6f824bbb1b4aa62" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", ] [[package]] @@ -5384,41 +5275,40 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zvariant" -version = "5.14.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e28c25bd8bb8da5a1f3e7065d0c156b9ee9a7973adf78b0e35eaefdf3b1b5c" +checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" dependencies = [ "endi", "enumflags2", "serde", "winnow 1.0.4", - "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.14.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d496a145685283b67e232bd9e47377f6b60ad9d51e3601b23867f77c42477f96" +checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 3.0.3", + "syn 2.0.119", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "4.0.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629d80ece222cad20fe0e8741be493c4ab166acf3b85341bdc2cdbcfd8f3c2d6" +checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" dependencies = [ "proc-macro2", "quote", "serde", - "syn 3.0.3", + "syn 2.0.119", "winnow 1.0.4", ] diff --git a/src/Cargo.toml b/src/Cargo.toml index c74d506..bbd9ae4 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bos-settings" -version = "0.8.2" +version = "0.8.0" description = "System settings app for BOS (Bread Operating System)" authors = ["Breadway"] edition = "2021" @@ -36,7 +36,7 @@ toml_edit = "0.22" tokio = { version = "1", features = ["process", "io-util", "time", "macros"] } notify = "7" regex = "1" -bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.4" } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["toml"] } anyhow = "1" diff --git a/src/capabilities/default.json b/src/capabilities/default.json index e895c6b..778bfb5 100644 --- a/src/capabilities/default.json +++ b/src/capabilities/default.json @@ -5,6 +5,7 @@ "windows": ["main"], "permissions": [ "core:default", + "opener:default", "dialog:default" ] } diff --git a/src/src/commands/about.rs b/src/src/commands/about.rs index b847a85..7ec9f56 100644 --- a/src/src/commands/about.rs +++ b/src/src/commands/about.rs @@ -23,10 +23,8 @@ fn os_pretty_name() -> String { fs::read_to_string("/etc/os-release") .ok() .and_then(|s| { - s.lines().find_map(|l| { - l.strip_prefix("PRETTY_NAME=") - .map(|v| v.trim_matches('"').to_string()) - }) + s.lines() + .find_map(|l| l.strip_prefix("PRETTY_NAME=").map(|v| v.trim_matches('"').to_string())) }) .unwrap_or_else(|| "BOS".to_string()) } @@ -51,15 +49,11 @@ fn cpu() -> String { let model = fs::read_to_string("/proc/cpuinfo") .ok() .and_then(|s| { - s.lines().find_map(|l| { - l.strip_prefix("model name") - .map(|v| v.trim_start_matches([':', ' ', '\t']).to_string()) - }) + s.lines() + .find_map(|l| l.strip_prefix("model name").map(|v| v.trim_start_matches([':', ' ', '\t']).to_string())) }) .unwrap_or_else(|| "unknown".to_string()); - let cores = std::thread::available_parallelism() - .map(|n| n.get()) - .unwrap_or(0); + let cores = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(0); if cores > 0 { format!("{model} ({cores} threads)") } else { @@ -68,12 +62,14 @@ fn cpu() -> String { } fn memory() -> String { - let kb = fs::read_to_string("/proc/meminfo").ok().and_then(|s| { - s.lines() - .find(|l| l.starts_with("MemTotal:")) - .and_then(|l| l.split_whitespace().nth(1)) - .and_then(|v| v.parse::().ok()) - }); + let kb = fs::read_to_string("/proc/meminfo") + .ok() + .and_then(|s| { + s.lines() + .find(|l| l.starts_with("MemTotal:")) + .and_then(|l| l.split_whitespace().nth(1)) + .and_then(|v| v.parse::().ok()) + }); match kb { Some(kb) => format!("{:.1} GiB", kb as f64 / 1024.0 / 1024.0), None => "unknown".to_string(), @@ -100,11 +96,7 @@ async fn gpu() -> String { } async fn disk_usage() -> String { - let Ok(output) = Command::new("df") - .args(["-h", "--output=used,size,pcent", "/"]) - .output() - .await - else { + let Ok(output) = Command::new("df").args(["-h", "--output=used,size,pcent", "/"]).output().await else { return "unknown".to_string(); }; let text = String::from_utf8_lossy(&output.stdout); @@ -144,35 +136,11 @@ pub async fn get_system_info() -> SystemInfo { } } -/// RFC 1123 labels (digit start allowed), no leading `-`. Linux static -/// hostnames are also capped at `HOST_NAME_MAX` (64). -fn valid_hostname(name: &str) -> bool { - let name = name.trim(); - if name.is_empty() || name.len() > 64 || name.starts_with('-') { - return false; - } - if name.contains('\n') || name.contains('\r') || name.contains('\0') { - return false; - } - name.split('.').all(valid_dns_label) -} - -fn valid_dns_label(label: &str) -> bool { - let b = label.as_bytes(); - if b.is_empty() || b.len() > 63 { - return false; - } - if !b[0].is_ascii_alphanumeric() || !b[b.len() - 1].is_ascii_alphanumeric() { - return false; - } - b.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'-') -} - #[tauri::command] pub async fn set_hostname(name: String) -> Result<(), String> { let name = name.trim(); - if !valid_hostname(name) { - return Err("invalid hostname".into()); + if name.is_empty() { + return Err("Hostname can't be empty".into()); } let output = Command::new("pkexec") .args(["hostnamectl", "set-hostname", name]) @@ -185,24 +153,3 @@ pub async fn set_hostname(name: String) -> Result<(), String> { Err(String::from_utf8_lossy(&output.stderr).trim().to_string()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn hostname_rfc1123() { - assert!(valid_hostname("bos")); - assert!(valid_hostname("bos.local")); - assert!(valid_hostname("a1-b")); - assert!(valid_hostname("1host")); - assert!(!valid_hostname("")); - assert!(!valid_hostname("-bos")); - assert!(!valid_hostname("bos-")); - assert!(!valid_hostname("-foo.bar")); - assert!(!valid_hostname("foo_bar")); - assert!(!valid_hostname("bos\n-set-hostname evil")); - assert!(!valid_hostname("--help")); - assert!(!valid_hostname(&"a".repeat(65))); - } -} diff --git a/src/src/commands/aur.rs b/src/src/commands/aur.rs index 6c72b64..226af63 100644 --- a/src/src/commands/aur.rs +++ b/src/src/commands/aur.rs @@ -10,8 +10,6 @@ use serde::Serialize; -use super::util; - #[derive(Serialize, Clone)] pub struct AurResult { name: String, @@ -21,11 +19,7 @@ pub struct AurResult { #[tauri::command] pub async fn search_aur(query: String) -> Vec { - let Ok(output) = tokio::process::Command::new("yay") - .args(["-Ss", "--aur", &query]) - .output() - .await - else { + let Ok(output) = tokio::process::Command::new("yay").args(["-Ss", "--aur", &query]).output().await else { return Vec::new(); }; let text = String::from_utf8_lossy(&output.stdout); @@ -34,18 +28,12 @@ pub async fn search_aur(query: String) -> Vec { while let Some(header) = lines.next() { // "aur/name version (+votes score) [Orphaned]" — name/version are // always the first two whitespace-separated fields after "aur/". - let Some(rest) = header.strip_prefix("aur/") else { - continue; - }; + let Some(rest) = header.strip_prefix("aur/") else { continue }; let mut parts = rest.split_whitespace(); let Some(name) = parts.next() else { continue }; let version = parts.next().unwrap_or("").to_string(); let description = lines.next().unwrap_or("").trim().to_string(); - results.push(AurResult { - name: name.to_string(), - version, - description, - }); + results.push(AurResult { name: name.to_string(), version, description }); if results.len() >= 50 { break; } @@ -54,13 +42,6 @@ pub async fn search_aur(query: String) -> Vec { } #[tauri::command] -pub fn install_aur_package(pkg: String) -> Result<(), String> { - if !util::valid_pkg_name(&pkg) { - return Err(format!("refusing to install '{pkg}'")); - } - std::process::Command::new("kitty") - .args(["-e", "yay", "-S", &pkg]) - .spawn() - .map_err(|e| e.to_string())?; - Ok(()) +pub fn install_aur_package(pkg: String) { + let _ = std::process::Command::new("kitty").args(["-e", "yay", "-S", &pkg]).spawn(); } diff --git a/src/src/commands/breadpad.rs b/src/src/commands/breadpad.rs index 19db123..5f0fb5d 100644 --- a/src/src/commands/breadpad.rs +++ b/src/src/commands/breadpad.rs @@ -35,35 +35,22 @@ pub struct BreadpadConfig { pub fn get_breadpad_config() -> BreadpadConfig { let doc = config::load_doc(&config_path()); BreadpadConfig { - default_type: config::get_str(&doc, &["settings", "default_type"]) - .unwrap_or_else(|| "note".into()), + default_type: config::get_str(&doc, &["settings", "default_type"]).unwrap_or_else(|| "note".into()), workspace_tag: config::get_bool(&doc, &["settings", "workspace_tag"]).unwrap_or(true), snooze_options: config::get_str_list(&doc, &["settings", "snooze_options"]), - archive_after_days: config::get_i64(&doc, &["settings", "archive_after_days"]) - .unwrap_or(30), + archive_after_days: config::get_i64(&doc, &["settings", "archive_after_days"]).unwrap_or(30), model_path: config::get_str(&doc, &["model", "path"]).unwrap_or_default(), tokenizer_path: config::get_str(&doc, &["model", "tokenizer"]).unwrap_or_default(), ollama_enabled: config::get_bool(&doc, &["model", "ollama", "enabled"]).unwrap_or(true), - ollama_endpoint: config::get_str(&doc, &["model", "ollama", "endpoint"]) - .unwrap_or_default(), + ollama_endpoint: config::get_str(&doc, &["model", "ollama", "endpoint"]).unwrap_or_default(), ollama_model: config::get_str(&doc, &["model", "ollama", "model"]).unwrap_or_default(), - ollama_confidence_threshold: config::get_f64( - &doc, - &["model", "ollama", "confidence_threshold"], - ) - .unwrap_or(0.6), - reminders_default_morning: config::get_str(&doc, &["reminders", "default_morning"]) - .unwrap_or_else(|| "7:00".into()), - reminders_missed_grace_minutes: config::get_i64( - &doc, - &["reminders", "missed_grace_minutes"], - ) - .unwrap_or(60), + ollama_confidence_threshold: config::get_f64(&doc, &["model", "ollama", "confidence_threshold"]).unwrap_or(0.6), + reminders_default_morning: config::get_str(&doc, &["reminders", "default_morning"]).unwrap_or_else(|| "7:00".into()), + reminders_missed_grace_minutes: config::get_i64(&doc, &["reminders", "missed_grace_minutes"]).unwrap_or(60), calendar_enabled: config::get_bool(&doc, &["calendar", "enabled"]).unwrap_or(false), calendar_url: config::get_str(&doc, &["calendar", "url"]).unwrap_or_default(), calendar_username: config::get_str(&doc, &["calendar", "username"]).unwrap_or_default(), - // Write-only to the webview, same as restic — never round-trip the secret. - calendar_password: String::new(), + calendar_password: config::get_str(&doc, &["calendar", "password"]).unwrap_or_default(), } } @@ -73,76 +60,19 @@ pub fn save_breadpad_config(cfg: BreadpadConfig) -> Result<(), String> { let mut doc = config::load_doc(&path); config::set_str(&mut doc, &["settings", "default_type"], &cfg.default_type); config::set_bool(&mut doc, &["settings", "workspace_tag"], cfg.workspace_tag); - config::set_str_list( - &mut doc, - &["settings", "snooze_options"], - &cfg.snooze_options, - ); - config::set_i64( - &mut doc, - &["settings", "archive_after_days"], - cfg.archive_after_days, - ); + config::set_str_list(&mut doc, &["settings", "snooze_options"], &cfg.snooze_options); + config::set_i64(&mut doc, &["settings", "archive_after_days"], cfg.archive_after_days); config::set_str_or_remove(&mut doc, &["model", "path"], &cfg.model_path); config::set_str_or_remove(&mut doc, &["model", "tokenizer"], &cfg.tokenizer_path); - config::set_bool( - &mut doc, - &["model", "ollama", "enabled"], - cfg.ollama_enabled, - ); - config::set_str_or_remove( - &mut doc, - &["model", "ollama", "endpoint"], - &cfg.ollama_endpoint, - ); + config::set_bool(&mut doc, &["model", "ollama", "enabled"], cfg.ollama_enabled); + config::set_str_or_remove(&mut doc, &["model", "ollama", "endpoint"], &cfg.ollama_endpoint); config::set_str_or_remove(&mut doc, &["model", "ollama", "model"], &cfg.ollama_model); - config::set_f64( - &mut doc, - &["model", "ollama", "confidence_threshold"], - cfg.ollama_confidence_threshold, - ); - config::set_str_or_remove( - &mut doc, - &["reminders", "default_morning"], - &cfg.reminders_default_morning, - ); - config::set_i64( - &mut doc, - &["reminders", "missed_grace_minutes"], - cfg.reminders_missed_grace_minutes, - ); + config::set_f64(&mut doc, &["model", "ollama", "confidence_threshold"], cfg.ollama_confidence_threshold); + config::set_str_or_remove(&mut doc, &["reminders", "default_morning"], &cfg.reminders_default_morning); + config::set_i64(&mut doc, &["reminders", "missed_grace_minutes"], cfg.reminders_missed_grace_minutes); config::set_bool(&mut doc, &["calendar", "enabled"], cfg.calendar_enabled); config::set_str_or_remove(&mut doc, &["calendar", "url"], &cfg.calendar_url); config::set_str_or_remove(&mut doc, &["calendar", "username"], &cfg.calendar_username); - apply_calendar_password(&mut doc, &cfg.calendar_password); + config::set_str_or_remove(&mut doc, &["calendar", "password"], &cfg.calendar_password); config::save_doc(&path, &doc).map_err(|e| e.to_string()) } - -/// Empty incoming password keeps the existing secret (PasswordField is write-only). -fn apply_calendar_password(doc: &mut toml_edit::DocumentMut, incoming: &str) { - if incoming.is_empty() { - return; - } - config::set_str(doc, &["calendar", "password"], incoming); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn empty_password_keeps_existing_secret() { - let mut doc: toml_edit::DocumentMut = - "[calendar]\npassword = \"secret\"\n".parse().unwrap(); - apply_calendar_password(&mut doc, ""); - assert_eq!( - config::get_str(&doc, &["calendar", "password"]).as_deref(), - Some("secret") - ); - apply_calendar_password(&mut doc, "newpass"); - assert_eq!( - config::get_str(&doc, &["calendar", "password"]).as_deref(), - Some("newpass") - ); - } -} diff --git a/src/src/commands/datetime.rs b/src/src/commands/datetime.rs index 5413cc2..2e8660b 100644 --- a/src/src/commands/datetime.rs +++ b/src/src/commands/datetime.rs @@ -26,12 +26,7 @@ async fn list_timezones() -> Vec { .output() .await .ok() - .map(|o| { - String::from_utf8_lossy(&o.stdout) - .lines() - .map(str::to_string) - .collect() - }) + .map(|o| String::from_utf8_lossy(&o.stdout).lines().map(str::to_string).collect()) .unwrap_or_default() } @@ -65,32 +60,10 @@ pub async fn get_datetime_info() -> DateTimeInfo { } } -/// Reject flags, path traversal, and newlines before we ever exec. Charset -/// matches IANA names (`Area/City`, `UTC`, `Etc/GMT+6`). -fn timezone_looks_safe(tz: &str) -> bool { - let tz = tz.trim(); - if tz.is_empty() || tz.len() > 64 || tz.starts_with('-') { - return false; - } - if tz.contains('\n') || tz.contains('\r') || tz.contains('\0') || tz.contains("..") { - return false; - } - tz.chars() - .all(|c| c.is_ascii_alphanumeric() || matches!(c, '/' | '_' | '+' | '-')) -} - #[tauri::command] pub async fn set_timezone(tz: String) -> Result<(), String> { - let tz = tz.trim(); - if !timezone_looks_safe(tz) { - return Err("invalid timezone".into()); - } - let listed = list_timezones().await; - if !listed.is_empty() && !listed.iter().any(|t| t == tz) { - return Err("unknown timezone".into()); - } let output = Command::new("pkexec") - .args(["timedatectl", "set-timezone", tz]) + .args(["timedatectl", "set-timezone", &tz]) .output() .await .map_err(|e| e.to_string())?; @@ -104,28 +77,6 @@ pub async fn set_timezone(tz: String) -> Result<(), String> { #[tauri::command] pub async fn set_ntp_enabled(enabled: bool) -> Result<(), String> { let val = if enabled { "true" } else { "false" }; - Command::new("pkexec") - .args(["timedatectl", "set-ntp", val]) - .status() - .await - .map_err(|e| e.to_string())?; + Command::new("pkexec").args(["timedatectl", "set-ntp", val]).status().await.map_err(|e| e.to_string())?; Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn timezone_rejects_flags_and_traversal() { - assert!(timezone_looks_safe("UTC")); - assert!(timezone_looks_safe("America/New_York")); - assert!(timezone_looks_safe("Etc/GMT+6")); - assert!(!timezone_looks_safe("")); - assert!(!timezone_looks_safe("-UTC")); - assert!(!timezone_looks_safe("--help")); - assert!(!timezone_looks_safe("America/../UTC")); - assert!(!timezone_looks_safe("UTC\n--adjust")); - assert!(!timezone_looks_safe("UTC;reboot")); - } -} diff --git a/src/src/commands/firewall.rs b/src/src/commands/firewall.rs index bfc6471..0841d7b 100644 --- a/src/src/commands/firewall.rs +++ b/src/src/commands/firewall.rs @@ -32,9 +32,7 @@ pub async fn get_firewall_status() -> Result { let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); return Err(if stderr.is_empty() { match output.status.code() { - Some(127) => { - "no polkit authentication agent is available in this session".to_string() - } + Some(127) => "no polkit authentication agent is available in this session".to_string(), Some(code) => format!("pkexec exited with status {code}"), None => "pkexec was terminated by a signal".to_string(), } @@ -43,10 +41,7 @@ pub async fn get_firewall_status() -> Result { }); } let text = String::from_utf8_lossy(&output.stdout); - let active = text - .lines() - .next() - .is_some_and(|l| l.trim() == "Status: active"); + let active = text.lines().next().is_some_and(|l| l.trim() == "Status: active"); let rules = text .lines() .filter_map(|l| { @@ -56,10 +51,7 @@ pub async fn get_firewall_status() -> Result { } let (num, rest) = l.split_once(']')?; let number = num.trim_start_matches('[').trim().to_string(); - Some(FirewallRule { - number, - text: rest.trim().to_string(), - }) + Some(FirewallRule { number, text: rest.trim().to_string() }) }) .collect(); Ok(FirewallStatus { active, rules }) @@ -68,11 +60,7 @@ pub async fn get_firewall_status() -> Result { #[tauri::command] pub async fn set_firewall_enabled(enabled: bool) -> Result<(), String> { let verb = if enabled { "enable" } else { "disable" }; - let output = Command::new("pkexec") - .args(["ufw", "--force", verb]) - .output() - .await - .map_err(|e| e.to_string())?; + let output = Command::new("pkexec").args(["ufw", "--force", verb]).output().await.map_err(|e| e.to_string())?; if output.status.success() { Ok(()) } else { @@ -80,54 +68,9 @@ pub async fn set_firewall_enabled(enabled: bool) -> Result<(), String> { } } -/// Port, optional `/tcp`/`/udp`, or optional space-separated proto. No -/// service names, IPs, or flags — those become extra `ufw allow` operands. -fn valid_firewall_rule(rule: &str) -> bool { - let rule = rule.trim(); - if rule.is_empty() || rule.len() > 16 || rule.starts_with('-') { - return false; - } - if rule.contains('\n') || rule.contains('\r') || rule.contains('\0') { - return false; - } - let (port, proto) = if let Some((p, rest)) = rule.split_once('/') { - (p, Some(rest)) - } else if let Some((p, rest)) = rule.split_once(' ') { - (p, Some(rest.trim())) - } else { - (rule, None) - }; - let Ok(n) = port.parse::() else { - return false; - }; - if n == 0 { - return false; - } - match proto { - None => true, - Some(p) => p == "tcp" || p == "udp", - } -} - -fn valid_rule_number(number: &str) -> bool { - let t = number.trim(); - !t.is_empty() - && t.len() <= 8 - && t.bytes().all(|b| b.is_ascii_digit()) - && t.parse::().is_ok_and(|n| n > 0) -} - #[tauri::command] pub async fn add_firewall_rule(rule: String) -> Result<(), String> { - let rule = rule.trim(); - if !valid_firewall_rule(rule) { - return Err("invalid firewall rule".into()); - } - let output = Command::new("pkexec") - .args(["ufw", "allow", rule]) - .output() - .await - .map_err(|e| e.to_string())?; + let output = Command::new("pkexec").args(["ufw", "allow", rule.trim()]).output().await.map_err(|e| e.to_string())?; if output.status.success() { Ok(()) } else { @@ -137,48 +80,10 @@ pub async fn add_firewall_rule(rule: String) -> Result<(), String> { #[tauri::command] pub async fn remove_firewall_rule(number: String) -> Result<(), String> { - if !valid_rule_number(&number) { - return Err("invalid rule number".into()); - } - let output = Command::new("pkexec") - .args(["ufw", "--force", "delete", number.trim()]) - .output() - .await - .map_err(|e| e.to_string())?; + let output = Command::new("pkexec").args(["ufw", "--force", "delete", &number]).output().await.map_err(|e| e.to_string())?; if output.status.success() { Ok(()) } else { Err(String::from_utf8_lossy(&output.stderr).trim().to_string()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn firewall_rule_is_port_and_optional_proto() { - assert!(valid_firewall_rule("22")); - assert!(valid_firewall_rule("8080/tcp")); - assert!(valid_firewall_rule("53/udp")); - assert!(valid_firewall_rule("80 tcp")); - assert!(!valid_firewall_rule("OpenSSH")); - assert!(!valid_firewall_rule("-f")); - assert!(!valid_firewall_rule("22;id")); - assert!(!valid_firewall_rule("22/tcp\nallow 23")); - assert!(!valid_firewall_rule("0")); - assert!(!valid_firewall_rule("65536")); - assert!(!valid_firewall_rule("22/all")); - } - - #[test] - fn firewall_delete_is_positive_int() { - assert!(valid_rule_number("1")); - assert!(valid_rule_number("12")); - assert!(!valid_rule_number("0")); - assert!(!valid_rule_number("-1")); - assert!(!valid_rule_number("1;2")); - assert!(!valid_rule_number("1\n2")); - assert!(!valid_rule_number("")); - } -} diff --git a/src/src/commands/power.rs b/src/src/commands/power.rs index d1d6820..3d82def 100644 --- a/src/src/commands/power.rs +++ b/src/src/commands/power.rs @@ -7,22 +7,13 @@ use serde::Serialize; use tokio::process::Command; -use super::util; - async fn upower_device(kind: &str) -> Option { let out = Command::new("upower").arg("-e").output().await.ok()?; - String::from_utf8_lossy(&out.stdout) - .lines() - .find(|l| l.to_lowercase().contains(kind)) - .map(str::to_string) + String::from_utf8_lossy(&out.stdout).lines().find(|l| l.to_lowercase().contains(kind)).map(str::to_string) } async fn upower_field(device: &str, field: &str) -> Option { - let out = Command::new("upower") - .args(["-i", device]) - .output() - .await - .ok()?; + let out = Command::new("upower").args(["-i", device]).output().await.ok()?; let text = String::from_utf8_lossy(&out.stdout); text.lines() .find(|l| l.trim_start().starts_with(field)) @@ -48,18 +39,12 @@ async fn battery_summary() -> Vec<(String, String)> { if let Some(t) = t { rows.push(("Time remaining".to_string(), t)); } - let full: Option = upower_field(&bat, "energy-full") - .await - .and_then(|v| v.split_whitespace().next()?.parse().ok()); - let design: Option = upower_field(&bat, "energy-full-design") - .await - .and_then(|v| v.split_whitespace().next()?.parse().ok()); + let full: Option = upower_field(&bat, "energy-full").await.and_then(|v| v.split_whitespace().next()?.parse().ok()); + let design: Option = + upower_field(&bat, "energy-full-design").await.and_then(|v| v.split_whitespace().next()?.parse().ok()); if let (Some(full), Some(design)) = (full, design) { if design > 0.0 { - rows.push(( - "Battery health".to_string(), - format!("{:.0}% of design capacity", full / design * 100.0), - )); + rows.push(("Battery health".to_string(), format!("{:.0}% of design capacity", full / design * 100.0))); } } rows @@ -79,19 +64,12 @@ async fn power_source() -> String { async fn tlp_profile() -> Option { let out = Command::new("tlp-stat").arg("-s").output().await.ok()?; let text = String::from_utf8_lossy(&out.stdout); - text.lines() - .find(|l| l.trim_start().starts_with("TLP profile")) - .and_then(|l| l.split('=').nth(1)) - .map(|v| v.trim().to_string()) + text.lines().find(|l| l.trim_start().starts_with("TLP profile")).and_then(|l| l.split('=').nth(1)).map(|v| v.trim().to_string()) } async fn brightness_device() -> Option { let out = Command::new("brightnessctl").output().await.ok()?; - String::from_utf8_lossy(&out.stdout) - .lines() - .find(|l| l.starts_with("Device")) - .and_then(|l| l.split('\'').nth(1)) - .map(str::to_string) + String::from_utf8_lossy(&out.stdout).lines().find(|l| l.starts_with("Device")).and_then(|l| l.split('\'').nth(1)).map(str::to_string) } async fn brightness_pct() -> Option { @@ -120,10 +98,7 @@ fn charge_threshold_paths() -> Option<(std::path::PathBuf, std::path::PathBuf)> } fn read_threshold(path: &std::path::Path) -> i64 { - std::fs::read_to_string(path) - .ok() - .and_then(|s| s.trim().parse().ok()) - .unwrap_or(100) + std::fs::read_to_string(path).ok().and_then(|s| s.trim().parse().ok()).unwrap_or(100) } #[derive(Serialize)] @@ -155,57 +130,16 @@ pub async fn set_brightness(percent: i64) -> Result<(), String> { return Err("No controllable backlight found".into()); }; let pct = format!("{percent}%"); - Command::new("brightnessctl") - .args(["--device", &device, "set", &pct]) - .status() - .await - .map_err(|e| e.to_string())?; + Command::new("brightnessctl").args(["--device", &device, "set", &pct]).status().await.map_err(|e| e.to_string())?; Ok(()) } -fn charge_threshold_write(which: &str, percent: i64) -> Result<(String, i64), String> { - if which != "start" && which != "end" { - return Err("threshold must be start or end".into()); - } - Ok((which.to_string(), percent.clamp(0, 100))) -} - #[tauri::command] pub async fn set_charge_threshold(which: String, percent: i64) -> Result<(), String> { - let (which, percent) = charge_threshold_write(&which, percent)?; let Some((start, end)) = charge_threshold_paths() else { return Err("No charge threshold support on this hardware".into()); }; let path = if which == "start" { start } else { end }; - // GNU tee writes stdin to its path operands — the percent must be piped, - // not passed as a second path argument. - let input = format!("{percent}\n"); - if util::run_with_stdin(&["pkexec", "tee", &path.display().to_string()], &input).await { - Ok(()) - } else { - Err("Failed to set charge threshold".into()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn charge_threshold_clamps_and_restricts_which() { - assert_eq!( - charge_threshold_write("start", 80).unwrap(), - ("start".into(), 80) - ); - assert_eq!( - charge_threshold_write("end", 150).unwrap(), - ("end".into(), 100) - ); - assert_eq!( - charge_threshold_write("start", -5).unwrap(), - ("start".into(), 0) - ); - assert!(charge_threshold_write("both", 50).is_err()); - assert!(charge_threshold_write("-start", 50).is_err()); - } + Command::new("pkexec").args(["tee", &path.display().to_string()]).arg(percent.to_string()).output().await.map_err(|e| e.to_string())?; + Ok(()) } diff --git a/src/src/commands/service.rs b/src/src/commands/service.rs index 17a1540..c99fee4 100644 --- a/src/src/commands/service.rs +++ b/src/src/commands/service.rs @@ -21,19 +21,6 @@ pub enum ServiceAction { Restart, } -/// Units the frontend already hardcodes in ServiceControl call sites. -const ALLOWED_UNITS: &[&str] = &[ - "breadd.service", - "breadclipd.service", - "breadcrumbs.service", - "breadmill.service", - "breadbox-sync.service", -]; - -fn allowed_unit(unit: &str) -> bool { - ALLOWED_UNITS.contains(&unit) -} - async fn systemctl_active(unit: &str) -> bool { Command::new("systemctl") .args(["--user", "is-active", "--quiet", unit]) @@ -53,21 +40,15 @@ async fn systemctl_enabled(unit: &str) -> bool { } #[tauri::command] -pub async fn get_service_status(unit: String) -> Result { - if !allowed_unit(&unit) { - return Err("unknown service".into()); - } - Ok(ServiceStatus { +pub async fn get_service_status(unit: String) -> ServiceStatus { + ServiceStatus { active: systemctl_active(&unit).await, enabled: systemctl_enabled(&unit).await, - }) + } } #[tauri::command] pub async fn service_action(unit: String, action: ServiceAction) -> Result<(), String> { - if !allowed_unit(&unit) { - return Err("unknown service".into()); - } let verb = match action { ServiceAction::Start => "start", ServiceAction::Stop => "stop", @@ -89,31 +70,8 @@ pub async fn service_action(unit: String, action: ServiceAction) -> Result<(), S /// panel, no reason to pull an open-ended `journalctl -f` tail into the /// webview. #[tauri::command] -pub fn open_logs(unit: String) -> Result<(), String> { - if !allowed_unit(&unit) { - return Err("unknown service".into()); - } - std::process::Command::new("kitty") +pub fn open_logs(unit: String) { + let _ = std::process::Command::new("kitty") .args(["-e", "journalctl", "--user", "-u", &unit, "-f"]) - .spawn() - .map_err(|e| e.to_string())?; - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn units_match_frontend_hardcoded_list() { - assert!(allowed_unit("breadd.service")); - assert!(allowed_unit("breadclipd.service")); - assert!(allowed_unit("breadcrumbs.service")); - assert!(allowed_unit("breadmill.service")); - assert!(allowed_unit("breadbox-sync.service")); - assert!(!allowed_unit("sshd.service")); - assert!(!allowed_unit("breadd.service;reboot")); - assert!(!allowed_unit("../sshd.service")); - assert!(!allowed_unit("-u sshd")); - } + .spawn(); } diff --git a/src/src/commands/theme.rs b/src/src/commands/theme.rs index 1e5e4b0..d9dcc91 100644 --- a/src/src/commands/theme.rs +++ b/src/src/commands/theme.rs @@ -11,26 +11,17 @@ use tauri::{AppHandle, Emitter, Manager}; /// Initial theme fetch — called once by the frontend at startup. #[tauri::command] -pub fn get_theme_css(window: tauri::WebviewWindow) -> String { - render_theme_css(&palette_for_window(&window)) +pub fn get_theme_css() -> String { + render_theme_css() } -fn palette_for_window(window: &tauri::WebviewWindow) -> bread_theme::Palette { - window - .current_monitor() - .ok() - .flatten() - .and_then(|m| m.name().map(|s| s.to_string())) - .map(|name| bread_theme::load_palette_for(&name)) - .unwrap_or_else(bread_theme::load_palette) -} - -fn render_theme_css(palette: &bread_theme::Palette) -> String { +fn render_theme_css() -> String { + let palette = bread_theme::load_palette(); // bread-theme v0.7.1 exposes Palette + ink_on + tokens, but not the // later css_custom_properties / css_tokens helpers (those landed after // the tag). Emit the same :root custom-property names the Svelte app // already uses so a tag pin doesn't require a web-side rename. - format!("{}\n{}", css_custom_properties(palette), css_tokens()) + format!("{}\n{}", css_custom_properties(&palette), css_tokens()) } fn css_custom_properties(p: &bread_theme::Palette) -> String { @@ -112,11 +103,7 @@ pub fn watch_and_emit(app: &AppHandle) { EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) ) && event.paths.iter().any(|p| p == &target_for_watcher); if touches_target { - let css = app_for_watcher - .get_webview_window("main") - .map(|w| render_theme_css(&palette_for_window(&w))) - .unwrap_or_else(|| render_theme_css(&bread_theme::load_palette())); - let _ = app_for_watcher.emit("theme-changed", css); + let _ = app_for_watcher.emit("theme-changed", render_theme_css()); } }, notify::Config::default(), diff --git a/src/src/commands/users.rs b/src/src/commands/users.rs index 8b0db82..d26ff59 100644 --- a/src/src/commands/users.rs +++ b/src/src/commands/users.rs @@ -3,10 +3,9 @@ //! `pkexec`. use serde::Serialize; +use tokio::io::AsyncWriteExt; use tokio::process::Command; -use super::util; - #[derive(Serialize, Clone)] pub struct Account { username: String, @@ -27,16 +26,10 @@ fn list_accounts() -> Vec { let shell = f[6]; // Real human accounts: normal UID range, a real login shell // (excludes system/service accounts like greeter, avahi, etc). - if !(1000..60000).contains(&uid) - || shell.ends_with("nologin") - || shell.ends_with("/false") - { + if !(1000..60000).contains(&uid) || shell.ends_with("nologin") || shell.ends_with("/false") { return None; } - Some(Account { - username: f[0].to_string(), - full_name: f[4].split(',').next().unwrap_or("").to_string(), - }) + Some(Account { username: f[0].to_string(), full_name: f[4].split(',').next().unwrap_or("").to_string() }) }) .collect() } @@ -49,69 +42,29 @@ pub struct UsersInfo { #[tauri::command] pub fn get_users_info() -> UsersInfo { - UsersInfo { - accounts: list_accounts(), - current_user: std::env::var("USER").unwrap_or_default(), - } + UsersInfo { accounts: list_accounts(), current_user: std::env::var("USER").unwrap_or_default() } } -/// shadow-utils `USER_NAME_MAX` is 32; keep chpasswd/useradd operands inside it. -const USERNAME_MAX: usize = 32; - -/// `[a-z_][a-z0-9_-]*`, length-capped, no leading `-`. Also rejects `:`, -/// newlines, and other chpasswd field/line separators. -fn valid_username(name: &str) -> bool { - let bytes = name.as_bytes(); - if bytes.is_empty() || bytes.len() > USERNAME_MAX { +/// Runs a root command that needs a line of input on stdin (chpasswd's own +/// "user:password" format). `pkexec` inherits the spawning process's stdin +/// only when explicitly piped, so this pipes it through. +async fn run_with_stdin(args: &[&str], input: String) -> bool { + let Ok(mut child) = Command::new(args[0]).args(&args[1..]).stdin(std::process::Stdio::piped()).stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null()).spawn() + else { return false; + }; + if let Some(mut stdin) = child.stdin.take() { + if stdin.write_all(input.as_bytes()).await.is_err() { + return false; + } } - let first = bytes[0]; - if first != b'_' && !first.is_ascii_lowercase() { - return false; - } - bytes[1..] - .iter() - .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || matches!(*b, b'_' | b'-')) -} - -/// chpasswd reads `user:password` lines — a `:`, `\n`, or `\r` in either -/// field injects extra passwd entries or shifts columns. -fn valid_chpasswd_password(password: &str) -> bool { - !password.is_empty() - && password.len() <= 512 - && !password.contains('\n') - && !password.contains('\r') - && !password.contains(':') - && !password.contains('\0') -} - -fn chpasswd_input(username: &str, password: &str) -> Result { - if !valid_username(username) { - return Err("invalid username".into()); - } - if !valid_chpasswd_password(password) { - return Err("invalid password".into()); - } - Ok(format!("{username}:{password}\n")) -} - -fn may_delete_user(username: &str, current: &str) -> Result<(), String> { - if !valid_username(username) { - return Err("invalid username".into()); - } - if username == "root" { - return Err("refusing to remove root".into()); - } - if !current.is_empty() && username == current { - return Err("refusing to remove the current user".into()); - } - Ok(()) + child.wait().await.map(|s| s.success()).unwrap_or(false) } #[tauri::command] pub async fn change_password(username: String, password: String) -> Result<(), String> { - let input = chpasswd_input(&username, &password)?; - if util::run_with_stdin(&["pkexec", "chpasswd"], &input).await { + let input = format!("{username}:{password}\n"); + if run_with_stdin(&["pkexec", "chpasswd"], input).await { Ok(()) } else { Err("Failed to change password".into()) @@ -120,13 +73,7 @@ pub async fn change_password(username: String, password: String) -> Result<(), S #[tauri::command] pub async fn remove_user(username: String) -> Result<(), String> { - let current = std::env::var("USER").unwrap_or_default(); - may_delete_user(&username, ¤t)?; - let output = Command::new("pkexec") - .args(["userdel", "-r", &username]) - .output() - .await - .map_err(|e| e.to_string())?; + let output = Command::new("pkexec").args(["userdel", "-r", &username]).output().await.map_err(|e| e.to_string())?; if output.status.success() { Ok(()) } else { @@ -136,68 +83,22 @@ pub async fn remove_user(username: String) -> Result<(), String> { #[tauri::command] pub async fn add_user(username: String, full_name: String, password: String) -> Result<(), String> { - let username = username.trim(); - let input = chpasswd_input(username, &password)?; - let mut useradd_args = vec![ - "pkexec".to_string(), - "useradd".to_string(), - "-m".to_string(), - "-s".to_string(), - "/bin/bash".to_string(), - ]; + let username = username.trim().to_string(); + let mut useradd_args = vec!["pkexec".to_string(), "useradd".to_string(), "-m".to_string(), "-s".to_string(), "/bin/bash".to_string()]; if !full_name.trim().is_empty() { useradd_args.push("-c".to_string()); useradd_args.push(full_name.trim().to_string()); } - useradd_args.push(username.to_string()); + useradd_args.push(username.clone()); let args_ref: Vec<&str> = useradd_args.iter().map(String::as_str).collect(); - let output = Command::new(args_ref[0]) - .args(&args_ref[1..]) - .output() - .await - .map_err(|e| e.to_string())?; + let output = Command::new(args_ref[0]).args(&args_ref[1..]).output().await.map_err(|e| e.to_string())?; if !output.status.success() { return Err(String::from_utf8_lossy(&output.stderr).trim().to_string()); } - if util::run_with_stdin(&["pkexec", "chpasswd"], &input).await { + let input = format!("{username}:{password}\n"); + if run_with_stdin(&["pkexec", "chpasswd"], input).await { Ok(()) } else { Err("User created, but setting the password failed.".into()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn chpasswd_rejects_newline_injection() { - assert!(chpasswd_input("alice", "pw\nroot:evil").is_err()); - assert!(chpasswd_input("alice\nroot", "pw").is_err()); - assert!(chpasswd_input("alice\rroot", "pw").is_err()); - assert!(chpasswd_input("alice", "pw\rroot:x").is_err()); - assert!(chpasswd_input("al:ice", "pw").is_err()); - assert!(chpasswd_input("alice", "p:w").is_err()); - assert_eq!(chpasswd_input("alice", "secret").unwrap(), "alice:secret\n"); - } - - #[test] - fn username_grammar() { - assert!(valid_username("alice")); - assert!(valid_username("_svc")); - assert!(valid_username("a1-b_c")); - assert!(!valid_username("")); - assert!(!valid_username("-alice")); - assert!(!valid_username("Alice")); - assert!(!valid_username("root user")); - assert!(!valid_username(&"a".repeat(USERNAME_MAX + 1))); - } - - #[test] - fn remove_user_refuses_root_and_self() { - assert!(may_delete_user("root", "alice").is_err()); - assert!(may_delete_user("alice", "alice").is_err()); - assert!(may_delete_user("root\n", "alice").is_err()); - assert!(may_delete_user("bob", "alice").is_ok()); - } -} diff --git a/src/src/commands/util.rs b/src/src/commands/util.rs index d7a40d6..1acb283 100644 --- a/src/src/commands/util.rs +++ b/src/src/commands/util.rs @@ -90,30 +90,6 @@ pub fn bos_settings_dir() -> PathBuf { config::config_dir().join("bos-settings") } -/// Pipe `input` to a command's stdin (`pkexec` does not inherit a piped -/// stdin unless we set it). Used by chpasswd and `pkexec tee`. -pub async fn run_with_stdin(args: &[&str], input: &str) -> bool { - if args.is_empty() { - return false; - } - let Ok(mut child) = tokio::process::Command::new(args[0]) - .args(&args[1..]) - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .spawn() - else { - return false; - }; - if let Some(mut stdin) = child.stdin.take() { - use tokio::io::AsyncWriteExt; - if stdin.write_all(input.as_bytes()).await.is_err() { - return false; - } - } - child.wait().await.map(|s| s.success()).unwrap_or(false) -} - /// Atomic write with mode 0600 set on the new inode before/after replace, /// matching breadcrumbs' `networks.toml` care. pub fn write_secure(path: &Path, contents: &str) -> Result<(), String> {