Initial implementation of clipboard forwarding

This commit is contained in:
John Doty 2024-06-23 08:54:41 -07:00
parent fb86cbd0de
commit a40a493d39
7 changed files with 776 additions and 44 deletions

584
Cargo.lock generated
View file

@ -71,6 +71,18 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "bumpalo"
version = "3.14.0"
@ -89,6 +101,32 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "calloop"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
dependencies = [
"bitflags 2.5.0",
"log",
"polling",
"rustix 0.38.34",
"slab",
"thiserror",
]
[[package]]
name = "calloop-wayland-source"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
dependencies = [
"calloop",
"rustix 0.38.34",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "cassowary"
version = "0.3.0"
@ -122,6 +160,39 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "clipboard-win"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342"
dependencies = [
"lazy-bytes-cast",
"winapi",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "copypasta"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858"
dependencies = [
"clipboard-win",
"objc",
"objc-foundation",
"objc_id",
"smithay-clipboard",
"x11-clipboard",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
@ -137,13 +208,19 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"crossterm_winapi",
"futures-core",
"libc",
@ -164,13 +241,34 @@ dependencies = [
]
[[package]]
name = "errno"
version = "0.3.7"
name = "cursor-icon"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
[[package]]
name = "dlib"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -197,17 +295,19 @@ checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "fwd"
version = "0.8.1"
version = "0.9.0"
dependencies = [
"anyhow",
"assert_matches",
"bytes",
"copypasta",
"crossterm",
"home",
"indoc",
"log",
"open",
"procfs",
"rand 0.8.5",
"tempdir",
"thiserror",
"tokio",
@ -218,6 +318,27 @@ dependencies = [
"xdg",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.1"
@ -230,6 +351,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hermit-abi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "hex"
version = "0.4.3"
@ -280,7 +407,7 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"hermit-abi 0.3.3",
"libc",
"windows-sys 0.48.0",
]
@ -294,6 +421,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy-bytes-cast"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -302,9 +435,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
]
[[package]]
name = "linux-raw-sys"
@ -312,6 +455,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -328,12 +477,30 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memmap2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
dependencies = [
"libc",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@ -370,10 +537,39 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"hermit-abi 0.3.3",
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "object"
version = "0.32.1"
@ -434,6 +630,33 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "polling"
version = "3.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.4.0",
"pin-project-lite",
"rustix 0.38.34",
"tracing",
"windows-sys 0.52.0",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
@ -449,13 +672,22 @@ version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"byteorder",
"chrono",
"flate2",
"hex",
"lazy_static",
"rustix",
"rustix 0.36.17",
]
[[package]]
name = "quick-xml"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
dependencies = [
"memchr",
]
[[package]]
@ -480,6 +712,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
@ -495,6 +748,15 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rdrand"
version = "0.4.0"
@ -510,7 +772,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
@ -534,14 +796,33 @@ version = "0.36.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"linux-raw-sys 0.1.4",
"windows-sys 0.45.0",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys 0.4.14",
"windows-sys 0.52.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -598,12 +879,57 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "smithay-client-toolkit"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
dependencies = [
"bitflags 2.5.0",
"calloop",
"calloop-wayland-source",
"cursor-icon",
"libc",
"log",
"memmap2",
"rustix 0.38.34",
"thiserror",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols",
"wayland-protocols-wlr",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-clipboard"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d"
dependencies = [
"libc",
"smithay-client-toolkit",
"wayland-backend",
]
[[package]]
name = "socket2"
version = "0.5.5"
@ -631,7 +957,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand",
"rand 0.4.6",
"remove_dir_all",
]
@ -704,13 +1030,29 @@ dependencies = [
"serde",
]
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
[[package]]
name = "tui"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cassowary",
"crossterm",
"unicode-segmentation",
@ -805,6 +1147,102 @@ version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
[[package]]
name = "wayland-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34e9e6b6d4a2bb4e7e69433e0b35c7923b95d4dc8503a84d25ec917a4bbfdf07"
dependencies = [
"cc",
"downcast-rs",
"rustix 0.38.34",
"scoped-tls",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e63801c85358a431f986cffa74ba9599ff571fc5774ac113ed3b490c19a1133"
dependencies = [
"bitflags 2.5.0",
"rustix 0.38.34",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-csd-frame"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [
"bitflags 2.5.0",
"cursor-icon",
"wayland-backend",
]
[[package]]
name = "wayland-cursor"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a206e8b2b53b1d3fcb9428fec72bc278ce539e2fa81fe2bfc1ab27703d5187b9"
dependencies = [
"rustix 0.38.34",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [
"bitflags 2.5.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [
"bitflags 2.5.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67da50b9f80159dec0ea4c11c13e24ef9e7574bd6ce24b01860a175010cea565"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12"
dependencies = [
"dlib",
"log",
"once_cell",
"pkg-config",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -869,6 +1307,15 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
@ -899,6 +1346,22 @@ dependencies = [
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@ -911,6 +1374,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@ -923,6 +1392,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@ -935,6 +1410,18 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@ -947,6 +1434,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@ -959,6 +1452,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@ -971,6 +1470,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@ -983,8 +1488,53 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "x11-clipboard"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98785a09322d7446e28a13203d2cae1059a0dd3dfb32cb06d0a225f023d8286"
dependencies = [
"libc",
"x11rb",
]
[[package]]
name = "x11rb"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
dependencies = [
"gethostname",
"rustix 0.38.34",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
[[package]]
name = "xcursor"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911"
[[package]]
name = "xdg"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"

View file

@ -1,6 +1,6 @@
[package]
name = "fwd"
version = "0.9"
version = "0.9.0"
edition = "2021"
license = "MIT"
description = "Automatically forward ports to a remote server over ssh"
@ -16,11 +16,13 @@ bench = false
[dependencies]
anyhow = "1.0"
bytes = "1"
copypasta = "0.10.1"
crossterm = { version = "0.25", features = ["event-stream"] }
home = "0.5.4"
indoc = "1"
log = { version = "0.4", features = ["std"] }
open = "3"
rand = "0.8.5"
thiserror = "1.0"
tokio = { version = "1", features = ["io-std", "io-util", "macros", "net", "process", "rt", "rt-multi-thread", "fs"] }
tokio-stream = "0.1"

View file

@ -1,8 +1,10 @@
use crate::message::{Message, MessageReader, MessageWriter};
use anyhow::{bail, Result};
use bytes::BytesMut;
use copypasta::{ClipboardContext, ClipboardProvider};
use log::LevelFilter;
use log::{debug, error, info, warn};
use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddrV4};
use tokio::io::{
AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite,
@ -15,6 +17,14 @@ use tokio::sync::mpsc;
mod config;
mod ui;
// 256MB clipboard. Operating systems do not generally have a maximum size
// but I need to buffer it all in memory in order to use the `copypasta`
// crate and so I want to set a limit somewhere and this seems.... rational.
// You should use another transfer mechanism other than this if you need more
// than 256 MB of data. (Obviously future me will come along and shake his
// head at the limitations of my foresight, but oh well.)
const MAX_CLIPBOARD_SIZE: usize = 256 * 1024 * 1024;
/// Wait for the server to be ready; we know the server is there and
/// listening when we see the special sync marker, which is 8 NUL bytes in a
/// row.
@ -162,25 +172,22 @@ async fn client_handle_connection(
/// Listen on a port that we are currently forwarding, and use the SOCKS5
/// proxy on the specified port to handle the connections.
async fn client_listen(port: u16, socks_port: u16) -> Result<()> {
let listener =
TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)).await?;
loop {
let listener =
TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port))
.await?;
loop {
// The second item contains the IP and port of the new
// connection, but we don't care.
let (socket, _) = listener.accept().await?;
// The second item contains the IP and port of the new
// connection, but we don't care.
let (socket, _) = listener.accept().await?;
tokio::spawn(async move {
if let Err(e) =
client_handle_connection(socks_port, port, socket).await
{
error!("Error handling connection: {:?}", e);
} else {
debug!("Done???");
}
});
}
tokio::spawn(async move {
if let Err(e) =
client_handle_connection(socks_port, port, socket).await
{
error!("Error handling connection: {:?}", e);
} else {
debug!("Done???");
}
});
}
}
@ -188,20 +195,60 @@ async fn client_handle_messages<T: AsyncRead + Unpin>(
mut reader: MessageReader<T>,
events: mpsc::Sender<ui::UIEvent>,
) -> Result<()> {
let mut clipboard_messages = HashMap::new();
loop {
use Message::*;
match reader.read().await? {
Ping => (),
Ports(ports) => {
if let Err(_) = events.send(ui::UIEvent::Ports(ports)).await {
// TODO: Log
if let Err(e) = events.send(ui::UIEvent::Ports(ports)).await {
error!("Error sending ports request: {:?}", e);
}
}
Browse(url) => {
// TODO: Uh, security?
info!("Browsing to {url}...");
_ = open::that(url);
}
ClipStart(id) => {
info!("Starting clip op {id}");
clipboard_messages.insert(id, Vec::new());
}
ClipData(id, mut data) => match clipboard_messages.get_mut(&id) {
Some(bytes) => {
info!(
"Received data for clip op {id} ({len} bytes)",
len = data.len()
);
if bytes.len() < MAX_CLIPBOARD_SIZE {
bytes.append(&mut data);
}
}
None => {
warn!("Received data for unknown clip op {id}");
}
},
ClipEnd(id) => {
let Some(data) = clipboard_messages.remove(&id) else {
warn!("Received end for unknown clip op {id}");
continue;
};
let Ok(data) = String::from_utf8(data) else {
warn!("Received invalid utf8 for clipboard on op {id}");
continue;
};
let mut ctx = ClipboardContext::new().unwrap();
if let Err(e) = ctx.set_contents(data) {
error!("Unable to set clipboard data for op {id}: {e:?}");
}
}
message => error!("Unsupported: {:?}", message),
};
}

View file

@ -5,4 +5,5 @@ mod server;
pub use client::run_client;
pub use reverse::browse_url;
pub use reverse::clip_file;
pub use server::run_server;

View file

@ -14,6 +14,10 @@ connect to.
On a server that already has a client connected to it you can use `fwd browse
<url>` to open `<url>` in the default browser of the client.
On a server that already has a client connected to it you can use `fwd clip`
to read stdin and send it to the clipboard of the client, or `fwd clip <file>`
to send the the contents of `file`.
Options:
--version Print the version of fwd and exit
--sudo, -s Run the server side of fwd with `sudo`. This allows the
@ -32,6 +36,7 @@ enum Args {
Server,
Client(String, bool),
Browse(String),
Clip(Option<String>),
Error,
}
@ -60,13 +65,21 @@ fn parse_args(args: Vec<String>) -> Args {
} else {
Args::Error
}
} else if rest.len() > 1 {
} else if rest.len() >= 1 {
if rest[0] == "browse" {
if rest.len() == 2 {
Args::Browse(rest[1].to_string())
} else {
Args::Error
}
} else if rest[0] == "clip" {
if rest.len() == 1 {
Args::Clip(None)
} else if rest.len() == 2 {
Args::Clip(Some(rest[1].to_string()))
} else {
Args::Error
}
} else {
Args::Error
}
@ -85,6 +98,14 @@ async fn browse_url(url: &str) {
}
}
async fn clip_file(file: Option<String>) {
if let Err(e) = fwd::clip_file(file.as_deref()).await {
eprintln!("Unable to copy to the clipboard");
eprintln!("{}", e);
std::process::exit(1);
}
}
#[tokio::main]
async fn main() {
match parse_args(std::env::args().collect()) {
@ -100,6 +121,9 @@ async fn main() {
Args::Browse(url) => {
browse_url(&url).await;
}
Args::Clip(file) => {
clip_file(file).await;
}
Args::Client(server, sudo) => {
fwd::run_client(&server, sudo).await;
}

View file

@ -57,6 +57,11 @@ pub enum Message {
// Browse a thing
Browse(String),
// Send data to the remote clipboard
ClipStart(u64),
ClipData(u64, Vec<u8>),
ClipEnd(u64),
}
impl Message {
@ -103,6 +108,19 @@ impl Message {
result.put_u8(0x07);
put_string(result, url);
}
ClipStart(id) => {
result.put_u8(0x08);
result.put_u64(*id);
}
ClipData(id, data) => {
result.put_u8(0x09);
result.put_u64(*id);
put_data(result, data);
}
ClipEnd(id) => {
result.put_u8(0x0A);
result.put_u64(*id);
}
};
}
@ -132,7 +150,20 @@ impl Message {
Ok(Ports(ports))
}
0x07 => Ok(Browse(get_string(cursor)?)),
b => Err(Error::Unknown(b).into()),
0x08 => {
let id = get_u64(cursor)?;
Ok(ClipStart(id))
}
0x09 => {
let id = get_u64(cursor)?;
let data = get_data(cursor)?;
Ok(Self::ClipData(id, data))
}
0x0A => {
let id = get_u64(cursor)?;
Ok(ClipEnd(id))
}
b => Err(Error::Unknown(b)),
}
}
}
@ -151,6 +182,13 @@ fn get_u16(cursor: &mut Cursor<&[u8]>) -> Result<u16> {
Ok(cursor.get_u16())
}
fn get_u64(cursor: &mut Cursor<&[u8]>) -> Result<u64> {
if cursor.remaining() < 8 {
return Err(Error::Incomplete);
}
Ok(cursor.get_u64())
}
fn get_bytes(cursor: &mut Cursor<&[u8]>, length: usize) -> Result<Bytes> {
if cursor.remaining() < length {
return Err(Error::Incomplete);
@ -182,6 +220,22 @@ fn put_string<T: BufMut>(target: &mut T, str: &str) {
target.put_slice(str.as_bytes());
}
fn put_data<T: BufMut>(target: &mut T, data: &[u8]) {
target.put_u16(data.len().try_into().expect("Buffer is too long"));
target.put_slice(data);
}
fn get_data(cursor: &mut Cursor<&[u8]>) -> Result<Vec<u8>> {
let length = get_u16(cursor)?;
if cursor.remaining() < length.into() {
return Err(Error::Incomplete);
}
let mut data: Vec<u8> = vec![0; length.into()];
cursor.copy_to_slice(&mut data);
Ok(data)
}
// ----------------------------------------------------------------------------
// Message IO
@ -193,14 +247,14 @@ impl<T: AsyncWrite + Unpin> MessageWriter<T> {
pub fn new(writer: T) -> MessageWriter<T> {
MessageWriter { writer }
}
pub async fn write(self: &mut Self, msg: Message) -> Result<()> {
pub async fn write(&mut self, msg: Message) -> Result<()> {
// TODO: Optimize buffer usage please this is bad
// eprintln!("? {:?}", msg);
let mut buffer = msg.encode();
let buffer = msg.encode();
self.writer
.write_u32(buffer.len().try_into().expect("Message too large"))
.await?;
self.writer.write_all(&mut buffer).await?;
self.writer.write_all(&buffer).await?;
self.writer.flush().await?;
Ok(())
}
@ -214,7 +268,8 @@ impl<T: AsyncRead + Unpin> MessageReader<T> {
pub fn new(reader: T) -> MessageReader<T> {
MessageReader { reader }
}
pub async fn read(self: &mut Self) -> Result<Message> {
pub async fn read(&mut self) -> Result<Message> {
let frame_length = self.reader.read_u32().await?;
let mut data = vec![0; frame_length.try_into().unwrap()];
self.reader.read_exact(&mut data).await?;
@ -283,6 +338,12 @@ mod message_tests {
},
]));
assert_round_trip(Browse("https://google.com/".to_string()));
assert_round_trip(ClipStart(0x1234567890ABCDEF));
assert_round_trip(ClipData(
0x1234567890ABCDEF,
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
));
assert_round_trip(ClipEnd(0x1234567890ABCDEF));
}
#[test]

View file

@ -1,4 +1,6 @@
use anyhow::Result;
use rand::random;
use tokio::io::{AsyncRead, AsyncReadExt};
#[cfg(target_family = "unix")]
mod unix;
@ -27,3 +29,48 @@ pub async fn handle_reverse_connections(
pub async fn browse_url(url: &str) -> Result<()> {
send_reverse_message(Message::Browse(url.to_string())).await
}
async fn clip_reader<T: AsyncRead + Unpin>(reader: &mut T) -> Result<()> {
let clip_id: u64 = random();
send_reverse_message(Message::ClipStart(clip_id)).await?;
let mut count = 0;
let mut buf = vec![0; 1024];
loop {
let read = reader.read(&mut buf[count..]).await?;
if read == 0 {
break;
}
count += read;
if count == buf.len() {
send_reverse_message(Message::ClipData(clip_id, buf)).await?;
buf = vec![0; 1024];
count = 0;
}
}
if count > 0 {
buf.resize(count, 0);
send_reverse_message(Message::ClipData(clip_id, buf)).await?;
}
send_reverse_message(Message::ClipEnd(clip_id)).await?;
Ok(())
}
#[inline]
pub async fn clip_file(file: Option<&str>) -> Result<()> {
// send_reverse_message(Message::Browse(url.to_string())).await
match file {
Some(path) => {
let mut file = tokio::fs::File::open(path).await?;
clip_reader(&mut file).await?;
}
None => {
let mut stdin = tokio::io::stdin();
clip_reader(&mut stdin).await?;
}
}
Ok(())
}