#[macro_use]
extern crate clap;
extern crate base64;
use std::{
cmp::Ordering,
env,
error::Error,
fs::{self, File},
io::stdin,
path::{Path, PathBuf},
sync::Arc,
};
use std::net::TcpStream;
use clap::ArgMatches;
use flate2::{
Compression,
write::GzEncoder,
};
use indicatif::ProgressBar;
use nu_ansi_term::Color::{Green, Red};
use ssh_rs::{ssh, error::SshError, LocalSession};
use crate::arg::get_matches;
use crate::config::{Config, ServerSpace};
use crate::utils as util;
use crate::aes::{encrypt, decrypt};
use crate::msg::{
ADD_SUCCESS,
HOST_ADDRESS_IS_EMPTY,
INPUT_HOST_ADDRESS,
INPUT_PASSWORD,
INPUT_SPACE_NAME_MSG,
INPUT_TARGET_PATH,
INPUT_USERNAME,
IS_NOT_DIR,
PASSWORD_IS_EMPTY,
REMOVE_SUCCESS,
RMRF_CONFIRM,
RMRF_SUCCESS,
SPACE_LIST_IS_EMPTY,
SPACE_LIST_TITLE,
SPACE_NAME_IS_EMPTY,
SPACE_NAME_IS_EXISTED,
SPACE_NAME_IS_NOT_EXISTED,
TARGET_PATH_IS_EMPTY,
UPLOAD_ERR,
UPLOAD_SUCCESS,
USERNAME_IS_EMPTY
};
use crate::util::read_console;
mod config;
mod arg;
mod utils;
mod aes;
mod msg;
pub fn run() {
let arg_matches = get_matches();
if let Some(_) = arg_matches.subcommand_matches("add") {
handle_command_add();
}
if let Some(_) = arg_matches.subcommand_matches("list") {
handle_command_list();
}
if let Some(arg_matches) = arg_matches.subcommand_matches("detail") {
handle_command_detail(arg_matches);
}
if let Some(arg_matches) = arg_matches.subcommand_matches("remove") {
handle_command_remove(arg_matches);
}
if let Some(arg_matches) = arg_matches.subcommand_matches("push") {
handle_command_push(arg_matches);
}
if let Some(arg_matches) = arg_matches.subcommand_matches("rmrf") {
handle_command_rmrf(arg_matches);
}
}
fn handle_command_add() {
println!("{}", Green.paint(INPUT_SPACE_NAME_MSG));
let name = read_console();
if util::is_empty(&name) {
eprintln!("{}", SPACE_NAME_IS_EMPTY);
return;
}
if !Config::check_server_space_name_available(&name) {
eprintln!("{}", SPACE_NAME_IS_EXISTED);
return;
}
println!("{}", Green.paint(INPUT_HOST_ADDRESS));
let host = read_console();
if util::is_empty(&host) {
eprintln!("{}", HOST_ADDRESS_IS_EMPTY);
return;
}
println!("{}", Green.paint(INPUT_TARGET_PATH));
let path = read_console();
if util::is_empty(&path) {
eprintln!("{}", TARGET_PATH_IS_EMPTY);
return;
}
println!("{}", Green.paint(INPUT_USERNAME));
let user = read_console();
if util::is_empty(&user) {
eprintln!("{}", USERNAME_IS_EMPTY);
return;
}
println!("{}", Green.paint(INPUT_PASSWORD));
let pass = rpassword::read_password().unwrap();
if util::is_empty(&pass.trim()) {
eprintln!("{}", PASSWORD_IS_EMPTY);
return;
}
let pass = encrypt(&pass).unwrap();
let server_space = ServerSpace::new(&name, &host, &path, &user, &pass);
match Config::add_server_space(server_space) {
Ok(_) => println!("{}", ADD_SUCCESS),
Err(msg) => eprintln!("😔{}", msg)
}
}
fn handle_command_list() {
let server_space_list = Config::list_server_space();
if server_space_list.is_empty() {
println!("{}", SPACE_LIST_IS_EMPTY);
return;
}
println!("{}", SPACE_LIST_TITLE);
for name in server_space_list {
println!("{}", Green.paint(name));
}
}
fn handle_command_detail(arg_matches: &ArgMatches) {
let server_space_name = arg_matches.get_one::<String>("space_name").unwrap();
let server_space_option = Config::server_space_detail(server_space_name);
match server_space_option {
Some(server_space) => println!("{}", server_space),
None => eprintln!("😔没有这个空间名称!")
}
}
fn handle_command_remove(arg_matches: &ArgMatches) {
let server_space_name = arg_matches.get_one::<String>("space_name").unwrap();
match Config::remove_server_space(server_space_name) {
Ok(_) => println!("{}", REMOVE_SUCCESS),
Err(_) => eprintln!("{}", SPACE_NAME_IS_NOT_EXISTED)
}
}
fn handle_command_push(arg_matches: &ArgMatches) {
let pushed_dir = arg_matches.get_one::<String>("pushed_dir").unwrap();
let server_space_name = arg_matches.get_one::<String>("space_name").unwrap();
let pushed_dir = util::del_start_separator(pushed_dir).to_string();
let server_space_name = server_space_name.to_string();
let pushed_dir_abs = PathBuf::from(env::current_dir().unwrap()).join(&pushed_dir);
if !pushed_dir_abs.is_dir() {
eprintln!("{}", IS_NOT_DIR);
return;
}
let server_space = Config::server_space_detail(&server_space_name);
if let Some(server_space) = server_space {
let pb = ProgressBar::new(100);
pb.set_position(20);
let pushed_file_name = Arc::new(format!("{}.tar.gz", pushed_dir));
let pushed_file_path = format!("{}.tar.gz", pushed_dir_abs.to_str().unwrap());
let pushed_file_name_copy = pushed_file_name.clone();
let t = std::thread::spawn(move || {
let tar_gz = File::create(pushed_file_name_copy.as_ref()).unwrap();
let enc = GzEncoder::new(tar_gz, Compression::best());
let mut tar = tar::Builder::new(enc);
tar.append_dir_all("", pushed_dir).unwrap();
});
t.join().unwrap();
pb.set_position(50);
if let Err(err) = push_file(&server_space, &pushed_file_name, &pushed_file_path) {
eprintln!("{} {:?}", UPLOAD_ERR, err);
} else {
pb.finish();
println!("{}", UPLOAD_SUCCESS);
}
fs::remove_file(Path::new(&pushed_file_path)).unwrap();
} else {
eprintln!("{}", SPACE_NAME_IS_NOT_EXISTED);
}
}
fn get_ssh_session(server_space: &ServerSpace) -> Result<LocalSession<TcpStream>, SshError> {
let pass = decrypt(&server_space.pass).unwrap();
let session = ssh::create_session()
.username(&server_space.user)
.password(&pass)
.connect(format!("{}:22", server_space.host))
.unwrap()
.run_local();
Ok(session)
}
fn push_file(server_space: &ServerSpace, pushed_file_name: &str, pushed_file_path: &str) -> Result<(), Box<dyn Error>> {
let mut session = get_ssh_session(server_space)?;
let scp = session.open_scp()?;
scp.upload(pushed_file_path, &server_space.path)?;
session.open_exec()
.unwrap()
.send_command(&format!("cd {};tar zxf {};rm -rf {}", server_space.path, pushed_file_name, pushed_file_name))?;
session.close();
Ok(())
}
fn handle_command_rmrf(arg_matches: &ArgMatches) {
let server_space_name = arg_matches.get_one::<String>("space_name").unwrap();
if let Some(server_space) = Config::server_space_detail(server_space_name) {
println!("{}", Red.paint(RMRF_CONFIRM));
let mut confirm = String::new();
stdin().read_line(&mut confirm).unwrap();
if let Ordering::Equal = confirm.to_lowercase().trim().cmp("yes") {
let target_path = format!("{}/*", server_space.path);
let mut session = get_ssh_session(&server_space).unwrap();
session.open_exec()
.unwrap()
.send_command(&format!("rm -rf {}", target_path))
.unwrap();
println!("{}", RMRF_SUCCESS);
session.close()
}
} else {
eprintln!("{}", SPACE_NAME_IS_NOT_EXISTED);
}
}