mirror of
https://github.com/MeexReay/bRAC.git
synced 2025-05-06 21:48:03 +03:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
fa75ca60c4 | |||
091c1bca03 | |||
73f7c565e1 | |||
06c27aac63 | |||
2a3473853b | |||
e55d5a7a55 | |||
cb17464d8f | |||
015d8fca59 | |||
cc01dd1e49 | |||
cdbe45254d | |||
88b66af84e | |||
233a5eb9d5 | |||
f08f97f267 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
/result
|
/result
|
||||||
/build
|
/build
|
||||||
/config.yml
|
/config.yml
|
||||||
|
/bRAC
|
346
Cargo.lock
generated
346
Cargo.lock
generated
@ -94,9 +94,11 @@ version = "0.1.4+2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"gdk-pixbuf 0.3.0",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"homedir",
|
"homedir",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libnotify",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
@ -104,14 +106,32 @@ dependencies = [
|
|||||||
"serde_default",
|
"serde_default",
|
||||||
"serde_yml",
|
"serde_yml",
|
||||||
"socks",
|
"socks",
|
||||||
|
"tungstenite",
|
||||||
|
"winapi",
|
||||||
|
"winresource",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.17.0"
|
version = "3.17.0"
|
||||||
@ -124,15 +144,21 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.20.7"
|
version = "0.20.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7"
|
checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -142,7 +168,7 @@ version = "0.20.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df"
|
checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
@ -254,6 +280,25 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
@ -289,6 +334,22 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -405,27 +466,54 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk-pixbuf"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16160d212ae91abe9f3324c3fb233929ba322dde63585d15cda3336f8c529ed1"
|
||||||
|
dependencies = [
|
||||||
|
"gdk-pixbuf-sys 0.5.0",
|
||||||
|
"glib 0.4.1",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf"
|
name = "gdk-pixbuf"
|
||||||
version = "0.20.9"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7563afd6ff0a221edfbb70a78add5075b8d9cb48e637a40a24c3ece3fea414d0"
|
checksum = "7563afd6ff0a221edfbb70a78add5075b8d9cb48e637a40a24c3ece3fea414d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys 0.20.7",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk-pixbuf-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "798f97101eea8180da363d0e80e07ec7ec6d1809306601c0100c1de5bc8b4f52"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"gio-sys 0.5.0",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf-sys"
|
name = "gdk-pixbuf-sys"
|
||||||
version = "0.20.7"
|
version = "0.20.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c"
|
checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gio-sys",
|
"gio-sys 0.20.9",
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
@ -437,10 +525,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4850c9d9c1aecd1a3eb14fadc1cdb0ac0a2298037e116264c7473e1740a32d60"
|
checksum = "4850c9d9c1aecd1a3eb14fadc1cdb0ac0a2298037e116264c7473e1740a32d60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf 0.20.9",
|
||||||
"gdk4-sys",
|
"gdk4-sys",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"pango",
|
"pango",
|
||||||
]
|
]
|
||||||
@ -452,16 +540,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6f6eb95798e2b46f279cf59005daf297d5b69555428f185650d71974a910473a"
|
checksum = "6f6eb95798e2b46f279cf59005daf297d5b69555428f185650d71974a910473a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys 0.20.7",
|
||||||
"gio-sys",
|
"gio-sys 0.20.9",
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"pango-sys",
|
"pango-sys",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -484,42 +582,68 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gio-sys",
|
"gio-sys 0.20.9",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gio-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a303bbf7a5e75ab3b627117ff10e495d1b9e97e1d68966285ac2b1f6270091bc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio-sys"
|
name = "gio-sys"
|
||||||
version = "0.20.9"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e"
|
checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glib"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib"
|
name = "glib"
|
||||||
version = "0.20.9"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686"
|
checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gio-sys",
|
"gio-sys 0.20.9",
|
||||||
"glib-macros",
|
"glib-macros",
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -538,6 +662,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glib-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib-sys"
|
name = "glib-sys"
|
||||||
version = "0.20.9"
|
version = "0.20.9"
|
||||||
@ -548,13 +683,25 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gobject-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gobject-sys"
|
name = "gobject-sys"
|
||||||
version = "0.20.9"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3"
|
checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
@ -565,7 +712,7 @@ version = "0.20.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cbc5911bfb32d68dcfa92c9510c462696c2f715548fcd7f3f1be424c739de19"
|
checksum = "3cbc5911bfb32d68dcfa92c9510c462696c2f715548fcd7f3f1be424c739de19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"graphene-sys",
|
"graphene-sys",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -576,7 +723,7 @@ version = "0.20.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a"
|
checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
@ -590,7 +737,7 @@ checksum = "61f5e72f931c8c9f65fbfc89fe0ddc7746f147f822f127a53a9854666ac1f855"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"gdk4",
|
"gdk4",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"graphene-rs",
|
"graphene-rs",
|
||||||
"gsk4-sys",
|
"gsk4-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -605,8 +752,8 @@ checksum = "755059de55fa6f85a46bde8caf03e2184c96bfda1f6206163c72fb0ea12436dc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk4-sys",
|
"gdk4-sys",
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"graphene-sys",
|
"graphene-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"pango-sys",
|
"pango-sys",
|
||||||
@ -622,10 +769,10 @@ dependencies = [
|
|||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"field-offset",
|
"field-offset",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf 0.20.9",
|
||||||
"gdk4",
|
"gdk4",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"graphene-rs",
|
"graphene-rs",
|
||||||
"gsk4",
|
"gsk4",
|
||||||
"gtk4-macros",
|
"gtk4-macros",
|
||||||
@ -653,11 +800,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6"
|
checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys 0.20.7",
|
||||||
"gdk4-sys",
|
"gdk4-sys",
|
||||||
"gio-sys",
|
"gio-sys 0.20.9",
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"graphene-sys",
|
"graphene-sys",
|
||||||
"gsk4-sys",
|
"gsk4-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -689,6 +836,23 @@ dependencies = [
|
|||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httparse"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.63"
|
version = "0.1.63"
|
||||||
@ -763,6 +927,34 @@ version = "0.2.169"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libnotify"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10506a4f8bc6f8f7ccc6fde3a8290378d7aed3d1a26dca606a73e2ffe140cc2d"
|
||||||
|
dependencies = [
|
||||||
|
"gdk-pixbuf 0.3.0",
|
||||||
|
"gdk-pixbuf-sys 0.5.0",
|
||||||
|
"glib 0.4.1",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"libnotify-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libnotify-sys"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0a716b9b7d24ed10f1eb431e1527fa13c9a4bf2d4fa68bb3e54da1d0747383c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"gdk-pixbuf-sys 0.5.0",
|
||||||
|
"glib-sys 0.5.0",
|
||||||
|
"gobject-sys 0.5.0",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libyml"
|
name = "libyml"
|
||||||
version = "0.0.5"
|
version = "0.0.5"
|
||||||
@ -823,7 +1015,7 @@ version = "0.29.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
@ -850,7 +1042,7 @@ version = "0.10.70"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
|
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@ -895,7 +1087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6b1f5dc1b8cf9bc08bfc0843a04ee0fa2e78f1e1fa4b126844a383af4f25f0ec"
|
checksum = "6b1f5dc1b8cf9bc08bfc0843a04ee0fa2e78f1e1fa4b126844a383af4f25f0ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"pango-sys",
|
"pango-sys",
|
||||||
]
|
]
|
||||||
@ -906,8 +1098,8 @@ version = "0.20.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0dbb9b751673bd8fe49eb78620547973a1e719ed431372122b20abd12445bab5"
|
checksum = "0dbb9b751673bd8fe49eb78620547973a1e719ed431372122b20abd12445bab5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys 0.20.9",
|
||||||
"gobject-sys",
|
"gobject-sys 0.20.9",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
@ -1040,7 +1232,7 @@ version = "0.38.44"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@ -1074,7 +1266,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1153,6 +1345,17 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1235,6 +1438,26 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.20"
|
version = "0.8.20"
|
||||||
@ -1269,12 +1492,41 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tungstenite"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"data-encoding",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"thiserror",
|
||||||
|
"utf-8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -1535,13 +1787,23 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winresource"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba4a67c78ee5782c0c1cb41bebc7e12c6e79644daa1650ebbc1de5d5b08593f7"
|
||||||
|
dependencies = [
|
||||||
|
"toml",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen-rt"
|
name = "wit-bindgen-rt"
|
||||||
version = "0.33.0"
|
version = "0.33.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
12
Cargo.toml
12
Cargo.toml
@ -16,3 +16,15 @@ gtk4 = { version = "0.9.6", features = [ "v4_10" ] }
|
|||||||
chrono = "0.4.40"
|
chrono = "0.4.40"
|
||||||
serde_default = "0.2.0"
|
serde_default = "0.2.0"
|
||||||
socks = "0.3.4"
|
socks = "0.3.4"
|
||||||
|
libnotify = { version = "1.0.3", optional = true }
|
||||||
|
gdk-pixbuf = { version = "0.3.0", optional = true }
|
||||||
|
winapi = { version = "0.3.9", optional = true, features = ["wincon", "winuser"] }
|
||||||
|
tungstenite = "0.26.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
libnotify = ["dep:libnotify", "dep:gdk-pixbuf"]
|
||||||
|
winapi = ["dep:winapi"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
winresource = "0.1.20"
|
||||||
|
13
Makefile
13
Makefile
@ -1,11 +1,13 @@
|
|||||||
.PHONY: clean build
|
.PHONY: clean build windows build
|
||||||
|
|
||||||
build: build/windows-x86_64 build/linux-x86_64
|
build: build/windows-x86_64 build/linux-x86_64
|
||||||
|
windows: build/windows-x86_64
|
||||||
|
linux: build/linux-x86_64
|
||||||
|
|
||||||
build/windows-x86_64:
|
build/windows-x86_64:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
cargo build -r --target x86_64-pc-windows-gnu
|
cargo build -r -F winapi --target x86_64-pc-windows-gnu
|
||||||
curl -s https://api.github.com/repos/wingtk/gvsbuild/releases/latest \
|
curl -s https://api.github.com/repos/wingtk/gvsbuild/releases/latest \
|
||||||
| grep -o ".*browser_download_url.*GTK4_Gvsbuild.*_x64.zip.*" \
|
| grep -o ".*browser_download_url.*GTK4_Gvsbuild.*_x64.zip.*" \
|
||||||
| cut -d : -f 2,3 \
|
| cut -d : -f 2,3 \
|
||||||
@ -16,12 +18,19 @@ build/windows-x86_64:
|
|||||||
mv $@/bin/* $@/
|
mv $@/bin/* $@/
|
||||||
cp target/x86_64-pc-windows-gnu/release/bRAC.exe $@
|
cp target/x86_64-pc-windows-gnu/release/bRAC.exe $@
|
||||||
rm -r $@/bin
|
rm -r $@/bin
|
||||||
|
cp install.bat $@
|
||||||
|
cp uninstall.bat $@
|
||||||
|
|
||||||
build/linux-x86_64:
|
build/linux-x86_64:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
cargo build -r --target x86_64-unknown-linux-gnu
|
cargo build -r --target x86_64-unknown-linux-gnu
|
||||||
|
# patchbin target/x86_64-unknown-linux-gnu/release/bRAC
|
||||||
cp target/x86_64-unknown-linux-gnu/release/bRAC $@
|
cp target/x86_64-unknown-linux-gnu/release/bRAC $@
|
||||||
|
cp ru.themixray.bRAC.png $@
|
||||||
|
cp ru.themixray.bRAC.desktop $@
|
||||||
|
cp install.sh $@
|
||||||
|
cp uninstall.sh $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r build
|
rm -r build
|
16
build.rs
Normal file
16
build.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use {
|
||||||
|
std::{
|
||||||
|
env,
|
||||||
|
io,
|
||||||
|
},
|
||||||
|
winresource::WindowsResource,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() -> io::Result<()> {
|
||||||
|
if env::var_os("CARGO_CFG_WINDOWS").is_some() {
|
||||||
|
WindowsResource::new()
|
||||||
|
.set_icon("icon.ico")
|
||||||
|
.compile()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
33
flake.lock
generated
33
flake.lock
generated
@ -18,6 +18,23 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "flake-utils",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744463964,
|
"lastModified": 1744463964,
|
||||||
@ -68,6 +85,7 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
}
|
}
|
||||||
@ -89,6 +107,21 @@
|
|||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
44
flake.nix
44
flake.nix
@ -7,16 +7,15 @@
|
|||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs:
|
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
|
||||||
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
systems = [ "x86_64-linux" ];
|
|
||||||
perSystem = { config, self', pkgs, lib, system, ... }:
|
|
||||||
let
|
let
|
||||||
devDeps = with pkgs; [ pkg-config openssl gtk4 pango ];
|
devDeps = with pkgs; [ pkg-config openssl gtk4 pango libnotify ];
|
||||||
|
|
||||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||||
msrv = cargoToml.package.rust-version;
|
overlays = [ (import rust-overlay) ];
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system overlays;
|
||||||
|
};
|
||||||
mkDevShell = rustc:
|
mkDevShell = rustc:
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
@ -26,34 +25,19 @@
|
|||||||
nativeBuildInputs = devDeps ++ [ rustc ];
|
nativeBuildInputs = devDeps ++ [ rustc ];
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
_module.args.pkgs = import inputs.nixpkgs {
|
devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default)));
|
||||||
inherit system;
|
devShells.default = (mkDevShell pkgs.rust-bin.stable.latest.default);
|
||||||
overlays = [ (import inputs.rust-overlay) ];
|
|
||||||
};
|
|
||||||
|
|
||||||
devShells.default = self'.devShells.stable;
|
packages.default = (pkgs.makeRustPlatform {
|
||||||
|
|
||||||
packages.default = let
|
|
||||||
deps = with pkgs; [
|
|
||||||
pkg-config
|
|
||||||
openssl
|
|
||||||
gtk4
|
|
||||||
pango
|
|
||||||
];
|
|
||||||
in (pkgs.makeRustPlatform {
|
|
||||||
cargo = pkgs.rust-bin.stable.latest.minimal;
|
cargo = pkgs.rust-bin.stable.latest.minimal;
|
||||||
rustc = pkgs.rust-bin.stable.latest.minimal;
|
rustc = pkgs.rust-bin.stable.latest.minimal;
|
||||||
}).buildRustPackage {
|
}).buildRustPackage {
|
||||||
inherit (cargoToml.package) name version;
|
inherit (cargoToml.package) name version;
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoLock.lockFile = ./Cargo.lock;
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
buildInputs = deps;
|
buildInputs = devDeps;
|
||||||
nativeBuildInputs = deps;
|
nativeBuildInputs = devDeps ++ [ pkgs.rustc ];
|
||||||
};
|
|
||||||
|
|
||||||
devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default)));
|
|
||||||
devShells.stable = (mkDevShell pkgs.rust-bin.stable.latest.default);
|
|
||||||
devShells.msrv = (mkDevShell pkgs.rust-bin.stable.${msrv}.default);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
30
install.bat
Normal file
30
install.bat
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
@echo off
|
||||||
|
net session >nul 2>&1 || (
|
||||||
|
echo This script requires administrator privileges.
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set "DEST=C:\Program Files\bRAC"
|
||||||
|
mkdir "%DEST%" 2>nul
|
||||||
|
xcopy "." "%DEST%\" /E /I /H /Y >nul
|
||||||
|
|
||||||
|
for /d %%u in ("C:\Users\*") do (
|
||||||
|
if exist "%%u\AppData\Roaming\Microsoft\Windows\Desktop" (
|
||||||
|
call :s "%%u\AppData\Roaming\Microsoft\Windows\Desktop\bRAC.lnk" "%DEST%\bRAC.exe"
|
||||||
|
) else if exist "%%u\Desktop" (
|
||||||
|
call :s "%%u\Desktop\bRAC.lnk" "%DEST%\bRAC.exe"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
exit /b
|
||||||
|
|
||||||
|
:s
|
||||||
|
set "v=%TEMP%\_s.vbs"
|
||||||
|
> "%v%" echo Set o=CreateObject("WScript.Shell")
|
||||||
|
>>"%v%" echo Set l=o.CreateShortcut("%~1")
|
||||||
|
>>"%v%" echo l.TargetPath="%~2"
|
||||||
|
>>"%v%" echo l.WorkingDirectory="%~dp2"
|
||||||
|
>>"%v%" echo l.Save
|
||||||
|
wscript "%v%" >nul
|
||||||
|
del "%v%" >nul
|
||||||
|
exit /b
|
11
install.sh
Executable file
11
install.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp bRAC /bin/bRAC
|
||||||
|
chmod +x /bin/bRAC
|
||||||
|
cp ru.themixray.bRAC.png /usr/share/pixmaps
|
||||||
|
cp ru.themixray.bRAC.desktop /usr/share/applications
|
12
ru.themixray.bRAC.desktop
Normal file
12
ru.themixray.bRAC.desktop
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=bRAC
|
||||||
|
Version=0.1.4
|
||||||
|
Type=Application
|
||||||
|
Comment=better RAC client
|
||||||
|
Icon=ru.themixray.bRAC.png
|
||||||
|
Exec=bRAC
|
||||||
|
Categories=Network;
|
||||||
|
StartupNotify=true
|
||||||
|
DBusActivatable=true
|
||||||
|
Terminal=false
|
||||||
|
X-GNOME-UsesNotifications=true
|
BIN
ru.themixray.bRAC.png
Normal file
BIN
ru.themixray.bRAC.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
@ -26,7 +26,9 @@ pub struct Config {
|
|||||||
#[serde(default = "default_true")] pub chunked_enabled: bool,
|
#[serde(default = "default_true")] pub chunked_enabled: bool,
|
||||||
#[serde(default = "default_true")] pub formatting_enabled: bool,
|
#[serde(default = "default_true")] pub formatting_enabled: bool,
|
||||||
#[serde(default = "default_true")] pub commands_enabled: bool,
|
#[serde(default = "default_true")] pub commands_enabled: bool,
|
||||||
|
#[serde(default)] pub wrac_enabled: bool,
|
||||||
#[serde(default)] pub proxy: Option<String>,
|
#[serde(default)] pub proxy: Option<String>,
|
||||||
|
#[serde(default = "default_true")] pub notifications_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config_path() -> PathBuf {
|
pub fn get_config_path() -> PathBuf {
|
||||||
@ -115,6 +117,8 @@ pub struct Args {
|
|||||||
#[arg(long)] pub chunked_enabled: Option<bool>,
|
#[arg(long)] pub chunked_enabled: Option<bool>,
|
||||||
#[arg(long)] pub formatting_enabled: Option<bool>,
|
#[arg(long)] pub formatting_enabled: Option<bool>,
|
||||||
#[arg(long)] pub commands_enabled: Option<bool>,
|
#[arg(long)] pub commands_enabled: Option<bool>,
|
||||||
|
#[arg(long)] pub notifications_enabled: Option<bool>,
|
||||||
|
#[arg(long)] pub wrac_enabled: Option<bool>,
|
||||||
#[arg(long)] pub proxy: Option<String>,
|
#[arg(long)] pub proxy: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,14 +128,16 @@ impl Args {
|
|||||||
if let Some(v) = self.name.clone() { config.name = Some(v) }
|
if let Some(v) = self.name.clone() { config.name = Some(v) }
|
||||||
if let Some(v) = self.proxy.clone() { config.proxy = Some(v) }
|
if let Some(v) = self.proxy.clone() { config.proxy = Some(v) }
|
||||||
if let Some(v) = self.message_format.clone() { config.message_format = v }
|
if let Some(v) = self.message_format.clone() { config.message_format = v }
|
||||||
if let Some(v) = self.update_time.clone() { config.update_time = v }
|
if let Some(v) = self.update_time { config.update_time = v }
|
||||||
if let Some(v) = self.max_messages.clone() { config.max_messages = v }
|
if let Some(v) = self.max_messages { config.max_messages = v }
|
||||||
if let Some(v) = self.hide_my_ip.clone() { config.hide_my_ip = v }
|
if let Some(v) = self.hide_my_ip { config.hide_my_ip = v }
|
||||||
if let Some(v) = self.show_other_ip.clone() { config.show_other_ip = v }
|
if let Some(v) = self.show_other_ip { config.show_other_ip = v }
|
||||||
if let Some(v) = self.auth_enabled.clone() { config.auth_enabled = v }
|
if let Some(v) = self.auth_enabled { config.auth_enabled = v }
|
||||||
if let Some(v) = self.ssl_enabled.clone() { config.ssl_enabled = v }
|
if let Some(v) = self.ssl_enabled { config.ssl_enabled = v }
|
||||||
if let Some(v) = self.chunked_enabled.clone() { config.chunked_enabled = v }
|
if let Some(v) = self.chunked_enabled { config.chunked_enabled = v }
|
||||||
if let Some(v) = self.formatting_enabled.clone() { config.formatting_enabled = v }
|
if let Some(v) = self.formatting_enabled { config.formatting_enabled = v }
|
||||||
if let Some(v) = self.commands_enabled.clone() { config.commands_enabled = v }
|
if let Some(v) = self.commands_enabled { config.commands_enabled = v }
|
||||||
|
if let Some(v) = self.notifications_enabled { config.notifications_enabled = v }
|
||||||
|
if let Some(v) = self.wrac_enabled { config.wrac_enabled = v }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -77,7 +77,8 @@ macro_rules! connect_rac {
|
|||||||
&mut connect(
|
&mut connect(
|
||||||
&$ctx.config(|o| o.host.clone()),
|
&$ctx.config(|o| o.host.clone()),
|
||||||
$ctx.config(|o| o.ssl_enabled),
|
$ctx.config(|o| o.ssl_enabled),
|
||||||
$ctx.config(|o| o.proxy.clone())
|
$ctx.config(|o| o.proxy.clone()),
|
||||||
|
$ctx.config(|o| o.wrac_enabled)
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
}
|
}
|
181
src/chat/gui.rs
181
src/chat/gui.rs
@ -1,22 +1,23 @@
|
|||||||
use std::sync::{mpsc::{channel, Receiver}, Arc};
|
use std::sync::{mpsc::{channel, Receiver}, Arc, RwLock};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
use gtk4::{self as gtk, glib::timeout_add_once};
|
use gtk4 as gtk;
|
||||||
|
|
||||||
|
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::gdk::{Cursor, Display, Texture};
|
use gtk::gdk::{Cursor, Display, Texture};
|
||||||
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
|
||||||
use gtk::gio::{self, ActionEntry, ApplicationFlags, MemoryInputStream, Menu};
|
use gtk::gio::{self, ActionEntry, ApplicationFlags, MemoryInputStream, Menu};
|
||||||
use gtk::glib::clone;
|
use gtk::glib::clone;
|
||||||
use gtk::glib::{
|
use gtk::glib::{
|
||||||
self, clone::Downgrade,
|
self, clone::Downgrade,
|
||||||
timeout_add_local,
|
timeout_add_local,
|
||||||
source::timeout_add_local_once,
|
source::timeout_add_local_once,
|
||||||
ControlFlow
|
ControlFlow,
|
||||||
|
timeout_add_once
|
||||||
};
|
};
|
||||||
use gtk::pango::WrapMode;
|
use gtk::pango::WrapMode;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
@ -30,7 +31,13 @@ ctx::Context, on_send_message, parse_message, print_message, recv_tick, sanitize
|
|||||||
|
|
||||||
struct UiModel {
|
struct UiModel {
|
||||||
chat_box: GtkBox,
|
chat_box: GtkBox,
|
||||||
chat_scrolled: ScrolledWindow
|
chat_scrolled: ScrolledWindow,
|
||||||
|
app: Application,
|
||||||
|
window: ApplicationWindow,
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
notifications: Arc<RwLock<Vec<libnotify::Notification>>>,
|
||||||
|
#[cfg(not(feature = "libnotify"))]
|
||||||
|
notifications: Arc<RwLock<Vec<String>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
@ -158,9 +165,11 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
let show_other_ip_entry = gui_checkbox_setting!("Show Other IP", show_other_ip, ctx, vbox);
|
let show_other_ip_entry = gui_checkbox_setting!("Show Other IP", show_other_ip, ctx, vbox);
|
||||||
let auth_enabled_entry = gui_checkbox_setting!("Fake Auth Enabled", auth_enabled, ctx, vbox);
|
let auth_enabled_entry = gui_checkbox_setting!("Fake Auth Enabled", auth_enabled, ctx, vbox);
|
||||||
let ssl_enabled_entry = gui_checkbox_setting!("SSL Enabled", ssl_enabled, ctx, vbox);
|
let ssl_enabled_entry = gui_checkbox_setting!("SSL Enabled", ssl_enabled, ctx, vbox);
|
||||||
|
let wrac_enabled_entry = gui_checkbox_setting!("WRAC Enabled", wrac_enabled, ctx, vbox);
|
||||||
let chunked_enabled_entry = gui_checkbox_setting!("Chunked Enabled", chunked_enabled, ctx, vbox);
|
let chunked_enabled_entry = gui_checkbox_setting!("Chunked Enabled", chunked_enabled, ctx, vbox);
|
||||||
let formatting_enabled_entry = gui_checkbox_setting!("Formatting Enabled", formatting_enabled, ctx, vbox);
|
let formatting_enabled_entry = gui_checkbox_setting!("Formatting Enabled", formatting_enabled, ctx, vbox);
|
||||||
let commands_enabled_entry = gui_checkbox_setting!("Commands Enabled", commands_enabled, ctx, vbox);
|
let commands_enabled_entry = gui_checkbox_setting!("Commands Enabled", commands_enabled, ctx, vbox);
|
||||||
|
let notifications_enabled_entry = gui_checkbox_setting!("Notifications Enabled", notifications_enabled, ctx, vbox);
|
||||||
|
|
||||||
let save_button = Button::builder()
|
let save_button = Button::builder()
|
||||||
.label("Save")
|
.label("Save")
|
||||||
@ -182,6 +191,8 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
#[weak] chunked_enabled_entry,
|
#[weak] chunked_enabled_entry,
|
||||||
#[weak] formatting_enabled_entry,
|
#[weak] formatting_enabled_entry,
|
||||||
#[weak] commands_enabled_entry,
|
#[weak] commands_enabled_entry,
|
||||||
|
#[weak] notifications_enabled_entry,
|
||||||
|
#[weak] wrac_enabled_entry,
|
||||||
#[weak] proxy_entry,
|
#[weak] proxy_entry,
|
||||||
move |_| {
|
move |_| {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
@ -222,9 +233,11 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
show_other_ip: show_other_ip_entry.is_active(),
|
show_other_ip: show_other_ip_entry.is_active(),
|
||||||
auth_enabled: auth_enabled_entry.is_active(),
|
auth_enabled: auth_enabled_entry.is_active(),
|
||||||
ssl_enabled: ssl_enabled_entry.is_active(),
|
ssl_enabled: ssl_enabled_entry.is_active(),
|
||||||
|
wrac_enabled: wrac_enabled_entry.is_active(),
|
||||||
chunked_enabled: chunked_enabled_entry.is_active(),
|
chunked_enabled: chunked_enabled_entry.is_active(),
|
||||||
formatting_enabled: formatting_enabled_entry.is_active(),
|
formatting_enabled: formatting_enabled_entry.is_active(),
|
||||||
commands_enabled: commands_enabled_entry.is_active(),
|
commands_enabled: commands_enabled_entry.is_active(),
|
||||||
|
notifications_enabled: notifications_enabled_entry.is_active(),
|
||||||
proxy: {
|
proxy: {
|
||||||
let proxy = proxy_entry.text().to_string();
|
let proxy = proxy_entry.text().to_string();
|
||||||
|
|
||||||
@ -257,9 +270,11 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
#[weak] show_other_ip_entry,
|
#[weak] show_other_ip_entry,
|
||||||
#[weak] auth_enabled_entry,
|
#[weak] auth_enabled_entry,
|
||||||
#[weak] ssl_enabled_entry,
|
#[weak] ssl_enabled_entry,
|
||||||
|
#[weak] wrac_enabled_entry,
|
||||||
#[weak] chunked_enabled_entry,
|
#[weak] chunked_enabled_entry,
|
||||||
#[weak] formatting_enabled_entry,
|
#[weak] formatting_enabled_entry,
|
||||||
#[weak] commands_enabled_entry,
|
#[weak] commands_enabled_entry,
|
||||||
|
#[weak] notifications_enabled_entry,
|
||||||
#[weak] proxy_entry,
|
#[weak] proxy_entry,
|
||||||
move |_| {
|
move |_| {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
@ -275,9 +290,11 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
show_other_ip_entry.set_active(config.show_other_ip);
|
show_other_ip_entry.set_active(config.show_other_ip);
|
||||||
auth_enabled_entry.set_active(config.auth_enabled);
|
auth_enabled_entry.set_active(config.auth_enabled);
|
||||||
ssl_enabled_entry.set_active(config.ssl_enabled);
|
ssl_enabled_entry.set_active(config.ssl_enabled);
|
||||||
|
wrac_enabled_entry.set_active(config.wrac_enabled);
|
||||||
chunked_enabled_entry.set_active(config.chunked_enabled);
|
chunked_enabled_entry.set_active(config.chunked_enabled);
|
||||||
formatting_enabled_entry.set_active(config.formatting_enabled);
|
formatting_enabled_entry.set_active(config.formatting_enabled);
|
||||||
commands_enabled_entry.set_active(config.commands_enabled);
|
commands_enabled_entry.set_active(config.commands_enabled);
|
||||||
|
notifications_enabled_entry.set_active(config.notifications_enabled);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -290,6 +307,22 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
.child(&vbox)
|
.child(&vbox)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
let controller = gtk::EventControllerKey::new();
|
||||||
|
controller.connect_key_pressed({
|
||||||
|
let window = window.clone();
|
||||||
|
|
||||||
|
move |_, key, _, _| {
|
||||||
|
if key == gtk::gdk::Key::Escape {
|
||||||
|
window.close();
|
||||||
|
gtk::glib::Propagation::Proceed
|
||||||
|
} else {
|
||||||
|
gtk::glib::Propagation::Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.add_controller(controller);
|
||||||
|
|
||||||
window.present();
|
window.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +408,7 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
|
|
||||||
let server_list = ListBox::new();
|
let server_list = ListBox::new();
|
||||||
|
|
||||||
for url in ["meex.lol:42666", "meex.lol:11234", "91.192.22.20:42666"] {
|
for url in ["rac://meex.lol", "rac://meex.lol:11234", "rac://91.192.22.20"] {
|
||||||
let url = url.to_string();
|
let url = url.to_string();
|
||||||
|
|
||||||
let label = Label::builder()
|
let label = Label::builder()
|
||||||
@ -583,11 +616,17 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
|
|
||||||
UiModel {
|
UiModel {
|
||||||
chat_scrolled,
|
chat_scrolled,
|
||||||
chat_box
|
chat_box,
|
||||||
|
app: app.clone(),
|
||||||
|
window: window.clone(),
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
notifications: Arc::new(RwLock::new(Vec::<libnotify::Notification>::new())),
|
||||||
|
#[cfg(not(feature = "libnotify"))]
|
||||||
|
notifications: Arc::new(RwLock::new(Vec::<String>::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(ctx: Arc<Context>, ui: UiModel) {
|
fn setup(_: &Application, ctx: Arc<Context>, ui: UiModel) {
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
|
|
||||||
*ctx.sender.write().unwrap() = Some(Arc::new(sender));
|
*ctx.sender.write().unwrap() = Some(Arc::new(sender));
|
||||||
@ -596,6 +635,32 @@ fn setup(ctx: Arc<Context>, ui: UiModel) {
|
|||||||
|
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
ui.window.connect_notify(Some("is-active"), move |a, _| {
|
||||||
|
if a.is_active() {
|
||||||
|
GLOBAL.with(|global| {
|
||||||
|
if let Some((ui, _)) = &*global.borrow() {
|
||||||
|
for i in ui.notifications.read().unwrap().clone() {
|
||||||
|
i.close().expect("libnotify close error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(not(feature = "libnotify"))]
|
||||||
|
ui.window.connect_notify(Some("is-active"), move |a, _| {
|
||||||
|
if a.is_active() {
|
||||||
|
GLOBAL.with(|global| {
|
||||||
|
if let Some((ui, _)) = &*global.borrow() {
|
||||||
|
for i in ui.notifications.read().unwrap().clone() {
|
||||||
|
ui.app.withdraw_notification(&i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
GLOBAL.with(|global| {
|
GLOBAL.with(|global| {
|
||||||
*global.borrow_mut() = Some((ui, rx));
|
*global.borrow_mut() = Some((ui, rx));
|
||||||
});
|
});
|
||||||
@ -651,6 +716,40 @@ fn load_css() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
fn send_notification(_: Arc<Context>, ui: &UiModel, title: &str, message: &str) {
|
||||||
|
use libnotify::Notification;
|
||||||
|
|
||||||
|
let notification = Notification::new(title, message, None);
|
||||||
|
notification.set_app_name("bRAC");
|
||||||
|
let pixbuf_loader = gdk_pixbuf::PixbufLoader::new();
|
||||||
|
pixbuf_loader.loader_write(include_bytes!("images/icon.png")).unwrap();
|
||||||
|
pixbuf_loader.close().unwrap();
|
||||||
|
notification.set_image_from_pixbuf(&pixbuf_loader.get_pixbuf().unwrap());
|
||||||
|
notification.show().expect("libnotify send error");
|
||||||
|
|
||||||
|
ui.notifications.write().unwrap().push(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "libnotify"))]
|
||||||
|
fn send_notification(_: Arc<Context>, ui: &UiModel, title: &str, message: &str) {
|
||||||
|
use std::{hash::{DefaultHasher, Hasher}, time::UNIX_EPOCH};
|
||||||
|
|
||||||
|
use gtk4::gio::Notification;
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
hash.write(title.as_bytes());
|
||||||
|
hash.write(message.as_bytes());
|
||||||
|
|
||||||
|
let id = format!("bRAC-{}-{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(), hash.finish());
|
||||||
|
|
||||||
|
let notif = Notification::new(title);
|
||||||
|
notif.set_body(Some(&message));
|
||||||
|
ui.app.send_notification(Some(&id), ¬if);
|
||||||
|
|
||||||
|
ui.notifications.write().unwrap().push(id);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
||||||
let Some(message) = sanitize_message(message) else { return; };
|
let Some(message) = sanitize_message(message) else { return; };
|
||||||
|
|
||||||
@ -663,8 +762,8 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
|
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
if ctx.config(|o| o.show_other_ip) {
|
if ctx.config(|o| o.show_other_ip) {
|
||||||
let ip = Label::builder()
|
let ip_label = Label::builder()
|
||||||
.label(ip)
|
.label(&ip)
|
||||||
.margin_end(10)
|
.margin_end(10)
|
||||||
.halign(Align::Start)
|
.halign(Align::Start)
|
||||||
.valign(Align::Start)
|
.valign(Align::Start)
|
||||||
@ -672,11 +771,11 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
.selectable(true)
|
.selectable(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hbox.append(&ip);
|
hbox.append(&ip_label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let date = Label::builder()
|
let date_label = Label::builder()
|
||||||
.label(format!("[{date}]"))
|
.label(format!("[{date}]"))
|
||||||
.halign(Align::Start)
|
.halign(Align::Start)
|
||||||
.valign(Align::Start)
|
.valign(Align::Start)
|
||||||
@ -684,10 +783,10 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
.selectable(true)
|
.selectable(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hbox.append(&date);
|
hbox.append(&date_label);
|
||||||
|
|
||||||
if let Some((name, color)) = nick {
|
if let Some((name, color)) = nick {
|
||||||
let name = Label::builder()
|
let name_label = Label::builder()
|
||||||
.label(format!("<{name}>"))
|
.label(format!("<{name}>"))
|
||||||
.halign(Align::Start)
|
.halign(Align::Start)
|
||||||
.valign(Align::Start)
|
.valign(Align::Start)
|
||||||
@ -695,11 +794,29 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
.selectable(true)
|
.selectable(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hbox.append(&name);
|
hbox.append(&name_label);
|
||||||
|
|
||||||
|
if !ui.window.is_active() {
|
||||||
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
|
send_notification(ctx.clone(), ui, &format!("{}'s Message", &name), &content);
|
||||||
|
// let notif = Notification::new(&format!("{}'s Message", &name));
|
||||||
|
// notif.set_body(Some(&content));
|
||||||
|
// app.send_notification(Some("user-message"), ¬if);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !ui.window.is_active() {
|
||||||
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
|
send_notification(ctx.clone(), ui, "System Message", &content);
|
||||||
|
// let notif = Notification::new("System Message");
|
||||||
|
// notif.set_body(Some(&content));
|
||||||
|
// app.send_notification(Some("system-message"), ¬if);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = Label::builder()
|
let content_label = Label::builder()
|
||||||
.label(content)
|
.label(&content)
|
||||||
.halign(Align::Start)
|
.halign(Align::Start)
|
||||||
.valign(Align::Start)
|
.valign(Align::Start)
|
||||||
.css_classes(["message-content"])
|
.css_classes(["message-content"])
|
||||||
@ -708,10 +825,11 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
.wrap_mode(WrapMode::Char)
|
.wrap_mode(WrapMode::Char)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hbox.append(&content);
|
hbox.append(&content_label);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let content = Label::builder()
|
let content_label = Label::builder()
|
||||||
.label(message)
|
.label(&message)
|
||||||
.halign(Align::Start)
|
.halign(Align::Start)
|
||||||
.valign(Align::Start)
|
.valign(Align::Start)
|
||||||
.css_classes(["message-content"])
|
.css_classes(["message-content"])
|
||||||
@ -720,7 +838,16 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
.wrap_mode(WrapMode::Char)
|
.wrap_mode(WrapMode::Char)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hbox.append(&content);
|
hbox.append(&content_label);
|
||||||
|
|
||||||
|
if !ui.window.is_active() {
|
||||||
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
|
send_notification(ctx.clone(), ui, "Chat Message", &message);
|
||||||
|
// let notif = Notification::new("Chat Message");
|
||||||
|
// notif.set_body(Some(&message));
|
||||||
|
// app.send_notification(Some("chat-message"), ¬if);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.chat_box.append(&hbox);
|
ui.chat_box.append(&hbox);
|
||||||
@ -747,6 +874,11 @@ fn run_recv_loop(ctx: Arc<Context>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_main_loop(ctx: Arc<Context>) {
|
pub fn run_main_loop(ctx: Arc<Context>) {
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
{
|
||||||
|
libnotify::init("ru.themixray.bRAC").expect("libnotify init error");
|
||||||
|
}
|
||||||
|
|
||||||
let application = Application::builder()
|
let application = Application::builder()
|
||||||
.application_id("ru.themixray.bRAC")
|
.application_id("ru.themixray.bRAC")
|
||||||
.flags(ApplicationFlags::FLAGS_NONE)
|
.flags(ApplicationFlags::FLAGS_NONE)
|
||||||
@ -757,7 +889,7 @@ pub fn run_main_loop(ctx: Arc<Context>) {
|
|||||||
|
|
||||||
move |app| {
|
move |app| {
|
||||||
let ui = build_ui(ctx.clone(), app);
|
let ui = build_ui(ctx.clone(), app);
|
||||||
setup(ctx.clone(), ui);
|
setup(app, ctx.clone(), ui);
|
||||||
load_css();
|
load_css();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -771,4 +903,9 @@ pub fn run_main_loop(ctx: Arc<Context>) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
application.run_with_args::<&str>(&[]);
|
application.run_with_args::<&str>(&[]);
|
||||||
|
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
{
|
||||||
|
libnotify::uninit();
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,12 +2,9 @@ use std::{
|
|||||||
error::Error, sync::Arc, thread, time::{Duration, SystemTime, UNIX_EPOCH}
|
error::Error, sync::Arc, thread, time::{Duration, SystemTime, UNIX_EPOCH}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{connect_rac, proto::{register_user, send_message_auth}};
|
use crate::connect_rac;
|
||||||
|
|
||||||
use super::{
|
use super::proto::{connect, read_messages, send_message, send_message_spoof_auth, register_user, send_message_auth};
|
||||||
proto::{connect, read_messages, send_message, send_message_spoof_auth},
|
|
||||||
util::sanitize_text
|
|
||||||
};
|
|
||||||
|
|
||||||
use gui::{add_chat_message, clear_chat_messages};
|
use gui::{add_chat_message, clear_chat_messages};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@ -19,6 +16,9 @@ pub use gui::run_main_loop;
|
|||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
static ref ANSI_REGEX: Regex = Regex::new(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])").unwrap();
|
||||||
|
static ref CONTROL_CHARS_REGEX: Regex = Regex::new(r"[\x00-\x1F\x7F]").unwrap();
|
||||||
|
|
||||||
pub static ref DATE_REGEX: Regex = Regex::new(r"\[(.*?)\] (.*)").unwrap();
|
pub static ref DATE_REGEX: Regex = Regex::new(r"\[(.*?)\] (.*)").unwrap();
|
||||||
pub static ref IP_REGEX: Regex = Regex::new(r"\{(.*?)\} (.*)").unwrap();
|
pub static ref IP_REGEX: Regex = Regex::new(r"\{(.*?)\} (.*)").unwrap();
|
||||||
|
|
||||||
@ -44,6 +44,11 @@ const HELP_MESSAGE: &str = "Help message:
|
|||||||
/spam n text - send message with text n times
|
/spam n text - send message with text n times
|
||||||
/ping - check server ping";
|
/ping - check server ping";
|
||||||
|
|
||||||
|
pub fn sanitize_text(input: &str) -> String {
|
||||||
|
let without_ansi = ANSI_REGEX.replace_all(input, "");
|
||||||
|
let cleaned_text = CONTROL_CHARS_REGEX.replace_all(&without_ansi, "");
|
||||||
|
cleaned_text.into_owned()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn Error>> {
|
pub fn add_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn Error>> {
|
||||||
for i in message.split("\n")
|
for i in message.split("\n")
|
||||||
@ -205,9 +210,7 @@ pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = format!("Read messages error: {}", e.to_string()).to_string();
|
println!("Read messages error: {}", e.to_string())
|
||||||
ctx.add_message(ctx.config(|o| o.max_messages), vec![msg.clone()]);
|
|
||||||
add_chat_message(ctx.clone(), msg.clone());
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,7 @@
|
|||||||
|
|
||||||
.message-name { font-weight: bold; }
|
.message-name { font-weight: bold; }
|
||||||
|
|
||||||
.message-name-black { color: #2E2E2E; }
|
.message-name-green { color: #70fa7a; }
|
||||||
.message-name-bright-black { color: #555555; }
|
.message-name-red { color: #fa7070; }
|
||||||
.message-name-red { color: #8B0000; }
|
.message-name-magenta { color: #da70fa; }
|
||||||
.message-name-bright-red { color: #FF0000; }
|
.message-name-cyan { color: #70fadc; }
|
||||||
.message-name-green { color: #006400; }
|
|
||||||
.message-name-bright-green { color: #00FF00; }
|
|
||||||
.message-name-yellow { color: #8B8B00; }
|
|
||||||
.message-name-bright-yellow { color: #FFFF00; }
|
|
||||||
.message-name-blue { color: #00008B; }
|
|
||||||
.message-name-bright-blue { color: #0000FF; }
|
|
||||||
.message-name-bright-magenta { color: #FF00FF; }
|
|
||||||
.message-name-magenta { color: #8B008B; }
|
|
||||||
.message-name-cyan { color: #008B8B; }
|
|
||||||
.message-name-bright-cyan { color: #00FFFF; }
|
|
||||||
.message-name-white { color: #A9A9A9; }
|
|
||||||
.message-name-bright-white { color: #FFFFFF; }
|
|
@ -1,5 +1,4 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
pub mod chat;
|
pub mod chat;
|
||||||
pub mod util;
|
|
||||||
pub mod proto;
|
pub mod proto;
|
@ -4,8 +4,10 @@ use bRAC::proto::{connect, read_messages, send_message};
|
|||||||
use bRAC::chat::{config::{get_config_path, load_config, Args}, ctx::Context, run_main_loop};
|
use bRAC::chat::{config::{get_config_path, load_config, Args}, ctx::Context, run_main_loop};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(feature = "winapi")]
|
||||||
|
unsafe { winapi::um::wincon::FreeConsole() };
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let config_path = get_config_path();
|
let config_path = get_config_path();
|
||||||
@ -18,7 +20,7 @@ fn main() {
|
|||||||
let mut config = load_config(config_path);
|
let mut config = load_config(config_path);
|
||||||
|
|
||||||
if args.read_messages {
|
if args.read_messages {
|
||||||
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone()).expect("Error reading message");
|
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone(), config.wrac_enabled).expect("Error reading message");
|
||||||
|
|
||||||
print!("{}", read_messages(
|
print!("{}", read_messages(
|
||||||
&mut stream,
|
&mut stream,
|
||||||
@ -33,7 +35,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = &args.send_message {
|
if let Some(message) = &args.send_message {
|
||||||
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone()).expect("Error sending message");
|
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone(), config.wrac_enabled).expect("Error sending message");
|
||||||
|
|
||||||
send_message(
|
send_message(
|
||||||
&mut stream,
|
&mut stream,
|
||||||
|
267
src/proto/mod.rs
Normal file
267
src/proto/mod.rs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
use std::{error::Error, fmt::Debug, io::{Read, Write}, net::{TcpStream, ToSocketAddrs}, time::Duration};
|
||||||
|
|
||||||
|
use native_tls::{TlsConnector, TlsStream};
|
||||||
|
use socks::Socks5Stream;
|
||||||
|
use tungstenite::WebSocket;
|
||||||
|
|
||||||
|
pub mod rac;
|
||||||
|
pub mod wrac;
|
||||||
|
|
||||||
|
pub trait Stream: Read + Write + Unpin + Send + Sync + Debug {
|
||||||
|
fn set_read_timeout(&self, timeout: Duration);
|
||||||
|
fn set_write_timeout(&self, timeout: Duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for TcpStream {
|
||||||
|
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(&self, Some(timeout)); }
|
||||||
|
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(&self, Some(timeout)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for Socks5Stream {
|
||||||
|
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(self.get_ref(), Some(timeout)); }
|
||||||
|
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(self.get_ref(), Some(timeout)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Stream> Stream for TlsStream<T> {
|
||||||
|
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
||||||
|
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for TlsStream<Box<dyn Stream>> {
|
||||||
|
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
||||||
|
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RacStream {
|
||||||
|
WRAC(WebSocket<Box<dyn Stream>>),
|
||||||
|
RAC(Box<dyn Stream>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `socks5://user:pass@127.0.0.1:12345/path -> ("127.0.0.1:12345", ("user", "pass"))` \
|
||||||
|
/// `socks5://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||||
|
/// `https://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||||
|
/// `127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||||
|
/// `user:pass@127.0.0.1:12345 -> ("127.0.0.1:12345", ("user", "pass"))`
|
||||||
|
pub fn parse_socks5_url(url: &str) -> Option<(String, Option<(String, String)>)> {
|
||||||
|
let (_, url) = url.split_once("://").unwrap_or(("", url));
|
||||||
|
let (url, _) = url.split_once("/").unwrap_or((url, ""));
|
||||||
|
if let Some((auth, url)) = url.split_once("@") {
|
||||||
|
let (user, pass) = auth.split_once(":")?;
|
||||||
|
Some((url.to_string(), Some((user.to_string(), pass.to_string()))))
|
||||||
|
} else {
|
||||||
|
Some((url.to_string(), None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// url -> (host, ssl, wrac) \
|
||||||
|
/// `127.0.0.1` -> `("127.0.0.1:42666", false, false)` \
|
||||||
|
/// `127.0.0.1:12345` -> `("127.0.0.1:12345", false, false)` \
|
||||||
|
/// `rac://127.0.0.1/` -> `("127.0.0.1:42666", false, false)` \
|
||||||
|
/// `racs://127.0.0.1/` -> `("127.0.0.1:42667", true, false)` \
|
||||||
|
/// `wrac://127.0.0.1/` -> `("127.0.0.1:52666", false, true)` \
|
||||||
|
/// `wracs://127.0.0.1/` -> `(127.0.0.1:52667, true, true)` \
|
||||||
|
pub fn parse_rac_url(url: &str) -> Option<(String, bool, bool)> {
|
||||||
|
let (scheme, url) = url.split_once("://").unwrap_or(("rac", url));
|
||||||
|
let (host, _) = url.split_once("/").unwrap_or((url, ""));
|
||||||
|
match scheme.to_lowercase().as_str() {
|
||||||
|
"rac" => {
|
||||||
|
Some((
|
||||||
|
if host.contains(":") {
|
||||||
|
host.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{host}:42666")
|
||||||
|
},
|
||||||
|
false, false
|
||||||
|
))
|
||||||
|
},
|
||||||
|
"racs" => {
|
||||||
|
Some((
|
||||||
|
if host.contains(":") {
|
||||||
|
host.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{host}:42667")
|
||||||
|
},
|
||||||
|
true, false
|
||||||
|
))
|
||||||
|
},
|
||||||
|
"wrac" => {
|
||||||
|
Some((
|
||||||
|
if host.contains(":") {
|
||||||
|
host.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{host}:52666")
|
||||||
|
},
|
||||||
|
false, true
|
||||||
|
))
|
||||||
|
},
|
||||||
|
"wracs" => {
|
||||||
|
Some((
|
||||||
|
if host.contains(":") {
|
||||||
|
host.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{host}:52667")
|
||||||
|
},
|
||||||
|
true, true
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create RAC connection (also you can just TcpStream::connect)
|
||||||
|
///
|
||||||
|
/// host - host string, example: "example.com:12345", "example.com" (default port is 42666)
|
||||||
|
/// ssl - wrap with ssl client, write false if you dont know what it is
|
||||||
|
/// proxy - socks5 proxy (host, (user, pass))
|
||||||
|
/// wrac - to use wrac protocol
|
||||||
|
pub fn connect(host: &str, ssl: bool, proxy: Option<String>, wrac: bool) -> Result<RacStream, Box<dyn Error>> {
|
||||||
|
let (host, ssl_, wrac_) = parse_rac_url(host).ok_or::<Box<dyn Error>>("url parse error".into())?;
|
||||||
|
let (ssl, wrac) = (ssl_ || ssl, wrac_ || wrac);
|
||||||
|
|
||||||
|
let stream: Box<dyn Stream> = if let Some(proxy) = proxy {
|
||||||
|
if let Some((proxy, auth)) = parse_socks5_url(&proxy) {
|
||||||
|
if let Some((user, pass)) = auth {
|
||||||
|
Box::new(Socks5Stream::connect_with_password(&proxy, host.as_str(), &user, &pass)?)
|
||||||
|
} else {
|
||||||
|
Box::new(Socks5Stream::connect(&proxy, host.as_str())?)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("proxy parse error".into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let addr = host.to_socket_addrs()?.next().ok_or::<Box<dyn Error>>("addr parse error".into())?;
|
||||||
|
|
||||||
|
Box::new(TcpStream::connect(&addr)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
let stream = if ssl {
|
||||||
|
let ip: String = host.split_once(":")
|
||||||
|
.map(|o| o.0.to_string())
|
||||||
|
.unwrap_or(host.clone());
|
||||||
|
|
||||||
|
Box::new(TlsConnector::builder()
|
||||||
|
.danger_accept_invalid_certs(true)
|
||||||
|
.danger_accept_invalid_hostnames(true)
|
||||||
|
.build()?
|
||||||
|
.connect(&ip, stream)?)
|
||||||
|
} else {
|
||||||
|
stream
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.set_read_timeout(Duration::from_secs(3));
|
||||||
|
stream.set_write_timeout(Duration::from_secs(3));
|
||||||
|
|
||||||
|
if wrac {
|
||||||
|
let (client, _) = tungstenite::client(
|
||||||
|
&format!("ws{}://{host}", if ssl { "s" } else { "" }),
|
||||||
|
stream
|
||||||
|
)?;
|
||||||
|
Ok(RacStream::WRAC(client))
|
||||||
|
} else {
|
||||||
|
Ok(RacStream::RAC(stream))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send message with fake auth
|
||||||
|
///
|
||||||
|
/// Explaination:
|
||||||
|
///
|
||||||
|
/// let (name, message) = message.split("> ") else { return send_message(stream, message) }
|
||||||
|
/// if send_message_auth(name, name, message) != 0 {
|
||||||
|
/// let name = "\x1f" + name
|
||||||
|
/// register_user(stream, name, name)
|
||||||
|
/// send_message_spoof_auth(stream, name + "> " + message)
|
||||||
|
/// }
|
||||||
|
pub fn send_message_spoof_auth(stream: &mut RacStream, message: &str, remove_null: bool) -> Result<(), Box<dyn Error>> {
|
||||||
|
let Some((name, message)) = message.split_once("> ") else { return send_message(stream, message) };
|
||||||
|
|
||||||
|
if let Ok(f) = send_message_auth(stream, &name, &message, &message, remove_null) {
|
||||||
|
if f != 0 {
|
||||||
|
let name = format!("\x1f{name}");
|
||||||
|
register_user(stream, &name, &name, remove_null)?;
|
||||||
|
send_message_spoof_auth(stream, &format!("{name}> {message}"), remove_null)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Send message
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// message - message text
|
||||||
|
pub fn send_message(
|
||||||
|
stream: &mut RacStream,
|
||||||
|
message: &str
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
match stream {
|
||||||
|
RacStream::WRAC(websocket) => wrac::send_message(websocket, message),
|
||||||
|
RacStream::RAC(stream) => rac::send_message(stream, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register user
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// name - user name
|
||||||
|
/// password - user password
|
||||||
|
/// remove_null - remove null bytes on reading
|
||||||
|
///
|
||||||
|
/// returns whether the user was registered
|
||||||
|
pub fn register_user(
|
||||||
|
stream: &mut RacStream,
|
||||||
|
name: &str,
|
||||||
|
password: &str,
|
||||||
|
remove_null: bool
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
match stream {
|
||||||
|
RacStream::WRAC(websocket) => wrac::register_user(websocket, name, password),
|
||||||
|
RacStream::RAC(stream) => rac::register_user(stream, name, password, remove_null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send message with auth
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// message - message text
|
||||||
|
/// name - user name
|
||||||
|
/// password - user password
|
||||||
|
/// remove_null - remove null bytes on reading
|
||||||
|
///
|
||||||
|
/// returns 0 if the message was sent successfully
|
||||||
|
/// returns 1 if the user does not exist
|
||||||
|
/// returns 2 if the password is incorrect
|
||||||
|
pub fn send_message_auth(
|
||||||
|
stream: &mut RacStream,
|
||||||
|
name: &str,
|
||||||
|
password: &str,
|
||||||
|
message: &str,
|
||||||
|
remove_null: bool
|
||||||
|
) -> Result<u8, Box<dyn Error>> {
|
||||||
|
match stream {
|
||||||
|
RacStream::WRAC(websocket) => wrac::send_message_auth(websocket, name, password, message),
|
||||||
|
RacStream::RAC(stream) => rac::send_message_auth(stream, name, password, message, remove_null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read messages
|
||||||
|
///
|
||||||
|
/// max_messages - max messages in list
|
||||||
|
/// last_size - last returned packet size
|
||||||
|
/// remove_null - start with skipping null bytes
|
||||||
|
/// chunked - is chunked reading enabled
|
||||||
|
///
|
||||||
|
/// returns (messages, packet size)
|
||||||
|
pub fn read_messages(
|
||||||
|
stream: &mut RacStream,
|
||||||
|
max_messages: usize,
|
||||||
|
last_size: usize,
|
||||||
|
remove_null: bool,
|
||||||
|
chunked: bool
|
||||||
|
) -> Result<Option<(Vec<String>, usize)>, Box<dyn Error>> {
|
||||||
|
match stream {
|
||||||
|
RacStream::WRAC(websocket) => wrac::read_messages(websocket, max_messages, last_size, chunked),
|
||||||
|
RacStream::RAC(stream) => rac::read_messages(stream, max_messages, last_size, remove_null, chunked)
|
||||||
|
}
|
||||||
|
}
|
@ -1,83 +1,4 @@
|
|||||||
#![allow(unused)]
|
use std::{error::Error, io::{Read, Write}};
|
||||||
|
|
||||||
use std::{error::Error, fmt::Debug, io::{Read, Write}, net::{SocketAddr, TcpStream, ToSocketAddrs}, str::FromStr, time::Duration};
|
|
||||||
use native_tls::{TlsConnector, TlsStream};
|
|
||||||
use socks::Socks5Stream;
|
|
||||||
|
|
||||||
use crate::util::parse_socks5_url;
|
|
||||||
|
|
||||||
pub trait RacStream: Read + Write + Unpin + Send + Sync + Debug {
|
|
||||||
fn set_read_timeout(&self, timeout: Duration);
|
|
||||||
fn set_write_timeout(&self, timeout: Duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RacStream for TcpStream {
|
|
||||||
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(&self, Some(timeout)); }
|
|
||||||
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(&self, Some(timeout)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RacStream for Socks5Stream {
|
|
||||||
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(self.get_ref(), Some(timeout)); }
|
|
||||||
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(self.get_ref(), Some(timeout)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: RacStream> RacStream for TlsStream<T> {
|
|
||||||
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
|
||||||
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RacStream for TlsStream<Box<dyn RacStream>> {
|
|
||||||
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
|
||||||
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create RAC connection (also you can just TcpStream::connect)
|
|
||||||
///
|
|
||||||
/// host - host string, example: "example.com:12345", "example.com" (default port is 42666)
|
|
||||||
/// ssl - wrap with ssl client, write false if you dont know what it is
|
|
||||||
/// proxy - socks5 proxy (host, (user, pass))
|
|
||||||
pub fn connect(host: &str, ssl: bool, proxy: Option<String>) -> Result<Box<dyn RacStream>, Box<dyn Error>> {
|
|
||||||
let host = if host.contains(":") {
|
|
||||||
host.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{host}:42666")
|
|
||||||
};
|
|
||||||
|
|
||||||
let stream: Box<dyn RacStream> = if let Some(proxy) = proxy {
|
|
||||||
if let Some((proxy, auth)) = parse_socks5_url(&proxy) {
|
|
||||||
if let Some((user, pass)) = auth {
|
|
||||||
Box::new(Socks5Stream::connect_with_password(&proxy, host.as_str(), &user, &pass)?)
|
|
||||||
} else {
|
|
||||||
Box::new(Socks5Stream::connect(&proxy, host.as_str())?)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err("proxy parse error".into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let addr = host.to_socket_addrs()?.next().ok_or::<Box<dyn Error>>("addr parse error".into())?;
|
|
||||||
|
|
||||||
Box::new(TcpStream::connect(&addr)?)
|
|
||||||
};
|
|
||||||
|
|
||||||
let stream = if ssl {
|
|
||||||
let ip: String = host.split_once(":")
|
|
||||||
.map(|o| o.0.to_string())
|
|
||||||
.unwrap_or(host.clone());
|
|
||||||
|
|
||||||
Box::new(TlsConnector::builder()
|
|
||||||
.danger_accept_invalid_certs(true)
|
|
||||||
.danger_accept_invalid_hostnames(true)
|
|
||||||
.build()?
|
|
||||||
.connect(&ip, stream)?)
|
|
||||||
} else {
|
|
||||||
stream
|
|
||||||
};
|
|
||||||
|
|
||||||
stream.set_read_timeout(Duration::from_secs(3));
|
|
||||||
stream.set_write_timeout(Duration::from_secs(3));
|
|
||||||
|
|
||||||
Ok(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send message
|
/// Send message
|
||||||
///
|
///
|
||||||
@ -155,30 +76,6 @@ pub fn send_message_auth(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send message with fake auth
|
|
||||||
///
|
|
||||||
/// Explaination:
|
|
||||||
///
|
|
||||||
/// let (name, message) = message.split("> ") else { return send_message(stream, message) }
|
|
||||||
/// if send_message_auth(name, name, message) != 0 {
|
|
||||||
/// let name = "\x1f" + name
|
|
||||||
/// register_user(stream, name, name)
|
|
||||||
/// send_message_spoof_auth(stream, name + "> " + message)
|
|
||||||
/// }
|
|
||||||
pub fn send_message_spoof_auth(stream: &mut (impl Write + Read), message: &str, remove_null: bool) -> Result<(), Box<dyn Error>> {
|
|
||||||
let Some((name, message)) = message.split_once("> ") else { return send_message(stream, message) };
|
|
||||||
|
|
||||||
if let Ok(f) = send_message_auth(stream, &name, &message, &message, remove_null) {
|
|
||||||
if f != 0 {
|
|
||||||
let name = format!("\x1f{name}");
|
|
||||||
register_user(stream, &name, &name, remove_null);
|
|
||||||
send_message_spoof_auth(stream, &format!("{name}> {message}"), remove_null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Skip null bytes and return first non-null byte
|
/// Skip null bytes and return first non-null byte
|
||||||
pub fn skip_null(stream: &mut impl Read) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn skip_null(stream: &mut impl Read) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
loop {
|
loop {
|
128
src/proto/wrac.rs
Normal file
128
src/proto/wrac.rs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
use std::{error::Error, io::{Read, Write}};
|
||||||
|
use tungstenite::{WebSocket, Message};
|
||||||
|
|
||||||
|
|
||||||
|
/// Send message
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// message - message text
|
||||||
|
pub fn send_message(
|
||||||
|
stream: &mut WebSocket<impl Write + Read>,
|
||||||
|
message: &str
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
stream.write(Message::Binary(format!("\x01{message}").as_bytes().to_vec().into()))?;
|
||||||
|
stream.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register user
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// name - user name
|
||||||
|
/// password - user password
|
||||||
|
///
|
||||||
|
/// returns whether the user was registered
|
||||||
|
pub fn register_user(
|
||||||
|
stream: &mut WebSocket<impl Write + Read>,
|
||||||
|
name: &str,
|
||||||
|
password: &str
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
stream.write(Message::Binary(format!("\x03{name}\n{password}").as_bytes().to_vec().into()))?;
|
||||||
|
stream.flush()?;
|
||||||
|
if let Ok(msg) = stream.read() {
|
||||||
|
Ok(!msg.is_binary() || msg.into_data().get(0).unwrap_or(&0) == &0)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send message with auth
|
||||||
|
///
|
||||||
|
/// stream - any stream that can be written to
|
||||||
|
/// message - message text
|
||||||
|
/// name - user name
|
||||||
|
/// password - user password
|
||||||
|
///
|
||||||
|
/// returns 0 if the message was sent successfully
|
||||||
|
/// returns 1 if the user does not exist
|
||||||
|
/// returns 2 if the password is incorrect
|
||||||
|
pub fn send_message_auth(
|
||||||
|
stream: &mut WebSocket<impl Write + Read>,
|
||||||
|
name: &str,
|
||||||
|
password: &str,
|
||||||
|
message: &str
|
||||||
|
) -> Result<u8, Box<dyn Error>> {
|
||||||
|
stream.write(Message::Binary(format!("\x02{name}\n{password}\n{message}").as_bytes().to_vec().into()))?;
|
||||||
|
stream.flush()?;
|
||||||
|
if let Ok(msg) = stream.read() {
|
||||||
|
if msg.is_binary() {
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
Ok(*msg.into_data().get(0).unwrap_or(&0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read messages
|
||||||
|
///
|
||||||
|
/// max_messages - max messages in list
|
||||||
|
/// last_size - last returned packet size
|
||||||
|
/// chunked - is chunked reading enabled
|
||||||
|
///
|
||||||
|
/// returns (messages, packet size)
|
||||||
|
pub fn read_messages(
|
||||||
|
stream: &mut WebSocket<impl Write + Read>,
|
||||||
|
max_messages: usize,
|
||||||
|
last_size: usize,
|
||||||
|
chunked: bool
|
||||||
|
) -> Result<Option<(Vec<String>, usize)>, Box<dyn Error>> {
|
||||||
|
stream.write(Message::Binary(vec![0x00].into()))?;
|
||||||
|
stream.flush()?;
|
||||||
|
|
||||||
|
let packet_size = {
|
||||||
|
let msg = stream.read()?;
|
||||||
|
if !msg.is_binary() {
|
||||||
|
return Err("msg is not binary".into());
|
||||||
|
}
|
||||||
|
let len = msg.into_data().to_vec();
|
||||||
|
|
||||||
|
String::from_utf8(len)?
|
||||||
|
.trim_matches(char::from(0))
|
||||||
|
.parse()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if last_size == packet_size {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let to_read = if !chunked || last_size == 0 {
|
||||||
|
stream.write(Message::Binary(vec![0x00, 0x01].into()))?;
|
||||||
|
packet_size
|
||||||
|
} else {
|
||||||
|
stream.write(Message::Binary(format!("\x00\x02{}", last_size).as_bytes().to_vec().into()))?;
|
||||||
|
packet_size - last_size
|
||||||
|
};
|
||||||
|
stream.flush()?;
|
||||||
|
|
||||||
|
let msg = stream.read()?;
|
||||||
|
if !msg.is_binary() {
|
||||||
|
return Err("msg is not binary".into());
|
||||||
|
}
|
||||||
|
let packet_data = msg.into_data().to_vec();
|
||||||
|
|
||||||
|
if packet_data.len() > to_read {
|
||||||
|
return Err("too big msg".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let packet_data = String::from_utf8_lossy(&packet_data).to_string();
|
||||||
|
|
||||||
|
let lines: Vec<&str> = packet_data.split("\n").collect();
|
||||||
|
let lines: Vec<String> = lines.clone().into_iter()
|
||||||
|
.skip(if lines.len() >= max_messages { lines.len() - max_messages } else { 0 })
|
||||||
|
.map(|o| o.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Some((lines, packet_size)))
|
||||||
|
}
|
29
src/util.rs
29
src/util.rs
@ -1,29 +0,0 @@
|
|||||||
use lazy_static::lazy_static;
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref ANSI_REGEX: Regex = Regex::new(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])").unwrap();
|
|
||||||
static ref CONTROL_CHARS_REGEX: Regex = Regex::new(r"[\x00-\x1F\x7F]").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sanitize_text(input: &str) -> String {
|
|
||||||
let without_ansi = ANSI_REGEX.replace_all(input, "");
|
|
||||||
let cleaned_text = CONTROL_CHARS_REGEX.replace_all(&without_ansi, "");
|
|
||||||
cleaned_text.into_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `socks5://user:pass@127.0.0.1:12345/path -> ("127.0.0.1:12345", ("user", "pass"))` \
|
|
||||||
/// `socks5://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
|
||||||
/// `https://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
|
||||||
/// `127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
|
||||||
/// `user:pass@127.0.0.1:12345 -> ("127.0.0.1:12345", ("user", "pass"))`
|
|
||||||
pub fn parse_socks5_url(url: &str) -> Option<(String, Option<(String, String)>)> {
|
|
||||||
let (_, url) = url.split_once("://").unwrap_or(("", url));
|
|
||||||
let (url, _) = url.split_once("/").unwrap_or((url, ""));
|
|
||||||
if let Some((auth, url)) = url.split_once("@") {
|
|
||||||
let (user, pass) = auth.split_once(":")?;
|
|
||||||
Some((url.to_string(), Some((user.to_string(), pass.to_string()))))
|
|
||||||
} else {
|
|
||||||
Some((url.to_string(), None))
|
|
||||||
}
|
|
||||||
}
|
|
35
uninstall.bat
Normal file
35
uninstall.bat
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@echo off
|
||||||
|
net session >nul 2>&1 || (
|
||||||
|
echo This script requires administrator privileges.
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set "TARGET=C:\Program Files\bRAC\bRAC.exe"
|
||||||
|
|
||||||
|
for /d %%u in ("C:\Users\*") do (
|
||||||
|
call :d "%%u\AppData\Roaming\Microsoft\Windows\Desktop"
|
||||||
|
call :d "%%u\Desktop"
|
||||||
|
)
|
||||||
|
|
||||||
|
cd /d "%TEMP%"
|
||||||
|
rmdir /s /q "C:\Program Files\bRAC"
|
||||||
|
exit /b
|
||||||
|
|
||||||
|
:d
|
||||||
|
if not exist "%~1" exit /b
|
||||||
|
for %%f in ("%~1\*.lnk") do (
|
||||||
|
call :c "%%~f"
|
||||||
|
)
|
||||||
|
exit /b
|
||||||
|
|
||||||
|
:c
|
||||||
|
set "v=%TEMP%\_c.vbs"
|
||||||
|
> "%v%" echo Set o=CreateObject("WScript.Shell")
|
||||||
|
>>"%v%" echo Set l=o.CreateShortcut("%~1")
|
||||||
|
>>"%v%" echo WScript.Echo l.TargetPath
|
||||||
|
for /f "usebackq delims=" %%t in (`wscript //nologo "%v%"`) do (
|
||||||
|
if /I "%%t"=="%TARGET%" del /f /q "%~1"
|
||||||
|
)
|
||||||
|
del "%v%" >nul
|
||||||
|
exit /b
|
14
uninstall.sh
Executable file
14
uninstall.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
getent passwd | while IFS=: read -r name password uid gid gecos home shell; do
|
||||||
|
rm -rf $home/.config/bRAC;
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f /bin/bRAC
|
||||||
|
rm -f ru.themixray.bRAC.png /usr/share/pixmaps
|
||||||
|
rm -f ru.themixray.bRAC.desktop /usr/share/applications
|
Loading…
Reference in New Issue
Block a user