SshConfigFileConfiguration.php (5365B)
1 <?php 2 3 namespace Ssh; 4 5 use RuntimeException; 6 7 /** 8 * SSH Config File Configuration 9 * 10 * @author Cam Spiers <camspiers@gmail.com> 11 */ 12 class SshConfigFileConfiguration extends Configuration 13 { 14 15 const DEFAULT_SSH_IDENTITY = '~/.ssh/id_rsa'; 16 17 protected $configs = array(); 18 protected $config; 19 protected $match = array(); 20 21 /** 22 * Constructor 23 * 24 * @param string $file 25 * @param string $host 26 * @param integer $port 27 * @param array $methods 28 * @param array $callbacks 29 * @param string $identity 30 */ 31 public function __construct( 32 $file, $host, $port = 22, array $methods = array(), array $callbacks = array(), $identity = null 33 ) 34 { 35 $this->parseSshConfigFile($this->processPath($file)); 36 $this->identity = is_null($identity) ? self::DEFAULT_SSH_IDENTITY : $identity; 37 $this->config = $this->getConfigForHost($host); 38 39 parent::__construct( 40 isset($this->config['hostname']) ? $this->config['hostname'] : $host, 41 isset($this->config['port']) ? $this->config['port'] : $port, 42 $methods, 43 $callbacks 44 ); 45 } 46 47 /** 48 * Replaces '~'' with users home path 49 * @param string $path 50 * @return string 51 */ 52 protected function processPath($path) 53 { 54 return preg_replace('/^~/', getenv('HOME'), $path); 55 } 56 57 /** 58 * Parses the ssh config file into an array of configs for later matching against hosts 59 * @param string $file 60 */ 61 protected function parseSshConfigFile($file) 62 { 63 if (!file_exists($file) || !is_readable($file)) { 64 throw new RuntimeException("The file '$file' does not exist or is not readable"); 65 } 66 $contents = file_get_contents($file); 67 $configs = array(); 68 $lineNumber = 1; 69 foreach (explode(PHP_EOL, $contents) as $line) { 70 $line = trim($line); 71 if ($line == '' || $line[0] == '#') { 72 continue; 73 } 74 $pos = strpos($line, '='); 75 if ($pos !== false) { 76 $key = strtolower(trim(substr($line, 0, $pos))); 77 $value = trim(substr($line, $pos + 1, strlen($line))); 78 } else { 79 $i = 0; 80 while ($i < strlen($line)) { 81 if ($line[$i] == ' ') { 82 break; 83 } 84 $i++; 85 } 86 if ($i == strlen($line)) { 87 throw new RuntimeException("The file '$file' is not parsable at line '$lineNumber'"); 88 } 89 $key = strtolower(trim(substr($line, 0, $i))); 90 $value = trim(substr($line, $i + 1, strlen($line))); 91 } 92 if ($key == 'host') { 93 $this->configs = array_merge($this->configs, $configs); 94 $configs = array(); 95 $hosts = explode(' ', $value); 96 foreach ($hosts as $host) { 97 $configs[] = array('host' => $host); 98 } 99 100 } else { 101 foreach ($configs as $host => $config) { 102 $configs[$host][$key] = $value; 103 } 104 } 105 $lineNumber++; 106 } 107 $this->configs = array_merge($this->configs, $configs); 108 } 109 110 /** 111 * Merges matches of the host in the config file using fnmatch and a length comparison 112 * @param string $host 113 * @return array 114 */ 115 public function getConfigForHost($host) 116 { 117 $matches = array(); 118 foreach ($this->configs as $config) { 119 if (fnmatch($config['host'], $host)) { 120 $matches[] = $config; 121 } 122 } 123 if (count($matches) == 0) { 124 throw new RuntimeException("Unable to find configuration for host '{$host}'"); 125 } 126 usort($matches, function ($a, $b) { 127 return strlen($a['host']) > strlen($b['host']); 128 }); 129 $result = array(); 130 foreach ($matches as $match) { 131 $result = array_merge($result, $match); 132 } 133 unset($result['host']); 134 if (isset($result['identityfile'])) { 135 $result['identityfile'] = $this->processPath($result['identityfile']); 136 } else if (file_exists($file = $this->processPath($this->getIdentity()))) { 137 $result['identityfile'] = $file; 138 } 139 140 return $result; 141 } 142 143 /** 144 * Return an authentication mechanism based on the configuration file 145 * @param string|null $passphrase 146 * @param string|null $user 147 * @return PublicKeyFile|None 148 */ 149 public function getAuthentication($passphrase = null, $user = null) 150 { 151 if (is_null($user) && !isset($this->config['user'])) { 152 throw new RuntimeException("Can not authenticate for '{$this->host}' could not find user to authenticate as"); 153 } 154 $user = $user ?: $this->config['user']; 155 if (isset($this->config['identityfile'])) { 156 return new Authentication\PublicKeyFile( 157 $user, 158 $this->config['identityfile'] . '.pub', 159 $this->config['identityfile'], 160 $passphrase 161 ); 162 } else { 163 return new Authentication\None( 164 $user 165 ); 166 } 167 } 168 }