1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
extern crate tokio;
extern crate futures;
extern crate thrussh;
extern crate regex;
#[macro_use]
extern crate log;
#[macro_use]
extern crate error_chain;
extern crate dirs;

use std::io::Read;
use std::path::Path;

error_chain!{
    foreign_links {
        IO(std::io::Error);
    }
    errors {
        HostNotFound {
        }
        NoHome {
        }
    }
}

mod proxy;
pub use proxy::*;

#[derive(Debug, Default)]
pub struct Config {
    pub user: Option<String>,
    pub host_name: Option<String>,
    pub port: Option<u16>,
    pub identity_file: Option<String>,
    pub proxy_command: Option<String>,
}

impl Config {
    pub fn update_proxy_command(&mut self) {
        if let Some(ref h) = self.host_name {
            if let Some(ref mut prox) = self.proxy_command {
                *prox = prox.replace("%h", h);
            }
        }
        if let Some(ref p) = self.port {
            if let Some(ref mut prox) = self.proxy_command {
                *prox = prox.replace("%p", &format!("{}", p));
            }
        }
    }
}

pub fn parse_home(host: &str) -> Result<Config> {
    let mut home = if let Some(home) = dirs::home_dir() {
        home
    } else {
        return Err(ErrorKind::NoHome.into())
    };
    home.push(".ssh");
    home.push("config");
    parse_path(&home, host)
}

pub fn parse_path<P:AsRef<Path>>(path: P, host: &str) -> Result<Config> {
    let mut s = String::new();
    let mut b = std::fs::File::open(path)?;
    b.read_to_string(&mut s)?;
    parse(&s, host)
}

pub fn parse(file: &str, host: &str) -> Result<Config> {
    let mut config: Option<Config> = None;
    for line in file.lines() {
        let line = line.trim();
        if let Some(n) = line.find(' ') {
            let (key, value) = line.split_at(n);
            let lower = key.to_lowercase();
            if let Some(ref mut config) = config {
                match lower.as_str() {
                    "host" => break,
                    "user" => config.user = Some(value.trim_left().to_string()),
                    "hostname" => config.host_name = Some(value.trim_left().to_string()),
                    "port" => config.port = value.trim_left().parse().ok(),
                    "identityfile" => config.identity_file = Some(value.trim_left().to_string()),
                    "proxycommand" => config.proxy_command = Some(value.trim_left().to_string()),
                    key => {
                        debug!("{:?}", key);
                    }
                }
            } else {
                match lower.as_str() {
                    "host" => {
                        if value.trim_left() == host {
                            config = Some(Config::default())
                        }
                    }
                    _ => {}
                }
            }
        }

    }
    if let Some(config) = config {
        Ok(config)
    } else {
        Err(ErrorKind::HostNotFound.into())
    }
}