use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::format::{Reader, Writer};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChannelRequest {
PtyReq {
term: String,
cols: u32,
rows: u32,
px_w: u32,
px_h: u32,
modes: Vec<u8>,
},
Shell,
Exec {
command: String,
},
Subsystem {
name: String,
},
Env {
name: String,
value: String,
},
WindowChange {
cols: u32,
rows: u32,
px_w: u32,
px_h: u32,
},
Signal {
name: String,
},
ExitStatus {
code: u32,
},
ExitSignal {
name: String,
core_dumped: bool,
message: String,
language: String,
},
Other {
name: String,
raw: Vec<u8>,
},
}
impl ChannelRequest {
pub fn name(&self) -> &str {
match self {
ChannelRequest::PtyReq { .. } => "pty-req",
ChannelRequest::Shell => "shell",
ChannelRequest::Exec { .. } => "exec",
ChannelRequest::Subsystem { .. } => "subsystem",
ChannelRequest::Env { .. } => "env",
ChannelRequest::WindowChange { .. } => "window-change",
ChannelRequest::Signal { .. } => "signal",
ChannelRequest::ExitStatus { .. } => "exit-status",
ChannelRequest::ExitSignal { .. } => "exit-signal",
ChannelRequest::Other { name, .. } => name.as_str(),
}
}
pub fn encode(&self, w: &mut Writer) {
match self {
ChannelRequest::PtyReq {
term,
cols,
rows,
px_w,
px_h,
modes,
} => {
w.write_string(term.as_bytes());
w.write_u32(*cols);
w.write_u32(*rows);
w.write_u32(*px_w);
w.write_u32(*px_h);
w.write_string(modes);
}
ChannelRequest::Shell => {}
ChannelRequest::Exec { command } => {
w.write_string(command.as_bytes());
}
ChannelRequest::Subsystem { name } => {
w.write_string(name.as_bytes());
}
ChannelRequest::Env { name, value } => {
w.write_string(name.as_bytes());
w.write_string(value.as_bytes());
}
ChannelRequest::WindowChange {
cols,
rows,
px_w,
px_h,
} => {
w.write_u32(*cols);
w.write_u32(*rows);
w.write_u32(*px_w);
w.write_u32(*px_h);
}
ChannelRequest::Signal { name } => {
w.write_string(name.as_bytes());
}
ChannelRequest::ExitStatus { code } => {
w.write_u32(*code);
}
ChannelRequest::ExitSignal {
name,
core_dumped,
message,
language,
} => {
w.write_string(name.as_bytes());
w.write_bool(*core_dumped);
w.write_string(message.as_bytes());
w.write_string(language.as_bytes());
}
ChannelRequest::Other { raw, .. } => {
w.write_raw(raw);
}
}
}
pub fn decode(name: &str, body: &[u8]) -> Result<Self> {
let mut r = Reader::new(body);
match name {
"pty-req" => {
let term = read_utf8(&mut r)?;
let cols = r.read_u32()?;
let rows = r.read_u32()?;
let px_w = r.read_u32()?;
let px_h = r.read_u32()?;
let modes = r.read_string()?.to_vec();
Ok(ChannelRequest::PtyReq {
term,
cols,
rows,
px_w,
px_h,
modes,
})
}
"shell" => Ok(ChannelRequest::Shell),
"exec" => Ok(ChannelRequest::Exec {
command: read_utf8(&mut r)?,
}),
"subsystem" => Ok(ChannelRequest::Subsystem {
name: read_utf8(&mut r)?,
}),
"env" => {
let name = read_utf8(&mut r)?;
let value = read_utf8(&mut r)?;
Ok(ChannelRequest::Env { name, value })
}
"window-change" => {
let cols = r.read_u32()?;
let rows = r.read_u32()?;
let px_w = r.read_u32()?;
let px_h = r.read_u32()?;
Ok(ChannelRequest::WindowChange {
cols,
rows,
px_w,
px_h,
})
}
"signal" => Ok(ChannelRequest::Signal {
name: read_utf8(&mut r)?,
}),
"exit-status" => Ok(ChannelRequest::ExitStatus {
code: r.read_u32()?,
}),
"exit-signal" => {
let name = read_utf8(&mut r)?;
let core_dumped = r.read_bool()?;
let message = read_utf8(&mut r)?;
let language = read_utf8(&mut r)?;
Ok(ChannelRequest::ExitSignal {
name,
core_dumped,
message,
language,
})
}
other => Ok(ChannelRequest::Other {
name: other.to_string(),
raw: body.to_vec(),
}),
}
}
}
fn read_utf8(r: &mut Reader<'_>) -> Result<String> {
let bytes = r.read_string()?;
core::str::from_utf8(bytes)
.map(|s| s.to_string())
.map_err(|_| Error::Format("invalid utf-8 in channel request"))
}