Passed
Push — master ( 62403d...0c3e2f )
by Joas
14:50 queued 14s
created
apps/files_external/lib/Lib/Storage/FTP.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -38,120 +38,120 @@
 block discarded – undo
38 38
 use Icewind\Streams\RetryWrapper;
39 39
 
40 40
 class FTP extends StreamWrapper{
41
-	private $password;
42
-	private $user;
43
-	private $host;
44
-	private $secure;
45
-	private $root;
41
+    private $password;
42
+    private $user;
43
+    private $host;
44
+    private $secure;
45
+    private $root;
46 46
 
47
-	public function __construct($params) {
48
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
49
-			$this->host=$params['host'];
50
-			$this->user=$params['user'];
51
-			$this->password=$params['password'];
52
-			if (isset($params['secure'])) {
53
-				$this->secure = $params['secure'];
54
-			} else {
55
-				$this->secure = false;
56
-			}
57
-			$this->root=isset($params['root'])?$params['root']:'/';
58
-			if ( ! $this->root || $this->root[0]!=='/') {
59
-				$this->root='/'.$this->root;
60
-			}
61
-			if (substr($this->root, -1) !== '/') {
62
-				$this->root .= '/';
63
-			}
64
-		} else {
65
-			throw new \Exception('Creating FTP storage failed');
66
-		}
47
+    public function __construct($params) {
48
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
49
+            $this->host=$params['host'];
50
+            $this->user=$params['user'];
51
+            $this->password=$params['password'];
52
+            if (isset($params['secure'])) {
53
+                $this->secure = $params['secure'];
54
+            } else {
55
+                $this->secure = false;
56
+            }
57
+            $this->root=isset($params['root'])?$params['root']:'/';
58
+            if ( ! $this->root || $this->root[0]!=='/') {
59
+                $this->root='/'.$this->root;
60
+            }
61
+            if (substr($this->root, -1) !== '/') {
62
+                $this->root .= '/';
63
+            }
64
+        } else {
65
+            throw new \Exception('Creating FTP storage failed');
66
+        }
67 67
 		
68
-	}
68
+    }
69 69
 
70
-	public function getId(){
71
-		return 'ftp::' . $this->user . '@' . $this->host . '/' . $this->root;
72
-	}
70
+    public function getId(){
71
+        return 'ftp::' . $this->user . '@' . $this->host . '/' . $this->root;
72
+    }
73 73
 
74
-	/**
75
-	 * construct the ftp url
76
-	 * @param string $path
77
-	 * @return string
78
-	 */
79
-	public function constructUrl($path) {
80
-		$url='ftp';
81
-		if ($this->secure) {
82
-			$url.='s';
83
-		}
84
-		$url.='://'.urlencode($this->user).':'.urlencode($this->password).'@'.$this->host.$this->root.$path;
85
-		return $url;
86
-	}
74
+    /**
75
+     * construct the ftp url
76
+     * @param string $path
77
+     * @return string
78
+     */
79
+    public function constructUrl($path) {
80
+        $url='ftp';
81
+        if ($this->secure) {
82
+            $url.='s';
83
+        }
84
+        $url.='://'.urlencode($this->user).':'.urlencode($this->password).'@'.$this->host.$this->root.$path;
85
+        return $url;
86
+    }
87 87
 
88
-	/**
89
-	 * Unlinks file or directory
90
-	 * @param string $path
91
-	 */
92
-	public function unlink($path) {
93
-		if ($this->is_dir($path)) {
94
-			return $this->rmdir($path);
95
-		}
96
-		else {
97
-			$url = $this->constructUrl($path);
98
-			$result = unlink($url);
99
-			clearstatcache(true, $url);
100
-			return $result;
101
-		}
102
-	}
103
-	public function fopen($path,$mode) {
104
-		switch($mode) {
105
-			case 'r':
106
-			case 'rb':
107
-			case 'w':
108
-			case 'wb':
109
-			case 'a':
110
-			case 'ab':
111
-				//these are supported by the wrapper
112
-				$context = stream_context_create(['ftp' => ['overwrite' => true]]);
113
-				$handle = fopen($this->constructUrl($path), $mode, false, $context);
114
-				return RetryWrapper::wrap($handle);
115
-			case 'r+':
116
-			case 'w+':
117
-			case 'wb+':
118
-			case 'a+':
119
-			case 'x':
120
-			case 'x+':
121
-			case 'c':
122
-			case 'c+':
123
-				//emulate these
124
-				if (strrpos($path, '.')!==false) {
125
-					$ext=substr($path, strrpos($path, '.'));
126
-				} else {
127
-					$ext='';
128
-				}
129
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile();
130
-				if ($this->file_exists($path)) {
131
-					$this->getFile($path, $tmpFile);
132
-				}
133
-				$handle = fopen($tmpFile, $mode);
134
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
135
-					$this->writeBack($tmpFile, $path);
136
-				});
137
-		}
138
-		return false;
139
-	}
88
+    /**
89
+     * Unlinks file or directory
90
+     * @param string $path
91
+     */
92
+    public function unlink($path) {
93
+        if ($this->is_dir($path)) {
94
+            return $this->rmdir($path);
95
+        }
96
+        else {
97
+            $url = $this->constructUrl($path);
98
+            $result = unlink($url);
99
+            clearstatcache(true, $url);
100
+            return $result;
101
+        }
102
+    }
103
+    public function fopen($path,$mode) {
104
+        switch($mode) {
105
+            case 'r':
106
+            case 'rb':
107
+            case 'w':
108
+            case 'wb':
109
+            case 'a':
110
+            case 'ab':
111
+                //these are supported by the wrapper
112
+                $context = stream_context_create(['ftp' => ['overwrite' => true]]);
113
+                $handle = fopen($this->constructUrl($path), $mode, false, $context);
114
+                return RetryWrapper::wrap($handle);
115
+            case 'r+':
116
+            case 'w+':
117
+            case 'wb+':
118
+            case 'a+':
119
+            case 'x':
120
+            case 'x+':
121
+            case 'c':
122
+            case 'c+':
123
+                //emulate these
124
+                if (strrpos($path, '.')!==false) {
125
+                    $ext=substr($path, strrpos($path, '.'));
126
+                } else {
127
+                    $ext='';
128
+                }
129
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile();
130
+                if ($this->file_exists($path)) {
131
+                    $this->getFile($path, $tmpFile);
132
+                }
133
+                $handle = fopen($tmpFile, $mode);
134
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
135
+                    $this->writeBack($tmpFile, $path);
136
+                });
137
+        }
138
+        return false;
139
+    }
140 140
 
141
-	public function writeBack($tmpFile, $path) {
142
-		$this->uploadFile($tmpFile, $path);
143
-		unlink($tmpFile);
144
-	}
141
+    public function writeBack($tmpFile, $path) {
142
+        $this->uploadFile($tmpFile, $path);
143
+        unlink($tmpFile);
144
+    }
145 145
 
146
-	/**
147
-	 * check if php-ftp is installed
148
-	 */
149
-	public static function checkDependencies() {
150
-		if (function_exists('ftp_login')) {
151
-			return true;
152
-		} else {
153
-			return ['ftp'];
154
-		}
155
-	}
146
+    /**
147
+     * check if php-ftp is installed
148
+     */
149
+    public static function checkDependencies() {
150
+        if (function_exists('ftp_login')) {
151
+            return true;
152
+        } else {
153
+            return ['ftp'];
154
+        }
155
+    }
156 156
 
157 157
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SMB.php 1 patch
Indentation   +570 added lines, -570 removed lines patch added patch discarded remove patch
@@ -62,574 +62,574 @@
 block discarded – undo
62 62
 use OCP\ILogger;
63 63
 
64 64
 class SMB extends Common implements INotifyStorage {
65
-	/**
66
-	 * @var \Icewind\SMB\IServer
67
-	 */
68
-	protected $server;
69
-
70
-	/**
71
-	 * @var \Icewind\SMB\IShare
72
-	 */
73
-	protected $share;
74
-
75
-	/**
76
-	 * @var string
77
-	 */
78
-	protected $root;
79
-
80
-	/**
81
-	 * @var \Icewind\SMB\IFileInfo[]
82
-	 */
83
-	protected $statCache;
84
-
85
-	/** @var ILogger */
86
-	protected $logger;
87
-
88
-	/** @var bool */
89
-	protected $showHidden;
90
-
91
-	public function __construct($params) {
92
-		if (!isset($params['host'])) {
93
-			throw new \Exception('Invalid configuration, no host provided');
94
-		}
95
-
96
-		if (isset($params['auth'])) {
97
-			$auth = $params['auth'];
98
-		} else if (isset($params['user']) && isset($params['password']) && isset($params['share'])) {
99
-			list($workgroup, $user) = $this->splitUser($params['user']);
100
-			$auth = new BasicAuth($user, $workgroup, $params['password']);
101
-		} else {
102
-			throw new \Exception('Invalid configuration, no credentials provided');
103
-		}
104
-
105
-		if (isset($params['logger'])) {
106
-			$this->logger = $params['logger'];
107
-		} else {
108
-			$this->logger = \OC::$server->getLogger();
109
-		}
110
-
111
-		$options = new Options();
112
-		if (isset($params['timeout'])) {
113
-			$timeout = (int)$params['timeout'];
114
-			if ($timeout > 0) {
115
-				$options->setTimeout($timeout);
116
-			}
117
-		}
118
-		$serverFactory = new ServerFactory($options);
119
-		$this->server = $serverFactory->createServer($params['host'], $auth);
120
-		$this->share = $this->server->getShare(trim($params['share'], '/'));
121
-
122
-		$this->root = $params['root'] ?? '/';
123
-		$this->root = '/' . ltrim($this->root, '/');
124
-		$this->root = rtrim($this->root, '/') . '/';
125
-
126
-		$this->showHidden = isset($params['show_hidden']) && $params['show_hidden'];
127
-
128
-		$this->statCache = new CappedMemoryCache();
129
-		parent::__construct($params);
130
-	}
131
-
132
-	private function splitUser($user) {
133
-		if (strpos($user, '/')) {
134
-			return explode('/', $user, 2);
135
-		} elseif (strpos($user, '\\')) {
136
-			return explode('\\', $user);
137
-		} else {
138
-			return [null, $user];
139
-		}
140
-	}
141
-
142
-	/**
143
-	 * @return string
144
-	 */
145
-	public function getId() {
146
-		// FIXME: double slash to keep compatible with the old storage ids,
147
-		// failure to do so will lead to creation of a new storage id and
148
-		// loss of shares from the storage
149
-		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
150
-	}
151
-
152
-	/**
153
-	 * @param string $path
154
-	 * @return string
155
-	 */
156
-	protected function buildPath($path) {
157
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
158
-	}
159
-
160
-	protected function relativePath($fullPath) {
161
-		if ($fullPath === $this->root) {
162
-			return '';
163
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
164
-			return substr($fullPath, strlen($this->root));
165
-		} else {
166
-			return null;
167
-		}
168
-	}
169
-
170
-	/**
171
-	 * @param string $path
172
-	 * @return \Icewind\SMB\IFileInfo
173
-	 * @throws StorageAuthException
174
-	 */
175
-	protected function getFileInfo($path) {
176
-		try {
177
-			$path = $this->buildPath($path);
178
-			if (!isset($this->statCache[$path])) {
179
-				$this->statCache[$path] = $this->share->stat($path);
180
-			}
181
-			return $this->statCache[$path];
182
-		} catch (ConnectException $e) {
183
-			$this->throwUnavailable($e);
184
-		} catch (ForbiddenException $e) {
185
-			// with php-smbclient, this exceptions is thrown when the provided password is invalid.
186
-			// Possible is also ForbiddenException with a different error code, so we check it.
187
-			if($e->getCode() === 1) {
188
-				$this->throwUnavailable($e);
189
-			}
190
-			throw $e;
191
-		}
192
-	}
193
-
194
-	/**
195
-	 * @param \Exception $e
196
-	 * @throws StorageAuthException
197
-	 */
198
-	protected function throwUnavailable(\Exception $e) {
199
-		$this->logger->logException($e, ['message' => 'Error while getting file info']);
200
-		throw new StorageAuthException($e->getMessage(), $e);
201
-	}
202
-
203
-	/**
204
-	 * @param string $path
205
-	 * @return \Icewind\SMB\IFileInfo[]
206
-	 * @throws StorageNotAvailableException
207
-	 */
208
-	protected function getFolderContents($path) {
209
-		try {
210
-			$path = $this->buildPath($path);
211
-			$files = $this->share->dir($path);
212
-			foreach ($files as $file) {
213
-				$this->statCache[$path . '/' . $file->getName()] = $file;
214
-			}
215
-			return array_filter($files, function (IFileInfo $file) {
216
-				try {
217
-					// the isHidden check is done before checking the config boolean to ensure that the metadata is always fetch
218
-					// so we trigger the below exceptions where applicable
219
-					$hide = $file->isHidden() && !$this->showHidden;
220
-					if ($hide) {
221
-						$this->logger->debug('hiding hidden file ' . $file->getName());
222
-					}
223
-					return !$hide;
224
-				} catch (ForbiddenException $e) {
225
-					$this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding forbidden entry ' . $file->getName()]);
226
-					return false;
227
-				} catch (NotFoundException $e) {
228
-					$this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding not found entry ' . $file->getName()]);
229
-					return false;
230
-				}
231
-			});
232
-		} catch (ConnectException $e) {
233
-			$this->logger->logException($e, ['message' => 'Error while getting folder content']);
234
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
235
-		}
236
-	}
237
-
238
-	/**
239
-	 * @param \Icewind\SMB\IFileInfo $info
240
-	 * @return array
241
-	 */
242
-	protected function formatInfo($info) {
243
-		$result = [
244
-			'size' => $info->getSize(),
245
-			'mtime' => $info->getMTime(),
246
-		];
247
-		if ($info->isDirectory()) {
248
-			$result['type'] = 'dir';
249
-		} else {
250
-			$result['type'] = 'file';
251
-		}
252
-		return $result;
253
-	}
254
-
255
-	/**
256
-	 * Rename the files. If the source or the target is the root, the rename won't happen.
257
-	 *
258
-	 * @param string $source the old name of the path
259
-	 * @param string $target the new name of the path
260
-	 * @return bool true if the rename is successful, false otherwise
261
-	 */
262
-	public function rename($source, $target, $retry = true) {
263
-		if ($this->isRootDir($source) || $this->isRootDir($target)) {
264
-			return false;
265
-		}
266
-
267
-		$absoluteSource = $this->buildPath($source);
268
-		$absoluteTarget = $this->buildPath($target);
269
-		try {
270
-			$result = $this->share->rename($absoluteSource, $absoluteTarget);
271
-		} catch (AlreadyExistsException $e) {
272
-			if ($retry) {
273
-				$this->remove($target);
274
-				$result = $this->share->rename($absoluteSource, $absoluteTarget, false);
275
-			} else {
276
-				$this->logger->logException($e, ['level' => ILogger::WARN]);
277
-				return false;
278
-			}
279
-		} catch (InvalidArgumentException $e) {
280
-			if ($retry) {
281
-				$this->remove($target);
282
-				$result = $this->share->rename($absoluteSource, $absoluteTarget, false);
283
-			} else {
284
-				$this->logger->logException($e, ['level' => ILogger::WARN]);
285
-				return false;
286
-			}
287
-		} catch (\Exception $e) {
288
-			$this->logger->logException($e, ['level' => ILogger::WARN]);
289
-			return false;
290
-		}
291
-		unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
292
-		return $result;
293
-	}
294
-
295
-	public function stat($path, $retry = true) {
296
-		try {
297
-			$result = $this->formatInfo($this->getFileInfo($path));
298
-		} catch (ForbiddenException $e) {
299
-			return false;
300
-		} catch (NotFoundException $e) {
301
-			return false;
302
-		} catch (TimedOutException $e) {
303
-			if ($retry) {
304
-				return $this->stat($path, false);
305
-			} else {
306
-				throw $e;
307
-			}
308
-		}
309
-		if ($this->remoteIsShare() && $this->isRootDir($path)) {
310
-			$result['mtime'] = $this->shareMTime();
311
-		}
312
-		return $result;
313
-	}
314
-
315
-	/**
316
-	 * get the best guess for the modification time of the share
317
-	 *
318
-	 * @return int
319
-	 */
320
-	private function shareMTime() {
321
-		$highestMTime = 0;
322
-		$files = $this->share->dir($this->root);
323
-		foreach ($files as $fileInfo) {
324
-			try {
325
-				if ($fileInfo->getMTime() > $highestMTime) {
326
-					$highestMTime = $fileInfo->getMTime();
327
-				}
328
-			} catch (NotFoundException $e) {
329
-				// Ignore this, can happen on unavailable DFS shares
330
-			} catch (ForbiddenException $e) {
331
-				// Ignore this too - it's a symlink
332
-			}
333
-		}
334
-		return $highestMTime;
335
-	}
336
-
337
-	/**
338
-	 * Check if the path is our root dir (not the smb one)
339
-	 *
340
-	 * @param string $path the path
341
-	 * @return bool
342
-	 */
343
-	private function isRootDir($path) {
344
-		return $path === '' || $path === '/' || $path === '.';
345
-	}
346
-
347
-	/**
348
-	 * Check if our root points to a smb share
349
-	 *
350
-	 * @return bool true if our root points to a share false otherwise
351
-	 */
352
-	private function remoteIsShare() {
353
-		return $this->share->getName() && (!$this->root || $this->root === '/');
354
-	}
355
-
356
-	/**
357
-	 * @param string $path
358
-	 * @return bool
359
-	 */
360
-	public function unlink($path) {
361
-		if ($this->isRootDir($path)) {
362
-			return false;
363
-		}
364
-
365
-		try {
366
-			if ($this->is_dir($path)) {
367
-				return $this->rmdir($path);
368
-			} else {
369
-				$path = $this->buildPath($path);
370
-				unset($this->statCache[$path]);
371
-				$this->share->del($path);
372
-				return true;
373
-			}
374
-		} catch (NotFoundException $e) {
375
-			return false;
376
-		} catch (ForbiddenException $e) {
377
-			return false;
378
-		} catch (ConnectException $e) {
379
-			$this->logger->logException($e, ['message' => 'Error while deleting file']);
380
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
381
-		}
382
-	}
383
-
384
-	/**
385
-	 * check if a file or folder has been updated since $time
386
-	 *
387
-	 * @param string $path
388
-	 * @param int $time
389
-	 * @return bool
390
-	 */
391
-	public function hasUpdated($path, $time) {
392
-		if (!$path and $this->root === '/') {
393
-			// mtime doesn't work for shares, but giving the nature of the backend,
394
-			// doing a full update is still just fast enough
395
-			return true;
396
-		} else {
397
-			$actualTime = $this->filemtime($path);
398
-			return $actualTime > $time;
399
-		}
400
-	}
401
-
402
-	/**
403
-	 * @param string $path
404
-	 * @param string $mode
405
-	 * @return resource|false
406
-	 */
407
-	public function fopen($path, $mode) {
408
-		$fullPath = $this->buildPath($path);
409
-		try {
410
-			switch ($mode) {
411
-				case 'r':
412
-				case 'rb':
413
-					if (!$this->file_exists($path)) {
414
-						return false;
415
-					}
416
-					return $this->share->read($fullPath);
417
-				case 'w':
418
-				case 'wb':
419
-					$source = $this->share->write($fullPath);
420
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
421
-						unset($this->statCache[$fullPath]);
422
-					});
423
-				case 'a':
424
-				case 'ab':
425
-				case 'r+':
426
-				case 'w+':
427
-				case 'wb+':
428
-				case 'a+':
429
-				case 'x':
430
-				case 'x+':
431
-				case 'c':
432
-				case 'c+':
433
-					//emulate these
434
-					if (strrpos($path, '.') !== false) {
435
-						$ext = substr($path, strrpos($path, '.'));
436
-					} else {
437
-						$ext = '';
438
-					}
439
-					if ($this->file_exists($path)) {
440
-						if (!$this->isUpdatable($path)) {
441
-							return false;
442
-						}
443
-						$tmpFile = $this->getCachedFile($path);
444
-					} else {
445
-						if (!$this->isCreatable(dirname($path))) {
446
-							return false;
447
-						}
448
-						$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
449
-					}
450
-					$source = fopen($tmpFile, $mode);
451
-					$share = $this->share;
452
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
453
-						unset($this->statCache[$fullPath]);
454
-						$share->put($tmpFile, $fullPath);
455
-						unlink($tmpFile);
456
-					});
457
-			}
458
-			return false;
459
-		} catch (NotFoundException $e) {
460
-			return false;
461
-		} catch (ForbiddenException $e) {
462
-			return false;
463
-		} catch (ConnectException $e) {
464
-			$this->logger->logException($e, ['message' => 'Error while opening file']);
465
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
466
-		}
467
-	}
468
-
469
-	public function rmdir($path) {
470
-		if ($this->isRootDir($path)) {
471
-			return false;
472
-		}
473
-
474
-		try {
475
-			$this->statCache = [];
476
-			$content = $this->share->dir($this->buildPath($path));
477
-			foreach ($content as $file) {
478
-				if ($file->isDirectory()) {
479
-					$this->rmdir($path . '/' . $file->getName());
480
-				} else {
481
-					$this->share->del($file->getPath());
482
-				}
483
-			}
484
-			$this->share->rmdir($this->buildPath($path));
485
-			return true;
486
-		} catch (NotFoundException $e) {
487
-			return false;
488
-		} catch (ForbiddenException $e) {
489
-			return false;
490
-		} catch (ConnectException $e) {
491
-			$this->logger->logException($e, ['message' => 'Error while removing folder']);
492
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
493
-		}
494
-	}
495
-
496
-	public function touch($path, $time = null) {
497
-		try {
498
-			if (!$this->file_exists($path)) {
499
-				$fh = $this->share->write($this->buildPath($path));
500
-				fclose($fh);
501
-				return true;
502
-			}
503
-			return false;
504
-		} catch (ConnectException $e) {
505
-			$this->logger->logException($e, ['message' => 'Error while creating file']);
506
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
507
-		}
508
-	}
509
-
510
-	public function opendir($path) {
511
-		try {
512
-			$files = $this->getFolderContents($path);
513
-		} catch (NotFoundException $e) {
514
-			return false;
515
-		} catch (ForbiddenException $e) {
516
-			return false;
517
-		}
518
-		$names = array_map(function ($info) {
519
-			/** @var \Icewind\SMB\IFileInfo $info */
520
-			return $info->getName();
521
-		}, $files);
522
-		return IteratorDirectory::wrap($names);
523
-	}
524
-
525
-	public function filetype($path) {
526
-		try {
527
-			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
528
-		} catch (NotFoundException $e) {
529
-			return false;
530
-		} catch (ForbiddenException $e) {
531
-			return false;
532
-		}
533
-	}
534
-
535
-	public function mkdir($path) {
536
-		$path = $this->buildPath($path);
537
-		try {
538
-			$this->share->mkdir($path);
539
-			return true;
540
-		} catch (ConnectException $e) {
541
-			$this->logger->logException($e, ['message' => 'Error while creating folder']);
542
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
543
-		} catch (Exception $e) {
544
-			return false;
545
-		}
546
-	}
547
-
548
-	public function file_exists($path) {
549
-		try {
550
-			$this->getFileInfo($path);
551
-			return true;
552
-		} catch (NotFoundException $e) {
553
-			return false;
554
-		} catch (ForbiddenException $e) {
555
-			return false;
556
-		} catch (ConnectException $e) {
557
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
558
-		}
559
-	}
560
-
561
-	public function isReadable($path) {
562
-		try {
563
-			$info = $this->getFileInfo($path);
564
-			return $this->showHidden || !$info->isHidden();
565
-		} catch (NotFoundException $e) {
566
-			return false;
567
-		} catch (ForbiddenException $e) {
568
-			return false;
569
-		}
570
-	}
571
-
572
-	public function isUpdatable($path) {
573
-		try {
574
-			$info = $this->getFileInfo($path);
575
-			// following windows behaviour for read-only folders: they can be written into
576
-			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
577
-			return ($this->showHidden || !$info->isHidden()) && (!$info->isReadOnly() || $this->is_dir($path));
578
-		} catch (NotFoundException $e) {
579
-			return false;
580
-		} catch (ForbiddenException $e) {
581
-			return false;
582
-		}
583
-	}
584
-
585
-	public function isDeletable($path) {
586
-		try {
587
-			$info = $this->getFileInfo($path);
588
-			return ($this->showHidden || !$info->isHidden()) && !$info->isReadOnly();
589
-		} catch (NotFoundException $e) {
590
-			return false;
591
-		} catch (ForbiddenException $e) {
592
-			return false;
593
-		}
594
-	}
595
-
596
-	/**
597
-	 * check if smbclient is installed
598
-	 */
599
-	public static function checkDependencies() {
600
-		return (
601
-			(bool)\OC_Helper::findBinaryPath('smbclient')
602
-			|| NativeServer::available(new System())
603
-		) ? true : ['smbclient'];
604
-	}
605
-
606
-	/**
607
-	 * Test a storage for availability
608
-	 *
609
-	 * @return bool
610
-	 */
611
-	public function test() {
612
-		try {
613
-			return parent::test();
614
-		} catch (Exception $e) {
615
-			$this->logger->logException($e);
616
-			return false;
617
-		}
618
-	}
619
-
620
-	public function listen($path, callable $callback) {
621
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
622
-			if ($change instanceof IRenameChange) {
623
-				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
624
-			} else {
625
-				return $callback($change->getType(), $change->getPath());
626
-			}
627
-		});
628
-	}
629
-
630
-	public function notify($path) {
631
-		$path = '/' . ltrim($path, '/');
632
-		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
633
-		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
634
-	}
65
+    /**
66
+     * @var \Icewind\SMB\IServer
67
+     */
68
+    protected $server;
69
+
70
+    /**
71
+     * @var \Icewind\SMB\IShare
72
+     */
73
+    protected $share;
74
+
75
+    /**
76
+     * @var string
77
+     */
78
+    protected $root;
79
+
80
+    /**
81
+     * @var \Icewind\SMB\IFileInfo[]
82
+     */
83
+    protected $statCache;
84
+
85
+    /** @var ILogger */
86
+    protected $logger;
87
+
88
+    /** @var bool */
89
+    protected $showHidden;
90
+
91
+    public function __construct($params) {
92
+        if (!isset($params['host'])) {
93
+            throw new \Exception('Invalid configuration, no host provided');
94
+        }
95
+
96
+        if (isset($params['auth'])) {
97
+            $auth = $params['auth'];
98
+        } else if (isset($params['user']) && isset($params['password']) && isset($params['share'])) {
99
+            list($workgroup, $user) = $this->splitUser($params['user']);
100
+            $auth = new BasicAuth($user, $workgroup, $params['password']);
101
+        } else {
102
+            throw new \Exception('Invalid configuration, no credentials provided');
103
+        }
104
+
105
+        if (isset($params['logger'])) {
106
+            $this->logger = $params['logger'];
107
+        } else {
108
+            $this->logger = \OC::$server->getLogger();
109
+        }
110
+
111
+        $options = new Options();
112
+        if (isset($params['timeout'])) {
113
+            $timeout = (int)$params['timeout'];
114
+            if ($timeout > 0) {
115
+                $options->setTimeout($timeout);
116
+            }
117
+        }
118
+        $serverFactory = new ServerFactory($options);
119
+        $this->server = $serverFactory->createServer($params['host'], $auth);
120
+        $this->share = $this->server->getShare(trim($params['share'], '/'));
121
+
122
+        $this->root = $params['root'] ?? '/';
123
+        $this->root = '/' . ltrim($this->root, '/');
124
+        $this->root = rtrim($this->root, '/') . '/';
125
+
126
+        $this->showHidden = isset($params['show_hidden']) && $params['show_hidden'];
127
+
128
+        $this->statCache = new CappedMemoryCache();
129
+        parent::__construct($params);
130
+    }
131
+
132
+    private function splitUser($user) {
133
+        if (strpos($user, '/')) {
134
+            return explode('/', $user, 2);
135
+        } elseif (strpos($user, '\\')) {
136
+            return explode('\\', $user);
137
+        } else {
138
+            return [null, $user];
139
+        }
140
+    }
141
+
142
+    /**
143
+     * @return string
144
+     */
145
+    public function getId() {
146
+        // FIXME: double slash to keep compatible with the old storage ids,
147
+        // failure to do so will lead to creation of a new storage id and
148
+        // loss of shares from the storage
149
+        return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
150
+    }
151
+
152
+    /**
153
+     * @param string $path
154
+     * @return string
155
+     */
156
+    protected function buildPath($path) {
157
+        return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
158
+    }
159
+
160
+    protected function relativePath($fullPath) {
161
+        if ($fullPath === $this->root) {
162
+            return '';
163
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
164
+            return substr($fullPath, strlen($this->root));
165
+        } else {
166
+            return null;
167
+        }
168
+    }
169
+
170
+    /**
171
+     * @param string $path
172
+     * @return \Icewind\SMB\IFileInfo
173
+     * @throws StorageAuthException
174
+     */
175
+    protected function getFileInfo($path) {
176
+        try {
177
+            $path = $this->buildPath($path);
178
+            if (!isset($this->statCache[$path])) {
179
+                $this->statCache[$path] = $this->share->stat($path);
180
+            }
181
+            return $this->statCache[$path];
182
+        } catch (ConnectException $e) {
183
+            $this->throwUnavailable($e);
184
+        } catch (ForbiddenException $e) {
185
+            // with php-smbclient, this exceptions is thrown when the provided password is invalid.
186
+            // Possible is also ForbiddenException with a different error code, so we check it.
187
+            if($e->getCode() === 1) {
188
+                $this->throwUnavailable($e);
189
+            }
190
+            throw $e;
191
+        }
192
+    }
193
+
194
+    /**
195
+     * @param \Exception $e
196
+     * @throws StorageAuthException
197
+     */
198
+    protected function throwUnavailable(\Exception $e) {
199
+        $this->logger->logException($e, ['message' => 'Error while getting file info']);
200
+        throw new StorageAuthException($e->getMessage(), $e);
201
+    }
202
+
203
+    /**
204
+     * @param string $path
205
+     * @return \Icewind\SMB\IFileInfo[]
206
+     * @throws StorageNotAvailableException
207
+     */
208
+    protected function getFolderContents($path) {
209
+        try {
210
+            $path = $this->buildPath($path);
211
+            $files = $this->share->dir($path);
212
+            foreach ($files as $file) {
213
+                $this->statCache[$path . '/' . $file->getName()] = $file;
214
+            }
215
+            return array_filter($files, function (IFileInfo $file) {
216
+                try {
217
+                    // the isHidden check is done before checking the config boolean to ensure that the metadata is always fetch
218
+                    // so we trigger the below exceptions where applicable
219
+                    $hide = $file->isHidden() && !$this->showHidden;
220
+                    if ($hide) {
221
+                        $this->logger->debug('hiding hidden file ' . $file->getName());
222
+                    }
223
+                    return !$hide;
224
+                } catch (ForbiddenException $e) {
225
+                    $this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding forbidden entry ' . $file->getName()]);
226
+                    return false;
227
+                } catch (NotFoundException $e) {
228
+                    $this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding not found entry ' . $file->getName()]);
229
+                    return false;
230
+                }
231
+            });
232
+        } catch (ConnectException $e) {
233
+            $this->logger->logException($e, ['message' => 'Error while getting folder content']);
234
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
235
+        }
236
+    }
237
+
238
+    /**
239
+     * @param \Icewind\SMB\IFileInfo $info
240
+     * @return array
241
+     */
242
+    protected function formatInfo($info) {
243
+        $result = [
244
+            'size' => $info->getSize(),
245
+            'mtime' => $info->getMTime(),
246
+        ];
247
+        if ($info->isDirectory()) {
248
+            $result['type'] = 'dir';
249
+        } else {
250
+            $result['type'] = 'file';
251
+        }
252
+        return $result;
253
+    }
254
+
255
+    /**
256
+     * Rename the files. If the source or the target is the root, the rename won't happen.
257
+     *
258
+     * @param string $source the old name of the path
259
+     * @param string $target the new name of the path
260
+     * @return bool true if the rename is successful, false otherwise
261
+     */
262
+    public function rename($source, $target, $retry = true) {
263
+        if ($this->isRootDir($source) || $this->isRootDir($target)) {
264
+            return false;
265
+        }
266
+
267
+        $absoluteSource = $this->buildPath($source);
268
+        $absoluteTarget = $this->buildPath($target);
269
+        try {
270
+            $result = $this->share->rename($absoluteSource, $absoluteTarget);
271
+        } catch (AlreadyExistsException $e) {
272
+            if ($retry) {
273
+                $this->remove($target);
274
+                $result = $this->share->rename($absoluteSource, $absoluteTarget, false);
275
+            } else {
276
+                $this->logger->logException($e, ['level' => ILogger::WARN]);
277
+                return false;
278
+            }
279
+        } catch (InvalidArgumentException $e) {
280
+            if ($retry) {
281
+                $this->remove($target);
282
+                $result = $this->share->rename($absoluteSource, $absoluteTarget, false);
283
+            } else {
284
+                $this->logger->logException($e, ['level' => ILogger::WARN]);
285
+                return false;
286
+            }
287
+        } catch (\Exception $e) {
288
+            $this->logger->logException($e, ['level' => ILogger::WARN]);
289
+            return false;
290
+        }
291
+        unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
292
+        return $result;
293
+    }
294
+
295
+    public function stat($path, $retry = true) {
296
+        try {
297
+            $result = $this->formatInfo($this->getFileInfo($path));
298
+        } catch (ForbiddenException $e) {
299
+            return false;
300
+        } catch (NotFoundException $e) {
301
+            return false;
302
+        } catch (TimedOutException $e) {
303
+            if ($retry) {
304
+                return $this->stat($path, false);
305
+            } else {
306
+                throw $e;
307
+            }
308
+        }
309
+        if ($this->remoteIsShare() && $this->isRootDir($path)) {
310
+            $result['mtime'] = $this->shareMTime();
311
+        }
312
+        return $result;
313
+    }
314
+
315
+    /**
316
+     * get the best guess for the modification time of the share
317
+     *
318
+     * @return int
319
+     */
320
+    private function shareMTime() {
321
+        $highestMTime = 0;
322
+        $files = $this->share->dir($this->root);
323
+        foreach ($files as $fileInfo) {
324
+            try {
325
+                if ($fileInfo->getMTime() > $highestMTime) {
326
+                    $highestMTime = $fileInfo->getMTime();
327
+                }
328
+            } catch (NotFoundException $e) {
329
+                // Ignore this, can happen on unavailable DFS shares
330
+            } catch (ForbiddenException $e) {
331
+                // Ignore this too - it's a symlink
332
+            }
333
+        }
334
+        return $highestMTime;
335
+    }
336
+
337
+    /**
338
+     * Check if the path is our root dir (not the smb one)
339
+     *
340
+     * @param string $path the path
341
+     * @return bool
342
+     */
343
+    private function isRootDir($path) {
344
+        return $path === '' || $path === '/' || $path === '.';
345
+    }
346
+
347
+    /**
348
+     * Check if our root points to a smb share
349
+     *
350
+     * @return bool true if our root points to a share false otherwise
351
+     */
352
+    private function remoteIsShare() {
353
+        return $this->share->getName() && (!$this->root || $this->root === '/');
354
+    }
355
+
356
+    /**
357
+     * @param string $path
358
+     * @return bool
359
+     */
360
+    public function unlink($path) {
361
+        if ($this->isRootDir($path)) {
362
+            return false;
363
+        }
364
+
365
+        try {
366
+            if ($this->is_dir($path)) {
367
+                return $this->rmdir($path);
368
+            } else {
369
+                $path = $this->buildPath($path);
370
+                unset($this->statCache[$path]);
371
+                $this->share->del($path);
372
+                return true;
373
+            }
374
+        } catch (NotFoundException $e) {
375
+            return false;
376
+        } catch (ForbiddenException $e) {
377
+            return false;
378
+        } catch (ConnectException $e) {
379
+            $this->logger->logException($e, ['message' => 'Error while deleting file']);
380
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
381
+        }
382
+    }
383
+
384
+    /**
385
+     * check if a file or folder has been updated since $time
386
+     *
387
+     * @param string $path
388
+     * @param int $time
389
+     * @return bool
390
+     */
391
+    public function hasUpdated($path, $time) {
392
+        if (!$path and $this->root === '/') {
393
+            // mtime doesn't work for shares, but giving the nature of the backend,
394
+            // doing a full update is still just fast enough
395
+            return true;
396
+        } else {
397
+            $actualTime = $this->filemtime($path);
398
+            return $actualTime > $time;
399
+        }
400
+    }
401
+
402
+    /**
403
+     * @param string $path
404
+     * @param string $mode
405
+     * @return resource|false
406
+     */
407
+    public function fopen($path, $mode) {
408
+        $fullPath = $this->buildPath($path);
409
+        try {
410
+            switch ($mode) {
411
+                case 'r':
412
+                case 'rb':
413
+                    if (!$this->file_exists($path)) {
414
+                        return false;
415
+                    }
416
+                    return $this->share->read($fullPath);
417
+                case 'w':
418
+                case 'wb':
419
+                    $source = $this->share->write($fullPath);
420
+                    return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
421
+                        unset($this->statCache[$fullPath]);
422
+                    });
423
+                case 'a':
424
+                case 'ab':
425
+                case 'r+':
426
+                case 'w+':
427
+                case 'wb+':
428
+                case 'a+':
429
+                case 'x':
430
+                case 'x+':
431
+                case 'c':
432
+                case 'c+':
433
+                    //emulate these
434
+                    if (strrpos($path, '.') !== false) {
435
+                        $ext = substr($path, strrpos($path, '.'));
436
+                    } else {
437
+                        $ext = '';
438
+                    }
439
+                    if ($this->file_exists($path)) {
440
+                        if (!$this->isUpdatable($path)) {
441
+                            return false;
442
+                        }
443
+                        $tmpFile = $this->getCachedFile($path);
444
+                    } else {
445
+                        if (!$this->isCreatable(dirname($path))) {
446
+                            return false;
447
+                        }
448
+                        $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
449
+                    }
450
+                    $source = fopen($tmpFile, $mode);
451
+                    $share = $this->share;
452
+                    return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
453
+                        unset($this->statCache[$fullPath]);
454
+                        $share->put($tmpFile, $fullPath);
455
+                        unlink($tmpFile);
456
+                    });
457
+            }
458
+            return false;
459
+        } catch (NotFoundException $e) {
460
+            return false;
461
+        } catch (ForbiddenException $e) {
462
+            return false;
463
+        } catch (ConnectException $e) {
464
+            $this->logger->logException($e, ['message' => 'Error while opening file']);
465
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
466
+        }
467
+    }
468
+
469
+    public function rmdir($path) {
470
+        if ($this->isRootDir($path)) {
471
+            return false;
472
+        }
473
+
474
+        try {
475
+            $this->statCache = [];
476
+            $content = $this->share->dir($this->buildPath($path));
477
+            foreach ($content as $file) {
478
+                if ($file->isDirectory()) {
479
+                    $this->rmdir($path . '/' . $file->getName());
480
+                } else {
481
+                    $this->share->del($file->getPath());
482
+                }
483
+            }
484
+            $this->share->rmdir($this->buildPath($path));
485
+            return true;
486
+        } catch (NotFoundException $e) {
487
+            return false;
488
+        } catch (ForbiddenException $e) {
489
+            return false;
490
+        } catch (ConnectException $e) {
491
+            $this->logger->logException($e, ['message' => 'Error while removing folder']);
492
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
493
+        }
494
+    }
495
+
496
+    public function touch($path, $time = null) {
497
+        try {
498
+            if (!$this->file_exists($path)) {
499
+                $fh = $this->share->write($this->buildPath($path));
500
+                fclose($fh);
501
+                return true;
502
+            }
503
+            return false;
504
+        } catch (ConnectException $e) {
505
+            $this->logger->logException($e, ['message' => 'Error while creating file']);
506
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
507
+        }
508
+    }
509
+
510
+    public function opendir($path) {
511
+        try {
512
+            $files = $this->getFolderContents($path);
513
+        } catch (NotFoundException $e) {
514
+            return false;
515
+        } catch (ForbiddenException $e) {
516
+            return false;
517
+        }
518
+        $names = array_map(function ($info) {
519
+            /** @var \Icewind\SMB\IFileInfo $info */
520
+            return $info->getName();
521
+        }, $files);
522
+        return IteratorDirectory::wrap($names);
523
+    }
524
+
525
+    public function filetype($path) {
526
+        try {
527
+            return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
528
+        } catch (NotFoundException $e) {
529
+            return false;
530
+        } catch (ForbiddenException $e) {
531
+            return false;
532
+        }
533
+    }
534
+
535
+    public function mkdir($path) {
536
+        $path = $this->buildPath($path);
537
+        try {
538
+            $this->share->mkdir($path);
539
+            return true;
540
+        } catch (ConnectException $e) {
541
+            $this->logger->logException($e, ['message' => 'Error while creating folder']);
542
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
543
+        } catch (Exception $e) {
544
+            return false;
545
+        }
546
+    }
547
+
548
+    public function file_exists($path) {
549
+        try {
550
+            $this->getFileInfo($path);
551
+            return true;
552
+        } catch (NotFoundException $e) {
553
+            return false;
554
+        } catch (ForbiddenException $e) {
555
+            return false;
556
+        } catch (ConnectException $e) {
557
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
558
+        }
559
+    }
560
+
561
+    public function isReadable($path) {
562
+        try {
563
+            $info = $this->getFileInfo($path);
564
+            return $this->showHidden || !$info->isHidden();
565
+        } catch (NotFoundException $e) {
566
+            return false;
567
+        } catch (ForbiddenException $e) {
568
+            return false;
569
+        }
570
+    }
571
+
572
+    public function isUpdatable($path) {
573
+        try {
574
+            $info = $this->getFileInfo($path);
575
+            // following windows behaviour for read-only folders: they can be written into
576
+            // (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
577
+            return ($this->showHidden || !$info->isHidden()) && (!$info->isReadOnly() || $this->is_dir($path));
578
+        } catch (NotFoundException $e) {
579
+            return false;
580
+        } catch (ForbiddenException $e) {
581
+            return false;
582
+        }
583
+    }
584
+
585
+    public function isDeletable($path) {
586
+        try {
587
+            $info = $this->getFileInfo($path);
588
+            return ($this->showHidden || !$info->isHidden()) && !$info->isReadOnly();
589
+        } catch (NotFoundException $e) {
590
+            return false;
591
+        } catch (ForbiddenException $e) {
592
+            return false;
593
+        }
594
+    }
595
+
596
+    /**
597
+     * check if smbclient is installed
598
+     */
599
+    public static function checkDependencies() {
600
+        return (
601
+            (bool)\OC_Helper::findBinaryPath('smbclient')
602
+            || NativeServer::available(new System())
603
+        ) ? true : ['smbclient'];
604
+    }
605
+
606
+    /**
607
+     * Test a storage for availability
608
+     *
609
+     * @return bool
610
+     */
611
+    public function test() {
612
+        try {
613
+            return parent::test();
614
+        } catch (Exception $e) {
615
+            $this->logger->logException($e);
616
+            return false;
617
+        }
618
+    }
619
+
620
+    public function listen($path, callable $callback) {
621
+        $this->notify($path)->listen(function (IChange $change) use ($callback) {
622
+            if ($change instanceof IRenameChange) {
623
+                return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
624
+            } else {
625
+                return $callback($change->getType(), $change->getPath());
626
+            }
627
+        });
628
+    }
629
+
630
+    public function notify($path) {
631
+        $path = '/' . ltrim($path, '/');
632
+        $shareNotifyHandler = $this->share->notify($this->buildPath($path));
633
+        return new SMBNotifyHandler($shareNotifyHandler, $this->root);
634
+    }
635 635
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/Swift.php 1 patch
Indentation   +569 added lines, -569 removed lines patch added patch discarded remove patch
@@ -52,577 +52,577 @@
 block discarded – undo
52 52
 use OpenStack\ObjectStore\v1\Models\StorageObject;
53 53
 
54 54
 class Swift extends \OC\Files\Storage\Common {
55
-	/** @var SwiftFactory */
56
-	private $connectionFactory;
57
-	/**
58
-	 * @var \OpenStack\ObjectStore\v1\Models\Container
59
-	 */
60
-	private $container;
61
-	/**
62
-	 * @var string
63
-	 */
64
-	private $bucket;
65
-	/**
66
-	 * Connection parameters
67
-	 *
68
-	 * @var array
69
-	 */
70
-	private $params;
71
-
72
-	/** @var string */
73
-	private $id;
74
-
75
-	/** @var \OC\Files\ObjectStore\Swift */
76
-	private $objectStore;
77
-
78
-	/**
79
-	 * Key value cache mapping path to data object. Maps path to
80
-	 * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
81
-	 * paths and path to false for not existing paths.
82
-	 *
83
-	 * @var \OCP\ICache
84
-	 */
85
-	private $objectCache;
86
-
87
-	/**
88
-	 * @param string $path
89
-	 * @return mixed|string
90
-	 */
91
-	private function normalizePath(string $path) {
92
-		$path = trim($path, '/');
93
-
94
-		if (!$path) {
95
-			$path = '.';
96
-		}
97
-
98
-		$path = str_replace('#', '%23', $path);
99
-
100
-		return $path;
101
-	}
102
-
103
-	const SUBCONTAINER_FILE = '.subcontainers';
104
-
105
-	/**
106
-	 * translate directory path to container name
107
-	 *
108
-	 * @param string $path
109
-	 * @return string
110
-	 */
111
-
112
-	/**
113
-	 * Fetches an object from the API.
114
-	 * If the object is cached already or a
115
-	 * failed "doesn't exist" response was cached,
116
-	 * that one will be returned.
117
-	 *
118
-	 * @param string $path
119
-	 * @return StorageObject|bool object
120
-	 * or false if the object did not exist
121
-	 * @throws \OCP\Files\StorageAuthException
122
-	 * @throws \OCP\Files\StorageNotAvailableException
123
-	 */
124
-	private function fetchObject(string $path) {
125
-		if ($this->objectCache->hasKey($path)) {
126
-			// might be "false" if object did not exist from last check
127
-			return $this->objectCache->get($path);
128
-		}
129
-		try {
130
-			$object = $this->getContainer()->getObject($path);
131
-			$object->retrieve();
132
-			$this->objectCache->set($path, $object);
133
-			return $object;
134
-		} catch (BadResponseError $e) {
135
-			// Expected response is "404 Not Found", so only log if it isn't
136
-			if ($e->getResponse()->getStatusCode() !== 404) {
137
-				\OC::$server->getLogger()->logException($e, [
138
-					'level' => ILogger::ERROR,
139
-					'app' => 'files_external',
140
-				]);
141
-			}
142
-			$this->objectCache->set($path, false);
143
-			return false;
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * Returns whether the given path exists.
149
-	 *
150
-	 * @param string $path
151
-	 *
152
-	 * @return bool true if the object exist, false otherwise
153
-	 * @throws \OCP\Files\StorageAuthException
154
-	 * @throws \OCP\Files\StorageNotAvailableException
155
-	 */
156
-	private function doesObjectExist($path) {
157
-		return $this->fetchObject($path) !== false;
158
-	}
159
-
160
-	public function __construct($params) {
161
-		if ((empty($params['key']) and empty($params['password']))
162
-			or (empty($params['user']) && empty($params['userid'])) or empty($params['bucket'])
163
-			or empty($params['region'])
164
-		) {
165
-			throw new StorageBadConfigException("API Key or password, Username, Bucket and Region have to be configured.");
166
-		}
167
-
168
-		$user = $params['user'];
169
-		$this->id = 'swift::' . $user . md5($params['bucket']);
170
-
171
-		$bucketUrl = new Uri($params['bucket']);
172
-		if ($bucketUrl->getHost()) {
173
-			$params['bucket'] = basename($bucketUrl->getPath());
174
-			$params['endpoint_url'] = (string)$bucketUrl->withPath(dirname($bucketUrl->getPath()));
175
-		}
176
-
177
-		if (empty($params['url'])) {
178
-			$params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
179
-		}
180
-
181
-		if (empty($params['service_name'])) {
182
-			$params['service_name'] = 'cloudFiles';
183
-		}
184
-
185
-		$params['autocreate'] = true;
186
-
187
-		if (isset($params['domain'])) {
188
-			$params['user'] = [
189
-				'name' => $params['user'],
190
-				'password' => $params['password'],
191
-				'domain' => [
192
-					'name' => $params['domain'],
193
-				]
194
-			];
195
-		}
196
-
197
-		$this->params = $params;
198
-		// FIXME: private class...
199
-		$this->objectCache = new \OC\Cache\CappedMemoryCache();
200
-		$this->connectionFactory = new SwiftFactory(
201
-			\OC::$server->getMemCacheFactory()->createDistributed('swift/'),
202
-			$this->params,
203
-			\OC::$server->getLogger()
204
-		);
205
-		$this->objectStore = new \OC\Files\ObjectStore\Swift($this->params, $this->connectionFactory);
206
-		$this->bucket = $params['bucket'];
207
-	}
208
-
209
-	public function mkdir($path) {
210
-		$path = $this->normalizePath($path);
211
-
212
-		if ($this->is_dir($path)) {
213
-			return false;
214
-		}
215
-
216
-		if ($path !== '.') {
217
-			$path .= '/';
218
-		}
219
-
220
-		try {
221
-			$this->getContainer()->createObject([
222
-				'name' => $path,
223
-				'content' => '',
224
-				'headers' => ['content-type' => 'httpd/unix-directory']
225
-			]);
226
-			// invalidate so that the next access gets the real object
227
-			// with all properties
228
-			$this->objectCache->remove($path);
229
-		} catch (BadResponseError $e) {
230
-			\OC::$server->getLogger()->logException($e, [
231
-				'level' => ILogger::ERROR,
232
-				'app' => 'files_external',
233
-			]);
234
-			return false;
235
-		}
236
-
237
-		return true;
238
-	}
239
-
240
-	public function file_exists($path) {
241
-		$path = $this->normalizePath($path);
242
-
243
-		if ($path !== '.' && $this->is_dir($path)) {
244
-			$path .= '/';
245
-		}
246
-
247
-		return $this->doesObjectExist($path);
248
-	}
249
-
250
-	public function rmdir($path) {
251
-		$path = $this->normalizePath($path);
252
-
253
-		if (!$this->is_dir($path) || !$this->isDeletable($path)) {
254
-			return false;
255
-		}
256
-
257
-		$dh = $this->opendir($path);
258
-		while ($file = readdir($dh)) {
259
-			if (\OC\Files\Filesystem::isIgnoredDir($file)) {
260
-				continue;
261
-			}
262
-
263
-			if ($this->is_dir($path . '/' . $file)) {
264
-				$this->rmdir($path . '/' . $file);
265
-			} else {
266
-				$this->unlink($path . '/' . $file);
267
-			}
268
-		}
269
-
270
-		try {
271
-			$this->objectStore->deleteObject($path . '/');
272
-			$this->objectCache->remove($path . '/');
273
-		} catch (BadResponseError $e) {
274
-			\OC::$server->getLogger()->logException($e, [
275
-				'level' => ILogger::ERROR,
276
-				'app' => 'files_external',
277
-			]);
278
-			return false;
279
-		}
280
-
281
-		return true;
282
-	}
283
-
284
-	public function opendir($path) {
285
-		$path = $this->normalizePath($path);
286
-
287
-		if ($path === '.') {
288
-			$path = '';
289
-		} else {
290
-			$path .= '/';
291
-		}
55
+    /** @var SwiftFactory */
56
+    private $connectionFactory;
57
+    /**
58
+     * @var \OpenStack\ObjectStore\v1\Models\Container
59
+     */
60
+    private $container;
61
+    /**
62
+     * @var string
63
+     */
64
+    private $bucket;
65
+    /**
66
+     * Connection parameters
67
+     *
68
+     * @var array
69
+     */
70
+    private $params;
71
+
72
+    /** @var string */
73
+    private $id;
74
+
75
+    /** @var \OC\Files\ObjectStore\Swift */
76
+    private $objectStore;
77
+
78
+    /**
79
+     * Key value cache mapping path to data object. Maps path to
80
+     * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
81
+     * paths and path to false for not existing paths.
82
+     *
83
+     * @var \OCP\ICache
84
+     */
85
+    private $objectCache;
86
+
87
+    /**
88
+     * @param string $path
89
+     * @return mixed|string
90
+     */
91
+    private function normalizePath(string $path) {
92
+        $path = trim($path, '/');
93
+
94
+        if (!$path) {
95
+            $path = '.';
96
+        }
97
+
98
+        $path = str_replace('#', '%23', $path);
99
+
100
+        return $path;
101
+    }
102
+
103
+    const SUBCONTAINER_FILE = '.subcontainers';
104
+
105
+    /**
106
+     * translate directory path to container name
107
+     *
108
+     * @param string $path
109
+     * @return string
110
+     */
111
+
112
+    /**
113
+     * Fetches an object from the API.
114
+     * If the object is cached already or a
115
+     * failed "doesn't exist" response was cached,
116
+     * that one will be returned.
117
+     *
118
+     * @param string $path
119
+     * @return StorageObject|bool object
120
+     * or false if the object did not exist
121
+     * @throws \OCP\Files\StorageAuthException
122
+     * @throws \OCP\Files\StorageNotAvailableException
123
+     */
124
+    private function fetchObject(string $path) {
125
+        if ($this->objectCache->hasKey($path)) {
126
+            // might be "false" if object did not exist from last check
127
+            return $this->objectCache->get($path);
128
+        }
129
+        try {
130
+            $object = $this->getContainer()->getObject($path);
131
+            $object->retrieve();
132
+            $this->objectCache->set($path, $object);
133
+            return $object;
134
+        } catch (BadResponseError $e) {
135
+            // Expected response is "404 Not Found", so only log if it isn't
136
+            if ($e->getResponse()->getStatusCode() !== 404) {
137
+                \OC::$server->getLogger()->logException($e, [
138
+                    'level' => ILogger::ERROR,
139
+                    'app' => 'files_external',
140
+                ]);
141
+            }
142
+            $this->objectCache->set($path, false);
143
+            return false;
144
+        }
145
+    }
146
+
147
+    /**
148
+     * Returns whether the given path exists.
149
+     *
150
+     * @param string $path
151
+     *
152
+     * @return bool true if the object exist, false otherwise
153
+     * @throws \OCP\Files\StorageAuthException
154
+     * @throws \OCP\Files\StorageNotAvailableException
155
+     */
156
+    private function doesObjectExist($path) {
157
+        return $this->fetchObject($path) !== false;
158
+    }
159
+
160
+    public function __construct($params) {
161
+        if ((empty($params['key']) and empty($params['password']))
162
+            or (empty($params['user']) && empty($params['userid'])) or empty($params['bucket'])
163
+            or empty($params['region'])
164
+        ) {
165
+            throw new StorageBadConfigException("API Key or password, Username, Bucket and Region have to be configured.");
166
+        }
167
+
168
+        $user = $params['user'];
169
+        $this->id = 'swift::' . $user . md5($params['bucket']);
170
+
171
+        $bucketUrl = new Uri($params['bucket']);
172
+        if ($bucketUrl->getHost()) {
173
+            $params['bucket'] = basename($bucketUrl->getPath());
174
+            $params['endpoint_url'] = (string)$bucketUrl->withPath(dirname($bucketUrl->getPath()));
175
+        }
176
+
177
+        if (empty($params['url'])) {
178
+            $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
179
+        }
180
+
181
+        if (empty($params['service_name'])) {
182
+            $params['service_name'] = 'cloudFiles';
183
+        }
184
+
185
+        $params['autocreate'] = true;
186
+
187
+        if (isset($params['domain'])) {
188
+            $params['user'] = [
189
+                'name' => $params['user'],
190
+                'password' => $params['password'],
191
+                'domain' => [
192
+                    'name' => $params['domain'],
193
+                ]
194
+            ];
195
+        }
196
+
197
+        $this->params = $params;
198
+        // FIXME: private class...
199
+        $this->objectCache = new \OC\Cache\CappedMemoryCache();
200
+        $this->connectionFactory = new SwiftFactory(
201
+            \OC::$server->getMemCacheFactory()->createDistributed('swift/'),
202
+            $this->params,
203
+            \OC::$server->getLogger()
204
+        );
205
+        $this->objectStore = new \OC\Files\ObjectStore\Swift($this->params, $this->connectionFactory);
206
+        $this->bucket = $params['bucket'];
207
+    }
208
+
209
+    public function mkdir($path) {
210
+        $path = $this->normalizePath($path);
211
+
212
+        if ($this->is_dir($path)) {
213
+            return false;
214
+        }
215
+
216
+        if ($path !== '.') {
217
+            $path .= '/';
218
+        }
219
+
220
+        try {
221
+            $this->getContainer()->createObject([
222
+                'name' => $path,
223
+                'content' => '',
224
+                'headers' => ['content-type' => 'httpd/unix-directory']
225
+            ]);
226
+            // invalidate so that the next access gets the real object
227
+            // with all properties
228
+            $this->objectCache->remove($path);
229
+        } catch (BadResponseError $e) {
230
+            \OC::$server->getLogger()->logException($e, [
231
+                'level' => ILogger::ERROR,
232
+                'app' => 'files_external',
233
+            ]);
234
+            return false;
235
+        }
236
+
237
+        return true;
238
+    }
239
+
240
+    public function file_exists($path) {
241
+        $path = $this->normalizePath($path);
242
+
243
+        if ($path !== '.' && $this->is_dir($path)) {
244
+            $path .= '/';
245
+        }
246
+
247
+        return $this->doesObjectExist($path);
248
+    }
249
+
250
+    public function rmdir($path) {
251
+        $path = $this->normalizePath($path);
252
+
253
+        if (!$this->is_dir($path) || !$this->isDeletable($path)) {
254
+            return false;
255
+        }
256
+
257
+        $dh = $this->opendir($path);
258
+        while ($file = readdir($dh)) {
259
+            if (\OC\Files\Filesystem::isIgnoredDir($file)) {
260
+                continue;
261
+            }
262
+
263
+            if ($this->is_dir($path . '/' . $file)) {
264
+                $this->rmdir($path . '/' . $file);
265
+            } else {
266
+                $this->unlink($path . '/' . $file);
267
+            }
268
+        }
269
+
270
+        try {
271
+            $this->objectStore->deleteObject($path . '/');
272
+            $this->objectCache->remove($path . '/');
273
+        } catch (BadResponseError $e) {
274
+            \OC::$server->getLogger()->logException($e, [
275
+                'level' => ILogger::ERROR,
276
+                'app' => 'files_external',
277
+            ]);
278
+            return false;
279
+        }
280
+
281
+        return true;
282
+    }
283
+
284
+    public function opendir($path) {
285
+        $path = $this->normalizePath($path);
286
+
287
+        if ($path === '.') {
288
+            $path = '';
289
+        } else {
290
+            $path .= '/';
291
+        }
292 292
 
293 293
 //		$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
294 294
 
295
-		try {
296
-			$files = [];
297
-			$objects = $this->getContainer()->listObjects([
298
-				'prefix' => $path,
299
-				'delimiter' => '/'
300
-			]);
301
-
302
-			/** @var StorageObject $object */
303
-			foreach ($objects as $object) {
304
-				$file = basename($object->name);
305
-				if ($file !== basename($path) && $file !== '.') {
306
-					$files[] = $file;
307
-				}
308
-			}
309
-
310
-			return IteratorDirectory::wrap($files);
311
-		} catch (\Exception $e) {
312
-			\OC::$server->getLogger()->logException($e, [
313
-				'level' => ILogger::ERROR,
314
-				'app' => 'files_external',
315
-			]);
316
-			return false;
317
-		}
318
-
319
-	}
320
-
321
-	public function stat($path) {
322
-		$path = $this->normalizePath($path);
323
-
324
-		if ($path === '.') {
325
-			$path = '';
326
-		} else if ($this->is_dir($path)) {
327
-			$path .= '/';
328
-		}
329
-
330
-		try {
331
-			$object = $this->fetchObject($path);
332
-			if (!$object) {
333
-				return false;
334
-			}
335
-		} catch (BadResponseError $e) {
336
-			\OC::$server->getLogger()->logException($e, [
337
-				'level' => ILogger::ERROR,
338
-				'app' => 'files_external',
339
-			]);
340
-			return false;
341
-		}
342
-
343
-		$dateTime = $object->lastModified ? \DateTime::createFromFormat(\DateTime::RFC1123, $object->lastModified) : false;
344
-		$mtime = $dateTime ? $dateTime->getTimestamp() : null;
345
-		$objectMetadata = $object->getMetadata();
346
-		if (isset($objectMetadata['timestamp'])) {
347
-			$mtime = $objectMetadata['timestamp'];
348
-		}
349
-
350
-		if (!empty($mtime)) {
351
-			$mtime = floor($mtime);
352
-		}
353
-
354
-		$stat = [];
355
-		$stat['size'] = (int)$object->contentLength;
356
-		$stat['mtime'] = $mtime;
357
-		$stat['atime'] = time();
358
-		return $stat;
359
-	}
360
-
361
-	public function filetype($path) {
362
-		$path = $this->normalizePath($path);
363
-
364
-		if ($path !== '.' && $this->doesObjectExist($path)) {
365
-			return 'file';
366
-		}
367
-
368
-		if ($path !== '.') {
369
-			$path .= '/';
370
-		}
371
-
372
-		if ($this->doesObjectExist($path)) {
373
-			return 'dir';
374
-		}
375
-	}
376
-
377
-	public function unlink($path) {
378
-		$path = $this->normalizePath($path);
379
-
380
-		if ($this->is_dir($path)) {
381
-			return $this->rmdir($path);
382
-		}
383
-
384
-		try {
385
-			$this->objectStore->deleteObject($path);
386
-			$this->objectCache->remove($path);
387
-			$this->objectCache->remove($path . '/');
388
-		} catch (BadResponseError $e) {
389
-			if ($e->getResponse()->getStatusCode() !== 404) {
390
-				\OC::$server->getLogger()->logException($e, [
391
-					'level' => ILogger::ERROR,
392
-					'app' => 'files_external',
393
-				]);
394
-				throw $e;
395
-			}
396
-		}
397
-
398
-		return true;
399
-	}
400
-
401
-	public function fopen($path, $mode) {
402
-		$path = $this->normalizePath($path);
403
-
404
-		switch ($mode) {
405
-			case 'a':
406
-			case 'ab':
407
-			case 'a+':
408
-				return false;
409
-			case 'r':
410
-			case 'rb':
411
-				try {
412
-					return $this->objectStore->readObject($path);
413
-				} catch (BadResponseError $e) {
414
-					\OC::$server->getLogger()->logException($e, [
415
-						'level' => ILogger::ERROR,
416
-						'app' => 'files_external',
417
-					]);
418
-					return false;
419
-				}
420
-			case 'w':
421
-			case 'wb':
422
-			case 'r+':
423
-			case 'w+':
424
-			case 'wb+':
425
-			case 'x':
426
-			case 'x+':
427
-			case 'c':
428
-			case 'c+':
429
-				if (strrpos($path, '.') !== false) {
430
-					$ext = substr($path, strrpos($path, '.'));
431
-				} else {
432
-					$ext = '';
433
-				}
434
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
435
-				// Fetch existing file if required
436
-				if ($mode[0] !== 'w' && $this->file_exists($path)) {
437
-					if ($mode[0] === 'x') {
438
-						// File cannot already exist
439
-						return false;
440
-					}
441
-					$source = $this->fopen($path, 'r');
442
-					file_put_contents($tmpFile, $source);
443
-				}
444
-				$handle = fopen($tmpFile, $mode);
445
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
446
-					$this->writeBack($tmpFile, $path);
447
-				});
448
-		}
449
-	}
450
-
451
-	public function touch($path, $mtime = null) {
452
-		$path = $this->normalizePath($path);
453
-		if (is_null($mtime)) {
454
-			$mtime = time();
455
-		}
456
-		$metadata = ['timestamp' => (string)$mtime];
457
-		if ($this->file_exists($path)) {
458
-			if ($this->is_dir($path) && $path !== '.') {
459
-				$path .= '/';
460
-			}
461
-
462
-			$object = $this->fetchObject($path);
463
-			if ($object->mergeMetadata($metadata)) {
464
-				// invalidate target object to force repopulation on fetch
465
-				$this->objectCache->remove($path);
466
-			}
467
-			return true;
468
-		} else {
469
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
470
-			$this->getContainer()->createObject([
471
-				'name' => $path,
472
-				'content' => '',
473
-				'headers' => ['content-type' => 'httpd/unix-directory']
474
-			]);
475
-			// invalidate target object to force repopulation on fetch
476
-			$this->objectCache->remove($path);
477
-			return true;
478
-		}
479
-	}
480
-
481
-	public function copy($path1, $path2) {
482
-		$path1 = $this->normalizePath($path1);
483
-		$path2 = $this->normalizePath($path2);
484
-
485
-		$fileType = $this->filetype($path1);
486
-		if ($fileType) {
487
-			// make way
488
-			$this->unlink($path2);
489
-		}
490
-
491
-		if ($fileType === 'file') {
492
-			try {
493
-				$source = $this->fetchObject($path1);
494
-				$source->copy([
495
-					'destination' => $this->bucket . '/' . $path2
496
-				]);
497
-				// invalidate target object to force repopulation on fetch
498
-				$this->objectCache->remove($path2);
499
-				$this->objectCache->remove($path2 . '/');
500
-			} catch (BadResponseError $e) {
501
-				\OC::$server->getLogger()->logException($e, [
502
-					'level' => ILogger::ERROR,
503
-					'app' => 'files_external',
504
-				]);
505
-				return false;
506
-			}
507
-
508
-		} else if ($fileType === 'dir') {
509
-			try {
510
-				$source = $this->fetchObject($path1 . '/');
511
-				$source->copy([
512
-					'destination' => $this->bucket . '/' . $path2 . '/'
513
-				]);
514
-				// invalidate target object to force repopulation on fetch
515
-				$this->objectCache->remove($path2);
516
-				$this->objectCache->remove($path2 . '/');
517
-			} catch (BadResponseError $e) {
518
-				\OC::$server->getLogger()->logException($e, [
519
-					'level' => ILogger::ERROR,
520
-					'app' => 'files_external',
521
-				]);
522
-				return false;
523
-			}
524
-
525
-			$dh = $this->opendir($path1);
526
-			while ($file = readdir($dh)) {
527
-				if (\OC\Files\Filesystem::isIgnoredDir($file)) {
528
-					continue;
529
-				}
530
-
531
-				$source = $path1 . '/' . $file;
532
-				$target = $path2 . '/' . $file;
533
-				$this->copy($source, $target);
534
-			}
535
-
536
-		} else {
537
-			//file does not exist
538
-			return false;
539
-		}
540
-
541
-		return true;
542
-	}
543
-
544
-	public function rename($path1, $path2) {
545
-		$path1 = $this->normalizePath($path1);
546
-		$path2 = $this->normalizePath($path2);
547
-
548
-		$fileType = $this->filetype($path1);
549
-
550
-		if ($fileType === 'dir' || $fileType === 'file') {
551
-			// copy
552
-			if ($this->copy($path1, $path2) === false) {
553
-				return false;
554
-			}
555
-
556
-			// cleanup
557
-			if ($this->unlink($path1) === false) {
558
-				throw new \Exception('failed to remove original');
559
-				$this->unlink($path2);
560
-				return false;
561
-			}
562
-
563
-			return true;
564
-		}
565
-
566
-		return false;
567
-	}
568
-
569
-	public function getId() {
570
-		return $this->id;
571
-	}
572
-
573
-	/**
574
-	 * Returns the initialized object store container.
575
-	 *
576
-	 * @return \OpenStack\ObjectStore\v1\Models\Container
577
-	 * @throws \OCP\Files\StorageAuthException
578
-	 * @throws \OCP\Files\StorageNotAvailableException
579
-	 */
580
-	public function getContainer() {
581
-		if (is_null($this->container)) {
582
-			$this->container = $this->connectionFactory->getContainer();
583
-
584
-			if (!$this->file_exists('.')) {
585
-				$this->mkdir('.');
586
-			}
587
-		}
588
-		return $this->container;
589
-	}
590
-
591
-	public function writeBack($tmpFile, $path) {
592
-		$fileData = fopen($tmpFile, 'r');
593
-		$this->objectStore->writeObject($path, $fileData);
594
-		// invalidate target object to force repopulation on fetch
595
-		$this->objectCache->remove($path);
596
-		unlink($tmpFile);
597
-	}
598
-
599
-	public function hasUpdated($path, $time) {
600
-		if ($this->is_file($path)) {
601
-			return parent::hasUpdated($path, $time);
602
-		}
603
-		$path = $this->normalizePath($path);
604
-		$dh = $this->opendir($path);
605
-		$content = [];
606
-		while (($file = readdir($dh)) !== false) {
607
-			$content[] = $file;
608
-		}
609
-		if ($path === '.') {
610
-			$path = '';
611
-		}
612
-		$cachedContent = $this->getCache()->getFolderContents($path);
613
-		$cachedNames = array_map(function ($content) {
614
-			return $content['name'];
615
-		}, $cachedContent);
616
-		sort($cachedNames);
617
-		sort($content);
618
-		return $cachedNames !== $content;
619
-	}
620
-
621
-	/**
622
-	 * check if curl is installed
623
-	 */
624
-	public static function checkDependencies() {
625
-		return true;
626
-	}
295
+        try {
296
+            $files = [];
297
+            $objects = $this->getContainer()->listObjects([
298
+                'prefix' => $path,
299
+                'delimiter' => '/'
300
+            ]);
301
+
302
+            /** @var StorageObject $object */
303
+            foreach ($objects as $object) {
304
+                $file = basename($object->name);
305
+                if ($file !== basename($path) && $file !== '.') {
306
+                    $files[] = $file;
307
+                }
308
+            }
309
+
310
+            return IteratorDirectory::wrap($files);
311
+        } catch (\Exception $e) {
312
+            \OC::$server->getLogger()->logException($e, [
313
+                'level' => ILogger::ERROR,
314
+                'app' => 'files_external',
315
+            ]);
316
+            return false;
317
+        }
318
+
319
+    }
320
+
321
+    public function stat($path) {
322
+        $path = $this->normalizePath($path);
323
+
324
+        if ($path === '.') {
325
+            $path = '';
326
+        } else if ($this->is_dir($path)) {
327
+            $path .= '/';
328
+        }
329
+
330
+        try {
331
+            $object = $this->fetchObject($path);
332
+            if (!$object) {
333
+                return false;
334
+            }
335
+        } catch (BadResponseError $e) {
336
+            \OC::$server->getLogger()->logException($e, [
337
+                'level' => ILogger::ERROR,
338
+                'app' => 'files_external',
339
+            ]);
340
+            return false;
341
+        }
342
+
343
+        $dateTime = $object->lastModified ? \DateTime::createFromFormat(\DateTime::RFC1123, $object->lastModified) : false;
344
+        $mtime = $dateTime ? $dateTime->getTimestamp() : null;
345
+        $objectMetadata = $object->getMetadata();
346
+        if (isset($objectMetadata['timestamp'])) {
347
+            $mtime = $objectMetadata['timestamp'];
348
+        }
349
+
350
+        if (!empty($mtime)) {
351
+            $mtime = floor($mtime);
352
+        }
353
+
354
+        $stat = [];
355
+        $stat['size'] = (int)$object->contentLength;
356
+        $stat['mtime'] = $mtime;
357
+        $stat['atime'] = time();
358
+        return $stat;
359
+    }
360
+
361
+    public function filetype($path) {
362
+        $path = $this->normalizePath($path);
363
+
364
+        if ($path !== '.' && $this->doesObjectExist($path)) {
365
+            return 'file';
366
+        }
367
+
368
+        if ($path !== '.') {
369
+            $path .= '/';
370
+        }
371
+
372
+        if ($this->doesObjectExist($path)) {
373
+            return 'dir';
374
+        }
375
+    }
376
+
377
+    public function unlink($path) {
378
+        $path = $this->normalizePath($path);
379
+
380
+        if ($this->is_dir($path)) {
381
+            return $this->rmdir($path);
382
+        }
383
+
384
+        try {
385
+            $this->objectStore->deleteObject($path);
386
+            $this->objectCache->remove($path);
387
+            $this->objectCache->remove($path . '/');
388
+        } catch (BadResponseError $e) {
389
+            if ($e->getResponse()->getStatusCode() !== 404) {
390
+                \OC::$server->getLogger()->logException($e, [
391
+                    'level' => ILogger::ERROR,
392
+                    'app' => 'files_external',
393
+                ]);
394
+                throw $e;
395
+            }
396
+        }
397
+
398
+        return true;
399
+    }
400
+
401
+    public function fopen($path, $mode) {
402
+        $path = $this->normalizePath($path);
403
+
404
+        switch ($mode) {
405
+            case 'a':
406
+            case 'ab':
407
+            case 'a+':
408
+                return false;
409
+            case 'r':
410
+            case 'rb':
411
+                try {
412
+                    return $this->objectStore->readObject($path);
413
+                } catch (BadResponseError $e) {
414
+                    \OC::$server->getLogger()->logException($e, [
415
+                        'level' => ILogger::ERROR,
416
+                        'app' => 'files_external',
417
+                    ]);
418
+                    return false;
419
+                }
420
+            case 'w':
421
+            case 'wb':
422
+            case 'r+':
423
+            case 'w+':
424
+            case 'wb+':
425
+            case 'x':
426
+            case 'x+':
427
+            case 'c':
428
+            case 'c+':
429
+                if (strrpos($path, '.') !== false) {
430
+                    $ext = substr($path, strrpos($path, '.'));
431
+                } else {
432
+                    $ext = '';
433
+                }
434
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
435
+                // Fetch existing file if required
436
+                if ($mode[0] !== 'w' && $this->file_exists($path)) {
437
+                    if ($mode[0] === 'x') {
438
+                        // File cannot already exist
439
+                        return false;
440
+                    }
441
+                    $source = $this->fopen($path, 'r');
442
+                    file_put_contents($tmpFile, $source);
443
+                }
444
+                $handle = fopen($tmpFile, $mode);
445
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
446
+                    $this->writeBack($tmpFile, $path);
447
+                });
448
+        }
449
+    }
450
+
451
+    public function touch($path, $mtime = null) {
452
+        $path = $this->normalizePath($path);
453
+        if (is_null($mtime)) {
454
+            $mtime = time();
455
+        }
456
+        $metadata = ['timestamp' => (string)$mtime];
457
+        if ($this->file_exists($path)) {
458
+            if ($this->is_dir($path) && $path !== '.') {
459
+                $path .= '/';
460
+            }
461
+
462
+            $object = $this->fetchObject($path);
463
+            if ($object->mergeMetadata($metadata)) {
464
+                // invalidate target object to force repopulation on fetch
465
+                $this->objectCache->remove($path);
466
+            }
467
+            return true;
468
+        } else {
469
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
470
+            $this->getContainer()->createObject([
471
+                'name' => $path,
472
+                'content' => '',
473
+                'headers' => ['content-type' => 'httpd/unix-directory']
474
+            ]);
475
+            // invalidate target object to force repopulation on fetch
476
+            $this->objectCache->remove($path);
477
+            return true;
478
+        }
479
+    }
480
+
481
+    public function copy($path1, $path2) {
482
+        $path1 = $this->normalizePath($path1);
483
+        $path2 = $this->normalizePath($path2);
484
+
485
+        $fileType = $this->filetype($path1);
486
+        if ($fileType) {
487
+            // make way
488
+            $this->unlink($path2);
489
+        }
490
+
491
+        if ($fileType === 'file') {
492
+            try {
493
+                $source = $this->fetchObject($path1);
494
+                $source->copy([
495
+                    'destination' => $this->bucket . '/' . $path2
496
+                ]);
497
+                // invalidate target object to force repopulation on fetch
498
+                $this->objectCache->remove($path2);
499
+                $this->objectCache->remove($path2 . '/');
500
+            } catch (BadResponseError $e) {
501
+                \OC::$server->getLogger()->logException($e, [
502
+                    'level' => ILogger::ERROR,
503
+                    'app' => 'files_external',
504
+                ]);
505
+                return false;
506
+            }
507
+
508
+        } else if ($fileType === 'dir') {
509
+            try {
510
+                $source = $this->fetchObject($path1 . '/');
511
+                $source->copy([
512
+                    'destination' => $this->bucket . '/' . $path2 . '/'
513
+                ]);
514
+                // invalidate target object to force repopulation on fetch
515
+                $this->objectCache->remove($path2);
516
+                $this->objectCache->remove($path2 . '/');
517
+            } catch (BadResponseError $e) {
518
+                \OC::$server->getLogger()->logException($e, [
519
+                    'level' => ILogger::ERROR,
520
+                    'app' => 'files_external',
521
+                ]);
522
+                return false;
523
+            }
524
+
525
+            $dh = $this->opendir($path1);
526
+            while ($file = readdir($dh)) {
527
+                if (\OC\Files\Filesystem::isIgnoredDir($file)) {
528
+                    continue;
529
+                }
530
+
531
+                $source = $path1 . '/' . $file;
532
+                $target = $path2 . '/' . $file;
533
+                $this->copy($source, $target);
534
+            }
535
+
536
+        } else {
537
+            //file does not exist
538
+            return false;
539
+        }
540
+
541
+        return true;
542
+    }
543
+
544
+    public function rename($path1, $path2) {
545
+        $path1 = $this->normalizePath($path1);
546
+        $path2 = $this->normalizePath($path2);
547
+
548
+        $fileType = $this->filetype($path1);
549
+
550
+        if ($fileType === 'dir' || $fileType === 'file') {
551
+            // copy
552
+            if ($this->copy($path1, $path2) === false) {
553
+                return false;
554
+            }
555
+
556
+            // cleanup
557
+            if ($this->unlink($path1) === false) {
558
+                throw new \Exception('failed to remove original');
559
+                $this->unlink($path2);
560
+                return false;
561
+            }
562
+
563
+            return true;
564
+        }
565
+
566
+        return false;
567
+    }
568
+
569
+    public function getId() {
570
+        return $this->id;
571
+    }
572
+
573
+    /**
574
+     * Returns the initialized object store container.
575
+     *
576
+     * @return \OpenStack\ObjectStore\v1\Models\Container
577
+     * @throws \OCP\Files\StorageAuthException
578
+     * @throws \OCP\Files\StorageNotAvailableException
579
+     */
580
+    public function getContainer() {
581
+        if (is_null($this->container)) {
582
+            $this->container = $this->connectionFactory->getContainer();
583
+
584
+            if (!$this->file_exists('.')) {
585
+                $this->mkdir('.');
586
+            }
587
+        }
588
+        return $this->container;
589
+    }
590
+
591
+    public function writeBack($tmpFile, $path) {
592
+        $fileData = fopen($tmpFile, 'r');
593
+        $this->objectStore->writeObject($path, $fileData);
594
+        // invalidate target object to force repopulation on fetch
595
+        $this->objectCache->remove($path);
596
+        unlink($tmpFile);
597
+    }
598
+
599
+    public function hasUpdated($path, $time) {
600
+        if ($this->is_file($path)) {
601
+            return parent::hasUpdated($path, $time);
602
+        }
603
+        $path = $this->normalizePath($path);
604
+        $dh = $this->opendir($path);
605
+        $content = [];
606
+        while (($file = readdir($dh)) !== false) {
607
+            $content[] = $file;
608
+        }
609
+        if ($path === '.') {
610
+            $path = '';
611
+        }
612
+        $cachedContent = $this->getCache()->getFolderContents($path);
613
+        $cachedNames = array_map(function ($content) {
614
+            return $content['name'];
615
+        }, $cachedContent);
616
+        sort($cachedNames);
617
+        sort($content);
618
+        return $cachedNames !== $content;
619
+    }
620
+
621
+    /**
622
+     * check if curl is installed
623
+     */
624
+    public static function checkDependencies() {
625
+        return true;
626
+    }
627 627
 
628 628
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SFTP.php 1 patch
Indentation   +437 added lines, -437 removed lines patch added patch discarded remove patch
@@ -44,441 +44,441 @@
 block discarded – undo
44 44
 * provide access to SFTP servers.
45 45
 */
46 46
 class SFTP extends \OC\Files\Storage\Common {
47
-	private $host;
48
-	private $user;
49
-	private $root;
50
-	private $port = 22;
51
-
52
-	private $auth = [];
53
-
54
-	/**
55
-	 * @var \phpseclib\Net\SFTP
56
-	 */
57
-	protected $client;
58
-
59
-	/**
60
-	 * @param string $host protocol://server:port
61
-	 * @return array [$server, $port]
62
-	 */
63
-	private function splitHost($host) {
64
-		$input = $host;
65
-		if (strpos($host, '://') === false) {
66
-			// add a protocol to fix parse_url behavior with ipv6
67
-			$host = 'http://' . $host;
68
-		}
69
-
70
-		$parsed = parse_url($host);
71
-		if(is_array($parsed) && isset($parsed['port'])) {
72
-			return [$parsed['host'], $parsed['port']];
73
-		} else if (is_array($parsed)) {
74
-			return [$parsed['host'], 22];
75
-		} else {
76
-			return [$input, 22];
77
-		}
78
-	}
79
-
80
-	/**
81
-	 * {@inheritdoc}
82
-	 */
83
-	public function __construct($params) {
84
-		// Register sftp://
85
-		Stream::register();
86
-
87
-		$parsedHost =  $this->splitHost($params['host']);
88
-
89
-		$this->host = $parsedHost[0];
90
-		$this->port = $parsedHost[1];
91
-
92
-		if (!isset($params['user'])) {
93
-			throw new \UnexpectedValueException('no authentication parameters specified');
94
-		}
95
-		$this->user = $params['user'];
96
-
97
-		if (isset($params['public_key_auth'])) {
98
-			$this->auth[] = $params['public_key_auth'];
99
-		}
100
-		if (isset($params['password']) && $params['password'] !== '') {
101
-			$this->auth[] = $params['password'];
102
-		}
103
-
104
-		if ($this->auth === []) {
105
-			throw new \UnexpectedValueException('no authentication parameters specified');
106
-		}
107
-
108
-		$this->root
109
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
110
-
111
-		$this->root = '/' . ltrim($this->root, '/');
112
-		$this->root = rtrim($this->root, '/') . '/';
113
-	}
114
-
115
-	/**
116
-	 * Returns the connection.
117
-	 *
118
-	 * @return \phpseclib\Net\SFTP connected client instance
119
-	 * @throws \Exception when the connection failed
120
-	 */
121
-	public function getConnection() {
122
-		if (!is_null($this->client)) {
123
-			return $this->client;
124
-		}
125
-
126
-		$hostKeys = $this->readHostKeys();
127
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
128
-
129
-		// The SSH Host Key MUST be verified before login().
130
-		$currentHostKey = $this->client->getServerPublicHostKey();
131
-		if (array_key_exists($this->host, $hostKeys)) {
132
-			if ($hostKeys[$this->host] !== $currentHostKey) {
133
-				throw new \Exception('Host public key does not match known key');
134
-			}
135
-		} else {
136
-			$hostKeys[$this->host] = $currentHostKey;
137
-			$this->writeHostKeys($hostKeys);
138
-		}
139
-
140
-		$login = false;
141
-		foreach ($this->auth as $auth) {
142
-			$login = $this->client->login($this->user, $auth);
143
-			if ($login === true) {
144
-				break;
145
-			}
146
-		}
147
-
148
-		if ($login === false) {
149
-			throw new \Exception('Login failed');
150
-		}
151
-		return $this->client;
152
-	}
153
-
154
-	/**
155
-	 * {@inheritdoc}
156
-	 */
157
-	public function test() {
158
-		if (
159
-			!isset($this->host)
160
-			|| !isset($this->user)
161
-		) {
162
-			return false;
163
-		}
164
-		return $this->getConnection()->nlist() !== false;
165
-	}
166
-
167
-	/**
168
-	 * {@inheritdoc}
169
-	 */
170
-	public function getId(){
171
-		$id = 'sftp::' . $this->user . '@' . $this->host;
172
-		if ($this->port !== 22) {
173
-			$id .= ':' . $this->port;
174
-		}
175
-		// note: this will double the root slash,
176
-		// we should not change it to keep compatible with
177
-		// old storage ids
178
-		$id .= '/' . $this->root;
179
-		return $id;
180
-	}
181
-
182
-	/**
183
-	 * @return string
184
-	 */
185
-	public function getHost() {
186
-		return $this->host;
187
-	}
188
-
189
-	/**
190
-	 * @return string
191
-	 */
192
-	public function getRoot() {
193
-		return $this->root;
194
-	}
195
-
196
-	/**
197
-	 * @return mixed
198
-	 */
199
-	public function getUser() {
200
-		return $this->user;
201
-	}
202
-
203
-	/**
204
-	 * @param string $path
205
-	 * @return string
206
-	 */
207
-	private function absPath($path) {
208
-		return $this->root . $this->cleanPath($path);
209
-	}
210
-
211
-	/**
212
-	 * @return string|false
213
-	 */
214
-	private function hostKeysPath() {
215
-		try {
216
-			$storage_view = \OCP\Files::getStorage('files_external');
217
-			if ($storage_view) {
218
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
219
-					$storage_view->getAbsolutePath('') .
220
-					'ssh_hostKeys';
221
-			}
222
-		} catch (\Exception $e) {
223
-		}
224
-		return false;
225
-	}
226
-
227
-	/**
228
-	 * @param $keys
229
-	 * @return bool
230
-	 */
231
-	protected function writeHostKeys($keys) {
232
-		try {
233
-			$keyPath = $this->hostKeysPath();
234
-			if ($keyPath && file_exists($keyPath)) {
235
-				$fp = fopen($keyPath, 'w');
236
-				foreach ($keys as $host => $key) {
237
-					fwrite($fp, $host . '::' . $key . "\n");
238
-				}
239
-				fclose($fp);
240
-				return true;
241
-			}
242
-		} catch (\Exception $e) {
243
-		}
244
-		return false;
245
-	}
246
-
247
-	/**
248
-	 * @return array
249
-	 */
250
-	protected function readHostKeys() {
251
-		try {
252
-			$keyPath = $this->hostKeysPath();
253
-			if (file_exists($keyPath)) {
254
-				$hosts = [];
255
-				$keys = [];
256
-				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
257
-				if ($lines) {
258
-					foreach ($lines as $line) {
259
-						$hostKeyArray = explode("::", $line, 2);
260
-						if (count($hostKeyArray) === 2) {
261
-							$hosts[] = $hostKeyArray[0];
262
-							$keys[] = $hostKeyArray[1];
263
-						}
264
-					}
265
-					return array_combine($hosts, $keys);
266
-				}
267
-			}
268
-		} catch (\Exception $e) {
269
-		}
270
-		return [];
271
-	}
272
-
273
-	/**
274
-	 * {@inheritdoc}
275
-	 */
276
-	public function mkdir($path) {
277
-		try {
278
-			return $this->getConnection()->mkdir($this->absPath($path));
279
-		} catch (\Exception $e) {
280
-			return false;
281
-		}
282
-	}
283
-
284
-	/**
285
-	 * {@inheritdoc}
286
-	 */
287
-	public function rmdir($path) {
288
-		try {
289
-			$result = $this->getConnection()->delete($this->absPath($path), true);
290
-			// workaround: stray stat cache entry when deleting empty folders
291
-			// see https://github.com/phpseclib/phpseclib/issues/706
292
-			$this->getConnection()->clearStatCache();
293
-			return $result;
294
-		} catch (\Exception $e) {
295
-			return false;
296
-		}
297
-	}
298
-
299
-	/**
300
-	 * {@inheritdoc}
301
-	 */
302
-	public function opendir($path) {
303
-		try {
304
-			$list = $this->getConnection()->nlist($this->absPath($path));
305
-			if ($list === false) {
306
-				return false;
307
-			}
308
-
309
-			$id = md5('sftp:' . $path);
310
-			$dirStream = [];
311
-			foreach($list as $file) {
312
-				if ($file !== '.' && $file !== '..') {
313
-					$dirStream[] = $file;
314
-				}
315
-			}
316
-			return IteratorDirectory::wrap($dirStream);
317
-		} catch(\Exception $e) {
318
-			return false;
319
-		}
320
-	}
321
-
322
-	/**
323
-	 * {@inheritdoc}
324
-	 */
325
-	public function filetype($path) {
326
-		try {
327
-			$stat = $this->getConnection()->stat($this->absPath($path));
328
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
329
-				return 'file';
330
-			}
331
-
332
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
333
-				return 'dir';
334
-			}
335
-		} catch (\Exception $e) {
336
-
337
-		}
338
-		return false;
339
-	}
340
-
341
-	/**
342
-	 * {@inheritdoc}
343
-	 */
344
-	public function file_exists($path) {
345
-		try {
346
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
347
-		} catch (\Exception $e) {
348
-			return false;
349
-		}
350
-	}
351
-
352
-	/**
353
-	 * {@inheritdoc}
354
-	 */
355
-	public function unlink($path) {
356
-		try {
357
-			return $this->getConnection()->delete($this->absPath($path), true);
358
-		} catch (\Exception $e) {
359
-			return false;
360
-		}
361
-	}
362
-
363
-	/**
364
-	 * {@inheritdoc}
365
-	 */
366
-	public function fopen($path, $mode) {
367
-		try {
368
-			$absPath = $this->absPath($path);
369
-			switch($mode) {
370
-				case 'r':
371
-				case 'rb':
372
-					if ( !$this->file_exists($path)) {
373
-						return false;
374
-					}
375
-					SFTPReadStream::register();
376
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
377
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
378
-					return RetryWrapper::wrap($handle);
379
-				case 'w':
380
-				case 'wb':
381
-					SFTPWriteStream::register();
382
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
383
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
384
-				case 'a':
385
-				case 'ab':
386
-				case 'r+':
387
-				case 'w+':
388
-				case 'wb+':
389
-				case 'a+':
390
-				case 'x':
391
-				case 'x+':
392
-				case 'c':
393
-				case 'c+':
394
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
395
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
396
-					return RetryWrapper::wrap($handle);
397
-			}
398
-		} catch (\Exception $e) {
399
-		}
400
-		return false;
401
-	}
402
-
403
-	/**
404
-	 * {@inheritdoc}
405
-	 */
406
-	public function touch($path, $mtime=null) {
407
-		try {
408
-			if (!is_null($mtime)) {
409
-				return false;
410
-			}
411
-			if (!$this->file_exists($path)) {
412
-				$this->getConnection()->put($this->absPath($path), '');
413
-			} else {
414
-				return false;
415
-			}
416
-		} catch (\Exception $e) {
417
-			return false;
418
-		}
419
-		return true;
420
-	}
421
-
422
-	/**
423
-	 * @param string $path
424
-	 * @param string $target
425
-	 * @throws \Exception
426
-	 */
427
-	public function getFile($path, $target) {
428
-		$this->getConnection()->get($path, $target);
429
-	}
430
-
431
-	/**
432
-	 * @param string $path
433
-	 * @param string $target
434
-	 * @throws \Exception
435
-	 */
436
-	public function uploadFile($path, $target) {
437
-		$this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
438
-	}
439
-
440
-	/**
441
-	 * {@inheritdoc}
442
-	 */
443
-	public function rename($source, $target) {
444
-		try {
445
-			if ($this->file_exists($target)) {
446
-				$this->unlink($target);
447
-			}
448
-			return $this->getConnection()->rename(
449
-				$this->absPath($source),
450
-				$this->absPath($target)
451
-			);
452
-		} catch (\Exception $e) {
453
-			return false;
454
-		}
455
-	}
456
-
457
-	/**
458
-	 * {@inheritdoc}
459
-	 */
460
-	public function stat($path) {
461
-		try {
462
-			$stat = $this->getConnection()->stat($this->absPath($path));
463
-
464
-			$mtime = $stat ? $stat['mtime'] : -1;
465
-			$size = $stat ? $stat['size'] : 0;
466
-
467
-			return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
468
-		} catch (\Exception $e) {
469
-			return false;
470
-		}
471
-	}
472
-
473
-	/**
474
-	 * @param string $path
475
-	 * @return string
476
-	 */
477
-	public function constructUrl($path) {
478
-		// Do not pass the password here. We want to use the Net_SFTP object
479
-		// supplied via stream context or fail. We only supply username and
480
-		// hostname because this might show up in logs (they are not used).
481
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
482
-		return $url;
483
-	}
47
+    private $host;
48
+    private $user;
49
+    private $root;
50
+    private $port = 22;
51
+
52
+    private $auth = [];
53
+
54
+    /**
55
+     * @var \phpseclib\Net\SFTP
56
+     */
57
+    protected $client;
58
+
59
+    /**
60
+     * @param string $host protocol://server:port
61
+     * @return array [$server, $port]
62
+     */
63
+    private function splitHost($host) {
64
+        $input = $host;
65
+        if (strpos($host, '://') === false) {
66
+            // add a protocol to fix parse_url behavior with ipv6
67
+            $host = 'http://' . $host;
68
+        }
69
+
70
+        $parsed = parse_url($host);
71
+        if(is_array($parsed) && isset($parsed['port'])) {
72
+            return [$parsed['host'], $parsed['port']];
73
+        } else if (is_array($parsed)) {
74
+            return [$parsed['host'], 22];
75
+        } else {
76
+            return [$input, 22];
77
+        }
78
+    }
79
+
80
+    /**
81
+     * {@inheritdoc}
82
+     */
83
+    public function __construct($params) {
84
+        // Register sftp://
85
+        Stream::register();
86
+
87
+        $parsedHost =  $this->splitHost($params['host']);
88
+
89
+        $this->host = $parsedHost[0];
90
+        $this->port = $parsedHost[1];
91
+
92
+        if (!isset($params['user'])) {
93
+            throw new \UnexpectedValueException('no authentication parameters specified');
94
+        }
95
+        $this->user = $params['user'];
96
+
97
+        if (isset($params['public_key_auth'])) {
98
+            $this->auth[] = $params['public_key_auth'];
99
+        }
100
+        if (isset($params['password']) && $params['password'] !== '') {
101
+            $this->auth[] = $params['password'];
102
+        }
103
+
104
+        if ($this->auth === []) {
105
+            throw new \UnexpectedValueException('no authentication parameters specified');
106
+        }
107
+
108
+        $this->root
109
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
110
+
111
+        $this->root = '/' . ltrim($this->root, '/');
112
+        $this->root = rtrim($this->root, '/') . '/';
113
+    }
114
+
115
+    /**
116
+     * Returns the connection.
117
+     *
118
+     * @return \phpseclib\Net\SFTP connected client instance
119
+     * @throws \Exception when the connection failed
120
+     */
121
+    public function getConnection() {
122
+        if (!is_null($this->client)) {
123
+            return $this->client;
124
+        }
125
+
126
+        $hostKeys = $this->readHostKeys();
127
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
128
+
129
+        // The SSH Host Key MUST be verified before login().
130
+        $currentHostKey = $this->client->getServerPublicHostKey();
131
+        if (array_key_exists($this->host, $hostKeys)) {
132
+            if ($hostKeys[$this->host] !== $currentHostKey) {
133
+                throw new \Exception('Host public key does not match known key');
134
+            }
135
+        } else {
136
+            $hostKeys[$this->host] = $currentHostKey;
137
+            $this->writeHostKeys($hostKeys);
138
+        }
139
+
140
+        $login = false;
141
+        foreach ($this->auth as $auth) {
142
+            $login = $this->client->login($this->user, $auth);
143
+            if ($login === true) {
144
+                break;
145
+            }
146
+        }
147
+
148
+        if ($login === false) {
149
+            throw new \Exception('Login failed');
150
+        }
151
+        return $this->client;
152
+    }
153
+
154
+    /**
155
+     * {@inheritdoc}
156
+     */
157
+    public function test() {
158
+        if (
159
+            !isset($this->host)
160
+            || !isset($this->user)
161
+        ) {
162
+            return false;
163
+        }
164
+        return $this->getConnection()->nlist() !== false;
165
+    }
166
+
167
+    /**
168
+     * {@inheritdoc}
169
+     */
170
+    public function getId(){
171
+        $id = 'sftp::' . $this->user . '@' . $this->host;
172
+        if ($this->port !== 22) {
173
+            $id .= ':' . $this->port;
174
+        }
175
+        // note: this will double the root slash,
176
+        // we should not change it to keep compatible with
177
+        // old storage ids
178
+        $id .= '/' . $this->root;
179
+        return $id;
180
+    }
181
+
182
+    /**
183
+     * @return string
184
+     */
185
+    public function getHost() {
186
+        return $this->host;
187
+    }
188
+
189
+    /**
190
+     * @return string
191
+     */
192
+    public function getRoot() {
193
+        return $this->root;
194
+    }
195
+
196
+    /**
197
+     * @return mixed
198
+     */
199
+    public function getUser() {
200
+        return $this->user;
201
+    }
202
+
203
+    /**
204
+     * @param string $path
205
+     * @return string
206
+     */
207
+    private function absPath($path) {
208
+        return $this->root . $this->cleanPath($path);
209
+    }
210
+
211
+    /**
212
+     * @return string|false
213
+     */
214
+    private function hostKeysPath() {
215
+        try {
216
+            $storage_view = \OCP\Files::getStorage('files_external');
217
+            if ($storage_view) {
218
+                return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
219
+                    $storage_view->getAbsolutePath('') .
220
+                    'ssh_hostKeys';
221
+            }
222
+        } catch (\Exception $e) {
223
+        }
224
+        return false;
225
+    }
226
+
227
+    /**
228
+     * @param $keys
229
+     * @return bool
230
+     */
231
+    protected function writeHostKeys($keys) {
232
+        try {
233
+            $keyPath = $this->hostKeysPath();
234
+            if ($keyPath && file_exists($keyPath)) {
235
+                $fp = fopen($keyPath, 'w');
236
+                foreach ($keys as $host => $key) {
237
+                    fwrite($fp, $host . '::' . $key . "\n");
238
+                }
239
+                fclose($fp);
240
+                return true;
241
+            }
242
+        } catch (\Exception $e) {
243
+        }
244
+        return false;
245
+    }
246
+
247
+    /**
248
+     * @return array
249
+     */
250
+    protected function readHostKeys() {
251
+        try {
252
+            $keyPath = $this->hostKeysPath();
253
+            if (file_exists($keyPath)) {
254
+                $hosts = [];
255
+                $keys = [];
256
+                $lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
257
+                if ($lines) {
258
+                    foreach ($lines as $line) {
259
+                        $hostKeyArray = explode("::", $line, 2);
260
+                        if (count($hostKeyArray) === 2) {
261
+                            $hosts[] = $hostKeyArray[0];
262
+                            $keys[] = $hostKeyArray[1];
263
+                        }
264
+                    }
265
+                    return array_combine($hosts, $keys);
266
+                }
267
+            }
268
+        } catch (\Exception $e) {
269
+        }
270
+        return [];
271
+    }
272
+
273
+    /**
274
+     * {@inheritdoc}
275
+     */
276
+    public function mkdir($path) {
277
+        try {
278
+            return $this->getConnection()->mkdir($this->absPath($path));
279
+        } catch (\Exception $e) {
280
+            return false;
281
+        }
282
+    }
283
+
284
+    /**
285
+     * {@inheritdoc}
286
+     */
287
+    public function rmdir($path) {
288
+        try {
289
+            $result = $this->getConnection()->delete($this->absPath($path), true);
290
+            // workaround: stray stat cache entry when deleting empty folders
291
+            // see https://github.com/phpseclib/phpseclib/issues/706
292
+            $this->getConnection()->clearStatCache();
293
+            return $result;
294
+        } catch (\Exception $e) {
295
+            return false;
296
+        }
297
+    }
298
+
299
+    /**
300
+     * {@inheritdoc}
301
+     */
302
+    public function opendir($path) {
303
+        try {
304
+            $list = $this->getConnection()->nlist($this->absPath($path));
305
+            if ($list === false) {
306
+                return false;
307
+            }
308
+
309
+            $id = md5('sftp:' . $path);
310
+            $dirStream = [];
311
+            foreach($list as $file) {
312
+                if ($file !== '.' && $file !== '..') {
313
+                    $dirStream[] = $file;
314
+                }
315
+            }
316
+            return IteratorDirectory::wrap($dirStream);
317
+        } catch(\Exception $e) {
318
+            return false;
319
+        }
320
+    }
321
+
322
+    /**
323
+     * {@inheritdoc}
324
+     */
325
+    public function filetype($path) {
326
+        try {
327
+            $stat = $this->getConnection()->stat($this->absPath($path));
328
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
329
+                return 'file';
330
+            }
331
+
332
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
333
+                return 'dir';
334
+            }
335
+        } catch (\Exception $e) {
336
+
337
+        }
338
+        return false;
339
+    }
340
+
341
+    /**
342
+     * {@inheritdoc}
343
+     */
344
+    public function file_exists($path) {
345
+        try {
346
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
347
+        } catch (\Exception $e) {
348
+            return false;
349
+        }
350
+    }
351
+
352
+    /**
353
+     * {@inheritdoc}
354
+     */
355
+    public function unlink($path) {
356
+        try {
357
+            return $this->getConnection()->delete($this->absPath($path), true);
358
+        } catch (\Exception $e) {
359
+            return false;
360
+        }
361
+    }
362
+
363
+    /**
364
+     * {@inheritdoc}
365
+     */
366
+    public function fopen($path, $mode) {
367
+        try {
368
+            $absPath = $this->absPath($path);
369
+            switch($mode) {
370
+                case 'r':
371
+                case 'rb':
372
+                    if ( !$this->file_exists($path)) {
373
+                        return false;
374
+                    }
375
+                    SFTPReadStream::register();
376
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
377
+                    $handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
378
+                    return RetryWrapper::wrap($handle);
379
+                case 'w':
380
+                case 'wb':
381
+                    SFTPWriteStream::register();
382
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
383
+                    return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
384
+                case 'a':
385
+                case 'ab':
386
+                case 'r+':
387
+                case 'w+':
388
+                case 'wb+':
389
+                case 'a+':
390
+                case 'x':
391
+                case 'x+':
392
+                case 'c':
393
+                case 'c+':
394
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
395
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
396
+                    return RetryWrapper::wrap($handle);
397
+            }
398
+        } catch (\Exception $e) {
399
+        }
400
+        return false;
401
+    }
402
+
403
+    /**
404
+     * {@inheritdoc}
405
+     */
406
+    public function touch($path, $mtime=null) {
407
+        try {
408
+            if (!is_null($mtime)) {
409
+                return false;
410
+            }
411
+            if (!$this->file_exists($path)) {
412
+                $this->getConnection()->put($this->absPath($path), '');
413
+            } else {
414
+                return false;
415
+            }
416
+        } catch (\Exception $e) {
417
+            return false;
418
+        }
419
+        return true;
420
+    }
421
+
422
+    /**
423
+     * @param string $path
424
+     * @param string $target
425
+     * @throws \Exception
426
+     */
427
+    public function getFile($path, $target) {
428
+        $this->getConnection()->get($path, $target);
429
+    }
430
+
431
+    /**
432
+     * @param string $path
433
+     * @param string $target
434
+     * @throws \Exception
435
+     */
436
+    public function uploadFile($path, $target) {
437
+        $this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
438
+    }
439
+
440
+    /**
441
+     * {@inheritdoc}
442
+     */
443
+    public function rename($source, $target) {
444
+        try {
445
+            if ($this->file_exists($target)) {
446
+                $this->unlink($target);
447
+            }
448
+            return $this->getConnection()->rename(
449
+                $this->absPath($source),
450
+                $this->absPath($target)
451
+            );
452
+        } catch (\Exception $e) {
453
+            return false;
454
+        }
455
+    }
456
+
457
+    /**
458
+     * {@inheritdoc}
459
+     */
460
+    public function stat($path) {
461
+        try {
462
+            $stat = $this->getConnection()->stat($this->absPath($path));
463
+
464
+            $mtime = $stat ? $stat['mtime'] : -1;
465
+            $size = $stat ? $stat['size'] : 0;
466
+
467
+            return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
468
+        } catch (\Exception $e) {
469
+            return false;
470
+        }
471
+    }
472
+
473
+    /**
474
+     * @param string $path
475
+     * @return string
476
+     */
477
+    public function constructUrl($path) {
478
+        // Do not pass the password here. We want to use the Net_SFTP object
479
+        // supplied via stream context or fail. We only supply username and
480
+        // hostname because this might show up in logs (they are not used).
481
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
482
+        return $url;
483
+    }
484 484
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Controller/ApiController.php 1 patch
Indentation   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -36,73 +36,73 @@
 block discarded – undo
36 36
 
37 37
 class ApiController extends OCSController {
38 38
 
39
-	/** @var IUserSession */
40
-	private $userSession;
41
-
42
-	public function __construct(string $appName,
43
-								IRequest $request,
44
-								IUserSession $userSession) {
45
-		parent::__construct($appName, $request);
46
-
47
-		$this->userSession = $userSession;
48
-	}
49
-
50
-	/**
51
-	 * Formats the given mount config to a mount entry.
52
-	 *
53
-	 * @param string $mountPoint mount point name, relative to the data dir
54
-	 * @param array $mountConfig mount config to format
55
-	 *
56
-	 * @return array entry
57
-	 */
58
-	private function formatMount(string $mountPoint, array $mountConfig): array {
59
-		// strip "/$user/files" from mount point
60
-		$mountPoint = explode('/', trim($mountPoint, '/'), 3);
61
-		$mountPoint = $mountPoint[2] ?? '';
62
-
63
-		// split path from mount point
64
-		$path = \dirname($mountPoint);
65
-		if ($path === '.') {
66
-			$path = '';
67
-		}
68
-
69
-		$isSystemMount = !$mountConfig['personal'];
70
-
71
-		$permissions = \OCP\Constants::PERMISSION_READ;
72
-		// personal mounts can be deleted
73
-		if (!$isSystemMount) {
74
-			$permissions |= \OCP\Constants::PERMISSION_DELETE;
75
-		}
76
-
77
-		$entry = [
78
-			'name' => basename($mountPoint),
79
-			'path' => $path,
80
-			'type' => 'dir',
81
-			'backend' => $mountConfig['backend'],
82
-			'scope' => $isSystemMount ? 'system' : 'personal',
83
-			'permissions' => $permissions,
84
-			'id' => $mountConfig['id'],
85
-			'class' => $mountConfig['class']
86
-		];
87
-		return $entry;
88
-	}
89
-
90
-	/**
91
-	 * @NoAdminRequired
92
-	 *
93
-	 * Returns the mount points visible for this user.
94
-	 *
95
-	 * @return DataResponse share information
96
-	 */
97
-	public function getUserMounts(): DataResponse {
98
-		$entries = [];
99
-		$user = $this->userSession->getUser()->getUID();
100
-
101
-		$mounts = \OC_Mount_Config::getAbsoluteMountPoints($user);
102
-		foreach($mounts as $mountPoint => $mount) {
103
-			$entries[] = $this->formatMount($mountPoint, $mount);
104
-		}
105
-
106
-		return new DataResponse($entries);
107
-	}
39
+    /** @var IUserSession */
40
+    private $userSession;
41
+
42
+    public function __construct(string $appName,
43
+                                IRequest $request,
44
+                                IUserSession $userSession) {
45
+        parent::__construct($appName, $request);
46
+
47
+        $this->userSession = $userSession;
48
+    }
49
+
50
+    /**
51
+     * Formats the given mount config to a mount entry.
52
+     *
53
+     * @param string $mountPoint mount point name, relative to the data dir
54
+     * @param array $mountConfig mount config to format
55
+     *
56
+     * @return array entry
57
+     */
58
+    private function formatMount(string $mountPoint, array $mountConfig): array {
59
+        // strip "/$user/files" from mount point
60
+        $mountPoint = explode('/', trim($mountPoint, '/'), 3);
61
+        $mountPoint = $mountPoint[2] ?? '';
62
+
63
+        // split path from mount point
64
+        $path = \dirname($mountPoint);
65
+        if ($path === '.') {
66
+            $path = '';
67
+        }
68
+
69
+        $isSystemMount = !$mountConfig['personal'];
70
+
71
+        $permissions = \OCP\Constants::PERMISSION_READ;
72
+        // personal mounts can be deleted
73
+        if (!$isSystemMount) {
74
+            $permissions |= \OCP\Constants::PERMISSION_DELETE;
75
+        }
76
+
77
+        $entry = [
78
+            'name' => basename($mountPoint),
79
+            'path' => $path,
80
+            'type' => 'dir',
81
+            'backend' => $mountConfig['backend'],
82
+            'scope' => $isSystemMount ? 'system' : 'personal',
83
+            'permissions' => $permissions,
84
+            'id' => $mountConfig['id'],
85
+            'class' => $mountConfig['class']
86
+        ];
87
+        return $entry;
88
+    }
89
+
90
+    /**
91
+     * @NoAdminRequired
92
+     *
93
+     * Returns the mount points visible for this user.
94
+     *
95
+     * @return DataResponse share information
96
+     */
97
+    public function getUserMounts(): DataResponse {
98
+        $entries = [];
99
+        $user = $this->userSession->getUser()->getUID();
100
+
101
+        $mounts = \OC_Mount_Config::getAbsoluteMountPoints($user);
102
+        foreach($mounts as $mountPoint => $mount) {
103
+            $entries[] = $this->formatMount($mountPoint, $mount);
104
+        }
105
+
106
+        return new DataResponse($entries);
107
+    }
108 108
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Controller/GlobalStoragesController.php 1 patch
Indentation   +145 added lines, -145 removed lines patch added patch discarded remove patch
@@ -40,151 +40,151 @@
 block discarded – undo
40 40
  * Global storages controller
41 41
  */
42 42
 class GlobalStoragesController extends StoragesController {
43
-	/**
44
-	 * Creates a new global storages controller.
45
-	 *
46
-	 * @param string $AppName application name
47
-	 * @param IRequest $request request object
48
-	 * @param IL10N $l10n l10n service
49
-	 * @param GlobalStoragesService $globalStoragesService storage service
50
-	 * @param ILogger $logger
51
-	 */
52
-	public function __construct(
53
-		$AppName,
54
-		IRequest $request,
55
-		IL10N $l10n,
56
-		GlobalStoragesService $globalStoragesService,
57
-		ILogger $logger
58
-	) {
59
-		parent::__construct(
60
-			$AppName,
61
-			$request,
62
-			$l10n,
63
-			$globalStoragesService,
64
-			$logger
65
-		);
66
-	}
67
-
68
-	/**
69
-	 * Create an external storage entry.
70
-	 *
71
-	 * @param string $mountPoint storage mount point
72
-	 * @param string $backend backend identifier
73
-	 * @param string $authMechanism authentication mechanism identifier
74
-	 * @param array $backendOptions backend-specific options
75
-	 * @param array $mountOptions mount-specific options
76
-	 * @param array $applicableUsers users for which to mount the storage
77
-	 * @param array $applicableGroups groups for which to mount the storage
78
-	 * @param int $priority priority
79
-	 *
80
-	 * @return DataResponse
81
-	 */
82
-	public function create(
83
-		$mountPoint,
84
-		$backend,
85
-		$authMechanism,
86
-		$backendOptions,
87
-		$mountOptions,
88
-		$applicableUsers,
89
-		$applicableGroups,
90
-		$priority
91
-	) {
92
-		$newStorage = $this->createStorage(
93
-			$mountPoint,
94
-			$backend,
95
-			$authMechanism,
96
-			$backendOptions,
97
-			$mountOptions,
98
-			$applicableUsers,
99
-			$applicableGroups,
100
-			$priority
101
-		);
102
-		if ($newStorage instanceof DataResponse) {
103
-			return $newStorage;
104
-		}
105
-
106
-		$response = $this->validate($newStorage);
107
-		if (!empty($response)) {
108
-			return $response;
109
-		}
110
-
111
-		$newStorage = $this->service->addStorage($newStorage);
112
-
113
-		$this->updateStorageStatus($newStorage);
114
-
115
-		return new DataResponse(
116
-			$this->formatStorageForUI($newStorage),
117
-			Http::STATUS_CREATED
118
-		);
119
-	}
120
-
121
-	/**
122
-	 * Update an external storage entry.
123
-	 *
124
-	 * @param int $id storage id
125
-	 * @param string $mountPoint storage mount point
126
-	 * @param string $backend backend identifier
127
-	 * @param string $authMechanism authentication mechansim identifier
128
-	 * @param array $backendOptions backend-specific options
129
-	 * @param array $mountOptions mount-specific options
130
-	 * @param array $applicableUsers users for which to mount the storage
131
-	 * @param array $applicableGroups groups for which to mount the storage
132
-	 * @param int $priority priority
133
-	 * @param bool $testOnly whether to storage should only test the connection or do more things
134
-	 *
135
-	 * @return DataResponse
136
-	 */
137
-	public function update(
138
-		$id,
139
-		$mountPoint,
140
-		$backend,
141
-		$authMechanism,
142
-		$backendOptions,
143
-		$mountOptions,
144
-		$applicableUsers,
145
-		$applicableGroups,
146
-		$priority,
147
-		$testOnly = true
148
-	) {
149
-		$storage = $this->createStorage(
150
-			$mountPoint,
151
-			$backend,
152
-			$authMechanism,
153
-			$backendOptions,
154
-			$mountOptions,
155
-			$applicableUsers,
156
-			$applicableGroups,
157
-			$priority
158
-		);
159
-		if ($storage instanceof DataResponse) {
160
-			return $storage;
161
-		}
162
-		$storage->setId($id);
163
-
164
-		$response = $this->validate($storage);
165
-		if (!empty($response)) {
166
-			return $response;
167
-		}
168
-
169
-		try {
170
-			$storage = $this->service->updateStorage($storage);
171
-		} catch (NotFoundException $e) {
172
-			return new DataResponse(
173
-				[
174
-					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
175
-				],
176
-				Http::STATUS_NOT_FOUND
177
-			);
178
-		}
179
-
180
-		$this->updateStorageStatus($storage, $testOnly);
181
-
182
-		return new DataResponse(
183
-			$this->formatStorageForUI($storage),
184
-			Http::STATUS_OK
185
-		);
186
-
187
-	}
43
+    /**
44
+     * Creates a new global storages controller.
45
+     *
46
+     * @param string $AppName application name
47
+     * @param IRequest $request request object
48
+     * @param IL10N $l10n l10n service
49
+     * @param GlobalStoragesService $globalStoragesService storage service
50
+     * @param ILogger $logger
51
+     */
52
+    public function __construct(
53
+        $AppName,
54
+        IRequest $request,
55
+        IL10N $l10n,
56
+        GlobalStoragesService $globalStoragesService,
57
+        ILogger $logger
58
+    ) {
59
+        parent::__construct(
60
+            $AppName,
61
+            $request,
62
+            $l10n,
63
+            $globalStoragesService,
64
+            $logger
65
+        );
66
+    }
67
+
68
+    /**
69
+     * Create an external storage entry.
70
+     *
71
+     * @param string $mountPoint storage mount point
72
+     * @param string $backend backend identifier
73
+     * @param string $authMechanism authentication mechanism identifier
74
+     * @param array $backendOptions backend-specific options
75
+     * @param array $mountOptions mount-specific options
76
+     * @param array $applicableUsers users for which to mount the storage
77
+     * @param array $applicableGroups groups for which to mount the storage
78
+     * @param int $priority priority
79
+     *
80
+     * @return DataResponse
81
+     */
82
+    public function create(
83
+        $mountPoint,
84
+        $backend,
85
+        $authMechanism,
86
+        $backendOptions,
87
+        $mountOptions,
88
+        $applicableUsers,
89
+        $applicableGroups,
90
+        $priority
91
+    ) {
92
+        $newStorage = $this->createStorage(
93
+            $mountPoint,
94
+            $backend,
95
+            $authMechanism,
96
+            $backendOptions,
97
+            $mountOptions,
98
+            $applicableUsers,
99
+            $applicableGroups,
100
+            $priority
101
+        );
102
+        if ($newStorage instanceof DataResponse) {
103
+            return $newStorage;
104
+        }
105
+
106
+        $response = $this->validate($newStorage);
107
+        if (!empty($response)) {
108
+            return $response;
109
+        }
110
+
111
+        $newStorage = $this->service->addStorage($newStorage);
112
+
113
+        $this->updateStorageStatus($newStorage);
114
+
115
+        return new DataResponse(
116
+            $this->formatStorageForUI($newStorage),
117
+            Http::STATUS_CREATED
118
+        );
119
+    }
120
+
121
+    /**
122
+     * Update an external storage entry.
123
+     *
124
+     * @param int $id storage id
125
+     * @param string $mountPoint storage mount point
126
+     * @param string $backend backend identifier
127
+     * @param string $authMechanism authentication mechansim identifier
128
+     * @param array $backendOptions backend-specific options
129
+     * @param array $mountOptions mount-specific options
130
+     * @param array $applicableUsers users for which to mount the storage
131
+     * @param array $applicableGroups groups for which to mount the storage
132
+     * @param int $priority priority
133
+     * @param bool $testOnly whether to storage should only test the connection or do more things
134
+     *
135
+     * @return DataResponse
136
+     */
137
+    public function update(
138
+        $id,
139
+        $mountPoint,
140
+        $backend,
141
+        $authMechanism,
142
+        $backendOptions,
143
+        $mountOptions,
144
+        $applicableUsers,
145
+        $applicableGroups,
146
+        $priority,
147
+        $testOnly = true
148
+    ) {
149
+        $storage = $this->createStorage(
150
+            $mountPoint,
151
+            $backend,
152
+            $authMechanism,
153
+            $backendOptions,
154
+            $mountOptions,
155
+            $applicableUsers,
156
+            $applicableGroups,
157
+            $priority
158
+        );
159
+        if ($storage instanceof DataResponse) {
160
+            return $storage;
161
+        }
162
+        $storage->setId($id);
163
+
164
+        $response = $this->validate($storage);
165
+        if (!empty($response)) {
166
+            return $response;
167
+        }
168
+
169
+        try {
170
+            $storage = $this->service->updateStorage($storage);
171
+        } catch (NotFoundException $e) {
172
+            return new DataResponse(
173
+                [
174
+                    'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
175
+                ],
176
+                Http::STATUS_NOT_FOUND
177
+            );
178
+        }
179
+
180
+        $this->updateStorageStatus($storage, $testOnly);
181
+
182
+        return new DataResponse(
183
+            $this->formatStorageForUI($storage),
184
+            Http::STATUS_OK
185
+        );
186
+
187
+    }
188 188
 
189 189
 
190 190
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Controller/UserGlobalStoragesController.php 1 patch
Indentation   +160 added lines, -160 removed lines patch added patch discarded remove patch
@@ -45,165 +45,165 @@
 block discarded – undo
45 45
  * User global storages controller
46 46
  */
47 47
 class UserGlobalStoragesController extends StoragesController {
48
-	/**
49
-	 * @var IUserSession
50
-	 */
51
-	private $userSession;
52
-
53
-	/**
54
-	 * Creates a new user global storages controller.
55
-	 *
56
-	 * @param string $AppName application name
57
-	 * @param IRequest $request request object
58
-	 * @param IL10N $l10n l10n service
59
-	 * @param UserGlobalStoragesService $userGlobalStoragesService storage service
60
-	 * @param IUserSession $userSession
61
-	 */
62
-	public function __construct(
63
-		$AppName,
64
-		IRequest $request,
65
-		IL10N $l10n,
66
-		UserGlobalStoragesService $userGlobalStoragesService,
67
-		IUserSession $userSession,
68
-		ILogger $logger
69
-	) {
70
-		parent::__construct(
71
-			$AppName,
72
-			$request,
73
-			$l10n,
74
-			$userGlobalStoragesService,
75
-			$logger
76
-		);
77
-		$this->userSession = $userSession;
78
-	}
79
-
80
-	/**
81
-	 * Get all storage entries
82
-	 *
83
-	 * @return DataResponse
84
-	 *
85
-	 * @NoAdminRequired
86
-	 */
87
-	public function index() {
88
-		$storages = $this->formatStoragesForUI($this->service->getUniqueStorages());
89
-
90
-		// remove configuration data, this must be kept private
91
-		foreach ($storages as $storage) {
92
-			$this->sanitizeStorage($storage);
93
-		}
94
-
95
-		return new DataResponse(
96
-			$storages,
97
-			Http::STATUS_OK
98
-		);
99
-	}
100
-
101
-	protected function manipulateStorageConfig(StorageConfig $storage) {
102
-		/** @var AuthMechanism */
103
-		$authMechanism = $storage->getAuthMechanism();
104
-		$authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
105
-		/** @var Backend */
106
-		$backend = $storage->getBackend();
107
-		$backend->manipulateStorageConfig($storage, $this->userSession->getUser());
108
-	}
109
-
110
-	/**
111
-	 * Get an external storage entry.
112
-	 *
113
-	 * @param int $id storage id
114
-	 * @param bool $testOnly whether to storage should only test the connection or do more things
115
-	 * @return DataResponse
116
-	 *
117
-	 * @NoAdminRequired
118
-	 */
119
-	public function show($id, $testOnly = true) {
120
-		try {
121
-			$storage = $this->service->getStorage($id);
122
-
123
-			$this->updateStorageStatus($storage, $testOnly);
124
-		} catch (NotFoundException $e) {
125
-			return new DataResponse(
126
-				[
127
-					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
128
-				],
129
-				Http::STATUS_NOT_FOUND
130
-			);
131
-		}
132
-
133
-		$this->sanitizeStorage($storage);
134
-
135
-		return new DataResponse(
136
-			$this->formatStorageForUI($storage),
137
-			Http::STATUS_OK
138
-		);
139
-	}
140
-
141
-	/**
142
-	 * Update an external storage entry.
143
-	 * Only allows setting user provided backend fields
144
-	 *
145
-	 * @param int $id storage id
146
-	 * @param array $backendOptions backend-specific options
147
-	 * @param bool $testOnly whether to storage should only test the connection or do more things
148
-	 *
149
-	 * @return DataResponse
150
-	 *
151
-	 * @NoAdminRequired
152
-	 */
153
-	public function update(
154
-		$id,
155
-		$backendOptions,
156
-		$testOnly = true
157
-	) {
158
-		try {
159
-			$storage = $this->service->getStorage($id);
160
-			$authMechanism = $storage->getAuthMechanism();
161
-			if ($authMechanism instanceof IUserProvided || $authMechanism instanceof  UserGlobalAuth) {
162
-				$authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions);
163
-				$authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
164
-			} else {
165
-				return new DataResponse(
166
-					[
167
-						'message' => (string)$this->l10n->t('Storage with ID "%d" is not user editable', [$id])
168
-					],
169
-					Http::STATUS_FORBIDDEN
170
-				);
171
-			}
172
-		} catch (NotFoundException $e) {
173
-			return new DataResponse(
174
-				[
175
-					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
176
-				],
177
-				Http::STATUS_NOT_FOUND
178
-			);
179
-		}
180
-
181
-		$this->updateStorageStatus($storage, $testOnly);
182
-		$this->sanitizeStorage($storage);
183
-
184
-		return new DataResponse(
185
-			$this->formatStorageForUI($storage),
186
-			Http::STATUS_OK
187
-		);
188
-
189
-	}
190
-
191
-	/**
192
-	 * Remove sensitive data from a StorageConfig before returning it to the user
193
-	 *
194
-	 * @param StorageConfig $storage
195
-	 */
196
-	protected function sanitizeStorage(StorageConfig $storage) {
197
-		$storage->setBackendOptions([]);
198
-		$storage->setMountOptions([]);
199
-
200
-		if ($storage->getAuthMechanism() instanceof IUserProvided) {
201
-			try {
202
-				$storage->getAuthMechanism()->manipulateStorageConfig($storage, $this->userSession->getUser());
203
-			} catch (InsufficientDataForMeaningfulAnswerException $e) {
204
-				// not configured yet
205
-			}
206
-		}
207
-	}
48
+    /**
49
+     * @var IUserSession
50
+     */
51
+    private $userSession;
52
+
53
+    /**
54
+     * Creates a new user global storages controller.
55
+     *
56
+     * @param string $AppName application name
57
+     * @param IRequest $request request object
58
+     * @param IL10N $l10n l10n service
59
+     * @param UserGlobalStoragesService $userGlobalStoragesService storage service
60
+     * @param IUserSession $userSession
61
+     */
62
+    public function __construct(
63
+        $AppName,
64
+        IRequest $request,
65
+        IL10N $l10n,
66
+        UserGlobalStoragesService $userGlobalStoragesService,
67
+        IUserSession $userSession,
68
+        ILogger $logger
69
+    ) {
70
+        parent::__construct(
71
+            $AppName,
72
+            $request,
73
+            $l10n,
74
+            $userGlobalStoragesService,
75
+            $logger
76
+        );
77
+        $this->userSession = $userSession;
78
+    }
79
+
80
+    /**
81
+     * Get all storage entries
82
+     *
83
+     * @return DataResponse
84
+     *
85
+     * @NoAdminRequired
86
+     */
87
+    public function index() {
88
+        $storages = $this->formatStoragesForUI($this->service->getUniqueStorages());
89
+
90
+        // remove configuration data, this must be kept private
91
+        foreach ($storages as $storage) {
92
+            $this->sanitizeStorage($storage);
93
+        }
94
+
95
+        return new DataResponse(
96
+            $storages,
97
+            Http::STATUS_OK
98
+        );
99
+    }
100
+
101
+    protected function manipulateStorageConfig(StorageConfig $storage) {
102
+        /** @var AuthMechanism */
103
+        $authMechanism = $storage->getAuthMechanism();
104
+        $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
105
+        /** @var Backend */
106
+        $backend = $storage->getBackend();
107
+        $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
108
+    }
109
+
110
+    /**
111
+     * Get an external storage entry.
112
+     *
113
+     * @param int $id storage id
114
+     * @param bool $testOnly whether to storage should only test the connection or do more things
115
+     * @return DataResponse
116
+     *
117
+     * @NoAdminRequired
118
+     */
119
+    public function show($id, $testOnly = true) {
120
+        try {
121
+            $storage = $this->service->getStorage($id);
122
+
123
+            $this->updateStorageStatus($storage, $testOnly);
124
+        } catch (NotFoundException $e) {
125
+            return new DataResponse(
126
+                [
127
+                    'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
128
+                ],
129
+                Http::STATUS_NOT_FOUND
130
+            );
131
+        }
132
+
133
+        $this->sanitizeStorage($storage);
134
+
135
+        return new DataResponse(
136
+            $this->formatStorageForUI($storage),
137
+            Http::STATUS_OK
138
+        );
139
+    }
140
+
141
+    /**
142
+     * Update an external storage entry.
143
+     * Only allows setting user provided backend fields
144
+     *
145
+     * @param int $id storage id
146
+     * @param array $backendOptions backend-specific options
147
+     * @param bool $testOnly whether to storage should only test the connection or do more things
148
+     *
149
+     * @return DataResponse
150
+     *
151
+     * @NoAdminRequired
152
+     */
153
+    public function update(
154
+        $id,
155
+        $backendOptions,
156
+        $testOnly = true
157
+    ) {
158
+        try {
159
+            $storage = $this->service->getStorage($id);
160
+            $authMechanism = $storage->getAuthMechanism();
161
+            if ($authMechanism instanceof IUserProvided || $authMechanism instanceof  UserGlobalAuth) {
162
+                $authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions);
163
+                $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
164
+            } else {
165
+                return new DataResponse(
166
+                    [
167
+                        'message' => (string)$this->l10n->t('Storage with ID "%d" is not user editable', [$id])
168
+                    ],
169
+                    Http::STATUS_FORBIDDEN
170
+                );
171
+            }
172
+        } catch (NotFoundException $e) {
173
+            return new DataResponse(
174
+                [
175
+                    'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
176
+                ],
177
+                Http::STATUS_NOT_FOUND
178
+            );
179
+        }
180
+
181
+        $this->updateStorageStatus($storage, $testOnly);
182
+        $this->sanitizeStorage($storage);
183
+
184
+        return new DataResponse(
185
+            $this->formatStorageForUI($storage),
186
+            Http::STATUS_OK
187
+        );
188
+
189
+    }
190
+
191
+    /**
192
+     * Remove sensitive data from a StorageConfig before returning it to the user
193
+     *
194
+     * @param StorageConfig $storage
195
+     */
196
+    protected function sanitizeStorage(StorageConfig $storage) {
197
+        $storage->setBackendOptions([]);
198
+        $storage->setMountOptions([]);
199
+
200
+        if ($storage->getAuthMechanism() instanceof IUserProvided) {
201
+            try {
202
+                $storage->getAuthMechanism()->manipulateStorageConfig($storage, $this->userSession->getUser());
203
+            } catch (InsufficientDataForMeaningfulAnswerException $e) {
204
+                // not configured yet
205
+            }
206
+        }
207
+    }
208 208
 
209 209
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Controller/AjaxController.php 1 patch
Indentation   +73 added lines, -73 removed lines patch added patch discarded remove patch
@@ -37,84 +37,84 @@
 block discarded – undo
37 37
 use OCP\IUserSession;
38 38
 
39 39
 class AjaxController extends Controller {
40
-	/** @var RSA */
41
-	private $rsaMechanism;
42
-	/** @var GlobalAuth  */
43
-	private $globalAuth;
44
-	/** @var IUserSession */
45
-	private $userSession;
46
-	/** @var IGroupManager */
47
-	private $groupManager;
40
+    /** @var RSA */
41
+    private $rsaMechanism;
42
+    /** @var GlobalAuth  */
43
+    private $globalAuth;
44
+    /** @var IUserSession */
45
+    private $userSession;
46
+    /** @var IGroupManager */
47
+    private $groupManager;
48 48
 
49
-	/**
50
-	 * @param string $appName
51
-	 * @param IRequest $request
52
-	 * @param RSA $rsaMechanism
53
-	 * @param GlobalAuth $globalAuth
54
-	 * @param IUserSession $userSession
55
-	 * @param IGroupManager $groupManager
56
-	 */
57
-	public function __construct($appName,
58
-								IRequest $request,
59
-								RSA $rsaMechanism,
60
-								GlobalAuth $globalAuth,
61
-								IUserSession $userSession,
62
-								IGroupManager $groupManager) {
63
-		parent::__construct($appName, $request);
64
-		$this->rsaMechanism = $rsaMechanism;
65
-		$this->globalAuth = $globalAuth;
66
-		$this->userSession = $userSession;
67
-		$this->groupManager = $groupManager;
68
-	}
49
+    /**
50
+     * @param string $appName
51
+     * @param IRequest $request
52
+     * @param RSA $rsaMechanism
53
+     * @param GlobalAuth $globalAuth
54
+     * @param IUserSession $userSession
55
+     * @param IGroupManager $groupManager
56
+     */
57
+    public function __construct($appName,
58
+                                IRequest $request,
59
+                                RSA $rsaMechanism,
60
+                                GlobalAuth $globalAuth,
61
+                                IUserSession $userSession,
62
+                                IGroupManager $groupManager) {
63
+        parent::__construct($appName, $request);
64
+        $this->rsaMechanism = $rsaMechanism;
65
+        $this->globalAuth = $globalAuth;
66
+        $this->userSession = $userSession;
67
+        $this->groupManager = $groupManager;
68
+    }
69 69
 
70
-	/**
71
-	 * @param int $keyLength
72
-	 * @return array
73
-	 */
74
-	private function generateSshKeys($keyLength) {
75
-		$key = $this->rsaMechanism->createKey($keyLength);
76
-		// Replace the placeholder label with a more meaningful one
77
-		$key['publickey'] = str_replace('phpseclib-generated-key', gethostname(), $key['publickey']);
70
+    /**
71
+     * @param int $keyLength
72
+     * @return array
73
+     */
74
+    private function generateSshKeys($keyLength) {
75
+        $key = $this->rsaMechanism->createKey($keyLength);
76
+        // Replace the placeholder label with a more meaningful one
77
+        $key['publickey'] = str_replace('phpseclib-generated-key', gethostname(), $key['publickey']);
78 78
 
79
-		return $key;
80
-	}
79
+        return $key;
80
+    }
81 81
 
82
-	/**
83
-	 * Generates an SSH public/private key pair.
84
-	 *
85
-	 * @NoAdminRequired
86
-	 * @param int $keyLength
87
-	 */
88
-	public function getSshKeys($keyLength = 1024) {
89
-		$key = $this->generateSshKeys($keyLength);
90
-		return new JSONResponse(
91
-			['data' => [
92
-				'private_key' => $key['privatekey'],
93
-				'public_key' => $key['publickey']
94
-			],
95
-			'status' => 'success'
96
-		]);
97
-	}
82
+    /**
83
+     * Generates an SSH public/private key pair.
84
+     *
85
+     * @NoAdminRequired
86
+     * @param int $keyLength
87
+     */
88
+    public function getSshKeys($keyLength = 1024) {
89
+        $key = $this->generateSshKeys($keyLength);
90
+        return new JSONResponse(
91
+            ['data' => [
92
+                'private_key' => $key['privatekey'],
93
+                'public_key' => $key['publickey']
94
+            ],
95
+            'status' => 'success'
96
+        ]);
97
+    }
98 98
 
99
-	/**
100
-	 * @NoAdminRequired
101
-	 *
102
-	 * @param string $uid
103
-	 * @param string $user
104
-	 * @param string $password
105
-	 * @return bool
106
-	 */
107
-	public function saveGlobalCredentials($uid, $user, $password) {
108
-		$currentUser = $this->userSession->getUser();
99
+    /**
100
+     * @NoAdminRequired
101
+     *
102
+     * @param string $uid
103
+     * @param string $user
104
+     * @param string $password
105
+     * @return bool
106
+     */
107
+    public function saveGlobalCredentials($uid, $user, $password) {
108
+        $currentUser = $this->userSession->getUser();
109 109
 
110
-		// Non-admins can only edit their own credentials
111
-		$allowedToEdit = ($this->groupManager->isAdmin($currentUser->getUID()) || $currentUser->getUID() === $uid);
110
+        // Non-admins can only edit their own credentials
111
+        $allowedToEdit = ($this->groupManager->isAdmin($currentUser->getUID()) || $currentUser->getUID() === $uid);
112 112
 
113
-		if ($allowedToEdit) {
114
-			$this->globalAuth->saveAuth($uid, $user, $password);
115
-			return true;
116
-		} else {
117
-			return false;
118
-		}
119
-	}
113
+        if ($allowedToEdit) {
114
+            $this->globalAuth->saveAuth($uid, $user, $password);
115
+            return true;
116
+        } else {
117
+            return false;
118
+        }
119
+    }
120 120
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Controller/UserStoragesController.php 1 patch
Indentation   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -44,185 +44,185 @@
 block discarded – undo
44 44
  * User storages controller
45 45
  */
46 46
 class UserStoragesController extends StoragesController {
47
-	/**
48
-	 * @var IUserSession
49
-	 */
50
-	private $userSession;
51
-
52
-	/**
53
-	 * Creates a new user storages controller.
54
-	 *
55
-	 * @param string $AppName application name
56
-	 * @param IRequest $request request object
57
-	 * @param IL10N $l10n l10n service
58
-	 * @param UserStoragesService $userStoragesService storage service
59
-	 * @param IUserSession $userSession
60
-	 * @param ILogger $logger
61
-	 */
62
-	public function __construct(
63
-		$AppName,
64
-		IRequest $request,
65
-		IL10N $l10n,
66
-		UserStoragesService $userStoragesService,
67
-		IUserSession $userSession,
68
-		ILogger $logger
69
-	) {
70
-		parent::__construct(
71
-			$AppName,
72
-			$request,
73
-			$l10n,
74
-			$userStoragesService,
75
-			$logger
76
-		);
77
-		$this->userSession = $userSession;
78
-	}
79
-
80
-	protected function manipulateStorageConfig(StorageConfig $storage) {
81
-		/** @var AuthMechanism */
82
-		$authMechanism = $storage->getAuthMechanism();
83
-		$authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
84
-		/** @var Backend */
85
-		$backend = $storage->getBackend();
86
-		$backend->manipulateStorageConfig($storage, $this->userSession->getUser());
87
-	}
88
-
89
-	/**
90
-	 * Get all storage entries
91
-	 *
92
-	 * @NoAdminRequired
93
-	 *
94
-	 * @return DataResponse
95
-	 */
96
-	public function index() {
97
-		return parent::index();
98
-	}
99
-
100
-	/**
101
-	 * Return storage
102
-	 *
103
-	 * @NoAdminRequired
104
-	 *
105
-	 * {@inheritdoc}
106
-	 */
107
-	public function show($id, $testOnly = true) {
108
-		return parent::show($id, $testOnly);
109
-	}
110
-
111
-	/**
112
-	 * Create an external storage entry.
113
-	 *
114
-	 * @param string $mountPoint storage mount point
115
-	 * @param string $backend backend identifier
116
-	 * @param string $authMechanism authentication mechanism identifier
117
-	 * @param array $backendOptions backend-specific options
118
-	 * @param array $mountOptions backend-specific mount options
119
-	 *
120
-	 * @return DataResponse
121
-	 *
122
-	 * @NoAdminRequired
123
-	 */
124
-	public function create(
125
-		$mountPoint,
126
-		$backend,
127
-		$authMechanism,
128
-		$backendOptions,
129
-		$mountOptions
130
-	) {
131
-		$newStorage = $this->createStorage(
132
-			$mountPoint,
133
-			$backend,
134
-			$authMechanism,
135
-			$backendOptions,
136
-			$mountOptions
137
-		);
138
-		if ($newStorage instanceOf DataResponse) {
139
-			return $newStorage;
140
-		}
141
-
142
-		$response = $this->validate($newStorage);
143
-		if (!empty($response)) {
144
-			return $response;
145
-		}
146
-
147
-		$newStorage = $this->service->addStorage($newStorage);
148
-		$this->updateStorageStatus($newStorage);
149
-
150
-		return new DataResponse(
151
-			$this->formatStorageForUI($newStorage),
152
-			Http::STATUS_CREATED
153
-		);
154
-	}
155
-
156
-	/**
157
-	 * Update an external storage entry.
158
-	 *
159
-	 * @param int $id storage id
160
-	 * @param string $mountPoint storage mount point
161
-	 * @param string $backend backend identifier
162
-	 * @param string $authMechanism authentication mechanism identifier
163
-	 * @param array $backendOptions backend-specific options
164
-	 * @param array $mountOptions backend-specific mount options
165
-	 * @param bool $testOnly whether to storage should only test the connection or do more things
166
-	 *
167
-	 * @return DataResponse
168
-	 *
169
-	 * @NoAdminRequired
170
-	 */
171
-	public function update(
172
-		$id,
173
-		$mountPoint,
174
-		$backend,
175
-		$authMechanism,
176
-		$backendOptions,
177
-		$mountOptions,
178
-		$testOnly = true
179
-	) {
180
-		$storage = $this->createStorage(
181
-			$mountPoint,
182
-			$backend,
183
-			$authMechanism,
184
-			$backendOptions,
185
-			$mountOptions
186
-		);
187
-		if ($storage instanceOf DataResponse) {
188
-			return $storage;
189
-		}
190
-		$storage->setId($id);
191
-
192
-		$response = $this->validate($storage);
193
-		if (!empty($response)) {
194
-			return $response;
195
-		}
196
-
197
-		try {
198
-			$storage = $this->service->updateStorage($storage);
199
-		} catch (NotFoundException $e) {
200
-			return new DataResponse(
201
-				[
202
-					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
203
-				],
204
-				Http::STATUS_NOT_FOUND
205
-			);
206
-		}
207
-
208
-		$this->updateStorageStatus($storage, $testOnly);
209
-
210
-		return new DataResponse(
211
-			$this->formatStorageForUI($storage),
212
-			Http::STATUS_OK
213
-		);
214
-
215
-	}
216
-
217
-	/**
218
-	 * Delete storage
219
-	 *
220
-	 * @NoAdminRequired
221
-	 *
222
-	 * {@inheritdoc}
223
-	 */
224
-	public function destroy($id) {
225
-		return parent::destroy($id);
226
-	}
47
+    /**
48
+     * @var IUserSession
49
+     */
50
+    private $userSession;
51
+
52
+    /**
53
+     * Creates a new user storages controller.
54
+     *
55
+     * @param string $AppName application name
56
+     * @param IRequest $request request object
57
+     * @param IL10N $l10n l10n service
58
+     * @param UserStoragesService $userStoragesService storage service
59
+     * @param IUserSession $userSession
60
+     * @param ILogger $logger
61
+     */
62
+    public function __construct(
63
+        $AppName,
64
+        IRequest $request,
65
+        IL10N $l10n,
66
+        UserStoragesService $userStoragesService,
67
+        IUserSession $userSession,
68
+        ILogger $logger
69
+    ) {
70
+        parent::__construct(
71
+            $AppName,
72
+            $request,
73
+            $l10n,
74
+            $userStoragesService,
75
+            $logger
76
+        );
77
+        $this->userSession = $userSession;
78
+    }
79
+
80
+    protected function manipulateStorageConfig(StorageConfig $storage) {
81
+        /** @var AuthMechanism */
82
+        $authMechanism = $storage->getAuthMechanism();
83
+        $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
84
+        /** @var Backend */
85
+        $backend = $storage->getBackend();
86
+        $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
87
+    }
88
+
89
+    /**
90
+     * Get all storage entries
91
+     *
92
+     * @NoAdminRequired
93
+     *
94
+     * @return DataResponse
95
+     */
96
+    public function index() {
97
+        return parent::index();
98
+    }
99
+
100
+    /**
101
+     * Return storage
102
+     *
103
+     * @NoAdminRequired
104
+     *
105
+     * {@inheritdoc}
106
+     */
107
+    public function show($id, $testOnly = true) {
108
+        return parent::show($id, $testOnly);
109
+    }
110
+
111
+    /**
112
+     * Create an external storage entry.
113
+     *
114
+     * @param string $mountPoint storage mount point
115
+     * @param string $backend backend identifier
116
+     * @param string $authMechanism authentication mechanism identifier
117
+     * @param array $backendOptions backend-specific options
118
+     * @param array $mountOptions backend-specific mount options
119
+     *
120
+     * @return DataResponse
121
+     *
122
+     * @NoAdminRequired
123
+     */
124
+    public function create(
125
+        $mountPoint,
126
+        $backend,
127
+        $authMechanism,
128
+        $backendOptions,
129
+        $mountOptions
130
+    ) {
131
+        $newStorage = $this->createStorage(
132
+            $mountPoint,
133
+            $backend,
134
+            $authMechanism,
135
+            $backendOptions,
136
+            $mountOptions
137
+        );
138
+        if ($newStorage instanceOf DataResponse) {
139
+            return $newStorage;
140
+        }
141
+
142
+        $response = $this->validate($newStorage);
143
+        if (!empty($response)) {
144
+            return $response;
145
+        }
146
+
147
+        $newStorage = $this->service->addStorage($newStorage);
148
+        $this->updateStorageStatus($newStorage);
149
+
150
+        return new DataResponse(
151
+            $this->formatStorageForUI($newStorage),
152
+            Http::STATUS_CREATED
153
+        );
154
+    }
155
+
156
+    /**
157
+     * Update an external storage entry.
158
+     *
159
+     * @param int $id storage id
160
+     * @param string $mountPoint storage mount point
161
+     * @param string $backend backend identifier
162
+     * @param string $authMechanism authentication mechanism identifier
163
+     * @param array $backendOptions backend-specific options
164
+     * @param array $mountOptions backend-specific mount options
165
+     * @param bool $testOnly whether to storage should only test the connection or do more things
166
+     *
167
+     * @return DataResponse
168
+     *
169
+     * @NoAdminRequired
170
+     */
171
+    public function update(
172
+        $id,
173
+        $mountPoint,
174
+        $backend,
175
+        $authMechanism,
176
+        $backendOptions,
177
+        $mountOptions,
178
+        $testOnly = true
179
+    ) {
180
+        $storage = $this->createStorage(
181
+            $mountPoint,
182
+            $backend,
183
+            $authMechanism,
184
+            $backendOptions,
185
+            $mountOptions
186
+        );
187
+        if ($storage instanceOf DataResponse) {
188
+            return $storage;
189
+        }
190
+        $storage->setId($id);
191
+
192
+        $response = $this->validate($storage);
193
+        if (!empty($response)) {
194
+            return $response;
195
+        }
196
+
197
+        try {
198
+            $storage = $this->service->updateStorage($storage);
199
+        } catch (NotFoundException $e) {
200
+            return new DataResponse(
201
+                [
202
+                    'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id])
203
+                ],
204
+                Http::STATUS_NOT_FOUND
205
+            );
206
+        }
207
+
208
+        $this->updateStorageStatus($storage, $testOnly);
209
+
210
+        return new DataResponse(
211
+            $this->formatStorageForUI($storage),
212
+            Http::STATUS_OK
213
+        );
214
+
215
+    }
216
+
217
+    /**
218
+     * Delete storage
219
+     *
220
+     * @NoAdminRequired
221
+     *
222
+     * {@inheritdoc}
223
+     */
224
+    public function destroy($id) {
225
+        return parent::destroy($id);
226
+    }
227 227
 
228 228
 }
Please login to merge, or discard this patch.