Passed
Push — master ( 3c5692...34cd49 )
by Roeland
13:23 queued 11s
created
apps/files_external/lib/Lib/Storage/SFTP.php 1 patch
Indentation   +427 added lines, -427 removed lines patch added patch discarded remove patch
@@ -46,431 +46,431 @@
 block discarded – undo
46 46
  * provide access to SFTP servers.
47 47
  */
48 48
 class SFTP extends \OC\Files\Storage\Common {
49
-	private $host;
50
-	private $user;
51
-	private $root;
52
-	private $port = 22;
53
-
54
-	private $auth = [];
55
-
56
-	/**
57
-	 * @var \phpseclib\Net\SFTP
58
-	 */
59
-	protected $client;
60
-
61
-	/**
62
-	 * @param string $host protocol://server:port
63
-	 * @return array [$server, $port]
64
-	 */
65
-	private function splitHost($host) {
66
-		$input = $host;
67
-		if (strpos($host, '://') === false) {
68
-			// add a protocol to fix parse_url behavior with ipv6
69
-			$host = 'http://' . $host;
70
-		}
71
-
72
-		$parsed = parse_url($host);
73
-		if (is_array($parsed) && isset($parsed['port'])) {
74
-			return [$parsed['host'], $parsed['port']];
75
-		} elseif (is_array($parsed)) {
76
-			return [$parsed['host'], 22];
77
-		} else {
78
-			return [$input, 22];
79
-		}
80
-	}
81
-
82
-	/**
83
-	 * {@inheritdoc}
84
-	 */
85
-	public function __construct($params) {
86
-		// Register sftp://
87
-		Stream::register();
88
-
89
-		$parsedHost =  $this->splitHost($params['host']);
90
-
91
-		$this->host = $parsedHost[0];
92
-		$this->port = $parsedHost[1];
93
-
94
-		if (!isset($params['user'])) {
95
-			throw new \UnexpectedValueException('no authentication parameters specified');
96
-		}
97
-		$this->user = $params['user'];
98
-
99
-		if (isset($params['public_key_auth'])) {
100
-			$this->auth[] = $params['public_key_auth'];
101
-		}
102
-		if (isset($params['password']) && $params['password'] !== '') {
103
-			$this->auth[] = $params['password'];
104
-		}
105
-
106
-		if ($this->auth === []) {
107
-			throw new \UnexpectedValueException('no authentication parameters specified');
108
-		}
109
-
110
-		$this->root
111
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
112
-
113
-		$this->root = '/' . ltrim($this->root, '/');
114
-		$this->root = rtrim($this->root, '/') . '/';
115
-	}
116
-
117
-	/**
118
-	 * Returns the connection.
119
-	 *
120
-	 * @return \phpseclib\Net\SFTP connected client instance
121
-	 * @throws \Exception when the connection failed
122
-	 */
123
-	public function getConnection() {
124
-		if (!is_null($this->client)) {
125
-			return $this->client;
126
-		}
127
-
128
-		$hostKeys = $this->readHostKeys();
129
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
130
-
131
-		// The SSH Host Key MUST be verified before login().
132
-		$currentHostKey = $this->client->getServerPublicHostKey();
133
-		if (array_key_exists($this->host, $hostKeys)) {
134
-			if ($hostKeys[$this->host] !== $currentHostKey) {
135
-				throw new \Exception('Host public key does not match known key');
136
-			}
137
-		} else {
138
-			$hostKeys[$this->host] = $currentHostKey;
139
-			$this->writeHostKeys($hostKeys);
140
-		}
141
-
142
-		$login = false;
143
-		foreach ($this->auth as $auth) {
144
-			$login = $this->client->login($this->user, $auth);
145
-			if ($login === true) {
146
-				break;
147
-			}
148
-		}
149
-
150
-		if ($login === false) {
151
-			throw new \Exception('Login failed');
152
-		}
153
-		return $this->client;
154
-	}
155
-
156
-	/**
157
-	 * {@inheritdoc}
158
-	 */
159
-	public function test() {
160
-		if (
161
-			!isset($this->host)
162
-			|| !isset($this->user)
163
-		) {
164
-			return false;
165
-		}
166
-		return $this->getConnection()->nlist() !== false;
167
-	}
168
-
169
-	/**
170
-	 * {@inheritdoc}
171
-	 */
172
-	public function getId() {
173
-		$id = 'sftp::' . $this->user . '@' . $this->host;
174
-		if ($this->port !== 22) {
175
-			$id .= ':' . $this->port;
176
-		}
177
-		// note: this will double the root slash,
178
-		// we should not change it to keep compatible with
179
-		// old storage ids
180
-		$id .= '/' . $this->root;
181
-		return $id;
182
-	}
183
-
184
-	/**
185
-	 * @return string
186
-	 */
187
-	public function getHost() {
188
-		return $this->host;
189
-	}
190
-
191
-	/**
192
-	 * @return string
193
-	 */
194
-	public function getRoot() {
195
-		return $this->root;
196
-	}
197
-
198
-	/**
199
-	 * @return mixed
200
-	 */
201
-	public function getUser() {
202
-		return $this->user;
203
-	}
204
-
205
-	/**
206
-	 * @param string $path
207
-	 * @return string
208
-	 */
209
-	private function absPath($path) {
210
-		return $this->root . $this->cleanPath($path);
211
-	}
212
-
213
-	/**
214
-	 * @return string|false
215
-	 */
216
-	private function hostKeysPath() {
217
-		try {
218
-			$storage_view = \OCP\Files::getStorage('files_external');
219
-			if ($storage_view) {
220
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
221
-					$storage_view->getAbsolutePath('') .
222
-					'ssh_hostKeys';
223
-			}
224
-		} catch (\Exception $e) {
225
-		}
226
-		return false;
227
-	}
228
-
229
-	/**
230
-	 * @param $keys
231
-	 * @return bool
232
-	 */
233
-	protected function writeHostKeys($keys) {
234
-		try {
235
-			$keyPath = $this->hostKeysPath();
236
-			if ($keyPath && file_exists($keyPath)) {
237
-				$fp = fopen($keyPath, 'w');
238
-				foreach ($keys as $host => $key) {
239
-					fwrite($fp, $host . '::' . $key . "\n");
240
-				}
241
-				fclose($fp);
242
-				return true;
243
-			}
244
-		} catch (\Exception $e) {
245
-		}
246
-		return false;
247
-	}
248
-
249
-	/**
250
-	 * @return array
251
-	 */
252
-	protected function readHostKeys() {
253
-		try {
254
-			$keyPath = $this->hostKeysPath();
255
-			if (file_exists($keyPath)) {
256
-				$hosts = [];
257
-				$keys = [];
258
-				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
259
-				if ($lines) {
260
-					foreach ($lines as $line) {
261
-						$hostKeyArray = explode("::", $line, 2);
262
-						if (count($hostKeyArray) === 2) {
263
-							$hosts[] = $hostKeyArray[0];
264
-							$keys[] = $hostKeyArray[1];
265
-						}
266
-					}
267
-					return array_combine($hosts, $keys);
268
-				}
269
-			}
270
-		} catch (\Exception $e) {
271
-		}
272
-		return [];
273
-	}
274
-
275
-	/**
276
-	 * {@inheritdoc}
277
-	 */
278
-	public function mkdir($path) {
279
-		try {
280
-			return $this->getConnection()->mkdir($this->absPath($path));
281
-		} catch (\Exception $e) {
282
-			return false;
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * {@inheritdoc}
288
-	 */
289
-	public function rmdir($path) {
290
-		try {
291
-			$result = $this->getConnection()->delete($this->absPath($path), true);
292
-			// workaround: stray stat cache entry when deleting empty folders
293
-			// see https://github.com/phpseclib/phpseclib/issues/706
294
-			$this->getConnection()->clearStatCache();
295
-			return $result;
296
-		} catch (\Exception $e) {
297
-			return false;
298
-		}
299
-	}
300
-
301
-	/**
302
-	 * {@inheritdoc}
303
-	 */
304
-	public function opendir($path) {
305
-		try {
306
-			$list = $this->getConnection()->nlist($this->absPath($path));
307
-			if ($list === false) {
308
-				return false;
309
-			}
310
-
311
-			$id = md5('sftp:' . $path);
312
-			$dirStream = [];
313
-			foreach ($list as $file) {
314
-				if ($file !== '.' && $file !== '..') {
315
-					$dirStream[] = $file;
316
-				}
317
-			}
318
-			return IteratorDirectory::wrap($dirStream);
319
-		} catch (\Exception $e) {
320
-			return false;
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * {@inheritdoc}
326
-	 */
327
-	public function filetype($path) {
328
-		try {
329
-			$stat = $this->getConnection()->stat($this->absPath($path));
330
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
331
-				return 'file';
332
-			}
333
-
334
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
335
-				return 'dir';
336
-			}
337
-		} catch (\Exception $e) {
338
-		}
339
-		return false;
340
-	}
341
-
342
-	/**
343
-	 * {@inheritdoc}
344
-	 */
345
-	public function file_exists($path) {
346
-		try {
347
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
348
-		} catch (\Exception $e) {
349
-			return false;
350
-		}
351
-	}
352
-
353
-	/**
354
-	 * {@inheritdoc}
355
-	 */
356
-	public function unlink($path) {
357
-		try {
358
-			return $this->getConnection()->delete($this->absPath($path), true);
359
-		} catch (\Exception $e) {
360
-			return false;
361
-		}
362
-	}
363
-
364
-	/**
365
-	 * {@inheritdoc}
366
-	 */
367
-	public function fopen($path, $mode) {
368
-		try {
369
-			$absPath = $this->absPath($path);
370
-			switch ($mode) {
371
-				case 'r':
372
-				case 'rb':
373
-					if (!$this->file_exists($path)) {
374
-						return false;
375
-					}
376
-					SFTPReadStream::register();
377
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
378
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
379
-					return RetryWrapper::wrap($handle);
380
-				case 'w':
381
-				case 'wb':
382
-					SFTPWriteStream::register();
383
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
384
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
385
-				case 'a':
386
-				case 'ab':
387
-				case 'r+':
388
-				case 'w+':
389
-				case 'wb+':
390
-				case 'a+':
391
-				case 'x':
392
-				case 'x+':
393
-				case 'c':
394
-				case 'c+':
395
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
396
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
397
-					return RetryWrapper::wrap($handle);
398
-			}
399
-		} catch (\Exception $e) {
400
-		}
401
-		return false;
402
-	}
403
-
404
-	/**
405
-	 * {@inheritdoc}
406
-	 */
407
-	public function touch($path, $mtime=null) {
408
-		try {
409
-			if (!is_null($mtime)) {
410
-				return false;
411
-			}
412
-			if (!$this->file_exists($path)) {
413
-				$this->getConnection()->put($this->absPath($path), '');
414
-			} else {
415
-				return false;
416
-			}
417
-		} catch (\Exception $e) {
418
-			return false;
419
-		}
420
-		return true;
421
-	}
422
-
423
-	/**
424
-	 * @param string $path
425
-	 * @param string $target
426
-	 * @throws \Exception
427
-	 */
428
-	public function getFile($path, $target) {
429
-		$this->getConnection()->get($path, $target);
430
-	}
431
-
432
-	/**
433
-	 * {@inheritdoc}
434
-	 */
435
-	public function rename($source, $target) {
436
-		try {
437
-			if ($this->file_exists($target)) {
438
-				$this->unlink($target);
439
-			}
440
-			return $this->getConnection()->rename(
441
-				$this->absPath($source),
442
-				$this->absPath($target)
443
-			);
444
-		} catch (\Exception $e) {
445
-			return false;
446
-		}
447
-	}
448
-
449
-	/**
450
-	 * {@inheritdoc}
451
-	 */
452
-	public function stat($path) {
453
-		try {
454
-			$stat = $this->getConnection()->stat($this->absPath($path));
455
-
456
-			$mtime = $stat ? $stat['mtime'] : -1;
457
-			$size = $stat ? $stat['size'] : 0;
458
-
459
-			return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
460
-		} catch (\Exception $e) {
461
-			return false;
462
-		}
463
-	}
464
-
465
-	/**
466
-	 * @param string $path
467
-	 * @return string
468
-	 */
469
-	public function constructUrl($path) {
470
-		// Do not pass the password here. We want to use the Net_SFTP object
471
-		// supplied via stream context or fail. We only supply username and
472
-		// hostname because this might show up in logs (they are not used).
473
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
474
-		return $url;
475
-	}
49
+    private $host;
50
+    private $user;
51
+    private $root;
52
+    private $port = 22;
53
+
54
+    private $auth = [];
55
+
56
+    /**
57
+     * @var \phpseclib\Net\SFTP
58
+     */
59
+    protected $client;
60
+
61
+    /**
62
+     * @param string $host protocol://server:port
63
+     * @return array [$server, $port]
64
+     */
65
+    private function splitHost($host) {
66
+        $input = $host;
67
+        if (strpos($host, '://') === false) {
68
+            // add a protocol to fix parse_url behavior with ipv6
69
+            $host = 'http://' . $host;
70
+        }
71
+
72
+        $parsed = parse_url($host);
73
+        if (is_array($parsed) && isset($parsed['port'])) {
74
+            return [$parsed['host'], $parsed['port']];
75
+        } elseif (is_array($parsed)) {
76
+            return [$parsed['host'], 22];
77
+        } else {
78
+            return [$input, 22];
79
+        }
80
+    }
81
+
82
+    /**
83
+     * {@inheritdoc}
84
+     */
85
+    public function __construct($params) {
86
+        // Register sftp://
87
+        Stream::register();
88
+
89
+        $parsedHost =  $this->splitHost($params['host']);
90
+
91
+        $this->host = $parsedHost[0];
92
+        $this->port = $parsedHost[1];
93
+
94
+        if (!isset($params['user'])) {
95
+            throw new \UnexpectedValueException('no authentication parameters specified');
96
+        }
97
+        $this->user = $params['user'];
98
+
99
+        if (isset($params['public_key_auth'])) {
100
+            $this->auth[] = $params['public_key_auth'];
101
+        }
102
+        if (isset($params['password']) && $params['password'] !== '') {
103
+            $this->auth[] = $params['password'];
104
+        }
105
+
106
+        if ($this->auth === []) {
107
+            throw new \UnexpectedValueException('no authentication parameters specified');
108
+        }
109
+
110
+        $this->root
111
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
112
+
113
+        $this->root = '/' . ltrim($this->root, '/');
114
+        $this->root = rtrim($this->root, '/') . '/';
115
+    }
116
+
117
+    /**
118
+     * Returns the connection.
119
+     *
120
+     * @return \phpseclib\Net\SFTP connected client instance
121
+     * @throws \Exception when the connection failed
122
+     */
123
+    public function getConnection() {
124
+        if (!is_null($this->client)) {
125
+            return $this->client;
126
+        }
127
+
128
+        $hostKeys = $this->readHostKeys();
129
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
130
+
131
+        // The SSH Host Key MUST be verified before login().
132
+        $currentHostKey = $this->client->getServerPublicHostKey();
133
+        if (array_key_exists($this->host, $hostKeys)) {
134
+            if ($hostKeys[$this->host] !== $currentHostKey) {
135
+                throw new \Exception('Host public key does not match known key');
136
+            }
137
+        } else {
138
+            $hostKeys[$this->host] = $currentHostKey;
139
+            $this->writeHostKeys($hostKeys);
140
+        }
141
+
142
+        $login = false;
143
+        foreach ($this->auth as $auth) {
144
+            $login = $this->client->login($this->user, $auth);
145
+            if ($login === true) {
146
+                break;
147
+            }
148
+        }
149
+
150
+        if ($login === false) {
151
+            throw new \Exception('Login failed');
152
+        }
153
+        return $this->client;
154
+    }
155
+
156
+    /**
157
+     * {@inheritdoc}
158
+     */
159
+    public function test() {
160
+        if (
161
+            !isset($this->host)
162
+            || !isset($this->user)
163
+        ) {
164
+            return false;
165
+        }
166
+        return $this->getConnection()->nlist() !== false;
167
+    }
168
+
169
+    /**
170
+     * {@inheritdoc}
171
+     */
172
+    public function getId() {
173
+        $id = 'sftp::' . $this->user . '@' . $this->host;
174
+        if ($this->port !== 22) {
175
+            $id .= ':' . $this->port;
176
+        }
177
+        // note: this will double the root slash,
178
+        // we should not change it to keep compatible with
179
+        // old storage ids
180
+        $id .= '/' . $this->root;
181
+        return $id;
182
+    }
183
+
184
+    /**
185
+     * @return string
186
+     */
187
+    public function getHost() {
188
+        return $this->host;
189
+    }
190
+
191
+    /**
192
+     * @return string
193
+     */
194
+    public function getRoot() {
195
+        return $this->root;
196
+    }
197
+
198
+    /**
199
+     * @return mixed
200
+     */
201
+    public function getUser() {
202
+        return $this->user;
203
+    }
204
+
205
+    /**
206
+     * @param string $path
207
+     * @return string
208
+     */
209
+    private function absPath($path) {
210
+        return $this->root . $this->cleanPath($path);
211
+    }
212
+
213
+    /**
214
+     * @return string|false
215
+     */
216
+    private function hostKeysPath() {
217
+        try {
218
+            $storage_view = \OCP\Files::getStorage('files_external');
219
+            if ($storage_view) {
220
+                return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
221
+                    $storage_view->getAbsolutePath('') .
222
+                    'ssh_hostKeys';
223
+            }
224
+        } catch (\Exception $e) {
225
+        }
226
+        return false;
227
+    }
228
+
229
+    /**
230
+     * @param $keys
231
+     * @return bool
232
+     */
233
+    protected function writeHostKeys($keys) {
234
+        try {
235
+            $keyPath = $this->hostKeysPath();
236
+            if ($keyPath && file_exists($keyPath)) {
237
+                $fp = fopen($keyPath, 'w');
238
+                foreach ($keys as $host => $key) {
239
+                    fwrite($fp, $host . '::' . $key . "\n");
240
+                }
241
+                fclose($fp);
242
+                return true;
243
+            }
244
+        } catch (\Exception $e) {
245
+        }
246
+        return false;
247
+    }
248
+
249
+    /**
250
+     * @return array
251
+     */
252
+    protected function readHostKeys() {
253
+        try {
254
+            $keyPath = $this->hostKeysPath();
255
+            if (file_exists($keyPath)) {
256
+                $hosts = [];
257
+                $keys = [];
258
+                $lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
259
+                if ($lines) {
260
+                    foreach ($lines as $line) {
261
+                        $hostKeyArray = explode("::", $line, 2);
262
+                        if (count($hostKeyArray) === 2) {
263
+                            $hosts[] = $hostKeyArray[0];
264
+                            $keys[] = $hostKeyArray[1];
265
+                        }
266
+                    }
267
+                    return array_combine($hosts, $keys);
268
+                }
269
+            }
270
+        } catch (\Exception $e) {
271
+        }
272
+        return [];
273
+    }
274
+
275
+    /**
276
+     * {@inheritdoc}
277
+     */
278
+    public function mkdir($path) {
279
+        try {
280
+            return $this->getConnection()->mkdir($this->absPath($path));
281
+        } catch (\Exception $e) {
282
+            return false;
283
+        }
284
+    }
285
+
286
+    /**
287
+     * {@inheritdoc}
288
+     */
289
+    public function rmdir($path) {
290
+        try {
291
+            $result = $this->getConnection()->delete($this->absPath($path), true);
292
+            // workaround: stray stat cache entry when deleting empty folders
293
+            // see https://github.com/phpseclib/phpseclib/issues/706
294
+            $this->getConnection()->clearStatCache();
295
+            return $result;
296
+        } catch (\Exception $e) {
297
+            return false;
298
+        }
299
+    }
300
+
301
+    /**
302
+     * {@inheritdoc}
303
+     */
304
+    public function opendir($path) {
305
+        try {
306
+            $list = $this->getConnection()->nlist($this->absPath($path));
307
+            if ($list === false) {
308
+                return false;
309
+            }
310
+
311
+            $id = md5('sftp:' . $path);
312
+            $dirStream = [];
313
+            foreach ($list as $file) {
314
+                if ($file !== '.' && $file !== '..') {
315
+                    $dirStream[] = $file;
316
+                }
317
+            }
318
+            return IteratorDirectory::wrap($dirStream);
319
+        } catch (\Exception $e) {
320
+            return false;
321
+        }
322
+    }
323
+
324
+    /**
325
+     * {@inheritdoc}
326
+     */
327
+    public function filetype($path) {
328
+        try {
329
+            $stat = $this->getConnection()->stat($this->absPath($path));
330
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
331
+                return 'file';
332
+            }
333
+
334
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
335
+                return 'dir';
336
+            }
337
+        } catch (\Exception $e) {
338
+        }
339
+        return false;
340
+    }
341
+
342
+    /**
343
+     * {@inheritdoc}
344
+     */
345
+    public function file_exists($path) {
346
+        try {
347
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
348
+        } catch (\Exception $e) {
349
+            return false;
350
+        }
351
+    }
352
+
353
+    /**
354
+     * {@inheritdoc}
355
+     */
356
+    public function unlink($path) {
357
+        try {
358
+            return $this->getConnection()->delete($this->absPath($path), true);
359
+        } catch (\Exception $e) {
360
+            return false;
361
+        }
362
+    }
363
+
364
+    /**
365
+     * {@inheritdoc}
366
+     */
367
+    public function fopen($path, $mode) {
368
+        try {
369
+            $absPath = $this->absPath($path);
370
+            switch ($mode) {
371
+                case 'r':
372
+                case 'rb':
373
+                    if (!$this->file_exists($path)) {
374
+                        return false;
375
+                    }
376
+                    SFTPReadStream::register();
377
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
378
+                    $handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
379
+                    return RetryWrapper::wrap($handle);
380
+                case 'w':
381
+                case 'wb':
382
+                    SFTPWriteStream::register();
383
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
384
+                    return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
385
+                case 'a':
386
+                case 'ab':
387
+                case 'r+':
388
+                case 'w+':
389
+                case 'wb+':
390
+                case 'a+':
391
+                case 'x':
392
+                case 'x+':
393
+                case 'c':
394
+                case 'c+':
395
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
396
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
397
+                    return RetryWrapper::wrap($handle);
398
+            }
399
+        } catch (\Exception $e) {
400
+        }
401
+        return false;
402
+    }
403
+
404
+    /**
405
+     * {@inheritdoc}
406
+     */
407
+    public function touch($path, $mtime=null) {
408
+        try {
409
+            if (!is_null($mtime)) {
410
+                return false;
411
+            }
412
+            if (!$this->file_exists($path)) {
413
+                $this->getConnection()->put($this->absPath($path), '');
414
+            } else {
415
+                return false;
416
+            }
417
+        } catch (\Exception $e) {
418
+            return false;
419
+        }
420
+        return true;
421
+    }
422
+
423
+    /**
424
+     * @param string $path
425
+     * @param string $target
426
+     * @throws \Exception
427
+     */
428
+    public function getFile($path, $target) {
429
+        $this->getConnection()->get($path, $target);
430
+    }
431
+
432
+    /**
433
+     * {@inheritdoc}
434
+     */
435
+    public function rename($source, $target) {
436
+        try {
437
+            if ($this->file_exists($target)) {
438
+                $this->unlink($target);
439
+            }
440
+            return $this->getConnection()->rename(
441
+                $this->absPath($source),
442
+                $this->absPath($target)
443
+            );
444
+        } catch (\Exception $e) {
445
+            return false;
446
+        }
447
+    }
448
+
449
+    /**
450
+     * {@inheritdoc}
451
+     */
452
+    public function stat($path) {
453
+        try {
454
+            $stat = $this->getConnection()->stat($this->absPath($path));
455
+
456
+            $mtime = $stat ? $stat['mtime'] : -1;
457
+            $size = $stat ? $stat['size'] : 0;
458
+
459
+            return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
460
+        } catch (\Exception $e) {
461
+            return false;
462
+        }
463
+    }
464
+
465
+    /**
466
+     * @param string $path
467
+     * @return string
468
+     */
469
+    public function constructUrl($path) {
470
+        // Do not pass the password here. We want to use the Net_SFTP object
471
+        // supplied via stream context or fail. We only supply username and
472
+        // hostname because this might show up in logs (they are not used).
473
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
474
+        return $url;
475
+    }
476 476
 }
Please login to merge, or discard this patch.