depage-fs
Loading...
Searching...
No Matches
FsSsh.php
Go to the documentation of this file.
1<?php
2
3namespace Depage\Fs;
4
5class FsSsh extends Fs
6{
7 protected $session;
8 protected $connection;
9 protected $privateKeyFile;
10 protected $publicKeyFile;
11 protected $privateKey;
12 protected $publicKey;
13 protected $fingerprint;
14 protected $tmp;
15 public function __construct($params = array())
16 {
17 parent::__construct($params);
18 $this->privateKeyFile = (isset($params['privateKeyFile'])) ? $params['privateKeyFile'] : null;
19 $this->publicKeyFile = (isset($params['publicKeyFile'])) ? $params['publicKeyFile'] : null;
20 $this->privateKey = (isset($params['privateKey'])) ? $params['privateKey'] : null;
21 $this->publicKey = (isset($params['publicKey'])) ? $params['publicKey'] : null;
22 $this->tmp = (isset($params['tmp'])) ? $params['tmp'] : null;
23 $this->fingerprint = (isset($params['fingerprint'])) ? $params['fingerprint'] : null;
24 }
25 public function __destruct()
26 {
27 $this->disconnect();
28 }
29
30 public function getFingerprint()
31 {
33 return $fingerprint;
34 }
35 protected function getConnection(&$fingerprint = null)
36 {
37 $port = $this->url['port'] ?? 22;
38 $options = [
39 'client_to_server' => [
40 'comp' => 'zlib,none',
41 ],
42 'server_to_client' => [
43 'comp' => 'zlib,none',
44 ],
45 ];
46 if (!$this->connection) {
47 $this->connection = \ssh2_connect($this->url['host'], $port);
48 }
49 $fingerprint = \ssh2_fingerprint($this->connection);
50
51 return $this->connection;
52 }
53 protected function getSession()
54 {
55 if (!$this->session) {
57
58 if (
59 !is_null($this->fingerprint) &&
60 strcasecmp($this->fingerprint, $fingerprint) !== 0
61 ) {
62 throw new Exceptions\FsException('SSH RSA Fingerprints don\'t match.');
63 }
64
65 if (
66 $this->privateKeyFile
67 || $this->publicKeyFile
68 || $this->privateKey
69 || $this->publicKey
70 || $this->tmp
71 ) {
72 $authenticated = $this->authenticateByKey($connection);
73 } else {
74 $authenticated = $this->authenticateByPassword($connection);
75 }
76
77 if (!$authenticated) {
78 throw new Exceptions\FsException('SSH authentication failed for user "' . $this->url['user'] . '".');
79 }
80
81 $session = \ssh2_sftp($connection);
82
83 if (!$session) {
84 throw new Exceptions\FsException('Could not open SFTP session on host "' . $this->url['host'] . '".');
85 }
86
87 $this->session = $session;
88 }
89
90 return $this->session;
91 }
93 {
94 // the return value is checked by the caller; the ssh2 extension's
95 // warning on failure would be redundant (and leak unhandled, as
96 // authentication runs before the fs error handler is installed)
97 return @\ssh2_auth_password(
99 $this->url['user'],
100 $this->url['pass']
101 );
102 }
103 protected function authenticateByKey($connection)
104 {
105 if (!$this->isValidKeyCombination()) {
106 throw new Exceptions\FsException('Invalid SSH key combination.');
107 }
108
109 if ($this->privateKeyFile) {
110 $private = new PrivateSshKey($this->privateKeyFile);
111 } elseif ($this->privateKey) {
112 $private = new PrivateSshKey($this->privateKey, $this->tmp);
113 }
114
115 if ($this->publicKeyFile) {
116 $public = new PublicSshKey($this->publicKeyFile);
117 } elseif ($this->publicKey) {
118 $public = new PublicSshKey($this->publicKey, $this->tmp);
119 } else {
120 $public = $private->extractPublicKey($this->tmp);
121 }
122
123 $authenticated = @\ssh2_auth_pubkey_file(
125 $this->url['user'],
126 $public,
127 $private,
128 $this->url['pass']
129 );
130
131 $private->clean();
132 $public->clean();
133
134 return $authenticated;
135 }
136 protected function isValidKeyCombination()
137 {
138 return ($this->privateKeyFile && $this->publicKeyFile)
139 || ($this->tmp && ($this->privateKeyFile || $this->privateKey));
140 }
141 protected function disconnect()
142 {
143 $this->connection = null;
144 $this->session = null;
145 }
146
147 protected function lateConnect()
148 {
149 parent::lateConnect();
150 $this->getSession();
151 }
152
153 protected function buildUrl($parsed, $showPass = true)
154 {
155 $url = $parsed['scheme'] . '://';
156 $url .= $this->getSession();
157
158 $path = isset($parsed['path']) ? $parsed['path'] : '/';
159 // workaround for https://bugs.php.net/bug.php?id=64169
160 if ($path === '/') {
161 $path = '/.';
162 }
163
164 $url .= $path;
165
166 return $url;
167 }
168
169 protected function rename($source, $target)
170 {
171 // workaround, rename doesn't overwrite files via ssh
172 if (is_file($target) && is_file($source)) {
173 $this->rm($target);
174 }
175
176 // ssh2_sftp_rename wants plain remote paths, not wrapper urls
177 // (which are of the form ssh2.sftp://<session-resource>/path)
178 $plainSource = preg_replace(';^ssh2\.sftp://[^/]*;', '', $source);
179 $plainTarget = preg_replace(';^ssh2\.sftp://[^/]*;', '', $target);
180
181 if (!\ssh2_sftp_rename($this->session, $plainSource, $plainTarget)) {
182 throw new Exceptions\FsException('Cannot rename "' . $plainSource . '" to "' . $plainTarget . '".');
183 }
184
185 return true;
186 }
187}
188
189/* vim:set ft=php sw=4 sts=4 fdm=marker : */
rm($url)
Definition Fs.php:180
buildUrl($parsed, $showPass=true)
Definition FsSsh.php:153
isValidKeyCombination()
Definition FsSsh.php:136
__construct($params=array())
Definition FsSsh.php:15
rename($source, $target)
Hook, allows overriding of rename function.
Definition FsSsh.php:169
getConnection(&$fingerprint=null)
Definition FsSsh.php:35
authenticateByPassword($connection)
Definition FsSsh.php:92
authenticateByKey($connection)
Definition FsSsh.php:103