1use anyhow::bail;
2use async_compat::CompatExt;
3use std::string::ToString;
4use wezterm_ssh::{Config, ConfigMap, Session, SessionEvent};
5
6pub struct SessionBuilder {
7 pub user: String,
8 pub host: String,
9 pub pass: String,
10 pub port: u16,
11 pub identities_only: Option<bool>,
12 pub userknown_hosts_file: Option<String>,
13 pub wezterm_ssh_verbose: Option<bool>,
14 pub wezterm_ssh_backend: SshBackend,
15}
16
17#[derive(Display, Debug, Default)]
18pub enum SshBackend {
19 #[strum(serialize = "libssh")]
20 Libssh,
21 #[strum(serialize = "ssh2")]
22 #[default]
23 Ssh2,
24}
25
26impl SessionBuilder {
27 pub fn new_with_pass<U, H, P>(user: U, host: H, pass: P, port: u16) -> Self
28 where
29 U: AsRef<str>,
30 H: AsRef<str>,
31 P: AsRef<str>,
32 {
33 Self {
34 user: user.as_ref().to_string(),
35 host: host.as_ref().to_string(),
36 pass: pass.as_ref().to_string(),
37 port,
38 identities_only: None,
39 userknown_hosts_file: None,
40 wezterm_ssh_verbose: None,
41 wezterm_ssh_backend: Default::default(),
42 }
43 }
44
45 fn identities_only(&self) -> &str {
46 if let Some(i) = self.identities_only {
47 if i {
48 return "yes";
49 }
50 }
51 "no"
52 }
53
54 pub fn disable_userknown_hosts_file(&mut self) -> &mut Self {
55 self.userknown_hosts_file = Some("/dev/null".to_string());
56 self
57 }
58
59 fn wezterm_ssh_verbose(&self) -> &str {
60 if let Some(i) = self.wezterm_ssh_verbose {
61 if i {
62 return "true";
63 }
64 }
65 "false"
66 }
67
68 fn configmap(&self) -> ConfigMap {
69 let config = Config::new();
70 let mut config = config.for_host(&self.host);
71 config.insert("port".to_string(), self.port.to_string());
72 config.insert("user".to_string(), self.user.to_string());
73 config.insert(
74 "identitiesonly".to_string(),
75 self.identities_only().to_string(),
76 );
77 config.insert(
78 "wezterm_ssh_verbose".to_string(),
79 self.wezterm_ssh_verbose().to_string(),
80 );
81 config.insert(
82 "wezterm_ssh_backend".to_string(),
83 self.wezterm_ssh_backend.to_string(),
84 );
85
86 if let Some(path) = &self.userknown_hosts_file {
87 config.insert("userknownhostsfile".to_string(), path.to_string());
88 }
89
90 config
91 }
92
93 pub async fn connect_with_pass(&self) -> anyhow::Result<Session> {
94 let (session, events) = Session::connect(self.configmap())?;
95 while let Ok(event) = events.recv().await {
96 match event {
97 SessionEvent::Banner(banner) => {
98 info!("SessionEvent Banner:{:?}", banner);
99 }
100 SessionEvent::HostVerify(verify) => {
101 info!("SessionEvent HostVerify:{:?}", verify);
102 verify.answer(true).compat().await?;
103 }
104 SessionEvent::Authenticate(auth) => {
105 info!("SessionEvent Authenticate:{:?}", auth);
106 auth.answer(vec![self.pass.to_string()]).compat().await?;
107 }
108 SessionEvent::Error(err) => {
109 error!("SessionEvent Error:{:?}", err);
110 bail!(err)
111 }
112 SessionEvent::Authenticated => {
113 info!("SessionEvent Authenticated");
114 break;
115 }
116 }
117 }
118 Ok(session)
119 }
120}