diff --git a/Cargo.toml b/Cargo.toml index 6cf2ec4..29824df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ indoc = "1" log = { version = "0.4", features = ["std"] } open = "3" thiserror = "1.0" -tokio = { version = "1", features = ["io-std", "io-util", "macros", "net", "process", "rt", "rt-multi-thread"] } +tokio = { version = "1", features = ["io-std", "io-util", "macros", "net", "process", "rt", "rt-multi-thread", "fs"] } tokio-stream = "0.1" toml = "0.5" tui = "0.19" diff --git a/src/bin/fwd-browse.rs b/src/bin/fwd-browse.rs index bbebcdf..9899597 100644 --- a/src/bin/fwd-browse.rs +++ b/src/bin/fwd-browse.rs @@ -9,5 +9,10 @@ async fn main() { std::process::exit(1); } - fwd::browse_url(&args[1]).await; + let url = &args[1]; + if let Err(e) = fwd::browse_url(url).await { + eprintln!("Unable to open {url}"); + eprintln!("{}", e); + std::process::exit(1); + } } diff --git a/src/browse/mod.rs b/src/browse/mod.rs deleted file mode 100644 index cf83858..0000000 --- a/src/browse/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::message::Message; -use anyhow::Result; -use tokio::sync::mpsc; - -#[cfg(target_family = "unix")] -mod browse_unix; - -#[cfg(target_family = "unix")] -use browse_unix::{browse_url_impl, handle_browser_open_impl}; - -#[inline] -pub async fn browse_url(url: &String) { - if let Err(e) = browse_url_impl(url).await { - eprintln!("Unable to open {url}"); - eprintln!("{}", e); - std::process::exit(1); - } -} - -#[cfg(not(target_family = "unix"))] -pub async fn browse_url_impl(_url: &String) -> Result<()> { - use anyhow::anyhow; - Err(anyhow!( - "Opening a browser is not supported on this platform" - )) -} - -#[inline] -pub async fn handle_browser_open( - messages: mpsc::Sender, -) -> Result<()> { - handle_browser_open_impl(messages).await -} - -#[cfg(not(target_family = "unix"))] -async fn handle_browser_open_impl( - _messages: mpsc::Sender, -) -> Result<()> { - std::future::pending().await -} diff --git a/src/lib.rs b/src/lib.rs index 5eaba0d..6143ba3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ -mod browse; mod client; mod message; +mod reverse; mod server; -pub use browse::browse_url; pub use client::run_client; +pub use reverse::browse_url; pub use server::run_server; diff --git a/src/main.rs b/src/main.rs index 667ff1c..cf033ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ const VERSION: &str = env!("CARGO_PKG_VERSION"); fn usage() { println!(indoc! {" -usage: fwd [options] ( | browse ) +usage: fwd [options] ( | browse | clip []) To connect a client to a server that has an `fwd` installed in its path, run `fwd ` on the client, where is the name of the server to @@ -60,9 +60,13 @@ fn parse_args(args: Vec) -> Args { } else { Args::Error } - } else if rest.len() > 1 && rest[0] == "browse" { - if rest.len() == 2 { - Args::Browse(rest[1].to_string()) + } else if rest.len() > 1 { + if rest[0] == "browse" { + if rest.len() == 2 { + Args::Browse(rest[1].to_string()) + } else { + Args::Error + } } else { Args::Error } @@ -73,6 +77,14 @@ fn parse_args(args: Vec) -> Args { } } +async fn browse_url(url: &str) { + if let Err(e) = fwd::browse_url(&url).await { + eprintln!("Unable to open {url}"); + eprintln!("{}", e); + std::process::exit(1); + } +} + #[tokio::main] async fn main() { match parse_args(std::env::args().collect()) { @@ -86,7 +98,7 @@ async fn main() { fwd::run_server().await; } Args::Browse(url) => { - fwd::browse_url(&url).await; + browse_url(&url).await; } Args::Client(server, sudo) => { fwd::run_client(&server, sudo).await; diff --git a/src/reverse.rs b/src/reverse.rs new file mode 100644 index 0000000..bca8c1c --- /dev/null +++ b/src/reverse.rs @@ -0,0 +1,29 @@ +use anyhow::Result; + +#[cfg(target_family = "unix")] +mod unix; + +#[cfg(target_family = "unix")] +pub use unix::{handle_reverse_connections, send_reverse_message}; + +use crate::message::Message; + +#[cfg(not(target_family = "unix"))] +pub async fn send_reverse_message(_message: Message) -> Result<()> { + use anyhow::anyhow; + Err(anyhow!( + "Server-side operations are not supported on this platform" + )) +} + +#[cfg(not(target_family = "unix"))] +pub async fn handle_reverse_connections( + _messages: mpsc::Sender, +) -> Result<()> { + std::future::pending().await +} + +#[inline] +pub async fn browse_url(url: &str) -> Result<()> { + send_reverse_message(Message::Browse(url.to_string())).await +} diff --git a/src/browse/browse_unix.rs b/src/reverse/unix.rs similarity index 90% rename from src/browse/browse_unix.rs rename to src/reverse/unix.rs index 54cca9c..92025ce 100644 --- a/src/browse/browse_unix.rs +++ b/src/reverse/unix.rs @@ -1,14 +1,15 @@ -use crate::message::{Message, MessageReader, MessageWriter}; +// The reverse client connects to the server via a local connection to send +// commands back to the client. use anyhow::{bail, Context, Result}; use log::warn; use std::os::unix::fs::DirBuilderExt; use std::path::PathBuf; use tokio::net::{UnixListener, UnixStream}; use tokio::sync::mpsc; -use users; -use xdg; -pub async fn browse_url_impl(url: &String) -> Result<()> { +use crate::message::{Message, MessageReader, MessageWriter}; + +pub async fn send_reverse_message(message: Message) -> Result<()> { let path = socket_path().context("Error getting socket path")?; let stream = match UnixStream::connect(&path).await { Ok(s) => s, @@ -18,38 +19,22 @@ pub async fn browse_url_impl(url: &String) -> Result<()> { }; let mut writer = MessageWriter::new(stream); writer - .write(Message::Browse(url.clone())) + .write(message) .await .context("Error sending browse message")?; Ok(()) } -pub async fn handle_browser_open_impl( - messages: mpsc::Sender, -) -> Result<()> { - let path = socket_path().context("Error getting socket path")?; - handle_browser_open_with_path(messages, path).await -} - -async fn handle_browser_open_with_path( - messages: mpsc::Sender, - path: PathBuf, -) -> Result<()> { - let _ = std::fs::remove_file(&path); - let listener = UnixListener::bind(&path) - .with_context(|| format!("Failed to bind to {}", path.display()))?; - loop { - let (socket, _addr) = listener - .accept() - .await - .context("Error accepting connection")?; - - let sender = messages.clone(); - tokio::spawn(async move { - if let Err(e) = handle_connection(socket, sender).await { - warn!("Error handling socket connection: {:?}", e); - } - }); +fn socket_directory() -> Result { + let base_directories = xdg::BaseDirectories::new() + .context("Error creating BaseDirectories")?; + match base_directories.place_runtime_file("fwd") { + Ok(path) => Ok(path), + Err(_) => { + let mut path = std::env::temp_dir(); + path.push(format!("fwd{}", users::get_current_uid())); + Ok(path) + } } } @@ -68,16 +53,32 @@ pub fn socket_path() -> Result { Ok(socket_path) } -fn socket_directory() -> Result { - let base_directories = xdg::BaseDirectories::new() - .context("Error creating BaseDirectories")?; - match base_directories.place_runtime_file("fwd") { - Ok(path) => Ok(path), - Err(_) => { - let mut path = std::env::temp_dir(); - path.push(format!("fwd{}", users::get_current_uid())); - Ok(path) - } +pub async fn handle_reverse_connections( + messages: mpsc::Sender, +) -> Result<()> { + let path = socket_path().context("Error getting socket path")?; + handle_reverse_connections_with_path(messages, path).await +} + +async fn handle_reverse_connections_with_path( + messages: mpsc::Sender, + path: PathBuf, +) -> Result<()> { + let _ = std::fs::remove_file(&path); + let listener = UnixListener::bind(&path) + .with_context(|| format!("Failed to bind to {}", path.display()))?; + loop { + let (socket, _addr) = listener + .accept() + .await + .context("Error accepting connection")?; + + let sender = messages.clone(); + tokio::spawn(async move { + if let Err(e) = handle_connection(socket, sender).await { + warn!("Error handling socket connection: {:?}", e); + } + }); } } @@ -119,7 +120,7 @@ mod tests { let path_override = path.clone(); tokio::spawn(async move { - handle_browser_open_with_path(sender, path_override) + handle_reverse_connections_with_path(sender, path_override) .await .expect("Error in server!"); }); diff --git a/src/server/mod.rs b/src/server/mod.rs index 1b1f1b2..86e1ef7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,5 @@ -use crate::browse::handle_browser_open; use crate::message::{Message, MessageReader, MessageWriter}; +use crate::reverse::handle_reverse_connections; use anyhow::Result; use log::{error, warn}; use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}; @@ -13,7 +13,7 @@ mod refresh; async fn write_driver( messages: &mut mpsc::Receiver, writer: &mut MessageWriter, -) -> () { +) { while let Some(m) = messages.recv().await { writer.write(m).await.expect("Failed to write the message") } @@ -77,7 +77,7 @@ async fn server_main< tokio::select! { _ = write_driver(&mut receiver, &mut writer) => Ok(()), r = server_loop(&mut reader, &mut sender) => r, - r = handle_browser_open(browse_sender) => r, + r = handle_reverse_connections(browse_sender) => r, } }