From 53bc57051ded45c53a862ca1e0b347b3bf520f06 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Mon, 27 Jun 2022 23:30:31 +0800
Subject: [PATCH] misisng files

---
 async/src/cmdline_client.rs | 134 ++++++++++++++++++++++++++++++++++++
 async/src/pty.rs            |  98 ++++++++++++++++++++++++++
 2 files changed, 232 insertions(+)
 create mode 100644 async/src/cmdline_client.rs
 create mode 100644 async/src/pty.rs

diff --git a/async/src/cmdline_client.rs b/async/src/cmdline_client.rs
new file mode 100644
index 0000000..26d395a
--- /dev/null
+++ b/async/src/cmdline_client.rs
@@ -0,0 +1,134 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, log, trace, warn};
+
+use core::str::FromStr;
+
+use door::SignKey;
+use door_sshproto as door;
+use door_sshproto::{BhError, BhResult};
+use door_sshproto::{ChanMsg, ChanMsgDetails, Error, RespPackets, Result, Runner};
+
+use std::collections::VecDeque;
+
+use async_trait::async_trait;
+
+/// Command line interface SSH client behaviour
+pub struct CmdlineClient {
+    auth_done: bool,
+    main_ch: Option<u32>,
+    authkeys: VecDeque<SignKey>,
+    username: String,
+}
+
+impl CmdlineClient {
+    pub fn new(username: &impl AsRef<str>) -> Self {
+        CmdlineClient {
+            auth_done: false,
+            main_ch: None,
+            authkeys: VecDeque::new(),
+            username: username.as_ref().into(),
+        }
+    }
+
+    pub fn add_authkey(&mut self, k: SignKey) {
+        self.authkeys.push_back(k)
+    }
+}
+
+// #[async_trait(?Send)]
+#[async_trait]
+impl door::AsyncCliBehaviour for CmdlineClient {
+    async fn username(&mut self) -> BhResult<door::ResponseString> {
+        door::ResponseString::from_str(&self.username).map_err(|_| BhError::Fail)
+    }
+
+    async fn valid_hostkey(&mut self, key: &door::PubKey) -> BhResult<bool> {
+        trace!("valid_hostkey for {key:?}");
+        Ok(true)
+    }
+
+    async fn next_authkey(&mut self) -> BhResult<Option<door::SignKey>> {
+        Ok(self.authkeys.pop_front())
+    }
+
+    async fn auth_password(
+        &mut self,
+        pwbuf: &mut door::ResponseString,
+    ) -> BhResult<bool> {
+        let pw =
+            rpassword::prompt_password(format!("password for {}: ", self.username))
+                .map_err(|e| {
+                    warn!("read_password failed {e:}");
+                    BhError::Fail
+                })?;
+        if pwbuf.push_str(&pw).is_err() {
+            Err(BhError::Fail)
+        } else {
+            Ok(true)
+        }
+    }
+
+    async fn authenticated(&mut self) {
+        info!("Authentication succeeded");
+        self.auth_done = true;
+    }
+    // }
+
+    // impl door::BlockCliBehaviour for CmdlineClient {
+    //     fn chan_handler<'f>(&mut self, resp: &mut RespPackets, chan_msg: ChanMsg<'f>) -> Result<()> {
+    //         if Some(chan_msg.num) != self.main_ch {
+    //             return Err(Error::SSHProtoError)
+    //         }
+
+    //         match chan_msg.msg {
+    //             ChanMsgDetails::Data(buf) => {
+    //                 let _ = std::io::stdout().write_all(buf);
+    //             },
+    //             ChanMsgDetails::ExtData{..} => {
+    //             }
+    //             ChanMsgDetails::Req{..} => {
+    //             }
+    //             _ => {}
+    //         }
+    //         Ok(())
+    //     }
+
+    //     fn progress(&mut self, runner: &mut Runner) -> Result<()> {
+    //         if self.auth_done {
+    //             if self.main_ch.is_none() {
+    //                 let ch = runner.open_client_session(Some("cowsay it works"), false)?;
+    //                 self.main_ch = Some(ch);
+    //             }
+    //         }
+    //         Ok(())
+    //     }
+
+    //     fn username(&mut self) -> BhResult<door::ResponseString> {
+    //         // TODO unwrap
+    //         let mut p = door::ResponseString::new();
+    //         p.push_str("matt").unwrap();
+    //         Ok(p)
+    //     }
+
+    //     fn valid_hostkey(&mut self, key: &door::PubKey) -> BhResult<bool> {
+    //         trace!("valid_hostkey for {key:?}");
+    //         Ok(true)
+    //     }
+
+    //     fn auth_password(&mut self, pwbuf: &mut door::ResponseString) -> BhResult<bool> {
+    //         let pw = rpassword::prompt_password("password: ").map_err(|e| {
+    //             warn!("read_password failed {e:}");
+    //             BhError::Fail
+    //         })?;
+    //         if pwbuf.push_str(&pw).is_err() {
+    //             Err(BhError::Fail)
+    //         } else {
+    //             Ok(true)
+    //         }
+    //     }
+
+    //     fn authenticated(&mut self) {
+    //         info!("Authentication succeeded");
+    //         self.auth_done = true;
+    //     }
+}
diff --git a/async/src/pty.rs b/async/src/pty.rs
new file mode 100644
index 0000000..9bb12b2
--- /dev/null
+++ b/async/src/pty.rs
@@ -0,0 +1,98 @@
+#[allow(unused_imports)]
+use log::{debug, error, info, log, trace, warn};
+
+use std::io::Error as IoError;
+
+use libc::{ioctl, winsize, termios, tcgetattr, tcsetattr };
+
+use door_sshproto as door;
+use door::{Behaviour, AsyncCliBehaviour, Runner, Result, Pty};
+use door::config::*;
+
+
+pub fn current_pty() -> Result<Pty, IoError> {
+    let mut term = heapless::String::<MAX_TERM>::new();
+    let t = std::env::var("TERM").unwrap_or(DEFAULT_TERM.into());
+    // XXX error
+    term.push_str(&t).expect("TERM fits buffer");
+
+    let mut ws = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
+    let r = unsafe { ioctl(libc::STDIN_FILENO, libc::TIOCGWINSZ, &mut ws) };
+    if r != 0 {
+        return Err(IoError::last_os_error())
+    }
+
+    info!("rows {} cols {}", ws.ws_row, ws.ws_col);
+
+    Ok(Pty {
+        term,
+        rows: ws.ws_row as u32,
+        cols: ws.ws_col as u32,
+        width: ws.ws_xpixel as u32,
+        height: ws.ws_ypixel as u32,
+        modes: heapless::Vec::new(),
+    })
+
+}
+
+/// Puts stdin/stdout into raw mode. This assumes that stdin/stdout
+/// share a common file descriptor, as is the case with a pty.
+/// The returned `RawPtyGuard` reverts to previous terminal settings
+/// when it is dropped.
+pub fn raw_pty() -> Result<RawPtyGuard, IoError> {
+    RawPtyGuard::new()
+}
+
+pub struct RawPtyGuard {
+    saved: termios,
+}
+
+impl RawPtyGuard {
+    fn new() -> Result<Self, IoError> {
+        let mut saved: termios = unsafe { core::mem::zeroed() };
+        let r = unsafe { tcgetattr(libc::STDIN_FILENO, &mut saved) };
+        if r != 0 {
+            return Err(IoError::last_os_error())
+        }
+
+        Self::set_raw(&saved)?;
+
+        Ok(Self {
+            saved,
+        })
+    }
+
+    fn set_raw(current: &termios) -> Result<(), IoError> {
+        use libc::*;
+        let mut raw = current.clone();
+
+        raw.c_iflag |= IGNPAR;
+        // We could also set IUCLC but it isn't in posix
+        raw.c_iflag &= !(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+        raw.c_lflag &= !(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+        raw.c_oflag &= !OPOST;
+
+        let r = unsafe { tcsetattr(libc::STDIN_FILENO, TCSADRAIN, &raw) };
+        if r != 0 {
+            return Err(IoError::last_os_error())
+        }
+
+        info!("set_raw");
+
+        Ok(())
+
+    }
+}
+
+impl Drop for RawPtyGuard {
+    fn drop(&mut self) {
+
+        let r = unsafe { tcsetattr(libc::STDIN_FILENO, libc::TCSADRAIN, &self.saved) };
+        if r != 0 {
+            let e = IoError::last_os_error();
+            warn!("Failed restoring TTY: {e}");
+        } else {
+            info!("Restored TTY");
+        }
+    }
+}
-- 
GitLab