From c8c99cf06fdc45568fc10b98257963ae63e9c486 Mon Sep 17 00:00:00 2001 From: eatradish Date: Sat, 10 Jun 2023 13:40:11 +0800 Subject: rbw get add flag --clipboard to copy result to clipboard --- Cargo.lock | 424 ++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/bin/rbw/commands.rs | 219 +++++++++++++++++-------- src/bin/rbw/main.rs | 4 + 4 files changed, 565 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d8112b..ee40de6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,12 @@ dependencies = [ "digest", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -219,12 +225,36 @@ dependencies = [ "os_str_bytes", ] +[[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 = "const-oid" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "copypasta" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133fc8675ee3a4ec9aa513584deda9aa0faeda3586b87f7f0f2ba082c66fb172" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -312,6 +342,21 @@ dependencies = [ "windows-sys 0.45.0", ] +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -449,6 +494,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -687,6 +742,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" @@ -702,6 +763,16 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "libm" version = "0.2.6" @@ -742,12 +813,39 @@ dependencies = [ "libc", ] +[[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.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.7.1" @@ -763,6 +861,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "0.8.6" @@ -775,6 +879,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.26.2" @@ -784,11 +900,21 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", "static_assertions", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint-dig" version = "0.8.2" @@ -847,6 +973,35 @@ dependencies = [ "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 = "once_cell" version = "1.17.1" @@ -958,6 +1113,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1027,6 +1188,7 @@ dependencies = [ "cbc", "clap", "clap_complete", + "copypasta", "daemonize", "directories", "env_logger", @@ -1036,7 +1198,7 @@ dependencies = [ "humantime", "libc", "log", - "nix", + "nix 0.26.2", "pbkdf2", "percent-encoding", "pkcs8", @@ -1247,6 +1409,12 @@ dependencies = [ "windows-sys 0.42.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.1.0" @@ -1422,6 +1590,34 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" +[[package]] +name = "smithay-client-toolkit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +dependencies = [ + "bitflags", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] +name = "smithay-clipboard" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +dependencies = [ + "smithay-client-toolkit", + "wayland-client", +] + [[package]] name = "socket2" version = "0.4.9" @@ -1834,6 +2030,79 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.61" @@ -1879,6 +2148,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1891,13 +2169,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1906,7 +2184,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -1915,13 +2202,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1930,42 +2232,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" @@ -1975,6 +2319,52 @@ dependencies = [ "winapi", ] +[[package]] +name = "x11-clipboard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980b9aa9226c3b7de8e2adb11bf20124327c054e0e5812d2aac0b5b5a87e7464" +dependencies = [ + "x11rb", +] + +[[package]] +name = "x11rb" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +dependencies = [ + "gethostname", + "nix 0.24.3", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +dependencies = [ + "nix 0.24.3", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xml-rs" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" + [[package]] name = "zeroize" version = "1.5.7" diff --git a/Cargo.toml b/Cargo.toml index 778e9d4..e6cd8c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ totp-lite = "2.0.0" url = "2.3.1" uuid = { version = "1.3.0", features = ["v4"] } zeroize = "1.5.7" +copypasta = "0.8.2" [package.metadata.deb] depends = "pinentry" diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index 5d51f70..252c5ec 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -1,4 +1,5 @@ use anyhow::Context as _; +use copypasta::{ClipboardContext, ClipboardProvider}; use serde::Serialize; use std::io; use std::io::prelude::Write; @@ -26,7 +27,7 @@ struct DecryptedCipher { } impl DecryptedCipher { - fn display_short(&self, desc: &str) -> bool { + fn display_short(&self, desc: &str, clipboard: bool) -> bool { match &self.data { DecryptedData::Login { password, .. } => { password.as_ref().map_or_else( @@ -35,8 +36,8 @@ impl DecryptedCipher { false }, |password| { - println!("{password}"); - true + let res = val_display_or_store(clipboard, password); + res }, ) } @@ -47,8 +48,8 @@ impl DecryptedCipher { false }, |number| { - println!("{number}"); - true + let res = val_display_or_store(clipboard, number); + res }, ) } @@ -70,8 +71,11 @@ impl DecryptedCipher { eprintln!("entry for '{desc}' had no name"); false } else { - println!("{}", names.join(" ")); - true + let res = val_display_or_store( + clipboard, + &format!("{}", names.join(" ")), + ); + res } } DecryptedData::SecureNote {} => self.notes.as_ref().map_or_else( @@ -80,14 +84,14 @@ impl DecryptedCipher { false }, |notes| { - println!("{notes}"); - true + let res = val_display_or_store(clipboard, notes); + res }, ), } } - fn display_field(&self, desc: &str, field: &str) { + fn display_field(&self, desc: &str, field: &str, clipboard: bool) { // Convert the field name to lowercase let field = field.to_lowercase(); let field = field.as_str(); @@ -100,11 +104,11 @@ impl DecryptedCipher { } => match field { "notes" => { if let Some(notes) = &self.notes { - println!("{notes}"); + val_display_or_store(clipboard, notes); } } "username" | "user" => { - display_field("Username", username.as_deref()); + display_field("Username", username.as_deref(), clipboard); } "totp" | "code" => { if let Some(totp) = totp { @@ -116,12 +120,16 @@ impl DecryptedCipher { "uris" | "urls" | "sites" => { if let Some(uris) = uris { for uri in uris { - display_field("URI", Some(uri.uri.as_str())); + display_field( + "URI", + Some(uri.uri.as_str()), + clipboard, + ); } } } "password" => { - self.display_short(desc); + self.display_short(desc, clipboard); } _ => { for f in &self.fields { @@ -135,6 +143,7 @@ impl DecryptedCipher { display_field( f.name.as_deref().unwrap_or("(null)"), Some(f.value.as_deref().unwrap_or("")), + clipboard, ); } } @@ -149,34 +158,39 @@ impl DecryptedCipher { .. } => match field { "number" | "card" => { - self.display_short(desc); + self.display_short(desc, clipboard); } "exp" => { if let (Some(month), Some(year)) = (exp_month, exp_year) { display_field( "Exp", Some(format!("{month}/{year}").as_str()), + clipboard, ); } } "exp_month" | "month" => { - display_field("Month", exp_month.as_deref()); + display_field("Month", exp_month.as_deref(), clipboard); } "exp_year" | "year" => { - display_field("Year", exp_year.as_deref()); + display_field("Year", exp_year.as_deref(), clipboard); } "cvv" => { - display_field("CVV", code.as_deref()); + display_field("CVV", code.as_deref(), clipboard); } "name" | "cardholder" => { - display_field("Name", cardholder_name.as_deref()); + display_field( + "Name", + cardholder_name.as_deref(), + clipboard, + ); } "brand" | "type" => { - display_field("Brand", brand.as_deref()); + display_field("Brand", brand.as_deref(), clipboard); } "notes" => { if let Some(notes) = &self.notes { - println!("{notes}"); + val_display_or_store(clipboard, notes); } } _ => { @@ -191,6 +205,7 @@ impl DecryptedCipher { display_field( f.name.as_deref().unwrap_or("(null)"), Some(f.value.as_deref().unwrap_or("")), + clipboard, ); } } @@ -213,42 +228,50 @@ impl DecryptedCipher { .. } => match field { "name" => { - self.display_short(desc); + self.display_short(desc, clipboard); } "email" => { - display_field("Email", email.as_deref()); + display_field("Email", email.as_deref(), clipboard); } "address" => { - display_field("Address", address1.as_deref()); - display_field("Address", address2.as_deref()); - display_field("Address", address3.as_deref()); + display_field("Address", address1.as_deref(), clipboard); + display_field("Address", address2.as_deref(), clipboard); + display_field("Address", address3.as_deref(), clipboard); } "city" => { - display_field("City", city.as_deref()); + display_field("City", city.as_deref(), clipboard); } "state" => { - display_field("State", state.as_deref()); + display_field("State", state.as_deref(), clipboard); } "postcode" | "zipcode" | "zip" => { - display_field("Zip", postal_code.as_deref()); + display_field("Zip", postal_code.as_deref(), clipboard); } "country" => { - display_field("Country", country.as_deref()); + display_field("Country", country.as_deref(), clipboard); } "phone" => { - display_field("Phone", phone.as_deref()); + display_field("Phone", phone.as_deref(), clipboard); } "ssn" => { - display_field("SSN", ssn.as_deref()); + display_field("SSN", ssn.as_deref(), clipboard); } "license" => { - display_field("License", license_number.as_deref()); + display_field( + "License", + license_number.as_deref(), + clipboard, + ); } "passport" => { - display_field("Passport", passport_number.as_deref()); + display_field( + "Passport", + passport_number.as_deref(), + clipboard, + ); } "username" => { - display_field("Username", username.as_deref()); + display_field("Username", username.as_deref(), clipboard); } "notes" => { if let Some(notes) = &self.notes { @@ -267,6 +290,7 @@ impl DecryptedCipher { display_field( f.name.as_deref().unwrap_or("(null)"), Some(f.value.as_deref().unwrap_or("")), + clipboard, ); } } @@ -274,7 +298,7 @@ impl DecryptedCipher { }, DecryptedData::SecureNote {} => match field { "note" | "notes" => { - self.display_short(desc); + self.display_short(desc, clipboard); } _ => { for f in &self.fields { @@ -288,6 +312,7 @@ impl DecryptedCipher { display_field( f.name.as_deref().unwrap_or("(null)"), Some(f.value.as_deref().unwrap_or("")), + clipboard, ); } } @@ -296,7 +321,7 @@ impl DecryptedCipher { } } - fn display_long(&self, desc: &str) { + fn display_long(&self, desc: &str, clipboard: bool) { match &self.data { DecryptedData::Login { username, @@ -304,18 +329,22 @@ impl DecryptedCipher { uris, .. } => { - let mut displayed = self.display_short(desc); - displayed |= display_field("Username", username.as_deref()); - displayed |= display_field("TOTP Secret", totp.as_deref()); + let mut displayed = self.display_short(desc, clipboard); + displayed |= + display_field("Username", username.as_deref(), clipboard); + displayed |= + display_field("TOTP Secret", totp.as_deref(), clipboard); if let Some(uris) = uris { for uri in uris { - displayed |= display_field("URI", Some(&uri.uri)); + displayed |= + display_field("URI", Some(&uri.uri), clipboard); let match_type = uri.match_type.map(|ty| format!("{ty}")); displayed |= display_field( "Match type", match_type.as_deref(), + clipboard, ); } } @@ -324,6 +353,7 @@ impl DecryptedCipher { displayed |= display_field( field.name.as_deref().unwrap_or("(null)"), Some(field.value.as_deref().unwrap_or("")), + clipboard, ); } @@ -342,7 +372,7 @@ impl DecryptedCipher { code, .. } => { - let mut displayed = self.display_short(desc); + let mut displayed = self.display_short(desc, clipboard); if let (Some(exp_month), Some(exp_year)) = (exp_month, exp_year) @@ -350,10 +380,14 @@ impl DecryptedCipher { println!("Expiration: {exp_month}/{exp_year}"); displayed = true; } - displayed |= display_field("CVV", code.as_deref()); + displayed |= display_field("CVV", code.as_deref(), clipboard); + displayed |= display_field( + "Name", + cardholder_name.as_deref(), + clipboard, + ); displayed |= - display_field("Name", cardholder_name.as_deref()); - displayed |= display_field("Brand", brand.as_deref()); + display_field("Brand", brand.as_deref(), clipboard); if let Some(notes) = &self.notes { if displayed { @@ -378,24 +412,42 @@ impl DecryptedCipher { username, .. } => { - let mut displayed = self.display_short(desc); + let mut displayed = self.display_short(desc, clipboard); - displayed |= display_field("Address", address1.as_deref()); - displayed |= display_field("Address", address2.as_deref()); - displayed |= display_field("Address", address3.as_deref()); - displayed |= display_field("City", city.as_deref()); - displayed |= display_field("State", state.as_deref()); displayed |= - display_field("Postcode", postal_code.as_deref()); - displayed |= display_field("Country", country.as_deref()); - displayed |= display_field("Phone", phone.as_deref()); - displayed |= display_field("Email", email.as_deref()); - displayed |= display_field("SSN", ssn.as_deref()); + display_field("Address", address1.as_deref(), clipboard); + displayed |= + display_field("Address", address2.as_deref(), clipboard); + displayed |= + display_field("Address", address3.as_deref(), clipboard); + displayed |= + display_field("City", city.as_deref(), clipboard); + displayed |= + display_field("State", state.as_deref(), clipboard); + displayed |= display_field( + "Postcode", + postal_code.as_deref(), + clipboard, + ); + displayed |= + display_field("Country", country.as_deref(), clipboard); displayed |= - display_field("License", license_number.as_deref()); + display_field("Phone", phone.as_deref(), clipboard); displayed |= - display_field("Passport", passport_number.as_deref()); - displayed |= display_field("Username", username.as_deref()); + display_field("Email", email.as_deref(), clipboard); + displayed |= display_field("SSN", ssn.as_deref(), clipboard); + displayed |= display_field( + "License", + license_number.as_deref(), + clipboard, + ); + displayed |= display_field( + "Passport", + passport_number.as_deref(), + clipboard, + ); + displayed |= + display_field("Username", username.as_deref(), clipboard); if let Some(notes) = &self.notes { if displayed { @@ -405,7 +457,7 @@ impl DecryptedCipher { } } DecryptedData::SecureNote {} => { - self.display_short(desc); + self.display_short(desc, clipboard); } } } @@ -521,6 +573,27 @@ impl DecryptedCipher { } } +fn val_display_or_store( + clipboard: bool, + password: &str, +) -> bool { + if clipboard { + match clipboard_store(password) { + Ok(_) => { + println!("The results are already stored on the clipboard."); + true + } + Err(e) => { + println!("{e}"); + false + } + } + } else { + println!("{password}"); + true + } +} + #[derive(Debug, Clone, Serialize)] #[serde(untagged)] #[cfg_attr(test, derive(Eq, PartialEq))] @@ -686,6 +759,19 @@ pub fn config_unset(key: &str) -> anyhow::Result<()> { Ok(()) } +fn clipboard_store(val: &str) -> anyhow::Result<()> { + let mut ctx = ClipboardContext::new().map_err(|e| { + anyhow::anyhow!("Couldn't create clipboard context: {e}") + })?; + + ctx.set_contents(val.to_owned()) + .map_err(|e| anyhow::anyhow!("Couldn't store value to clipboard: {e}"))?; + + let _ = ctx.get_contents(); + + Ok(()) +} + pub fn register() -> anyhow::Result<()> { ensure_agent()?; crate::actions::register()?; @@ -780,6 +866,7 @@ pub fn get( field: Option<&str>, full: bool, raw: bool, + clipboard: bool, ) -> anyhow::Result<()> { unlock()?; @@ -796,11 +883,11 @@ pub fn get( if raw { decrypted.display_json(&desc)?; } else if full { - decrypted.display_long(&desc); + decrypted.display_long(&desc, clipboard); } else if let Some(field) = field { - decrypted.display_field(&desc, field); + decrypted.display_field(&desc, field, clipboard); } else { - decrypted.display_short(&desc); + decrypted.display_short(&desc, clipboard); } Ok(()) @@ -1906,12 +1993,12 @@ mod test { } } -fn display_field(name: &str, field: Option<&str>) -> bool { +fn display_field(name: &str, field: Option<&str>, clipboard: bool) -> bool { field.map_or_else( || false, |field| { - println!("{name}: {field}"); - true + let res = val_display_or_store(clipboard, &format!("{name}: {field}")); + res }, ) } diff --git a/src/bin/rbw/main.rs b/src/bin/rbw/main.rs index 4c706bd..3bede5b 100644 --- a/src/bin/rbw/main.rs +++ b/src/bin/rbw/main.rs @@ -83,6 +83,8 @@ enum Opt { full: bool, #[structopt(long, help = "Display output as JSON")] raw: bool, + #[structopt(long, help = "Copy result to clipboard")] + clipboard: bool, }, #[command(about = "Display the authenticator code for a given entry")] @@ -322,6 +324,7 @@ fn main() { field, full, raw, + clipboard } => commands::get( name, user.as_deref(), @@ -329,6 +332,7 @@ fn main() { field.as_deref(), *full, *raw, + *clipboard, ), Opt::Code { name, user, folder } => { commands::code(name, user.as_deref(), folder.as_deref()) -- cgit v1.2.3-54-g00ecf