use std::{ffi::OsStr, net::IpAddr, process::Stdio};
use tokio::process::Command;
use crate::croc::receive::CrocReceive;
use super::send::CrocSend;
pub struct Croc {
inner: Command,
internal_dns: bool,
debug: bool,
yes: bool,
compress: bool,
local: bool,
quiet: bool,
clipboard: ClipboardKind,
multicast: Option<IpAddr>,
curve: EncryptionCurve,
ip: Option<IpAddr>,
relay: Option<Relay>,
pass: Option<String>,
}
#[derive(Debug, Default)]
pub enum ClipboardKind {
Disabled,
#[default]
Enabled,
Extended,
}
#[derive(Debug, Default)]
pub enum EncryptionCurve {
P521,
#[default]
P256,
P384,
Siec,
Ed25519,
}
#[derive(Debug)]
struct Relay {
address: IpAddr,
port: u16,
}
impl Croc {
pub fn new() -> Self {
Self::new_with_path("croc")
}
pub fn new_with_path<P: AsRef<OsStr>>(name: P) -> Self {
let inner = Command::new(name);
let croc = Self {
inner,
internal_dns: false,
debug: false,
yes: true,
compress: true,
local: false,
clipboard: ClipboardKind::default(),
quiet: false,
multicast: None,
curve: EncryptionCurve::default(),
ip: None,
relay: None,
pass: None,
};
croc.no_window().no_stdin().no_stdout().pipe_stderr()
}
pub fn internal_dns(mut self, value: bool) -> Self {
self.internal_dns = value;
self
}
pub fn debug(mut self, value: bool) -> Self {
self.debug = value;
self
}
pub fn yes(mut self, value: bool) -> Self {
self.yes = value;
self
}
pub fn compress(mut self, value: bool) -> Self {
self.compress = value;
self
}
pub fn local(mut self, value: bool) -> Self {
self.local = value;
self
}
pub fn quiet(mut self, value: bool) -> Self {
self.quiet = value;
self
}
pub fn clipboard<Kind: Into<ClipboardKind>>(mut self, kind: Kind) -> Self {
self.clipboard = kind.into();
self
}
pub fn multicast<Ip: Into<IpAddr>>(mut self, address: Ip) -> Self {
self.multicast = Some(address.into());
self
}
pub fn curve(mut self, curve: EncryptionCurve) -> Self {
self.curve = curve;
self
}
pub fn ip<Ip: Into<IpAddr>>(mut self, ip: Ip) -> Self {
self.ip = Some(ip.into());
self
}
pub fn relay<Ip: Into<IpAddr>>(mut self, ip: Ip, port: u16) -> Self {
self.relay = Some(Relay::new(ip, port));
self
}
pub fn pass<S: ToString>(mut self, pass: S) -> Self {
self.pass = Some(pass.to_string());
self
}
pub fn no_stdout(mut self) -> Self {
self.inner.stdout(Stdio::null());
self
}
pub fn no_stdin(mut self) -> Self {
self.inner.stdin(Stdio::null());
self
}
pub fn no_stderr(mut self) -> Self {
self.inner.stderr(Stdio::null());
self
}
pub fn pipe_stderr(mut self) -> Self {
self.inner.stderr(Stdio::piped());
self
}
pub fn pipe_stdout(mut self) -> Self {
self.inner.stdout(Stdio::piped());
self
}
pub fn pipe_stdin(mut self) -> Self {
self.inner.stdin(Stdio::piped());
self
}
pub fn no_window(mut self) -> Self {
self.inner.disable_window();
self
}
pub fn send<F: AsRef<str>>(mut self) -> CrocSend<F> {
self.parse_options();
CrocSend::new(self.inner)
}
pub fn receive(mut self) -> CrocReceive {
self.parse_options();
CrocReceive::new(self.inner)
}
fn parse_options(&mut self) {
if self.internal_dns {
self.inner.arg("--internal-dns");
}
if self.debug {
self.inner.arg("--debug");
}
if self.yes {
self.inner.arg("--yes");
}
if !self.compress {
self.inner.arg("--no-compress");
}
if self.local {
self.inner.arg("--local");
}
if self.quiet {
self.inner.arg("--quiet");
}
if let Some(address) = self.multicast {
self.inner.arg(format!("--multicast={}", address));
}
if let Some(address) = self.ip {
self.inner.arg(format!("--ip={}", address));
}
if let Some(ref relay) = self.relay {
let key = match relay.address() {
IpAddr::V4(_) => "CROC_RELAY",
IpAddr::V6(_) => "CROC_RELAY6",
};
self.inner.env(key, relay.to_string());
}
if let Some(ref pass) = self.pass {
self.inner.env("CROC_PASS", pass);
}
match self.clipboard {
ClipboardKind::Disabled => {
self.inner.arg("--disable-clipboard");
}
ClipboardKind::Extended => {
self.inner.arg("--extended-clipboard");
}
ClipboardKind::Enabled => {}
}
match self.curve {
EncryptionCurve::P256 => {}
ref curve => {
self.inner.arg(format!("--curve={}", curve));
}
}
}
}
impl Relay {
pub fn new<Ip: Into<IpAddr>>(address: Ip, port: u16) -> Self {
Self {
address: address.into(),
port,
}
}
pub fn address(&self) -> &IpAddr {
&self.address
}
}
impl Into<ClipboardKind> for bool {
fn into(self) -> ClipboardKind {
match self {
true => ClipboardKind::Enabled,
false => ClipboardKind::Disabled,
}
}
}
trait DisableWindow {
fn disable_window(&mut self);
}
impl DisableWindow for Command {
fn disable_window(&mut self) {
#[cfg(target_os = "windows")]
std::os::windows::process::CommandExt::creation_flags(self, 0x08000000);
}
}
impl From<Croc> for Command {
fn from(croc: Croc) -> Self {
croc.inner
}
}
impl Default for Croc {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Croc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::Display for EncryptionCurve {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EncryptionCurve::P521 => write!(f, "p521"),
EncryptionCurve::P256 => write!(f, "p256"),
EncryptionCurve::P384 => write!(f, "p384"),
EncryptionCurve::Siec => write!(f, "siec"),
EncryptionCurve::Ed25519 => write!(f, "ed25519"),
}
}
}
impl std::fmt::Display for Relay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.address {
IpAddr::V4(address) => write!(f, "{}:{}", address, self.port),
IpAddr::V6(address) => write!(f, "[{}]:{}", address, self.port),
}
}
}