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
use std::{borrow::Cow, ffi::OsStr};

use super::{
    percent::{decode_percents, decode_percents_os_str, decode_percents_str, EncData, EncOsStr},
    DBusAddr, KeyValFmt, KeyValFmtAdd,
};
use crate::{Error, Result};

/// A sub-type of `unix:` transport.
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnixAddrKind<'a> {
    /// Path of the unix domain socket.
    Path(Cow<'a, OsStr>),
    /// Directory in which a socket file with a random file name starting with 'dbus-' should be
    /// created by a server.
    Dir(Cow<'a, OsStr>),
    /// The same as "dir", except that on platforms with abstract sockets, a server may attempt to
    /// create an abstract socket whose name starts with this directory instead of a path-based
    /// socket.
    Tmpdir(Cow<'a, OsStr>),
    /// Unique string in the abstract namespace, often syntactically resembling a path but
    /// unconnected to the filesystem namespace
    Abstract(Cow<'a, [u8]>),
    /// Listen on $XDG_RUNTIME_DIR/bus.
    Runtime,
}

impl KeyValFmtAdd for UnixAddrKind<'_> {
    fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> {
        match self {
            UnixAddrKind::Path(p) => kv.add("path", Some(EncOsStr(p))),
            UnixAddrKind::Dir(p) => kv.add("dir", Some(EncOsStr(p))),
            UnixAddrKind::Tmpdir(p) => kv.add("tmpdir", Some(EncOsStr(p))),
            UnixAddrKind::Abstract(p) => kv.add("abstract", Some(EncData(p))),
            UnixAddrKind::Runtime => kv.add("runtime", Some("yes")),
        }
    }
}

/// `unix:` D-Bus transport.
#[derive(Debug, PartialEq, Eq)]
pub struct Unix<'a> {
    kind: UnixAddrKind<'a>,
}

impl<'a> Unix<'a> {
    /// One of the various `unix:` addresses.
    pub fn kind(&self) -> &UnixAddrKind<'a> {
        &self.kind
    }
}

impl<'a> TryFrom<&'a DBusAddr<'a>> for Unix<'a> {
    type Error = Error;

    fn try_from(s: &'a DBusAddr<'a>) -> Result<Self> {
        let mut kind = None;
        let mut iter = s.key_val_iter();
        for (k, v) in &mut iter {
            match k {
                "path" | "dir" | "tmpdir" => {
                    let v = v.ok_or_else(|| Error::MissingValue(k.into()))?;
                    let v = decode_percents_os_str(v)?;
                    kind = Some(match k {
                        "path" => UnixAddrKind::Path(v),
                        "dir" => UnixAddrKind::Dir(v),
                        "tmpdir" => UnixAddrKind::Tmpdir(v),
                        // can't happen, we matched those earlier
                        _ => panic!(),
                    });
                    break;
                }
                "abstract" => {
                    let v = v.ok_or_else(|| Error::MissingValue(k.into()))?;
                    let v = decode_percents(v)?;
                    kind = Some(UnixAddrKind::Abstract(v));
                    break;
                }
                "runtime" => {
                    let v = v.ok_or_else(|| Error::MissingValue(k.into()))?;
                    let v = decode_percents_str(v)?;
                    if v != "yes" {
                        return Err(Error::InvalidValue(k.into()));
                    }
                    kind = Some(UnixAddrKind::Runtime);
                    break;
                }
                _ => continue,
            }
        }
        let Some(kind) = kind else {
            return Err(Error::Other(
                "invalid `unix:` address, missing required key".into(),
            ));
        };
        for (k, _) in iter {
            match k {
                "path" | "dir" | "tmpdir" | "abstract" | "runtime" => {
                    return Err(Error::Other("invalid address, only one of `path` `dir` `tmpdir` `abstract` or `runtime` expected".into()));
                }
                _ => (),
            }
        }
        Ok(Unix { kind })
    }
}

impl KeyValFmtAdd for Unix<'_> {
    fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> {
        self.kind().key_val_fmt_add(kv)
    }
}