1use prettytable::{Cell, Row, Table};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::io::{BufRead, BufReader};
4use std::io::{Error, ErrorKind};
5use std::process::{Command, Stdio};
6use tracing::{debug, info, warn};
7
8use super::secure::{check_secure, decrypt, encrypt};
9use super::session::SSHSession;
10use crate::config::CONFIG;
11use crate::db::{self, delete_index, get_connection, update_authorized};
12
13#[derive(Debug, Default, Serialize, Deserialize, Clone)]
14pub struct Remote {
15 pub index: usize,
18 pub user: String,
20 #[serde(deserialize_with = "depass", serialize_with = "enpass")]
22 pub password: String,
23 pub ip: String,
25 pub port: u16,
27 pub authorized: bool,
29 pub name: Option<String>,
31 pub note: Option<String>,
33}
34
35fn depass<'de, D>(deserializer: D) -> Result<String, D::Error>
36where
37 D: Deserializer<'de>,
38{
39 let password = String::deserialize(deserializer)?;
40 Ok(decrypt(&password))
41}
42
43fn enpass<S>(password: &String, serializer: S) -> Result<S::Ok, S::Error>
44where
45 S: Serializer,
46{
47 Serialize::serialize(&encrypt(password), serializer)
48}
49
50impl std::fmt::Display for Remote {
52 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53 write!(f, "{}@{}:{}", self.user, self.ip, self.port,)
54 }
55}
56
57impl Remote {
58 pub fn delete(&self) -> Result<(), Error> {
60 let session = SSHSession::new(&self.user, &self.password, &self.ip, self.port)?;
62 session.revoke()?;
63 let conn = get_connection().lock();
65 delete_index(&conn, self.index).map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
66 info!(remote = self.to_string(), "success delete");
67 Ok(())
68 }
69
70 fn authenticate(&self) -> Result<(), Error> {
71 let session = SSHSession::new(&self.user, &self.password, &self.ip, self.port)?;
73 session.authenticate()?;
74 if !self.authorized {
76 let conn = get_connection().lock();
79 update_authorized(&conn, self.index, true)
80 .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
81 }
82 info!(remote = self.to_string(), "success authenticate");
83 Ok(())
84 }
85
86 pub fn login(&self, reauth: bool) -> Result<(), Error> {
87 if !self.authorized || reauth {
89 self.authenticate()?;
90 }
91
92 let sshkey = CONFIG.get_private_key();
93 Command::new("ssh")
94 .arg(format!("{}@{}", self.user, self.ip))
95 .arg("-p")
96 .arg(self.port.to_string())
97 .arg("-i")
98 .arg(sshkey.to_str().unwrap())
99 .status()?;
100 info!(remote = self.to_string(), "success login");
101 Ok(())
102 }
103
104 fn scp(&self, args: &Vec<&str>) -> Result<(), Error> {
105 if !self.authorized {
107 debug!(remote = self.to_string(), "no authorized, try authenticate");
108 self.authenticate()?;
109 }
110 debug!(args=?args, "scp");
116 let stderr = Command::new("scp")
117 .args(args)
118 .stderr(Stdio::piped())
119 .spawn()?
120 .stderr;
121 if stderr.is_none() {
122 return Err(Error::new(ErrorKind::BrokenPipe, "stderr is none"));
123 }
124 let reader = BufReader::new(stderr.unwrap());
125 reader
126 .lines()
127 .filter_map(|line| line.ok())
128 .for_each(|line| println!("{}", line));
129 Ok(())
130 }
131
132 pub fn upload(&self, from: &str, to: &str) -> Result<(), Error> {
133 let port = self.port.to_string();
136 let remote = format!("{}@{}:{}", self.user, self.ip, to);
137 let cmd = vec![
138 "-r",
139 "-P",
140 &port,
141 "-i",
142 CONFIG.get_private_key().to_str().unwrap(),
143 from,
144 &remote,
145 ];
146 self.scp(&cmd)?;
147 info!(from=?from, to=?to, "susccess upload");
148 Ok(())
149 }
150
151 pub fn download(&self, from: &str, to: &str) -> Result<(), Error> {
152 let port = self.port.to_string();
155 let remote = format!("{}@{}:{}", self.user, self.ip, from);
156 let cmd = vec![
157 "-r",
158 "-P",
159 &port,
160 "-i",
161 CONFIG.get_private_key().to_str().unwrap(),
162 &remote,
163 to,
164 ];
165 self.scp(&cmd)?;
166 info!(from=?from, to=?to, "susccess download");
167 Ok(())
168 }
169}
170
171#[derive(Debug, Default, Serialize, Deserialize, Clone)]
172pub struct Remotes(pub Vec<Remote>);
173
174impl Remotes {
175 fn load() -> Result<Remotes, Error> {
176 let conn = db::get_connection().lock();
177 let remotes = db::query_all(&conn).map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
178 Ok(Remotes(remotes))
179 }
180 pub fn get(idx: usize) -> Result<Option<Remote>, Error> {
181 let remote = {
182 let conn = db::get_connection().lock();
183 db::query_index(&conn, idx)
184 }
185 .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
186
187 if remote.is_some() {
188 info!(index = idx, "susccess get remote");
189 } else {
190 warn!(index = idx, "remote not found");
191 }
192 Ok(remote)
193 }
194
195 pub fn try_get(idx: usize) -> Result<Remote, Error> {
196 let remote = Remotes::get(idx)?;
197 if remote.is_none() {
198 return Err(Error::new(
199 std::io::ErrorKind::NotFound,
200 format!("index {} remote not found", idx),
201 ));
202 }
203 Ok(remote.unwrap())
204 }
205
206 pub fn get_all() -> Result<Remotes, Error> {
207 let remotes = Remotes::load()?;
208 info!("susccess get all remotes {}", remotes.0.len());
209 Ok(remotes)
210 }
211
212 pub fn add(
213 user: &str,
214 password: &str,
215 ip: &str,
216 port: u16,
217 name: &Option<impl AsRef<str>>,
218 note: &Option<impl AsRef<str>>,
219 ) -> Result<usize, Error> {
220 check_secure()?;
221 let remote = Remote {
222 index: 0, user: user.to_string(),
224 password: password.to_string(),
225 ip: ip.to_string(),
226 port,
227 authorized: false,
228 name: name.as_ref().map(|n| n.as_ref().to_string()),
229 note: note.as_ref().map(|n| n.as_ref().to_string()),
230 };
231 let n = {
235 let conn = db::get_connection().lock();
236 db::insert(&conn, &remote)
237 }
238 .map_err(|e| Error::new(std::io::ErrorKind::Other, e))?;
239 info!(remote = remote.to_string(), "add remote success");
240 Ok(n)
241 }
242
243 pub fn delete(index: &Vec<usize>) -> Result<usize, Error> {
244 let mut n = 0;
245
246 for idx in index.iter() {
247 debug!(index = idx, "delete");
248 if let Some(remote) = Remotes::get(*idx)? {
249 remote.delete()?;
250 n += 1;
251 }
252 }
253 Ok(n)
254 }
255
256 pub fn pprint(&self, all: bool) {
267 let mut table = Table::new();
268 let mut titles = vec!["index", "name", "user", "ip", "port"];
269 if all {
270 titles.push("password");
271 titles.push("authorized");
272 titles.push("note");
273 }
274 table.set_titles(Row::new(
275 titles
276 .iter()
277 .map(|v| Cell::new(v).style_spec("bcFg"))
278 .collect::<Vec<Cell>>(),
279 ));
280
281 for remote in self.0.iter() {
282 let mut row = vec![
283 remote.index.to_string(),
284 remote.name.clone().unwrap_or_else(|| "".to_string()),
285 remote.user.clone(),
286 remote.ip.clone(),
287 remote.port.to_string(),
288 ];
289 if all {
290 if let Ok(_) = check_secure() {
291 row.push(remote.password.clone());
292 } else {
293 row.push(format!(
294 "{}..{}",
295 &remote.password[..3],
296 &remote.password[remote.password.len() - 5..]
297 ));
298 }
299 row.push(remote.authorized.to_string());
301 row.push(remote.note.clone().unwrap_or_else(|| "".to_string()));
302 }
303 table.add_row(Row::new(
304 row.iter()
305 .map(|v| Cell::new(v).style_spec("lFc"))
306 .collect::<Vec<Cell>>(),
307 ));
308 }
309 debug!("the remote list total: {}", self.0.len());
310 table.printstd();
311 }
312}