Passed
Push — master ( db1c2a...bcb1fc )
by John
15:24 queued 11s
created
apps/files_external/lib/Lib/Storage/SFTP.php 1 patch
Indentation   +431 added lines, -431 removed lines patch added patch discarded remove patch
@@ -45,435 +45,435 @@
 block discarded – undo
45 45
  * provide access to SFTP servers.
46 46
  */
47 47
 class SFTP extends \OC\Files\Storage\Common {
48
-	private $host;
49
-	private $user;
50
-	private $root;
51
-	private $port = 22;
52
-
53
-	private $auth = [];
54
-
55
-	/**
56
-	 * @var \phpseclib\Net\SFTP
57
-	 */
58
-	protected $client;
59
-
60
-	/**
61
-	 * @param string $host protocol://server:port
62
-	 * @return array [$server, $port]
63
-	 */
64
-	private function splitHost($host) {
65
-		$input = $host;
66
-		if (strpos($host, '://') === false) {
67
-			// add a protocol to fix parse_url behavior with ipv6
68
-			$host = 'http://' . $host;
69
-		}
70
-
71
-		$parsed = parse_url($host);
72
-		if (is_array($parsed) && isset($parsed['port'])) {
73
-			return [$parsed['host'], $parsed['port']];
74
-		} elseif (is_array($parsed)) {
75
-			return [$parsed['host'], 22];
76
-		} else {
77
-			return [$input, 22];
78
-		}
79
-	}
80
-
81
-	/**
82
-	 * {@inheritdoc}
83
-	 */
84
-	public function __construct($params) {
85
-		// Register sftp://
86
-		Stream::register();
87
-
88
-		$parsedHost = $this->splitHost($params['host']);
89
-
90
-		$this->host = $parsedHost[0];
91
-		$this->port = $parsedHost[1];
92
-
93
-		if (!isset($params['user'])) {
94
-			throw new \UnexpectedValueException('no authentication parameters specified');
95
-		}
96
-		$this->user = $params['user'];
97
-
98
-		if (isset($params['public_key_auth'])) {
99
-			$this->auth[] = $params['public_key_auth'];
100
-		}
101
-		if (isset($params['password']) && $params['password'] !== '') {
102
-			$this->auth[] = $params['password'];
103
-		}
104
-
105
-		if ($this->auth === []) {
106
-			throw new \UnexpectedValueException('no authentication parameters specified');
107
-		}
108
-
109
-		$this->root
110
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
111
-
112
-		$this->root = '/' . ltrim($this->root, '/');
113
-		$this->root = rtrim($this->root, '/') . '/';
114
-	}
115
-
116
-	/**
117
-	 * Returns the connection.
118
-	 *
119
-	 * @return \phpseclib\Net\SFTP connected client instance
120
-	 * @throws \Exception when the connection failed
121
-	 */
122
-	public function getConnection() {
123
-		if (!is_null($this->client)) {
124
-			return $this->client;
125
-		}
126
-
127
-		$hostKeys = $this->readHostKeys();
128
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
129
-
130
-		// The SSH Host Key MUST be verified before login().
131
-		$currentHostKey = $this->client->getServerPublicHostKey();
132
-		if (array_key_exists($this->host, $hostKeys)) {
133
-			if ($hostKeys[$this->host] !== $currentHostKey) {
134
-				throw new \Exception('Host public key does not match known key');
135
-			}
136
-		} else {
137
-			$hostKeys[$this->host] = $currentHostKey;
138
-			$this->writeHostKeys($hostKeys);
139
-		}
140
-
141
-		$login = false;
142
-		foreach ($this->auth as $auth) {
143
-			/** @psalm-suppress TooManyArguments */
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 (!is_array($stat) || !array_key_exists('type', $stat)) {
331
-				return false;
332
-			}
333
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
334
-				return 'file';
335
-			}
336
-
337
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
338
-				return 'dir';
339
-			}
340
-		} catch (\Exception $e) {
341
-		}
342
-		return false;
343
-	}
344
-
345
-	/**
346
-	 * {@inheritdoc}
347
-	 */
348
-	public function file_exists($path) {
349
-		try {
350
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
351
-		} catch (\Exception $e) {
352
-			return false;
353
-		}
354
-	}
355
-
356
-	/**
357
-	 * {@inheritdoc}
358
-	 */
359
-	public function unlink($path) {
360
-		try {
361
-			return $this->getConnection()->delete($this->absPath($path), true);
362
-		} catch (\Exception $e) {
363
-			return false;
364
-		}
365
-	}
366
-
367
-	/**
368
-	 * {@inheritdoc}
369
-	 */
370
-	public function fopen($path, $mode) {
371
-		try {
372
-			$absPath = $this->absPath($path);
373
-			switch ($mode) {
374
-				case 'r':
375
-				case 'rb':
376
-					if (!$this->file_exists($path)) {
377
-						return false;
378
-					}
379
-					SFTPReadStream::register();
380
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
381
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
382
-					return RetryWrapper::wrap($handle);
383
-				case 'w':
384
-				case 'wb':
385
-					SFTPWriteStream::register();
386
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
387
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
388
-				case 'a':
389
-				case 'ab':
390
-				case 'r+':
391
-				case 'w+':
392
-				case 'wb+':
393
-				case 'a+':
394
-				case 'x':
395
-				case 'x+':
396
-				case 'c':
397
-				case 'c+':
398
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
399
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
400
-					return RetryWrapper::wrap($handle);
401
-			}
402
-		} catch (\Exception $e) {
403
-		}
404
-		return false;
405
-	}
406
-
407
-	/**
408
-	 * {@inheritdoc}
409
-	 */
410
-	public function touch($path, $mtime = null) {
411
-		try {
412
-			if (!is_null($mtime)) {
413
-				return false;
414
-			}
415
-			if (!$this->file_exists($path)) {
416
-				$this->getConnection()->put($this->absPath($path), '');
417
-			} else {
418
-				return false;
419
-			}
420
-		} catch (\Exception $e) {
421
-			return false;
422
-		}
423
-		return true;
424
-	}
425
-
426
-	/**
427
-	 * @param string $path
428
-	 * @param string $target
429
-	 * @throws \Exception
430
-	 */
431
-	public function getFile($path, $target) {
432
-		$this->getConnection()->get($path, $target);
433
-	}
434
-
435
-	/**
436
-	 * {@inheritdoc}
437
-	 */
438
-	public function rename($source, $target) {
439
-		try {
440
-			if ($this->file_exists($target)) {
441
-				$this->unlink($target);
442
-			}
443
-			return $this->getConnection()->rename(
444
-				$this->absPath($source),
445
-				$this->absPath($target)
446
-			);
447
-		} catch (\Exception $e) {
448
-			return false;
449
-		}
450
-	}
451
-
452
-	/**
453
-	 * {@inheritdoc}
454
-	 */
455
-	public function stat($path) {
456
-		try {
457
-			$stat = $this->getConnection()->stat($this->absPath($path));
458
-
459
-			$mtime = $stat ? $stat['mtime'] : -1;
460
-			$size = $stat ? $stat['size'] : 0;
461
-
462
-			return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
463
-		} catch (\Exception $e) {
464
-			return false;
465
-		}
466
-	}
467
-
468
-	/**
469
-	 * @param string $path
470
-	 * @return string
471
-	 */
472
-	public function constructUrl($path) {
473
-		// Do not pass the password here. We want to use the Net_SFTP object
474
-		// supplied via stream context or fail. We only supply username and
475
-		// hostname because this might show up in logs (they are not used).
476
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
477
-		return $url;
478
-	}
48
+    private $host;
49
+    private $user;
50
+    private $root;
51
+    private $port = 22;
52
+
53
+    private $auth = [];
54
+
55
+    /**
56
+     * @var \phpseclib\Net\SFTP
57
+     */
58
+    protected $client;
59
+
60
+    /**
61
+     * @param string $host protocol://server:port
62
+     * @return array [$server, $port]
63
+     */
64
+    private function splitHost($host) {
65
+        $input = $host;
66
+        if (strpos($host, '://') === false) {
67
+            // add a protocol to fix parse_url behavior with ipv6
68
+            $host = 'http://' . $host;
69
+        }
70
+
71
+        $parsed = parse_url($host);
72
+        if (is_array($parsed) && isset($parsed['port'])) {
73
+            return [$parsed['host'], $parsed['port']];
74
+        } elseif (is_array($parsed)) {
75
+            return [$parsed['host'], 22];
76
+        } else {
77
+            return [$input, 22];
78
+        }
79
+    }
80
+
81
+    /**
82
+     * {@inheritdoc}
83
+     */
84
+    public function __construct($params) {
85
+        // Register sftp://
86
+        Stream::register();
87
+
88
+        $parsedHost = $this->splitHost($params['host']);
89
+
90
+        $this->host = $parsedHost[0];
91
+        $this->port = $parsedHost[1];
92
+
93
+        if (!isset($params['user'])) {
94
+            throw new \UnexpectedValueException('no authentication parameters specified');
95
+        }
96
+        $this->user = $params['user'];
97
+
98
+        if (isset($params['public_key_auth'])) {
99
+            $this->auth[] = $params['public_key_auth'];
100
+        }
101
+        if (isset($params['password']) && $params['password'] !== '') {
102
+            $this->auth[] = $params['password'];
103
+        }
104
+
105
+        if ($this->auth === []) {
106
+            throw new \UnexpectedValueException('no authentication parameters specified');
107
+        }
108
+
109
+        $this->root
110
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
111
+
112
+        $this->root = '/' . ltrim($this->root, '/');
113
+        $this->root = rtrim($this->root, '/') . '/';
114
+    }
115
+
116
+    /**
117
+     * Returns the connection.
118
+     *
119
+     * @return \phpseclib\Net\SFTP connected client instance
120
+     * @throws \Exception when the connection failed
121
+     */
122
+    public function getConnection() {
123
+        if (!is_null($this->client)) {
124
+            return $this->client;
125
+        }
126
+
127
+        $hostKeys = $this->readHostKeys();
128
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
129
+
130
+        // The SSH Host Key MUST be verified before login().
131
+        $currentHostKey = $this->client->getServerPublicHostKey();
132
+        if (array_key_exists($this->host, $hostKeys)) {
133
+            if ($hostKeys[$this->host] !== $currentHostKey) {
134
+                throw new \Exception('Host public key does not match known key');
135
+            }
136
+        } else {
137
+            $hostKeys[$this->host] = $currentHostKey;
138
+            $this->writeHostKeys($hostKeys);
139
+        }
140
+
141
+        $login = false;
142
+        foreach ($this->auth as $auth) {
143
+            /** @psalm-suppress TooManyArguments */
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 (!is_array($stat) || !array_key_exists('type', $stat)) {
331
+                return false;
332
+            }
333
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
334
+                return 'file';
335
+            }
336
+
337
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
338
+                return 'dir';
339
+            }
340
+        } catch (\Exception $e) {
341
+        }
342
+        return false;
343
+    }
344
+
345
+    /**
346
+     * {@inheritdoc}
347
+     */
348
+    public function file_exists($path) {
349
+        try {
350
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
351
+        } catch (\Exception $e) {
352
+            return false;
353
+        }
354
+    }
355
+
356
+    /**
357
+     * {@inheritdoc}
358
+     */
359
+    public function unlink($path) {
360
+        try {
361
+            return $this->getConnection()->delete($this->absPath($path), true);
362
+        } catch (\Exception $e) {
363
+            return false;
364
+        }
365
+    }
366
+
367
+    /**
368
+     * {@inheritdoc}
369
+     */
370
+    public function fopen($path, $mode) {
371
+        try {
372
+            $absPath = $this->absPath($path);
373
+            switch ($mode) {
374
+                case 'r':
375
+                case 'rb':
376
+                    if (!$this->file_exists($path)) {
377
+                        return false;
378
+                    }
379
+                    SFTPReadStream::register();
380
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
381
+                    $handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
382
+                    return RetryWrapper::wrap($handle);
383
+                case 'w':
384
+                case 'wb':
385
+                    SFTPWriteStream::register();
386
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
387
+                    return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
388
+                case 'a':
389
+                case 'ab':
390
+                case 'r+':
391
+                case 'w+':
392
+                case 'wb+':
393
+                case 'a+':
394
+                case 'x':
395
+                case 'x+':
396
+                case 'c':
397
+                case 'c+':
398
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
399
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
400
+                    return RetryWrapper::wrap($handle);
401
+            }
402
+        } catch (\Exception $e) {
403
+        }
404
+        return false;
405
+    }
406
+
407
+    /**
408
+     * {@inheritdoc}
409
+     */
410
+    public function touch($path, $mtime = null) {
411
+        try {
412
+            if (!is_null($mtime)) {
413
+                return false;
414
+            }
415
+            if (!$this->file_exists($path)) {
416
+                $this->getConnection()->put($this->absPath($path), '');
417
+            } else {
418
+                return false;
419
+            }
420
+        } catch (\Exception $e) {
421
+            return false;
422
+        }
423
+        return true;
424
+    }
425
+
426
+    /**
427
+     * @param string $path
428
+     * @param string $target
429
+     * @throws \Exception
430
+     */
431
+    public function getFile($path, $target) {
432
+        $this->getConnection()->get($path, $target);
433
+    }
434
+
435
+    /**
436
+     * {@inheritdoc}
437
+     */
438
+    public function rename($source, $target) {
439
+        try {
440
+            if ($this->file_exists($target)) {
441
+                $this->unlink($target);
442
+            }
443
+            return $this->getConnection()->rename(
444
+                $this->absPath($source),
445
+                $this->absPath($target)
446
+            );
447
+        } catch (\Exception $e) {
448
+            return false;
449
+        }
450
+    }
451
+
452
+    /**
453
+     * {@inheritdoc}
454
+     */
455
+    public function stat($path) {
456
+        try {
457
+            $stat = $this->getConnection()->stat($this->absPath($path));
458
+
459
+            $mtime = $stat ? $stat['mtime'] : -1;
460
+            $size = $stat ? $stat['size'] : 0;
461
+
462
+            return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
463
+        } catch (\Exception $e) {
464
+            return false;
465
+        }
466
+    }
467
+
468
+    /**
469
+     * @param string $path
470
+     * @return string
471
+     */
472
+    public function constructUrl($path) {
473
+        // Do not pass the password here. We want to use the Net_SFTP object
474
+        // supplied via stream context or fail. We only supply username and
475
+        // hostname because this might show up in logs (they are not used).
476
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
477
+        return $url;
478
+    }
479 479
 }
Please login to merge, or discard this patch.