diff --git a/Cargo.lock b/Cargo.lock index c7e12e5..c3f6d4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,7 +54,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.11.1", + "bitflags 2.12.1", "cc", "jni", "libc", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "android-build" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd" +checksum = "f9fc9904ad2ad097c3c1cfe2eacaaf0fc24710936fa9ed941cb310b7c6ed2ab7" dependencies = [ "windows-sys 0.52.0", ] @@ -321,9 +321,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" @@ -369,9 +369,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" [[package]] name = "block" @@ -421,9 +421,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -457,7 +457,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "log", "polling", "rustix 0.38.44", @@ -471,7 +471,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "polling", "rustix 1.1.4", "slab", @@ -513,9 +513,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "jobserver", @@ -643,9 +643,9 @@ dependencies = [ [[package]] name = "compact_str" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" dependencies = [ "castaway", "cfg-if", @@ -749,7 +749,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "core-foundation 0.10.1", "libc", ] @@ -769,7 +769,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "fontdb", "harfrust", "linebender_resource_handle", @@ -827,7 +827,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "crossterm_winapi", "derive_more", "document-features", @@ -995,7 +995,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", ] @@ -1037,9 +1037,9 @@ checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "endi" @@ -1468,7 +1468,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "gpu-alloc-types", ] @@ -1478,7 +1478,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -1499,7 +1499,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -1510,7 +1510,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -1541,7 +1541,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "core_maths", "read-fonts 0.35.0", @@ -1570,9 +1570,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1620,7 +1620,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ab1937d699403e7e69252ae743a902bcee9f4ab2052cc4c9a46fcf34729d85" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytes", "glam", "lilt", @@ -1664,7 +1664,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234ca1c2cec4155055f68fa5fad1b5242c496ac8238d80a259bca382fb44a102" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cosmic-text", "half", @@ -1736,7 +1736,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff144a999b0ca0f8a10257934500060240825c42e950ec0ebee9c8ae30561c13" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cryoglyph", "futures", @@ -1802,7 +1802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -1829,6 +1829,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1920,9 +1926,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -2010,14 +2016,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "libc", "plain", - "redox_syscall 0.7.5", + "redox_syscall 0.8.1", ] [[package]] @@ -2035,7 +2041,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -2073,9 +2079,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" [[package]] name = "lru" @@ -2107,9 +2113,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmap2" @@ -2141,7 +2147,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -2158,9 +2164,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -2201,7 +2207,7 @@ checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", "bit-set 0.8.0", - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -2225,7 +2231,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "jni-sys 0.3.1", "log", "ndk-sys", @@ -2255,7 +2261,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "cfg_aliases", "libc", @@ -2379,7 +2385,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -2395,7 +2401,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -2416,7 +2422,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -2429,7 +2435,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -2451,7 +2457,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2463,7 +2469,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -2474,7 +2480,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "dispatch2", "objc2 0.6.4", ] @@ -2485,7 +2491,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "dispatch2", "objc2 0.6.4", "objc2-core-foundation", @@ -2532,7 +2538,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -2544,7 +2550,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -2563,7 +2569,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "dispatch", "libc", @@ -2576,7 +2582,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -2589,7 +2595,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-core-foundation", ] @@ -2612,7 +2618,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2624,7 +2630,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2637,7 +2643,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -2659,7 +2665,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit 0.2.2", @@ -2691,7 +2697,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -2715,9 +2721,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "orbclient" -version = "0.3.54" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" dependencies = [ "libc", "libredox", @@ -2898,18 +2904,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -3028,9 +3034,9 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" [[package]] name = "quick-xml" -version = "0.39.3" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721da970c312655cde9b4ffe0547f20a8494866a4af5ff51f18b7c633d0c870b" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] @@ -3103,7 +3109,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "compact_str", "hashbrown 0.16.1", "indoc", @@ -3155,7 +3161,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "hashbrown 0.16.1", "indoc", "instability", @@ -3230,16 +3236,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] name = "redox_syscall" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -3279,12 +3285,12 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "rhai" -version = "1.24.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f9ef5dabe4c0b43d8f1187dc6beb67b53fe607fff7e30c5eb7f71b814b8c2c1" +checksum = "dd4dd0f8c36625202a4ba553c416c19b719947cd2a31d1bda06126e4a5727daf" dependencies = [ "ahash", - "bitflags 2.11.1", + "bitflags 2.12.1", "no-std-compat", "num-traits", "once_cell", @@ -3299,9 +3305,9 @@ dependencies = [ [[package]] name = "rhai_codegen" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6" +checksum = "3cd3a7535e50bf36857e7be7bec276d334e8c2dfa469c2201226fd01638ea5ca" dependencies = [ "proc-macro2", "quote", @@ -3341,7 +3347,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3354,7 +3360,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3451,9 +3457,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3486,9 +3492,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook" @@ -3605,7 +3611,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", @@ -3630,7 +3636,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "calloop 0.14.4", "calloop-wayland-source 0.4.1", "cursor-icon", @@ -3671,6 +3677,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "softbuffer" version = "0.4.8" @@ -3714,7 +3730,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -3855,7 +3871,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64", - "bitflags 2.11.1", + "bitflags 2.12.1", "fancy-regex", "filedescriptor", "finl_unicode", @@ -3897,10 +3913,12 @@ dependencies = [ "crossterm", "fs_extra", "iced", + "ipnet", "ratatui", "rayon", "rhai", "rustc-hash 2.1.2", + "tokio", "walkdir", ] @@ -4038,11 +4056,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.2" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -4056,9 +4093,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap", "toml_datetime", @@ -4118,9 +4155,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -4165,9 +4202,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-truncate" @@ -4200,9 +4237,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" dependencies = [ "atomic", "getrandom 0.4.2", @@ -4262,9 +4299,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.120" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -4275,9 +4312,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.70" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -4285,9 +4322,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4295,9 +4332,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -4308,9 +4345,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -4343,7 +4380,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -4383,7 +4420,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -4395,7 +4432,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cursor-icon", "wayland-backend", ] @@ -4417,7 +4454,7 @@ version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4429,7 +4466,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4442,7 +4479,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4455,7 +4492,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4468,7 +4505,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4500,9 +4537,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.97" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -4597,7 +4634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "cfg_aliases", "document-features", @@ -4628,7 +4665,7 @@ dependencies = [ "arrayvec", "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cfg_aliases", "document-features", @@ -4688,7 +4725,7 @@ dependencies = [ "arrayvec", "ash", "bit-set 0.8.0", - "bitflags 2.11.1", + "bitflags 2.12.1", "block", "bytemuck", "cfg-if", @@ -4733,7 +4770,7 @@ version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "js-sys", "log", @@ -5060,7 +5097,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "bytemuck", "calloop 0.13.0", @@ -5105,9 +5142,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -5176,7 +5213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags 2.12.1", "indexmap", "log", "serde", @@ -5250,7 +5287,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "dlib", "log", "once_cell", @@ -5277,9 +5314,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "zbus" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -5312,9 +5349,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5344,18 +5381,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", @@ -5370,9 +5407,9 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zvariant" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", @@ -5384,9 +5421,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5397,9 +5434,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 02341ed..16d318d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,10 @@ clap = { version = "4.6.1", features = ["derive"] } crossterm = "0.29.0" fs_extra = "1.3.0" iced = { version = "0.14.0", features = ["advanced", "tokio"] } +ipnet = "2.12.0" ratatui = "0.30.0" rayon = "1.12.0" rhai = { version = "1.24.0", features = ["metadata", "sync"] } rustc-hash = "2.1.2" +tokio = { version = "1.52.3", features = ["full"] } walkdir = "2.5.0" diff --git a/src/funcs.rs b/src/funcs.rs index 4c543cc..8c953fc 100644 --- a/src/funcs.rs +++ b/src/funcs.rs @@ -1,17 +1,31 @@ -use crate::*; +use crate::{ToolMessage::Input, *}; use crossterm::{ cursor, event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, execute, terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, }; -use iced::alignment::Vertical::Bottom; use ratatui::{ prelude::*, widgets::{Block, Borders, List, ListItem, ListState, Paragraph}, }; -use std::error::Error; +use std::sync::Arc; +use std::sync::Mutex; use std::time::Duration; +use std::{error::Error, time::Instant}; +use std::{ + io::{Read, Write}, + usize, +}; +use std::{net::TcpStream, thread}; + +pub enum ServerBrokerCmd { + ConnectServer(usize), + RegisterActionOutput((Server, String)), + Disconnect(Server), + DisconnectAll, + Exit, +} pub fn get_user_input(prompt: &str) -> Result> { println!("{}", prompt); @@ -20,6 +34,158 @@ pub fn get_user_input(prompt: &str) -> Result> { return Ok(response.trim().to_string()); } +pub fn server_broker( + rx: Receiver, + tx: Sender, + servers: Arc>>, +) { + loop { + if let Ok(msg) = rx.try_recv() { + match msg { + ServerBrokerCmd::ConnectServer(id) => { + let _ = tx.send(ToolMessage::Output(( + 0, + format!("attempting to connect to server {}", id), + ))); + if let Ok(mut lock) = servers.lock() { + lock.iter_mut().for_each(|s| { + let _ = + tx.send(ToolMessage::Output((0, format!("got server list lock!")))); + if s.id == id { + let _ = tx.send(ToolMessage::Output(( + 0, + format!("Found the server for {}: {}", s.id, s.address), + ))); + if let Ok(mut stream) = TcpStream::connect(s.address.clone()) { + let _ = tx.send(ToolMessage::Output(( + 0, + String::from("TCP successful to server!"), + ))); + let _ = stream.write(format!("HELLO|{}\n", s.id).as_bytes()); + let _ = tx.send(ToolMessage::Output(( + 0, + String::from("Hello message sent!"), + ))); + let mut buf = [0; 1024]; + if let Ok(bytes_read) = stream.read(&mut buf) { + let response = String::from_utf8_lossy(&buf[..bytes_read]); + if let Some((_, id)) = response.split_once("|") { + if let Ok(id) = id.trim().parse::() { + s.client_id = id; + s.connected = true; + let _ = tx.send(ToolMessage::Output(( + 0, + String::from("got client ID from server!"), + ))); + } else { + let _ = tx.send(ToolMessage::Output(( + 0, + String::from( + "failed to get response from server!", + ), + ))); + } + } + } + } + } + }); + } + } + ServerBrokerCmd::Disconnect(server) => { + if let Ok(mut lock) = servers.lock() { + let mut rid = 0; + let mut found = false; + lock.iter().enumerate().for_each(|(id, s)| { + if s.id == server.id { + rid = id; + found = true; + } + }); + if found { + lock.remove(rid); + } + } + } + ServerBrokerCmd::DisconnectAll => { + println!("todo"); + } + ServerBrokerCmd::Exit => { + println!("todo"); + break; + } + ServerBrokerCmd::RegisterActionOutput(text) => { + if let Ok(mut lock) = servers.lock() { + lock.iter_mut().for_each(|s| { + if s.id == text.0.id { + s.message_que.push(text.1.clone()); + } + }); + } + } + } + } + if let Ok(mut lock) = servers.try_lock() { + lock.iter_mut().for_each(|s| { + if s.connected { + if Instant::now().duration_since(s.last_check) >= s.timer { + if let Ok(mut stream) = TcpStream::connect(s.address.clone()) { + s.last_check = Instant::now(); + let mut success_ids = Vec::new(); + if s.message_que.len() > 0 { + s.message_que.iter().enumerate().for_each(|(id, msg)| { + if let Ok(_) = stream.write( + format!("{}|{}\n", s.client_id.clone(), msg).as_bytes(), + ) { + success_ids.push(id); + } + }); + } else { + let _ = stream.write(format!("{}|PING", s.client_id).as_bytes()); + } + for id in success_ids { + s.message_que.remove(id); + } + let mut buffer = [0; 1024]; + if let Ok(bytes_read) = stream.read(&mut buffer) { + let response = + String::from_utf8_lossy(&buffer[..bytes_read]).to_string(); + let (_, action_string) = + response.split_once("|").unwrap_or(("ACTIONS", "NONE")); + let actions: Vec<&str> = action_string.split(",").collect(); + for action in actions { + let (action, data) = + action.split_once("|").unwrap_or(("PING", "NONE")); + match action { + "OUTPUT" => { + let _ = tx.send(ToolMessage::Output(( + 0, + format!("FROM SERVER: {}", data), + ))); + } + "CMD" => { + let (rid_str, cmd) = + data.split_once("|").unwrap_or(("0", "NONE")); + if let Ok(rid) = rid_str.parse::() { + let _ = tx.send(ToolMessage::Input(( + rid, + cmd.to_string(), + ))); + } + } + _ => {} + } + } + } + } + } + } + }); + } + thread::sleep(Duration::from_millis(10)); + } +} + pub fn run_tui( mut state: AppState, main_rx: Receiver, @@ -32,6 +198,12 @@ pub fn run_tui( cursor::Hide, EnableMouseCapture )?; + let (server_tx, server_rx) = channel(); + let main_tx = state.main_tx.clone(); + let server_clone = state.servers.clone(); + std::thread::spawn(move || { + server_broker(server_rx, main_tx, server_clone); + }); let backend = CrosstermBackend::new(&stdout); let mut terminal = Terminal::new(backend)?; let (event_tx, event_rx) = channel::(); @@ -43,6 +215,9 @@ pub fn run_tui( } std::thread::spawn(move || { loop { + if !state.server_broker_running && !state.app_state_running { + break; + } if event::poll(Duration::from_millis(100)).unwrap_or(false) { match event::read() { Ok(Event::Key(key)) => { @@ -222,10 +397,24 @@ pub fn run_tui( if let Ok(event) = event_rx.recv() { match event { AppEvent::Worker(msg) => match msg { - ToolMessage::Output(txt) => { + ToolMessage::EndPrompt => { + state.prompt.reset(); + } + ToolMessage::Input(cmd) => { + state.log.push(cmd.1.clone()); + } + ToolMessage::Output((rid, txt)) => { state.log.push(txt.clone()); - state.output.push(txt); + state.output.push(txt.clone()); state.output_scroll = u16::MAX; + if rid != 0 { + if let Some(server) = state.selected_server.clone() { + let _ = server_tx.send(ServerBrokerCmd::RegisterActionOutput(( + server, + format!("OUTPUT|{}|{}", rid, txt), + ))); + } + } } ToolMessage::UpdateProject(index, project) => { if index < state.projects.len() { @@ -233,8 +422,16 @@ pub fn run_tui( } } ToolMessage::RebuildDB => { + state.main_tx.send(ToolMessage::Output(( + 0, + String::from("rebuilddb message recieved..."), + )))?; let project = state.projects[state.selected_project].clone(); - if let Some(db) = project.db { + if let Some(mut db) = project.db.clone() { + state.main_tx.send(ToolMessage::Output(( + 0, + String::from("distrobox detected..."), + )))?; disable_raw_mode()?; execute!( &stdout, @@ -243,67 +440,74 @@ pub fn run_tui( DisableMouseCapture )?; std::io::stdout().flush()?; - let mut status = Command::new("distrobox"); - let template_box = state.config.get("template_box").unwrap(); - status - .arg("create") - .arg("--root") - .arg("--clone") - .arg(&template_box) - .arg("--name") - .arg(format!( - "{}-{}-{}", - template_box, project.org_name, project.name - )); - for volume in db.volumes { - let mut dir_name = volume.split("/").last().unwrap(); - if volume.contains("files") { - dir_name = "/pentest"; + if db.created { + if let Err(_) = db.destroy(state.main_tx.clone(), 0) { + state.main_tx.send(ToolMessage::Output(( + 0, + String::from("failed to destroy the project box!"), + )))?; } - if volume.contains("/notes") { - dir_name = "notes"; - } - status - .arg("--volume") - .arg(format!("{}:{}:rw", volume, dir_name)); } - status - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()); - match status.status() { - Ok(status) if !status.success() => { - println!( - "\nCommand failed with exit code {status}, press enter to return to tetanus." - ); - let mut discard = String::new(); - let _ = std::io::stdin().read_line(&mut discard); - } - Err(e) => { - println!( - "\nFailed to execure distrobox: {e}, press enter to return to tetanus." - ); - let mut discard = String::new(); - let _ = std::io::stdin().read_line(&mut discard); - } - _ => {} + if let Err(_) = db.create(state.main_tx.clone(), 0) { + state.main_tx.send(ToolMessage::Output(( + 0, + String::from("failed to create the project box!"), + )))?; } enable_raw_mode()?; execute!( &stdout, EnterAlternateScreen, - DisableMouseCapture, + EnableMouseCapture, cursor::Hide )?; terminal.clear()?; } } - + ToolMessage::DestroyDB(mut db) => { + execute!( + &stdout, + LeaveAlternateScreen, + cursor::Show, + DisableMouseCapture + )?; + db.destroy(state.main_tx.clone(), 0)?; + enable_raw_mode()?; + execute!( + &stdout, + EnterAlternateScreen, + EnableMouseCapture, + cursor::Hide + )?; + terminal.clear()?; + } + ToolMessage::ConnectServer => { + if let Some(server) = state.selected_server.clone() { + let _ = server_tx.send(ServerBrokerCmd::ConnectServer(server.id)); + } + } + ToolMessage::SendServer(data) => { + let _ = server_tx.send(ServerBrokerCmd::RegisterActionOutput(data)); + } + ToolMessage::DisconnectAllServers => { + println!("todo"); + } + ToolMessage::DisconnectServer => { + println!("todo"); + } + ToolMessage::ServerBrokerExit => { + state.server_broker_running = false; + } + ToolMessage::AppStateExit => { + println!("todo"); + } _ => {} }, AppEvent::Key(key) => { match key.code { - KeyCode::Esc => break, + KeyCode::Esc => { + break; + } KeyCode::Enter => { let trimmed = state.curent_intput.trim().to_string(); if !trimmed.is_empty() { @@ -318,54 +522,81 @@ pub fn run_tui( state.execute_command( prompt.execute_command.as_str(), None, + 0, )?; + } else { + state.execute_command("prompt", None, 0)?; } } let (command, args) = trimmed.split_once(' ').unwrap_or((&trimmed, "")); - match command { - "exit" | "quit" => break, - "reload-modules" => { - state.initialize_modules(); - state.output.push("Reloading module paths...".into()); + if state.selected_client != 0 { + if let Some(server) = state.selected_server.clone() { + let _ = server_tx.send( + ServerBrokerCmd::RegisterActionOutput(( + server.clone(), + format!( + "CMD|{}|{}|{} {}", + server.client_id, + state.selected_client, + command, + args + ), + )), + ); } - "help" => { - state.output.push("Available Modules:".into()); - for name in state.module_loader.commands.keys() { - state.output.push(format!(" - {}", name)); + } else { + match command { + "exit" | "quit" => break, + "reload-modules" => { + state.initialize_modules(); + state.output.push("Reloading module paths...".into()); } - } - "new_project" | "np" => { - if args.split_once(' ').is_some() { - let _ = state - .execute_command(command, Some(args.to_string())); - } else { - state - .output - .push("Error: USAGE -> np ".into()); - } - } - command_name => { - if state.module_loader.commands.contains_key(command_name) { - state.output.push(format!( - "[Worker] Executing script '{}'...", - command_name - )); - if let Err(e) = - state.execute_command(command_name, None) - { - state - .output - .push(format!("[Error] Pipeline fail: {}", e)); + "help" => { + let help_text = state.help.clone().join("\n"); + for line in help_text.lines() { + state.output.push(line.to_string()); } - } else { - if args == "" { - let _ = state.execute_command(command, None); - } else { + } + "new_project" | "np" => { + if args.split_once(' ').is_some() { let _ = state.execute_command( command, Some(args.to_string()), + 0, ); + } else { + state + .output + .push("Error: USAGE -> np ".into()); + } + } + command_name => { + if state.module_loader.commands.contains_key(command) { + state.output.push(format!( + "[Worker] Executing script '{}'...", + command_name + )); + if let Err(e) = state.execute_command( + command, + Some(args.to_string()), + 0, + ) { + state.output.push(format!( + "[Error] Pipeline fail: {}", + e + )); + } + } else { + if args == "" { + let _ = state.execute_command(command, None, 0); + } else { + let _ = state.execute_command( + command, + Some(args.to_string()), + 0, + ); + } } } } @@ -457,6 +688,9 @@ pub fn run_tui( } } } + if !state.app_state_running && state.server_broker_running { + break; + } } } disable_raw_mode()?; diff --git a/src/install.rs b/src/install.rs index 58fb07f..7ed8a93 100644 --- a/src/install.rs +++ b/src/install.rs @@ -9,6 +9,26 @@ use std::process::exit; use tetanus::funcs::get_user_input; pub fn install() -> Result<(), Box> { + let client = get_user_input("would you like to install the Tetanus client on this computer?")? + .contains("y"); + let server = get_user_input("would you like to install the Tetanus server on this computer?")? + .contains("y"); + if client { + client_install()?; + } + if server { + println!("todo"); + } + if !client && !server { + println!( + "no install selected, please answer y to one or both of the above prompts next time!" + ); + exit(1); + } + return Ok(()); +} + +pub fn client_install() -> Result<(), Box> { if let Some(homedir) = env::home_dir() { let mut config_path = homedir.clone(); config_path.push(".config/tetanus"); @@ -40,6 +60,15 @@ pub fn install() -> Result<(), Box> { let template_box = get_user_input( "enter the name of your template distrobox, NONE in all caps if you are not using distrobox", )?; + println!( + "enter the command used to launch your terminal while running a command. Put the place holder |||COMMAND||| where the command would go" + ); + println!( + "if you use a profile to launch the distroboxes, and that profile needs the box name as an environment variable put ENV_NAME= then the name of the variable you use at the end of the command" + ); + let term_cmd = get_user_input( + "for example: konsole -e |||COMMAND||| or konsole --profile profile_name ENV_NAME=CURRENT_PROJECT_BOX", + )?; config_path.push("client.conf"); projects_path.push("default"); create_dir_all(&projects_path)?; @@ -70,6 +99,7 @@ pub fn install() -> Result<(), Box> { .write(format!("upcoming_notes: {}\n", upcoming_notes_path.display()).as_bytes())?; client_config_file.write(format!("module_path: {}\n", module_path.display()).as_bytes())?; client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?; + client_config_file.write(format!("term_cmd: {}\n", term_cmd).as_bytes())?; println!("\ndefault config files written!"); println!("downloading default notes and modules..."); create_dir_all("./temp")?; diff --git a/src/lib.rs b/src/lib.rs index 684d00e..4aff421 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,28 @@ use fs_extra::dir::{CopyOptions, copy}; +use ipnet::IpNet; use ratatui::crossterm::event; use rhai::{AST, Dynamic, Engine, Scope}; use std::collections::HashMap; use std::error::Error; use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all}; use std::io::{self, BufRead, BufReader, Write}; +use std::net::IpAddr; use std::path::PathBuf; use std::process::{Command, Stdio}; use std::sync::mpsc::Receiver; -use std::sync::{Arc, mpsc::Sender, mpsc::channel}; +use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel}; + +use std::time; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; pub mod funcs; +pub mod server; pub struct AppState { pub projects: Vec, - pub servers: Vec, + pub servers: Arc>>, pub config: HashMap, pub config_file: PathBuf, pub workers: rayon::ThreadPool, @@ -25,11 +33,16 @@ pub struct AppState { pub output: Vec, pub selected_server: Option, pub selected_project: usize, + pub selected_client: usize, pub curent_intput: String, pub module_loader: ModuleLoader, pub output_scroll: u16, prompt: Prompt, pub info_scroll: u16, + pub server_broker_running: bool, + pub app_state_running: bool, + pub remoting: bool, + pub help: Vec, } impl AppState { @@ -38,7 +51,7 @@ impl AppState { ( Self { projects: Vec::new(), - servers: Vec::new(), + servers: Arc::new(Mutex::new(Vec::new())), config: HashMap::new(), config_file: PathBuf::new(), workers: rayon::ThreadPoolBuilder::new() @@ -52,6 +65,7 @@ impl AppState { output: Vec::new(), selected_server: None, selected_project: 0, + selected_client: 0, curent_intput: String::new(), module_loader: ModuleLoader::new(), output_scroll: 0, @@ -60,8 +74,13 @@ impl AppState { responses: Vec::new(), execute_command: String::new(), num_responses: 0, + prompts: Vec::new(), }, info_scroll: 0, + server_broker_running: true, + app_state_running: true, + remoting: false, + help: Vec::new(), }, main_rx, ) @@ -80,8 +99,15 @@ impl AppState { let new_server = Server { address: address.trim().to_string(), connected: false, + timer: Duration::from_secs(60), + id: 0, + last_check: time::Instant::now(), + message_que: Vec::new(), + client_id: 0, }; - self.servers.push(new_server); + if let Ok(mut lock) = self.servers.lock() { + lock.push(new_server); + } } self.config .insert("servers".to_string(), line.trim().to_string()); @@ -132,6 +158,36 @@ impl AppState { } } } + self.help.push("help\nThat's MEEEEEEE\n".to_string()); + self.help.push("new_project\nadd a project to the upcoming project pool and create default notes based on templates\nusage:\nnew_project organization_name, project_name\n".to_string()); + self.help.push( + "current_project\nshow information on the currently selected project\n".to_string(), + ); + self.help + .push("promote_project\npromote a project from upcoming to current\n".to_string()); + self.help.push( + "remove_project\nremove a project, delete its folders, and destroy its distrobox\n" + .to_string(), + ); + self.help.push("new_terminal\nopen a new terminal in the distrobox of the currently selected project\n".to_string()); + self.help + .push("new_server\ncreate a new server object\n".to_string()); + self.help + .push("list_servers\nlist all servers currently tracked by the tool\n".to_string()); + self.help + .push("connect_server\nconnect to a tracked server\n".to_string()); + self.help + .push("save_all\nsave all projects and settings\n".to_string()); + self.help + .push("save_projects\nsave just the projects\n".to_string()); + self.help + .push("test_server\ntest the server connection\n".to_string()); + self.help.push( + "add_scope\nadd a host, cidr range, or ip address to the current project's scope" + .to_string(), + ); + self.help.push("parse_scope\nparse the projects general.md notes file for the scope copied from the workbook".to_string()); + self.help.push("exit\nquit the tool\n".to_string()); return Ok(()); } @@ -143,6 +199,13 @@ impl AppState { if let Err(e) = self.module_loader.load_all(&base_path) { eprintln!("Failed to load modules: {}", e); } else { + for module in &self.module_loader.commands { + self.help.push(format!( + "{}\n{}\n", + module.1.name.clone(), + module.1.help.clone() + )); + } println!( "Successfully loaded {} modules.", self.module_loader.commands.len() @@ -156,21 +219,22 @@ impl AppState { &mut self, command_name: &str, command_args: Option, + rid: usize, ) -> Result<(), Box> { let tx = self.main_tx.clone(); match command_name { "new_project" | "np" => { let args = command_args.unwrap(); let (org, name) = args.split_once(" ").unwrap(); - if let Err(e) = self.new_project(org.to_string(), name.to_string()) { - tx.send(ToolMessage::Output(format!( - "Error making {}-{}: {e}", - org, name + if let Err(e) = self.new_project(org.to_string(), name.to_string(), rid) { + tx.send(ToolMessage::Output(( + rid, + format!("Error making {}-{}: {e}", org, name), )))?; } else { - tx.send(ToolMessage::Output(format!( - "{}-{} created successfully!", - org, name + tx.send(ToolMessage::Output(( + rid, + format!("{}-{} created successfully!", org, name), )))?; } } @@ -205,11 +269,28 @@ impl AppState { out_vec.push(format!("status: upcoming")); } for line in out_vec { - let _ = self.main_tx.send(ToolMessage::Output(line)); + let _ = self.main_tx.send(ToolMessage::Output((rid, line))); } } "promote_project" | "pp" => { - self.promote_project()?; + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("promote_project command recieved!"), + )))?; + match self.promote_project(rid) { + Ok(_) => { + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("project promoted successfully!"), + )))?; + } + Err(e) => { + self.main_tx.send(ToolMessage::Output(( + rid, + format!("Error promoting project: {}", e), + )))?; + } + } } "remove_project_confirm" => { self.prompt.action = None; @@ -220,70 +301,315 @@ impl AppState { let mut config_file = project.config_folder.clone(); config_file.pop(); if let Err(e) = remove_dir_all(&project.config_folder) { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "failed to delete config: {e} on {}", - &project.config_folder.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "failed to delete config: {e} on {}", + &project.config_folder.display() + ), ))); } if let Err(e) = remove_dir_all(&project.notes) { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "failed to delete notes: {e} on {}", - &project.notes.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "failed to delete notes: {e} on {}", + &project.notes.display() + ), ))); } else if let Some(parent) = project.notes.parent() { if let Ok(mut entries) = read_dir(parent) { if entries.next().is_none() { if let Err(e) = remove_dir(parent) { - let _ = self.main_tx.send(ToolMessage::Output(format!("failed to delete the empty client notes folder: {e} on {}", parent.display()))); + let _ = self.main_tx.send(ToolMessage::Output((rid, format!("failed to delete the empty client notes folder: {e} on {}", parent.display())))); } } } } if let Err(e) = remove_dir_all(&project.files) { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "failed to delete files: {e} on {}", - &project.files.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "failed to delete files: {e} on {}", + &project.files.display() + ), ))); } else if let Some(parent) = project.files.parent() { if let Ok(mut entries) = read_dir(parent) { if entries.next().is_none() { if let Err(e) = remove_dir(parent) { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "failed to delete empty files parent: {e} on {}", - parent.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "failed to delete empty files parent: {e} on {}", + parent.display() + ), ))); } } } } - self.projects.remove(self.selected_project); - let _ = self.main_tx.send(ToolMessage::Output(format!( - "{}-{} was sucessfully removed!", - project.org_name, project.name + let remove_id = self.selected_project.clone(); + let project = self.projects[remove_id].clone(); + if let Some(db) = project.db { + self.main_tx.send(ToolMessage::DestroyDB(db))?; + } + self.selected_project -= 1; + self.projects.remove(remove_id); + self.prompt.responses.clear(); + self.prompt.action = None; + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "{}-{} was sucessfully removed!", + project.org_name, project.name + ), ))); } } - self.prompt.responses.clear(); } "remove_project" | "rp" => { let project = self.projects[self.selected_project].clone(); self.prompt.action = Some(ToolMessage::RemoveProject); self.prompt.num_responses = 1; self.prompt.execute_command = String::from("remove_project_confirm"); - let _ = self.main_tx.send(ToolMessage::Output(format!( - "{}, {} and all contents will be deleted. Continue? (y/N)", - project.files.display(), - project.notes.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "{}, {} and all contents will be deleted. Continue? (y/N)", + project.files.display(), + project.notes.display() + ), ))); } + "new_terminal" | "nt" => { + let project = self.projects[self.selected_project].clone(); + if let Some(db) = project.db { + if let Some(cmd) = self.config.get("term_cmd") { + db.launch_terminal(cmd.to_string()); + } + } + } + "new_server" | "ns" => { + self.prompt.num_responses = 2; + self.prompt.action = Some(ToolMessage::AddServer); + self.prompt.execute_command = String::from("add_server"); + self.prompt.prompts = vec![ + "What is the IP for the server?".to_string(), + "What is the port for the server?".to_string(), + ]; + let _ = self + .main_tx + .send(ToolMessage::Input((0, "prompt".to_string()))); + } + "add_server" => { + let new_server = Server { + address: format!("{}:{}", self.prompt.responses[0], self.prompt.responses[1]), + connected: false, + timer: Duration::from_secs(5), + id: 0, + last_check: time::Instant::now(), + message_que: Vec::new(), + client_id: 0, + }; + if let Ok(mut lock) = self.servers.lock() { + if lock.len() == 0 { + self.selected_server = Some(new_server.clone()); + } + lock.push(new_server); + } + self.prompt.reset(); + } + "connect_server" => { + let _ = self.main_tx.send(ToolMessage::ConnectServer); + } + "prompt" => { + if self.prompt.responses.len() == self.prompt.num_responses { + let _ = self + .main_tx + .send(ToolMessage::Input((0, self.prompt.execute_command.clone()))); + let _ = self.main_tx.send(ToolMessage::EndPrompt); + } else { + let mut query = self.prompt.prompts[0].clone(); + if self.prompt.responses.len() > 0 + && self.prompt.responses.len() < self.prompt.prompts.len() + { + query = self.prompt.prompts[self.prompt.responses.len() - 1].clone(); + } + let _ = self.main_tx.send(ToolMessage::Output((rid, query))); + } + } + "list_servers" => { + let mut selected_addr = String::new(); + if let Some(selected) = self.selected_server.clone() { + selected_addr = selected.address.clone(); + } + if let Ok(lock) = self.servers.lock() { + lock.iter().for_each(|s| { + if selected_addr == s.address { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} - Currently Selected", s.address), + ))); + } else { + let _ = self + .main_tx + .send(ToolMessage::Output((rid, s.address.clone()))); + } + }); + } + } + "select_server" => { + self.prompt.action = Some(ToolMessage::SelectServer); + self.prompt.num_responses = 1; + self.prompt.prompts.push(String::from("Selection?")); + self.prompt.execute_command = String::from("server_selected"); + let mut counter: usize = 0; + let mut selected_addr = String::new(); + if let Some(selected) = self.selected_server.clone() { + selected_addr = selected.address.clone(); + } + if let Ok(lock) = self.servers.lock() { + lock.iter().for_each(|server| { + if server.address == selected_addr { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "{}:{} - Currently Selected", + counter, + server.address.clone() + ), + ))); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{}: {}", counter, server.address.clone()), + ))); + } + counter += 1; + }); + } + } + "server_selected" => { + if self.prompt.responses.len() == 1 { + if let Ok(lock) = self.servers.lock() { + if let Ok(id) = self.prompt.responses[0].parse::() { + self.selected_server = Some(lock[id].clone()); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} Selected!", lock[id].address), + ))); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + String::from("Invalid selection."), + ))); + } + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + String::from("error selecting server: no prompt responses found!"), + ))); + } + let _ = self.main_tx.send(ToolMessage::EndPrompt); + } + "save_all" => { + self.save_all()?; + } + "save_projects" => { + self.save_projects()?; + } + "test_server" => { + if let Some(server) = self.selected_server.clone() { + let _ = self + .main_tx + .send(ToolMessage::SendServer((server, "TEST".to_string()))); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + "You need to select a server first!".to_string(), + ))); + } + } + "list_clients" => { + if let Some(server) = self.selected_server.clone() { + let _ = self.main_tx.send(ToolMessage::SendServer(( + server, + "LIST_CLIENTS".to_string(), + ))); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + "You need to select a server first!".to_string(), + ))); + } + } + "select_client" => { + if let Some(arg) = command_args { + if let Ok(id) = arg.parse::() { + self.selected_client = id; + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("error parsing client id from arg: {}", arg), + ))); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("No client identified. Usage: select_client client_id"), + ))); + } + } + "add_scope" => { + if let Some(args) = command_args { + args.split_whitespace().into_iter().for_each(|host| { + self.projects[self.selected_project].add_scope(host.to_string()); + }); + } + } + "parse_scope" => { + let mut general_path = self.projects[self.selected_project].notes.clone(); + general_path.push("general.md"); + let general_string = read_to_string(general_path)?; + if let Some((_, scope_section)) = general_string.split_once("# Scope") { + if let Some((scope_section, _)) = scope_section.split_once("#") { + scope_section.lines().into_iter().for_each(|line| { + if line.contains("|") { + if !line.contains("Subnet") { + if !line.contains("--") { + let host = + line.split("|").collect::>()[1].to_string(); + self.projects[self.selected_project].add_scope(host); + } + } + } + }); + } + } + } + "exit" => { + self.save_all()?; + let _ = self.main_tx.send(ToolMessage::AppStateExit); + let _ = self + .main_tx + .send(ToolMessage::Output((rid, String::from("Good bye!")))); + } _ => { + if self.module_loader.asts.get(command_name).is_none() { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("Error command not found: {}", command_name), + ))); + return Ok(()); + } let ast = self .module_loader .asts .get(command_name) .ok_or_else(|| format!("Command not found: {}", command_name))? .clone(); - let engine = Arc::clone(&self.module_loader.engine); let cmd_meta = self .module_loader @@ -293,7 +619,7 @@ impl AppState { .clone(); let mut scope = Scope::new(); for arg_requirement in &cmd_meta.args { - match arg_requirement.as_str() { + match arg_requirement.trim() { "project" => { scope.push("project", self.projects[self.selected_project].clone()); } @@ -350,16 +676,16 @@ impl AppState { }; if result_str.contains("\n") { result_str.lines().into_iter().for_each(|line| { - let _ = tx.send(ToolMessage::Output(line.to_string())); + let _ = tx.send(ToolMessage::Output((rid, line.to_string()))); }); } else { - let _ = tx.send(ToolMessage::Output(result_str)); + let _ = tx.send(ToolMessage::Output((rid, result_str))); } } Err(err) => { - let _ = tx.send(ToolMessage::Output(format!( - "Script Error in execution: {}", - err + let _ = tx.send(ToolMessage::Output(( + rid, + format!("Script Error in execution: {}", err), ))); } } @@ -369,7 +695,12 @@ impl AppState { Ok(()) } - pub fn new_project(&mut self, org: String, name: String) -> Result<(), Box> { + pub fn new_project( + &mut self, + org: String, + name: String, + rid: usize, + ) -> Result<(), Box> { let template_box = self.config.get("template_box").unwrap(); let project_files = PathBuf::from(self.config.get("upcoming_files").unwrap()) .join(format!("{}/{}", org, name)); @@ -387,27 +718,36 @@ impl AppState { match create_dir_all(&project_files) { Ok(_) => {} Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "error making project files! {e} on {}", - project_files.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "error making project files! {e} on {}", + project_files.display() + ), ))); } } match create_dir_all(&project_notes) { Ok(_) => {} Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "error making project notes! {e} on {}", - project_notes.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "error making project notes! {e} on {}", + project_notes.display() + ), ))); } } match create_dir_all(&project_conf_folder) { Ok(_) => {} Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "error making config folder! {e} on {}", - project_conf_folder.display() + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "error making config folder! {e} on {}", + project_conf_folder.display() + ), ))); } } @@ -425,8 +765,9 @@ impl AppState { match copy(entry.path(), dest, &options) { Ok(_) => {} Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "error copying note template! {e}" + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("error copying note template! {e}"), ))); } } @@ -434,8 +775,9 @@ impl AppState { match fs::copy(entry.path(), project_notes.join(file_name.as_ref())) { Ok(_) => {} Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(format!( - "error copying note template! {e}" + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("error copying note template! {e}"), ))); } } @@ -462,14 +804,19 @@ impl AppState { config_folder: project_conf_folder, current: false, db: Some(db), + scope: Vec::new(), }; new_project.save_config()?; self.projects.push(new_project); return Ok(()); } - pub fn promote_project(&mut self) -> Result> { - let mut return_string = String::from("Project promoted successfully!"); + pub fn promote_project(&mut self, rid: usize) -> Result> { + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("promoting project..."), + )))?; + let return_string = String::from("Project promoted successfully!"); let mut new_project = self.projects[self.selected_project].clone(); let new_files_path = PathBuf::from(self.config.get("current_files").unwrap()) .join(format!("{}/{}", new_project.org_name, new_project.name)); @@ -481,20 +828,23 @@ impl AppState { options.overwrite = true; copy(new_project.files.clone(), new_files_path.clone(), &options)?; copy(new_project.notes.clone(), new_notes_path.clone(), &options)?; - let cleanup_res = Command::new("rm") - .arg("-rf") - .arg(new_project.files.display().to_string()) - .arg(new_project.notes.display().to_string()) - .status(); - if cleanup_res.is_err() { - return_string = format!( - "Error deleting {} and {}, please clean up manually. Otherwise Project promotion succeeded!", - new_files_path.display(), - new_notes_path.display() - ); - } + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("folders copied successfully!"), + )))?; + remove_dir_all(&new_project.files)?; + remove_dir_all(&new_project.notes)?; new_project.files(new_files_path.clone()); new_project.notes(new_notes_path.clone()); + new_project.current = true; + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("upcoming folders cleaned up!"), + )))?; + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("creating distrobox..."), + )))?; if let Some(mut db) = new_project.db { db.volumes = vec![ new_files_path.display().to_string(), @@ -503,16 +853,122 @@ impl AppState { ]; new_project.db = Some(db); let _ = self.main_tx.send(ToolMessage::RebuildDB); + self.main_tx.send(ToolMessage::Output(( + rid, + String::from("distrobox sent to rebuild function..."), + )))?; } + new_project.save_config()?; self.projects[self.selected_project] = new_project; return Ok(return_string); } + + pub fn save_projects(&mut self) -> Result<(), Box> { + for mut project in self.projects.clone() { + project.save_config()?; + } + return Ok(()); + } + + pub fn save_config(&mut self) -> Result<(), Box> { + let mut config_vec = Vec::new(); + for key in self.config.keys() { + if let Some(value) = self.config.get(key) { + config_vec.push(format!("{}: {}", key, value)); + } + } + if config_vec.len() > 0 { + fs::write(self.config_file.clone(), config_vec.join("\n"))?; + } else { + return Err("No config loaded!".into()); + } + return Ok(()); + } + + pub fn save_all(&mut self) -> Result<(), Box> { + self.save_config()?; + self.save_projects()?; + return Ok(()); + } + + pub fn new_server(&mut self, rid: usize) -> Result<(), Box> { + let prompt = self.prompt.clone(); + if prompt.responses.len() == prompt.num_responses { + let new_server = Server { + address: format!( + "{}:{}", + self.prompt.responses[0].clone(), + self.prompt.responses[1].clone() + ), + connected: false, + timer: Duration::from_secs(60), + id: 0, + last_check: time::Instant::now(), + message_que: Vec::new(), + client_id: 0, + }; + if let Ok(mut lock) = self.servers.lock() { + lock.push(new_server); + self.prompt.action = None; + self.prompt.responses.clear(); + self.prompt.num_responses = 0; + } + return Ok(()); + } else { + match prompt.responses.len() { + 0 => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + String::from("What is the IP for the server?"), + ))); + } + 1 => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + String::from("What is the port for the server?"), + ))); + } + _ => {} + } + } + Ok(()) + } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Server { pub address: String, pub connected: bool, + pub timer: Duration, + pub id: usize, + pub last_check: time::Instant, + pub message_que: Vec, + pub client_id: usize, +} + +impl Server { + pub async fn checkin(&mut self, outputs: Vec) -> (String, String) { + let mut out_string = (String::new(), String::new()); + if let Ok(mut stream) = TcpStream::connect(self.address.clone()).await { + let payload = format!("{}:{}\n", self.id.clone(), outputs.join(":")); + if let Err(e) = stream.write_all(payload.as_bytes()).await { + out_string.0 = format!("Error sending response output to server! {e}:"); + } else { + out_string.0 = String::from("Success"); + } + let mut buffer = [0; 4096]; + if let Ok(bytes_read) = stream.read(&mut buffer).await { + if bytes_read > 0 { + let response = String::from_utf8_lossy(&buffer[..bytes_read]); + if response.starts_with("ACTIONS:") { + let actions_str = response.trim_start_matches("ACTIONS:").trim(); + out_string.1.push_str(actions_str); + } + } + } + } + return out_string; + } } #[derive(Clone, Debug)] @@ -525,6 +981,7 @@ pub struct Project { pub hosts: Vec, pub current: bool, pub db: Option, + pub scope: Vec, } impl Project { @@ -538,6 +995,7 @@ impl Project { hosts: Vec::new(), current: false, db: None, + scope: Vec::new(), } } @@ -565,6 +1023,18 @@ impl Project { self.config_folder = path; } + pub fn add_scope(&mut self, host: String) { + if let Ok(ip) = host.parse::() { + self.scope.push(ip.to_string()) + } else if let Ok(net) = host.parse::() { + net.hosts().into_iter().for_each(|ip| { + self.scope.push(ip.to_string()); + }); + } else { + self.scope.push(host); + } + } + pub fn load_config(&mut self) -> Result<(), Box> { let config_contents = read_to_string(&self.config_folder)?; for line in config_contents.lines() { @@ -580,6 +1050,12 @@ impl Project { self.current = true; } } + "scope" => { + let ips: Vec<&str> = parts[1].split(",").collect(); + ips.iter().for_each(|ip| { + self.add_scope(ip.trim().to_string()); + }); + } _ => {} } } @@ -674,11 +1150,12 @@ impl Project { conf_file.push("project.conf"); let mut file = File::create(conf_file)?; let mut out_string = format!( - "org_name: {}\nname: {}\nnotes: {}\nfiles: {}\n", + "org_name: {}\nname: {}\nnotes: {}\nfiles: {}\nscope: {}\n", self.org_name, self.name, self.notes.display(), - self.files.display() + self.files.display(), + self.scope.join(","), ); if self.current { out_string.push_str("stage: current"); @@ -837,6 +1314,7 @@ impl Host { } } +#[derive(Clone, Debug)] pub enum Destination { Server, Victim, @@ -867,12 +1345,24 @@ impl User { #[derive(Clone, Debug)] pub enum ToolMessage { - Input(String), - Output(String), + Input((usize, String)), + Output((usize, String)), UpdateHost(Host), RebuildDB, + DestroyDB(DistroBox), + StopDB(DistroBox), + StopTemplate(DistroBox), UpdateProject(usize, Project), RemoveProject, + AddServer, + EndPrompt, + ConnectServer, + SelectServer, + SendServer((Server, String)), + ServerBrokerExit, + AppStateExit, + DisconnectServer, + DisconnectAllServers, } #[derive(Clone, Debug)] @@ -948,10 +1438,27 @@ impl ModuleLoader { |p: &mut Project| p.current.clone(), |p: &mut Project, val: bool| p.current = val, ) + .register_get_set( + "notes", + |p: &mut Project| p.notes.clone(), + |p: &mut Project, val: PathBuf| p.notes = val, + ) + .register_get_set( + "files", + |p: &mut Project| p.files.clone(), + |p: &mut Project, val: PathBuf| p.files = val, + ) .register_fn("add_host", Project::add_host); engine.register_fn("get_host", |hosts: &mut Vec, index: i64| { hosts.get(index as usize).cloned().unwrap_or_else(Host::new) }); + engine.register_fn("Host", Host::new); + engine.register_fn("open_file", |path: &str| { + std::fs::read_to_string(path).unwrap_or_else(|_| String::new()) + }); + engine.register_fn("add_port", |host: &mut Host, port: i64| { + host.add_port(port as usize); + }); Self { engine: Arc::new(engine), commands: HashMap::new(), @@ -1027,6 +1534,7 @@ impl ModuleLoader { return None; } }; + let command = ToolCommand { name, path: script_path, @@ -1056,7 +1564,14 @@ pub struct DistroBox { } impl DistroBox { - pub fn create(&mut self, tx: Sender) -> Result<(), Box> { + pub fn create(&mut self, tx: Sender, rid: usize) -> Result<(), Box> { + if let Err(e) = self.stop_template(tx.clone(), rid) { + tx.send(ToolMessage::Output(( + rid, + format!("error stopping template box! {e}"), + )))?; + return Ok(()); + } let mut create_command = Command::new("distrobox"); create_command .arg("create") @@ -1065,11 +1580,15 @@ impl DistroBox { .arg(self.template.clone()) .arg("--name") .arg(self.name.clone()); + let mut added_volumes = Vec::new(); for volume in &self.volumes { let folder_name = volume.split("/").last().unwrap(); - create_command - .arg("--volume") - .arg(format!("{}:/{}:rw", volume, folder_name)); + if !added_volumes.contains(&String::from(folder_name)) { + create_command + .arg("--volume") + .arg(format!("{}:/{}:rw", volume, folder_name)); + added_volumes.push(folder_name.to_string()); + } } create_command.stdout(Stdio::piped()); create_command.stderr(Stdio::piped()); @@ -1079,7 +1598,8 @@ impl DistroBox { std::thread::spawn(move || { let reader = BufReader::new(stdout); for line in reader.lines().flatten() { - let _ = tx_out.send(ToolMessage::Output(line)); + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); } }); } @@ -1088,18 +1608,155 @@ impl DistroBox { std::thread::spawn(move || { let reader = BufReader::new(stderr); for line in reader.lines().flatten() { - let _ = tx_out.send(ToolMessage::Output(line)); + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); } }); } let status = child.wait()?; if !status.success() { - let _ = tx.send(ToolMessage::Output(format!("error creating distrobox!"))); + let _ = tx.send(ToolMessage::Output(( + rid, + format!("error creating distrobox!"), + ))); } self.created = true; return Ok(()); } + + pub fn stop_template( + &mut self, + tx: Sender, + rid: usize, + ) -> Result<(), Box> { + let mut stop_command = Command::new("distrobox"); + stop_command + .arg("stop") + .arg("--root") + .arg(self.template.clone()) + .arg("--yes"); + stop_command.stdin(Stdio::piped()); + stop_command.stdout(Stdio::piped()); + stop_command.stderr(Stdio::piped()); + let mut child = stop_command.spawn()?; + if let Some(stdout) = child.stdout.take() { + let tx_out = tx.clone(); + std::thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines().flatten() { + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); + } + }); + } + if let Some(stderr) = child.stderr.take() { + let tx_out = tx.clone(); + std::thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines().flatten() { + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); + } + }); + } + let status = child.wait()?; + if !status.success() { + let _ = tx.send(ToolMessage::Output(( + rid, + format!("error stopping template distrobox!"), + ))); + } + return Ok(()); + } + + pub fn stop(&mut self, tx: Sender, rid: usize) -> Result<(), Box> { + let mut stop_command = Command::new("distrobox"); + stop_command + .arg("stop") + .arg("--root") + .arg(self.name.clone()) + .arg("--yes"); + stop_command.stdin(Stdio::piped()); + stop_command.stdout(Stdio::piped()); + stop_command.stderr(Stdio::piped()); + let mut child = stop_command.spawn()?; + if let Some(stdout) = child.stdout.take() { + let tx_out = tx.clone(); + std::thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines().flatten() { + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); + } + }); + } + if let Some(stderr) = child.stderr.take() { + let tx_out = tx.clone(); + std::thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines().flatten() { + let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); + println!("{}", line); + } + }); + } + let status = child.wait()?; + if !status.success() { + let _ = tx.send(ToolMessage::Output(( + rid, + format!("error stopping distrobox!"), + ))); + } + return Ok(()); + } + + pub fn destroy(&mut self, tx: Sender, rid: usize) -> Result<(), Box> { + let mut destroycmd = Command::new("distrobox"); + destroycmd + .arg("rm") + .arg("--root") + .arg(self.name.clone()) + .arg("-f"); + let res = destroycmd.status()?; + if !res.success() { + let _ = tx.send(ToolMessage::Output(( + rid, + format!("Error destroying distrobox!"), + ))); + let _ = tx.send(ToolMessage::Output(( + rid, + String::from("pleas try destroying manually:"), + ))); + let _ = tx.send(ToolMessage::Output(( + rid, + format!("distrobox rm --root {} -f", self.name), + ))); + } + self.created = false; + return Ok(()); + } + + pub fn launch_terminal(&self, cmd: String) { + if let Some((terminal_cmd, args)) = cmd.split_once(" ") { + let mut term_cmd = Command::new(terminal_cmd); + args.split(" ").into_iter().for_each(|arg| { + if arg == "|||COMMAND|||" { + term_cmd + .arg("distrobox") + .arg("enter") + .arg("--root") + .arg(self.name.clone()); + } else if arg.contains("ENV_NAME=") { + let (_, env_name) = arg.split_once("=").unwrap(); + unsafe { std::env::set_var(env_name, self.name.clone()) }; + } else { + term_cmd.arg(arg); + } + }); + let _ = term_cmd.spawn(); + } + } } #[derive(Clone)] @@ -1108,4 +1765,15 @@ struct Prompt { responses: Vec, execute_command: String, num_responses: usize, + prompts: Vec, +} + +impl Prompt { + pub fn reset(&mut self) { + self.action = None; + self.responses.clear(); + self.execute_command.clear(); + self.num_responses = 0; + self.prompts.clear(); + } } diff --git a/src/main.rs b/src/main.rs index 3173d31..7c46270 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use clap::Parser; +use std::sync::Arc; use std::{env, path::PathBuf, process::exit}; use tetanus::AppState; use tetanus::funcs::*; +use tokio::sync::Mutex; mod install; @@ -20,45 +22,61 @@ struct Args { #[arg(short, long, help = "start gui client")] gui: bool, + + #[arg(short, long, help = "for testing stuff...")] + test: bool, } -fn main() { - println!("checking for server or client config files..."); - let mut config_path = env::home_dir().unwrap(); - config_path.push(".config/tetanus"); - if !config_path.exists() { - println!("no config directory found in home directory..."); - config_path = PathBuf::from("/etc/tetanus"); +#[tokio::main] +async fn main() { + let args = Args::parse(); + if args.server { + let server = Arc::new(Mutex::new(tetanus::server::ServerState { + id: 0, + running: true, + address: String::from("127.0.0.1:31337"), + clients: Vec::new(), + key: String::new(), + })); + tetanus::server::start_server(server).await.unwrap(); + } else { + println!("checking for server or client config files..."); + let mut config_path = env::home_dir().unwrap(); + config_path.push(".config/tetanus"); if !config_path.exists() { - println!("no config directory found in /etc/tetanus... Installing!"); - let install_result = install::install(); - if install_result.is_ok() { - install_result.unwrap(); - println!("install succeeded!"); - exit(0); + println!("no config directory found in home directory..."); + config_path = PathBuf::from("/etc/tetanus"); + if !config_path.exists() { + println!("no config directory found in /etc/tetanus... Installing!"); + let install_result = install::install(); + if install_result.is_ok() { + install_result.unwrap(); + println!("install succeeded!"); + exit(0); + } } } - } - let args = Args::parse(); - let mut client_config_path = config_path.clone(); - client_config_path.push("client.conf"); - let mut server_config_path = config_path.clone(); - server_config_path.push("server.conf"); - if args.client { - if !client_config_path.exists() { - eprintln!( - "error: no client config path found at {}", - client_config_path.display() - ); - exit(1); + let args = Args::parse(); + let mut client_config_path = config_path.clone(); + client_config_path.push("client.conf"); + let mut server_config_path = config_path.clone(); + server_config_path.push("server.conf"); + if args.client { + if !client_config_path.exists() { + eprintln!( + "error: no client config path found at {}", + client_config_path.display() + ); + exit(1); + } + let (mut appstate, rx) = AppState::new(); + let config_load = appstate.load_config(client_config_path.clone()); + if config_load.is_err() { + eprintln!("error loading config!"); + exit(1); + } + appstate.initialize_modules(); + let _res = run_tui(appstate, rx).unwrap(); } - let (mut appstate, rx) = AppState::new(); - let config_load = appstate.load_config(client_config_path.clone()); - if config_load.is_err() { - eprintln!("error loading config!"); - exit(1); - } - appstate.initialize_modules(); - let _res = run_tui(appstate, rx); } }