Blame view

sources/apps/files_external/lib/sftp.php 6.74 KB
03e52840d   Kload   Init
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
  <?php
  /**
   * Copyright (c) 2012 Henrik Kjölhede <hkjolhede@gmail.com>
   * This file is licensed under the Affero General Public License version 3 or
   * later.
   * See the COPYING-README file.
   */
  namespace OC\Files\Storage;
  
  set_include_path(get_include_path() . PATH_SEPARATOR .
  	\OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib');
  require 'Net/SFTP.php';
  
  class SFTP extends \OC\Files\Storage\Common {
  	private $host;
  	private $user;
  	private $password;
  	private $root;
  
  	private $client;
  
  	private static $tempFiles = array();
  
  	public function __construct($params) {
  		$this->host = $params['host'];
  		$proto = strpos($this->host, '://');
  		if ($proto != false) {
  			$this->host = substr($this->host, $proto+3);
  		}
  		$this->user = $params['user'];
  		$this->password = $params['password'];
31b7f2792   Kload   Upgrade to ownclo...
32
33
  		$this->root
  			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
03e52840d   Kload   Init
34

31b7f2792   Kload   Upgrade to ownclo...
35
36
37
38
39
40
41
  		if ($this->root[0] != '/') {
  			 $this->root = '/' . $this->root;
  		}
  
  		if (substr($this->root, -1, 1) != '/') {
  			$this->root .= '/';
  		}
03e52840d   Kload   Init
42

31b7f2792   Kload   Upgrade to ownclo...
43
  		$hostKeys = $this->readHostKeys();
03e52840d   Kload   Init
44
  		$this->client = new \Net_SFTP($this->host);
31b7f2792   Kload   Upgrade to ownclo...
45

03e52840d   Kload   Init
46
47
48
  		if (!$this->client->login($this->user, $this->password)) {
  			throw new \Exception('Login failed');
  		}
31b7f2792   Kload   Upgrade to ownclo...
49
  		$currentHostKey = $this->client->getServerPublicHostKey();
03e52840d   Kload   Init
50

31b7f2792   Kload   Upgrade to ownclo...
51
52
  		if (array_key_exists($this->host, $hostKeys)) {
  			if ($hostKeys[$this->host] != $currentHostKey) {
03e52840d   Kload   Init
53
54
55
  				throw new \Exception('Host public key does not match known key');
  			}
  		} else {
31b7f2792   Kload   Upgrade to ownclo...
56
57
  			$hostKeys[$this->host] = $currentHostKey;
  			$this->writeHostKeys($hostKeys);
03e52840d   Kload   Init
58
59
60
61
  		}
  	}
  
  	public function test() {
31b7f2792   Kload   Upgrade to ownclo...
62
63
64
65
66
67
  		if (
  			!isset($this->host)
  			|| !isset($this->user)
  			|| !isset($this->password)
  		) {
  			return false;
03e52840d   Kload   Init
68
  		}
31b7f2792   Kload   Upgrade to ownclo...
69
  		return $this->client->nlist() !== false;
03e52840d   Kload   Init
70
71
72
73
74
  	}
  
  	public function getId(){
  		return 'sftp::' . $this->user . '@' . $this->host . '/' . $this->root;
  	}
31b7f2792   Kload   Upgrade to ownclo...
75
  	private function absPath($path) {
03e52840d   Kload   Init
76
77
  		return $this->root . $this->cleanPath($path);
  	}
31b7f2792   Kload   Upgrade to ownclo...
78
  	private function hostKeysPath() {
03e52840d   Kload   Init
79
80
81
82
83
  		try {
  			$storage_view = \OCP\Files::getStorage('files_external');
  			if ($storage_view) {
  				return \OCP\Config::getSystemValue('datadirectory') .
  					$storage_view->getAbsolutePath('') .
31b7f2792   Kload   Upgrade to ownclo...
84
  					'ssh_hostKeys';
03e52840d   Kload   Init
85
86
87
88
89
  			}
  		} catch (\Exception $e) {
  		}
  		return false;
  	}
31b7f2792   Kload   Upgrade to ownclo...
90
  	private function writeHostKeys($keys) {
03e52840d   Kload   Init
91
  		try {
31b7f2792   Kload   Upgrade to ownclo...
92
93
94
95
96
97
98
99
100
  			$keyPath = $this->hostKeysPath();
  			if ($keyPath && file_exists($keyPath)) {
  				$fp = fopen($keyPath, 'w');
  				foreach ($keys as $host => $key) {
  					fwrite($fp, $host . '::' . $key . "
  ");
  				}
  				fclose($fp);
  				return true;
03e52840d   Kload   Init
101
  			}
03e52840d   Kload   Init
102
  		} catch (\Exception $e) {
03e52840d   Kload   Init
103
  		}
31b7f2792   Kload   Upgrade to ownclo...
104
  		return false;
03e52840d   Kload   Init
105
  	}
31b7f2792   Kload   Upgrade to ownclo...
106
  	private function readHostKeys() {
03e52840d   Kload   Init
107
  		try {
31b7f2792   Kload   Upgrade to ownclo...
108
109
  			$keyPath = $this->hostKeysPath();
  			if (file_exists($keyPath)) {
03e52840d   Kload   Init
110
111
  				$hosts = array();
  				$keys = array();
31b7f2792   Kload   Upgrade to ownclo...
112
  				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
03e52840d   Kload   Init
113
114
  				if ($lines) {
  					foreach ($lines as $line) {
31b7f2792   Kload   Upgrade to ownclo...
115
116
117
118
  						$hostKeyArray = explode("::", $line, 2);
  						if (count($hostKeyArray) == 2) {
  							$hosts[] = $hostKeyArray[0];
  							$keys[] = $hostKeyArray[1];
03e52840d   Kload   Init
119
120
121
122
123
124
125
126
127
128
129
130
  						}
  					}
  					return array_combine($hosts, $keys);
  				}
  			}
  		} catch (\Exception $e) {
  		}
  		return array();
  	}
  
  	public function mkdir($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
131
  			return $this->client->mkdir($this->absPath($path));
03e52840d   Kload   Init
132
133
134
135
136
137
138
  		} catch (\Exception $e) {
  			return false;
  		}
  	}
  
  	public function rmdir($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
139
  			return $this->client->delete($this->absPath($path), true);
03e52840d   Kload   Init
140
141
142
143
144
145
146
  		} catch (\Exception $e) {
  			return false;
  		}
  	}
  
  	public function opendir($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
147
  			$list = $this->client->nlist($this->absPath($path));
03e52840d   Kload   Init
148
149
  
  			$id = md5('sftp:' . $path);
31b7f2792   Kload   Upgrade to ownclo...
150
  			$dirStream = array();
03e52840d   Kload   Init
151
152
  			foreach($list as $file) {
  				if ($file != '.' && $file != '..') {
31b7f2792   Kload   Upgrade to ownclo...
153
  					$dirStream[] = $file;
03e52840d   Kload   Init
154
155
  				}
  			}
31b7f2792   Kload   Upgrade to ownclo...
156
  			\OC\Files\Stream\Dir::register($id, $dirStream);
03e52840d   Kload   Init
157
158
159
160
161
162
163
164
  			return opendir('fakedir://' . $id);
  		} catch(\Exception $e) {
  			return false;
  		}
  	}
  
  	public function filetype($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
165
166
167
168
169
170
171
172
  			$stat = $this->client->stat($this->absPath($path));
  			if ($stat['type'] == NET_SFTP_TYPE_REGULAR) {
  				return 'file';
  			}
  
  			if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
  				return 'dir';
  			}
03e52840d   Kload   Init
173
  		} catch (\Exeption $e) {
31b7f2792   Kload   Upgrade to ownclo...
174

03e52840d   Kload   Init
175
176
177
  		}
  		return false;
  	}
03e52840d   Kload   Init
178
179
  	public function file_exists($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
180
  			return $this->client->stat($this->absPath($path)) !== false;
03e52840d   Kload   Init
181
182
183
184
185
186
187
  		} catch (\Exception $e) {
  			return false;
  		}
  	}
  
  	public function unlink($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
188
  			return $this->client->delete($this->absPath($path), true);
03e52840d   Kload   Init
189
190
191
192
193
194
195
  		} catch (\Exception $e) {
  			return false;
  		}
  	}
  
  	public function fopen($path, $mode) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
196
  			$absPath = $this->absPath($path);
03e52840d   Kload   Init
197
198
199
  			switch($mode) {
  				case 'r':
  				case 'rb':
31b7f2792   Kload   Upgrade to ownclo...
200
201
202
  					if ( !$this->file_exists($path)) {
  						return false;
  					}
03e52840d   Kload   Init
203
204
205
206
207
208
  					if (strrpos($path, '.')!==false) {
  						$ext=substr($path, strrpos($path, '.'));
  					} else {
  						$ext='';
  					}
  					$tmp = \OC_Helper::tmpFile($ext);
31b7f2792   Kload   Upgrade to ownclo...
209
  					$this->getFile($absPath, $tmp);
03e52840d   Kload   Init
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  					return fopen($tmp, $mode);
  
  				case 'w':
  				case 'wb':
  				case 'a':
  				case 'ab':
  				case 'r+':
  				case 'w+':
  				case 'wb+':
  				case 'a+':
  				case 'x':
  				case 'x+':
  				case 'c':
  				case 'c+':
  					if (strrpos($path, '.')!==false) {
  						$ext=substr($path, strrpos($path, '.'));
  					} else {
  						$ext='';
  					}
31b7f2792   Kload   Upgrade to ownclo...
229

03e52840d   Kload   Init
230
  					$tmpFile=\OC_Helper::tmpFile($ext);
31b7f2792   Kload   Upgrade to ownclo...
231
232
233
234
  					\OC\Files\Stream\Close::registerCallback(
  						$tmpFile,
  						array($this, 'writeBack')
  					);
03e52840d   Kload   Init
235
  					if ($this->file_exists($path)) {
31b7f2792   Kload   Upgrade to ownclo...
236
  						$this->getFile($absPath, $tmpFile);
03e52840d   Kload   Init
237
  					}
31b7f2792   Kload   Upgrade to ownclo...
238
239
  
  					self::$tempFiles[$tmpFile]=$absPath;
03e52840d   Kload   Init
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  					return fopen('close://'.$tmpFile, $mode);
  			}
  		} catch (\Exception $e) {
  		}
  		return false;
  	}
  
  	public function writeBack($tmpFile) {
  		if (array_key_exists($tmpFile, self::$tempFiles)) {
  			$this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
  			unlink($tmpFile);
  			unset(self::$tempFiles[$tmpFile]);
  		}
  	}
  
  	public function touch($path, $mtime=null) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
257
258
259
  			if (!is_null($mtime)) {
  				return false;
  			}
03e52840d   Kload   Init
260
  			if (!$this->file_exists($path)) {
31b7f2792   Kload   Upgrade to ownclo...
261
  				$this->client->put($this->absPath($path), '');
03e52840d   Kload   Init
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  			} else {
  				return false;
  			}
  		} catch (\Exception $e) {
  			return false;
  		}
  		return true;
  	}
  
  	public function getFile($path, $target) {
  		$this->client->get($path, $target);
  	}
  
  	public function uploadFile($path, $target) {
  		$this->client->put($target, $path, NET_SFTP_LOCAL_FILE);
  	}
  
  	public function rename($source, $target) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
281
282
283
284
285
286
287
  			if (!$this->is_dir($target) && $this->file_exists($target)) {
  				$this->unlink($target);
  			}
  			return $this->client->rename(
  				$this->absPath($source),
  				$this->absPath($target)
  			);
03e52840d   Kload   Init
288
289
290
291
292
293
294
  		} catch (\Exception $e) {
  			return false;
  		}
  	}
  
  	public function stat($path) {
  		try {
31b7f2792   Kload   Upgrade to ownclo...
295
  			$stat = $this->client->stat($this->absPath($path));
03e52840d   Kload   Init
296
297
298
299
300
301
302
303
  
  			$mtime = $stat ? $stat['mtime'] : -1;
  			$size = $stat ? $stat['size'] : 0;
  
  			return array('mtime' => $mtime, 'size' => $size, 'ctime' => -1);
  		} catch (\Exception $e) {
  			return false;
  		}
03e52840d   Kload   Init
304
305
  	}
  }