zbus/connection/handshake/
command.rs

1use std::{borrow::Cow, fmt, str::FromStr};
2
3use crate::{conn::AuthMechanism, Error, Guid, OwnedGuid, Result};
4
5// The plain-text SASL profile authentication protocol described here:
6// <https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol>
7//
8// These are all the known commands, which can be parsed from or serialized to text.
9#[derive(Debug)]
10#[allow(clippy::upper_case_acronyms)]
11pub(super) enum Command {
12    Auth(Option<AuthMechanism>, Option<Vec<u8>>),
13    Cancel,
14    Begin,
15    Data(Option<Vec<u8>>),
16    Error(String),
17    NegotiateUnixFD,
18    Rejected(Cow<'static, str>),
19    Ok(OwnedGuid),
20    AgreeUnixFD,
21}
22
23impl From<&Command> for Vec<u8> {
24    fn from(c: &Command) -> Self {
25        c.to_string().into()
26    }
27}
28
29impl fmt::Display for Command {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Command::Auth(mech, resp) => match (mech, resp) {
33                (Some(mech), Some(resp)) => write!(f, "AUTH {mech} {}", hex::encode(resp)),
34                (Some(mech), None) => write!(f, "AUTH {mech}"),
35                _ => write!(f, "AUTH"),
36            },
37            Command::Cancel => write!(f, "CANCEL"),
38            Command::Begin => write!(f, "BEGIN"),
39            Command::Data(data) => match data {
40                None => write!(f, "DATA"),
41                Some(data) => write!(f, "DATA {}", hex::encode(data)),
42            },
43            Command::Error(expl) => write!(f, "ERROR {expl}"),
44            Command::NegotiateUnixFD => write!(f, "NEGOTIATE_UNIX_FD"),
45            Command::Rejected(mechs) => write!(f, "REJECTED {mechs}"),
46            Command::Ok(guid) => write!(f, "OK {guid}"),
47            Command::AgreeUnixFD => write!(f, "AGREE_UNIX_FD"),
48        }
49    }
50}
51
52impl FromStr for Command {
53    type Err = Error;
54
55    fn from_str(s: &str) -> Result<Self> {
56        let mut words = s.split_ascii_whitespace();
57        let cmd = match words.next() {
58            Some("AUTH") => {
59                let mech = if let Some(m) = words.next() {
60                    Some(m.parse()?)
61                } else {
62                    None
63                };
64                let resp = match words.next() {
65                    Some(resp) => Some(hex::decode(resp)?),
66                    None => None,
67                };
68                Command::Auth(mech, resp)
69            }
70            Some("CANCEL") => Command::Cancel,
71            Some("BEGIN") => Command::Begin,
72            Some("DATA") => {
73                let data = match words.next() {
74                    Some(data) => Some(hex::decode(data)?),
75                    None => None,
76                };
77
78                Command::Data(data)
79            }
80            Some("ERROR") => Command::Error(s.into()),
81            Some("NEGOTIATE_UNIX_FD") => Command::NegotiateUnixFD,
82            Some("REJECTED") => {
83                let mechs = words.collect::<Vec<_>>().join(" ").into();
84                Command::Rejected(mechs)
85            }
86            Some("OK") => {
87                let guid = words
88                    .next()
89                    .ok_or_else(|| Error::Handshake("Missing OK server GUID!".into()))?;
90                Command::Ok(Guid::from_str(guid)?.into())
91            }
92            Some("AGREE_UNIX_FD") => Command::AgreeUnixFD,
93            _ => return Err(Error::Handshake(format!("Unknown command: {s}"))),
94        };
95        Ok(cmd)
96    }
97}
98
99impl From<hex::FromHexError> for Error {
100    fn from(e: hex::FromHexError) -> Self {
101        Error::Handshake(format!("Invalid hexcode: {e}"))
102    }
103}