1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use super::{Result};
use super::specifier::{Specifier, SpecifierClass, SpecifierStack, SpecifierNode};
use std::rc::Rc;
use std::str::FromStr;

pub fn spec(s: &str) -> Result<Rc<dyn Specifier>> {
    Specifier::from_stack(&SpecifierStack::from_str(s)?)
}

fn some_checks(s: &str) -> Result<()> {
    #[cfg(not(feature = "ssl"))]
    {
        if s.starts_with("wss://") {
            Err("SSL is not compiled in. Use ws:// or get/make another Websocat build.\nYou can also try to workaround missing SSL by using ws-c:cmd:socat trick (see some ws-c: example)")?
        }
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    {
        if s.starts_with("abstract") {
            warn!("Abstract-namespaced UNIX sockets are unlikely to be supported here");
        }
    }

    if s.starts_with("open:") {
        return Err("There is no `open:` address type. Consider `open-async:` or `readfile:` or `writefile:` or `appendfile:`")?;
    }

    #[cfg(not(unix))]
    {
        if s.starts_with("unix") || s.starts_with("abstract") {
            Err("`unix*:` or `abstract*:` are not supported in this Websocat build")?
        }
    }

    #[cfg(not(feature = "tokio-process"))]
    {
        if s.starts_with("sh-c:") {
            Err("`sh-c:` is not supported in this Websocat build")?
        } else if s.starts_with("exec:") {
            Err("`exec:` is not supported in this Websocat build")?
        }
    }

    Ok(())
}

impl FromStr for SpecifierStack {
    type Err = Box<dyn (::std::error::Error)>;
    #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
    fn from_str(s: &str) -> Result<SpecifierStack> {
        some_checks(s)?;

        let mut s = s.to_string();
        let mut overlays = vec![];
        let addrtype;
        let addr;
        let mut found = false;

        'a: loop {
            macro_rules! my {
                ($x:expr) => {
                    for pre in $x.get_prefixes() {
                        if s.starts_with(pre) {
                            let rest = &s[pre.len()..].to_string();
                            if let Some(a) = $x.alias_info() {
                                s = format!("{}{}", a, rest);
                                continue 'a;
                            } else if $x.is_overlay() {
                                let cls = Rc::new($x) as Rc<dyn SpecifierClass>;
                                overlays.push(SpecifierNode{cls});
                                s = rest.to_string();
                                continue 'a;
                            } else {
                                addr = rest.to_string();
                                let cls = Rc::new($x) as Rc<dyn SpecifierClass>;
                                addrtype = SpecifierNode{cls};
                                #[allow(unused_assignments)]
                                {
                                    found = true;
                                }
                                break 'a;
                            }
                        }
                    }
                };
            }
            list_of_all_specifier_classes!(my);
            if !found {
                if let Some(colon) = s.find(':') {
                    Err(format!(
                        "Unknown address or overlay type of `{}:`",
                        &s[..colon]
                    ))?;
                } else {
                    Err(format!("Unknown address or overlay type of `{}`\nMaybe you forgot the `:` character?", s))?;
                }
            }
        }

        Ok(SpecifierStack {
            addr,
            addrtype,
            overlays,
        })
    }
}

impl dyn Specifier {
    pub fn from_stack(st: &SpecifierStack) -> Result<Rc<dyn Specifier>> {
        let mut x = st.addrtype.cls.construct(st.addr.as_str())?;
        for overlay in st.overlays.iter().rev() {
            x = overlay.cls.construct_overlay(x)?;
        }
        Ok(x)
    }
}