openaws-vpn-client 0.1.3

Unofficial open-source AWS VPN client written in Rust
use lazy_static::lazy_static;
use std::collections::HashSet;
use std::ffi::{OsStr, OsString};
use std::fs::{create_dir_all, remove_file, File};
use std::io::Read;
use std::io::Write;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;

type StdMutex<T> = std::sync::Mutex<T>;
type TokioMutex<T> = tokio::sync::Mutex<T>;

lazy_static! {
    static ref CLEAN_KEYS: HashSet<String> = {
        let mut set = HashSet::new();
        set.insert("remote ".to_string());
        set.insert("remote-random-hostname".to_string());
        set.insert("auth-user-pass".to_string());
        set.insert("auth-federate".to_string());
        set.insert("auth-retry interact".to_string());
        set
    };
}

pub struct Config {
    pub addresses: Arc<StdMutex<Option<Vec<IpAddr>>>>,
    pub remote: Arc<StdMutex<Option<(String, u16)>>>,
    pub config: Arc<StdMutex<Option<PathBuf>>>,
    pub pwd: Arc<TokioMutex<Option<Pwd>>>,
}

pub struct Pwd {
    pub pwd: String,
}

unsafe impl Send for Pwd {}
unsafe impl Sync for Pwd {}

impl Config {
    pub fn new() -> Config {
        Config {
            addresses: Arc::new(StdMutex::new(None)),
            remote: Arc::new(StdMutex::new(None)),
            config: Arc::new(StdMutex::new(None)),
            pwd: Arc::new(TokioMutex::new(None)),
        }
    }

    pub fn save_config<P: AsRef<Path>>(&self, file: P) {
        let path = file.as_ref();
        let content = {
            let mut file = File::open(path).unwrap();
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();
            content
        };

        let full_name = {
            let file_name = path.file_stem().unwrap();
            let extension = path.extension().unwrap();
            let mut str = OsString::new();
            str.push(file_name);
            str.push(OsStr::new("-oavc"));
            str.push(OsStr::new("."));
            str.push(extension);
            PathBuf::from(str)
        };

        let remote = get_remote(&content);
        let new_contents = content
            .lines()
            .filter(|l| !has_key(l.to_string()))
            .map(|v| v.to_string())
            .collect::<Vec<String>>()
            .join("\n");

        let file_dir = dirs::data_local_dir()
            .map(|v| v.join("openaws-vpn-client").join(full_name.clone()))
            .unwrap_or(full_name);

        if file_dir.exists() {
            remove_file(file_dir.clone()).unwrap();
        }

        if let Some(parent) = file_dir.parent() {
            create_dir_all(parent).unwrap();
        }

        let mut file = File::create(file_dir.clone()).unwrap();
        write!(file, "{}", new_contents).unwrap();
        println!("Saved at {:?}", &file_dir);
        println!("Remote {:?}", &remote);
        let mut config = self.config.lock().unwrap();
        *config = Some(file_dir);
        let mut re = self.remote.lock().unwrap();
        *re = Some(remote);
    }
}

fn has_key(key: String) -> bool {
    for k in CLEAN_KEYS.iter() {
        if key.starts_with(k) {
            return true;
        }
    }

    return false;
}

fn get_remote(content: &String) -> (String, u16) {
    return content
        .lines()
        .filter(|p| p.starts_with("remote "))
        .map(|p| {
            let addr = (&p["remote ".len()..p.rfind(" ").unwrap()]).to_string();
            let port = (&p[p.rfind(" ").unwrap() + 1..]).parse::<u16>().unwrap();
            (addr, port)
        })
        .next()
        .unwrap();
}