zbus/connection/handshake/
command.rs

1use std::{fmt, str::FromStr};
2
3use crate::{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(Vec<AuthMechanism>),
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) => {
46                write!(
47                    f,
48                    "REJECTED {}",
49                    mechs
50                        .iter()
51                        .map(|m| m.to_string())
52                        .collect::<Vec<_>>()
53                        .join(" ")
54                )
55            }
56            Command::Ok(guid) => write!(f, "OK {guid}"),
57            Command::AgreeUnixFD => write!(f, "AGREE_UNIX_FD"),
58        }
59    }
60}
61
62impl FromStr for Command {
63    type Err = Error;
64
65    fn from_str(s: &str) -> Result<Self> {
66        let mut words = s.split_ascii_whitespace();
67        let cmd = match words.next() {
68            Some("AUTH") => {
69                let mech = if let Some(m) = words.next() {
70                    Some(m.parse()?)
71                } else {
72                    None
73                };
74                let resp = match words.next() {
75                    Some(resp) => Some(hex::decode(resp)?),
76                    None => None,
77                };
78                Command::Auth(mech, resp)
79            }
80            Some("CANCEL") => Command::Cancel,
81            Some("BEGIN") => Command::Begin,
82            Some("DATA") => {
83                let data = match words.next() {
84                    Some(data) => Some(hex::decode(data)?),
85                    None => None,
86                };
87
88                Command::Data(data)
89            }
90            Some("ERROR") => Command::Error(s.into()),
91            Some("NEGOTIATE_UNIX_FD") => Command::NegotiateUnixFD,
92            Some("REJECTED") => {
93                let mechs = words.map(|m| m.parse()).collect::<Result<_>>()?;
94                Command::Rejected(mechs)
95            }
96            Some("OK") => {
97                let guid = words
98                    .next()
99                    .ok_or_else(|| Error::Handshake("Missing OK server GUID!".into()))?;
100                Command::Ok(Guid::from_str(guid)?.into())
101            }
102            Some("AGREE_UNIX_FD") => Command::AgreeUnixFD,
103            _ => return Err(Error::Handshake(format!("Unknown command: {s}"))),
104        };
105        Ok(cmd)
106    }
107}