Completed
Pull Request — master (#9578)
by Robin
279:22 queued 261:07
created
apps/files_external/lib/Lib/Storage/SMB.php 2 patches
Indentation   +489 added lines, -489 removed lines patch added patch discarded remove patch
@@ -57,493 +57,493 @@
 block discarded – undo
57 57
 use OCP\ILogger;
58 58
 
59 59
 class SMB extends Common implements INotifyStorage {
60
-	/**
61
-	 * @var \Icewind\SMB\IServer
62
-	 */
63
-	protected $server;
64
-
65
-	/**
66
-	 * @var \Icewind\SMB\IShare
67
-	 */
68
-	protected $share;
69
-
70
-	/**
71
-	 * @var string
72
-	 */
73
-	protected $root;
74
-
75
-	/**
76
-	 * @var \Icewind\SMB\IFileInfo[]
77
-	 */
78
-	protected $statCache;
79
-
80
-	public function __construct($params) {
81
-		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
82
-			list($workgroup, $user) = $this->splitUser($params['user']);
83
-			$auth = new BasicAuth($user, $workgroup, $params['password']);
84
-			$serverFactory = new ServerFactory();
85
-			$this->server = $serverFactory->createServer($params['host'], $auth);
86
-			$this->share = $this->server->getShare(trim($params['share'], '/'));
87
-
88
-			$this->root = $params['root'] ?? '/';
89
-			$this->root = '/' . ltrim($this->root, '/');
90
-			$this->root = rtrim($this->root, '/') . '/';
91
-		} else {
92
-			throw new \Exception('Invalid configuration: ' . json_encode($params));
93
-		}
94
-		$this->statCache = new CappedMemoryCache();
95
-		parent::__construct($params);
96
-	}
97
-
98
-	private function splitUser($user) {
99
-		if (strpos($user, '/')) {
100
-			return explode('/', $user, 2);
101
-		} elseif (strpos($user, '\\')) {
102
-			return explode('\\', $user);
103
-		} else {
104
-			return [null, $user];
105
-		}
106
-	}
107
-
108
-	/**
109
-	 * @return string
110
-	 */
111
-	public function getId() {
112
-		// FIXME: double slash to keep compatible with the old storage ids,
113
-		// failure to do so will lead to creation of a new storage id and
114
-		// loss of shares from the storage
115
-		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
116
-	}
117
-
118
-	/**
119
-	 * @param string $path
120
-	 * @return string
121
-	 */
122
-	protected function buildPath($path) {
123
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
124
-	}
125
-
126
-	protected function relativePath($fullPath) {
127
-		if ($fullPath === $this->root) {
128
-			return '';
129
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
130
-			return substr($fullPath, strlen($this->root));
131
-		} else {
132
-			return null;
133
-		}
134
-	}
135
-
136
-	/**
137
-	 * @param string $path
138
-	 * @return \Icewind\SMB\IFileInfo
139
-	 * @throws StorageNotAvailableException
140
-	 */
141
-	protected function getFileInfo($path) {
142
-		try {
143
-			$path = $this->buildPath($path);
144
-			if (!isset($this->statCache[$path])) {
145
-				$this->statCache[$path] = $this->share->stat($path);
146
-			}
147
-			return $this->statCache[$path];
148
-		} catch (ConnectException $e) {
149
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
150
-		}
151
-	}
152
-
153
-	/**
154
-	 * @param string $path
155
-	 * @return \Icewind\SMB\IFileInfo[]
156
-	 * @throws StorageNotAvailableException
157
-	 */
158
-	protected function getFolderContents($path) {
159
-		try {
160
-			$path = $this->buildPath($path);
161
-			$files = $this->share->dir($path);
162
-			foreach ($files as $file) {
163
-				$this->statCache[$path . '/' . $file->getName()] = $file;
164
-			}
165
-			return array_filter($files, function (IFileInfo $file) {
166
-				try {
167
-					return !$file->isHidden();
168
-				} catch (ForbiddenException $e) {
169
-					return false;
170
-				} catch (NotFoundException $e) {
171
-					return false;
172
-				}
173
-			});
174
-		} catch (ConnectException $e) {
175
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
176
-		}
177
-	}
178
-
179
-	/**
180
-	 * @param \Icewind\SMB\IFileInfo $info
181
-	 * @return array
182
-	 */
183
-	protected function formatInfo($info) {
184
-		$result = [
185
-			'size' => $info->getSize(),
186
-			'mtime' => $info->getMTime(),
187
-		];
188
-		if ($info->isDirectory()) {
189
-			$result['type'] = 'dir';
190
-		} else {
191
-			$result['type'] = 'file';
192
-		}
193
-		return $result;
194
-	}
195
-
196
-	/**
197
-	 * Rename the files. If the source or the target is the root, the rename won't happen.
198
-	 *
199
-	 * @param string $source the old name of the path
200
-	 * @param string $target the new name of the path
201
-	 * @return bool true if the rename is successful, false otherwise
202
-	 */
203
-	public function rename($source, $target) {
204
-		if ($this->isRootDir($source) || $this->isRootDir($target)) {
205
-			return false;
206
-		}
207
-
208
-		$absoluteSource = $this->buildPath($source);
209
-		$absoluteTarget = $this->buildPath($target);
210
-		try {
211
-			$result = $this->share->rename($absoluteSource, $absoluteTarget);
212
-		} catch (AlreadyExistsException $e) {
213
-			$this->remove($target);
214
-			$result = $this->share->rename($absoluteSource, $absoluteTarget);
215
-		} catch (\Exception $e) {
216
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
217
-			return false;
218
-		}
219
-		unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
220
-		return $result;
221
-	}
222
-
223
-	public function stat($path) {
224
-		try {
225
-			$result = $this->formatInfo($this->getFileInfo($path));
226
-		} catch (ForbiddenException $e) {
227
-			return false;
228
-		} catch (NotFoundException $e) {
229
-			return false;
230
-		}
231
-		if ($this->remoteIsShare() && $this->isRootDir($path)) {
232
-			$result['mtime'] = $this->shareMTime();
233
-		}
234
-		return $result;
235
-	}
236
-
237
-	/**
238
-	 * get the best guess for the modification time of the share
239
-	 *
240
-	 * @return int
241
-	 */
242
-	private function shareMTime() {
243
-		$highestMTime = 0;
244
-		$files = $this->share->dir($this->root);
245
-		foreach ($files as $fileInfo) {
246
-			try {
247
-				if ($fileInfo->getMTime() > $highestMTime) {
248
-					$highestMTime = $fileInfo->getMTime();
249
-				}
250
-			} catch (NotFoundException $e) {
251
-				// Ignore this, can happen on unavailable DFS shares
252
-			}
253
-		}
254
-		return $highestMTime;
255
-	}
256
-
257
-	/**
258
-	 * Check if the path is our root dir (not the smb one)
259
-	 *
260
-	 * @param string $path the path
261
-	 * @return bool
262
-	 */
263
-	private function isRootDir($path) {
264
-		return $path === '' || $path === '/' || $path === '.';
265
-	}
266
-
267
-	/**
268
-	 * Check if our root points to a smb share
269
-	 *
270
-	 * @return bool true if our root points to a share false otherwise
271
-	 */
272
-	private function remoteIsShare() {
273
-		return $this->share->getName() && (!$this->root || $this->root === '/');
274
-	}
275
-
276
-	/**
277
-	 * @param string $path
278
-	 * @return bool
279
-	 */
280
-	public function unlink($path) {
281
-		if ($this->isRootDir($path)) {
282
-			return false;
283
-		}
284
-
285
-		try {
286
-			if ($this->is_dir($path)) {
287
-				return $this->rmdir($path);
288
-			} else {
289
-				$path = $this->buildPath($path);
290
-				unset($this->statCache[$path]);
291
-				$this->share->del($path);
292
-				return true;
293
-			}
294
-		} catch (NotFoundException $e) {
295
-			return false;
296
-		} catch (ForbiddenException $e) {
297
-			return false;
298
-		} catch (ConnectException $e) {
299
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
300
-		}
301
-	}
302
-
303
-	/**
304
-	 * check if a file or folder has been updated since $time
305
-	 *
306
-	 * @param string $path
307
-	 * @param int $time
308
-	 * @return bool
309
-	 */
310
-	public function hasUpdated($path, $time) {
311
-		if (!$path and $this->root === '/') {
312
-			// mtime doesn't work for shares, but giving the nature of the backend,
313
-			// doing a full update is still just fast enough
314
-			return true;
315
-		} else {
316
-			$actualTime = $this->filemtime($path);
317
-			return $actualTime > $time;
318
-		}
319
-	}
320
-
321
-	/**
322
-	 * @param string $path
323
-	 * @param string $mode
324
-	 * @return resource|false
325
-	 */
326
-	public function fopen($path, $mode) {
327
-		$fullPath = $this->buildPath($path);
328
-		try {
329
-			switch ($mode) {
330
-				case 'r':
331
-				case 'rb':
332
-					if (!$this->file_exists($path)) {
333
-						return false;
334
-					}
335
-					return $this->share->read($fullPath);
336
-				case 'w':
337
-				case 'wb':
338
-					$source = $this->share->write($fullPath);
339
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
340
-						unset($this->statCache[$fullPath]);
341
-					});
342
-				case 'a':
343
-				case 'ab':
344
-				case 'r+':
345
-				case 'w+':
346
-				case 'wb+':
347
-				case 'a+':
348
-				case 'x':
349
-				case 'x+':
350
-				case 'c':
351
-				case 'c+':
352
-					//emulate these
353
-					if (strrpos($path, '.') !== false) {
354
-						$ext = substr($path, strrpos($path, '.'));
355
-					} else {
356
-						$ext = '';
357
-					}
358
-					if ($this->file_exists($path)) {
359
-						if (!$this->isUpdatable($path)) {
360
-							return false;
361
-						}
362
-						$tmpFile = $this->getCachedFile($path);
363
-					} else {
364
-						if (!$this->isCreatable(dirname($path))) {
365
-							return false;
366
-						}
367
-						$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
368
-					}
369
-					$source = fopen($tmpFile, $mode);
370
-					$share = $this->share;
371
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
372
-						unset($this->statCache[$fullPath]);
373
-						$share->put($tmpFile, $fullPath);
374
-						unlink($tmpFile);
375
-					});
376
-			}
377
-			return false;
378
-		} catch (NotFoundException $e) {
379
-			return false;
380
-		} catch (ForbiddenException $e) {
381
-			return false;
382
-		} catch (ConnectException $e) {
383
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
384
-		}
385
-	}
386
-
387
-	public function rmdir($path) {
388
-		if ($this->isRootDir($path)) {
389
-			return false;
390
-		}
391
-
392
-		try {
393
-			$this->statCache = array();
394
-			$content = $this->share->dir($this->buildPath($path));
395
-			foreach ($content as $file) {
396
-				if ($file->isDirectory()) {
397
-					$this->rmdir($path . '/' . $file->getName());
398
-				} else {
399
-					$this->share->del($file->getPath());
400
-				}
401
-			}
402
-			$this->share->rmdir($this->buildPath($path));
403
-			return true;
404
-		} catch (NotFoundException $e) {
405
-			return false;
406
-		} catch (ForbiddenException $e) {
407
-			return false;
408
-		} catch (ConnectException $e) {
409
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
410
-		}
411
-	}
412
-
413
-	public function touch($path, $time = null) {
414
-		try {
415
-			if (!$this->file_exists($path)) {
416
-				$fh = $this->share->write($this->buildPath($path));
417
-				fclose($fh);
418
-				return true;
419
-			}
420
-			return false;
421
-		} catch (ConnectException $e) {
422
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
423
-		}
424
-	}
425
-
426
-	public function opendir($path) {
427
-		try {
428
-			$files = $this->getFolderContents($path);
429
-		} catch (NotFoundException $e) {
430
-			return false;
431
-		} catch (ForbiddenException $e) {
432
-			return false;
433
-		}
434
-		$names = array_map(function ($info) {
435
-			/** @var \Icewind\SMB\IFileInfo $info */
436
-			return $info->getName();
437
-		}, $files);
438
-		return IteratorDirectory::wrap($names);
439
-	}
440
-
441
-	public function filetype($path) {
442
-		try {
443
-			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
444
-		} catch (NotFoundException $e) {
445
-			return false;
446
-		} catch (ForbiddenException $e) {
447
-			return false;
448
-		}
449
-	}
450
-
451
-	public function mkdir($path) {
452
-		$path = $this->buildPath($path);
453
-		try {
454
-			$this->share->mkdir($path);
455
-			return true;
456
-		} catch (ConnectException $e) {
457
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
458
-		} catch (Exception $e) {
459
-			return false;
460
-		}
461
-	}
462
-
463
-	public function file_exists($path) {
464
-		try {
465
-			$this->getFileInfo($path);
466
-			return true;
467
-		} catch (NotFoundException $e) {
468
-			return false;
469
-		} catch (ForbiddenException $e) {
470
-			return false;
471
-		} catch (ConnectException $e) {
472
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
473
-		}
474
-	}
475
-
476
-	public function isReadable($path) {
477
-		try {
478
-			$info = $this->getFileInfo($path);
479
-			return !$info->isHidden();
480
-		} catch (NotFoundException $e) {
481
-			return false;
482
-		} catch (ForbiddenException $e) {
483
-			return false;
484
-		}
485
-	}
486
-
487
-	public function isUpdatable($path) {
488
-		try {
489
-			$info = $this->getFileInfo($path);
490
-			// following windows behaviour for read-only folders: they can be written into
491
-			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
492
-			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
493
-		} catch (NotFoundException $e) {
494
-			return false;
495
-		} catch (ForbiddenException $e) {
496
-			return false;
497
-		}
498
-	}
499
-
500
-	public function isDeletable($path) {
501
-		try {
502
-			$info = $this->getFileInfo($path);
503
-			return !$info->isHidden() && !$info->isReadOnly();
504
-		} catch (NotFoundException $e) {
505
-			return false;
506
-		} catch (ForbiddenException $e) {
507
-			return false;
508
-		}
509
-	}
510
-
511
-	/**
512
-	 * check if smbclient is installed
513
-	 */
514
-	public static function checkDependencies() {
515
-		return (
516
-			(bool)\OC_Helper::findBinaryPath('smbclient')
517
-			|| NativeServer::available(new System())
518
-		) ? true : ['smbclient'];
519
-	}
520
-
521
-	/**
522
-	 * Test a storage for availability
523
-	 *
524
-	 * @return bool
525
-	 */
526
-	public function test() {
527
-		try {
528
-			return parent::test();
529
-		} catch (Exception $e) {
530
-			return false;
531
-		}
532
-	}
533
-
534
-	public function listen($path, callable $callback) {
535
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
536
-			if ($change instanceof IRenameChange) {
537
-				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
538
-			} else {
539
-				return $callback($change->getType(), $change->getPath());
540
-			}
541
-		});
542
-	}
543
-
544
-	public function notify($path) {
545
-		$path = '/' . ltrim($path, '/');
546
-		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
547
-		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
548
-	}
60
+    /**
61
+     * @var \Icewind\SMB\IServer
62
+     */
63
+    protected $server;
64
+
65
+    /**
66
+     * @var \Icewind\SMB\IShare
67
+     */
68
+    protected $share;
69
+
70
+    /**
71
+     * @var string
72
+     */
73
+    protected $root;
74
+
75
+    /**
76
+     * @var \Icewind\SMB\IFileInfo[]
77
+     */
78
+    protected $statCache;
79
+
80
+    public function __construct($params) {
81
+        if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
82
+            list($workgroup, $user) = $this->splitUser($params['user']);
83
+            $auth = new BasicAuth($user, $workgroup, $params['password']);
84
+            $serverFactory = new ServerFactory();
85
+            $this->server = $serverFactory->createServer($params['host'], $auth);
86
+            $this->share = $this->server->getShare(trim($params['share'], '/'));
87
+
88
+            $this->root = $params['root'] ?? '/';
89
+            $this->root = '/' . ltrim($this->root, '/');
90
+            $this->root = rtrim($this->root, '/') . '/';
91
+        } else {
92
+            throw new \Exception('Invalid configuration: ' . json_encode($params));
93
+        }
94
+        $this->statCache = new CappedMemoryCache();
95
+        parent::__construct($params);
96
+    }
97
+
98
+    private function splitUser($user) {
99
+        if (strpos($user, '/')) {
100
+            return explode('/', $user, 2);
101
+        } elseif (strpos($user, '\\')) {
102
+            return explode('\\', $user);
103
+        } else {
104
+            return [null, $user];
105
+        }
106
+    }
107
+
108
+    /**
109
+     * @return string
110
+     */
111
+    public function getId() {
112
+        // FIXME: double slash to keep compatible with the old storage ids,
113
+        // failure to do so will lead to creation of a new storage id and
114
+        // loss of shares from the storage
115
+        return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
116
+    }
117
+
118
+    /**
119
+     * @param string $path
120
+     * @return string
121
+     */
122
+    protected function buildPath($path) {
123
+        return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
124
+    }
125
+
126
+    protected function relativePath($fullPath) {
127
+        if ($fullPath === $this->root) {
128
+            return '';
129
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
130
+            return substr($fullPath, strlen($this->root));
131
+        } else {
132
+            return null;
133
+        }
134
+    }
135
+
136
+    /**
137
+     * @param string $path
138
+     * @return \Icewind\SMB\IFileInfo
139
+     * @throws StorageNotAvailableException
140
+     */
141
+    protected function getFileInfo($path) {
142
+        try {
143
+            $path = $this->buildPath($path);
144
+            if (!isset($this->statCache[$path])) {
145
+                $this->statCache[$path] = $this->share->stat($path);
146
+            }
147
+            return $this->statCache[$path];
148
+        } catch (ConnectException $e) {
149
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
150
+        }
151
+    }
152
+
153
+    /**
154
+     * @param string $path
155
+     * @return \Icewind\SMB\IFileInfo[]
156
+     * @throws StorageNotAvailableException
157
+     */
158
+    protected function getFolderContents($path) {
159
+        try {
160
+            $path = $this->buildPath($path);
161
+            $files = $this->share->dir($path);
162
+            foreach ($files as $file) {
163
+                $this->statCache[$path . '/' . $file->getName()] = $file;
164
+            }
165
+            return array_filter($files, function (IFileInfo $file) {
166
+                try {
167
+                    return !$file->isHidden();
168
+                } catch (ForbiddenException $e) {
169
+                    return false;
170
+                } catch (NotFoundException $e) {
171
+                    return false;
172
+                }
173
+            });
174
+        } catch (ConnectException $e) {
175
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
176
+        }
177
+    }
178
+
179
+    /**
180
+     * @param \Icewind\SMB\IFileInfo $info
181
+     * @return array
182
+     */
183
+    protected function formatInfo($info) {
184
+        $result = [
185
+            'size' => $info->getSize(),
186
+            'mtime' => $info->getMTime(),
187
+        ];
188
+        if ($info->isDirectory()) {
189
+            $result['type'] = 'dir';
190
+        } else {
191
+            $result['type'] = 'file';
192
+        }
193
+        return $result;
194
+    }
195
+
196
+    /**
197
+     * Rename the files. If the source or the target is the root, the rename won't happen.
198
+     *
199
+     * @param string $source the old name of the path
200
+     * @param string $target the new name of the path
201
+     * @return bool true if the rename is successful, false otherwise
202
+     */
203
+    public function rename($source, $target) {
204
+        if ($this->isRootDir($source) || $this->isRootDir($target)) {
205
+            return false;
206
+        }
207
+
208
+        $absoluteSource = $this->buildPath($source);
209
+        $absoluteTarget = $this->buildPath($target);
210
+        try {
211
+            $result = $this->share->rename($absoluteSource, $absoluteTarget);
212
+        } catch (AlreadyExistsException $e) {
213
+            $this->remove($target);
214
+            $result = $this->share->rename($absoluteSource, $absoluteTarget);
215
+        } catch (\Exception $e) {
216
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
217
+            return false;
218
+        }
219
+        unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
220
+        return $result;
221
+    }
222
+
223
+    public function stat($path) {
224
+        try {
225
+            $result = $this->formatInfo($this->getFileInfo($path));
226
+        } catch (ForbiddenException $e) {
227
+            return false;
228
+        } catch (NotFoundException $e) {
229
+            return false;
230
+        }
231
+        if ($this->remoteIsShare() && $this->isRootDir($path)) {
232
+            $result['mtime'] = $this->shareMTime();
233
+        }
234
+        return $result;
235
+    }
236
+
237
+    /**
238
+     * get the best guess for the modification time of the share
239
+     *
240
+     * @return int
241
+     */
242
+    private function shareMTime() {
243
+        $highestMTime = 0;
244
+        $files = $this->share->dir($this->root);
245
+        foreach ($files as $fileInfo) {
246
+            try {
247
+                if ($fileInfo->getMTime() > $highestMTime) {
248
+                    $highestMTime = $fileInfo->getMTime();
249
+                }
250
+            } catch (NotFoundException $e) {
251
+                // Ignore this, can happen on unavailable DFS shares
252
+            }
253
+        }
254
+        return $highestMTime;
255
+    }
256
+
257
+    /**
258
+     * Check if the path is our root dir (not the smb one)
259
+     *
260
+     * @param string $path the path
261
+     * @return bool
262
+     */
263
+    private function isRootDir($path) {
264
+        return $path === '' || $path === '/' || $path === '.';
265
+    }
266
+
267
+    /**
268
+     * Check if our root points to a smb share
269
+     *
270
+     * @return bool true if our root points to a share false otherwise
271
+     */
272
+    private function remoteIsShare() {
273
+        return $this->share->getName() && (!$this->root || $this->root === '/');
274
+    }
275
+
276
+    /**
277
+     * @param string $path
278
+     * @return bool
279
+     */
280
+    public function unlink($path) {
281
+        if ($this->isRootDir($path)) {
282
+            return false;
283
+        }
284
+
285
+        try {
286
+            if ($this->is_dir($path)) {
287
+                return $this->rmdir($path);
288
+            } else {
289
+                $path = $this->buildPath($path);
290
+                unset($this->statCache[$path]);
291
+                $this->share->del($path);
292
+                return true;
293
+            }
294
+        } catch (NotFoundException $e) {
295
+            return false;
296
+        } catch (ForbiddenException $e) {
297
+            return false;
298
+        } catch (ConnectException $e) {
299
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
300
+        }
301
+    }
302
+
303
+    /**
304
+     * check if a file or folder has been updated since $time
305
+     *
306
+     * @param string $path
307
+     * @param int $time
308
+     * @return bool
309
+     */
310
+    public function hasUpdated($path, $time) {
311
+        if (!$path and $this->root === '/') {
312
+            // mtime doesn't work for shares, but giving the nature of the backend,
313
+            // doing a full update is still just fast enough
314
+            return true;
315
+        } else {
316
+            $actualTime = $this->filemtime($path);
317
+            return $actualTime > $time;
318
+        }
319
+    }
320
+
321
+    /**
322
+     * @param string $path
323
+     * @param string $mode
324
+     * @return resource|false
325
+     */
326
+    public function fopen($path, $mode) {
327
+        $fullPath = $this->buildPath($path);
328
+        try {
329
+            switch ($mode) {
330
+                case 'r':
331
+                case 'rb':
332
+                    if (!$this->file_exists($path)) {
333
+                        return false;
334
+                    }
335
+                    return $this->share->read($fullPath);
336
+                case 'w':
337
+                case 'wb':
338
+                    $source = $this->share->write($fullPath);
339
+                    return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
340
+                        unset($this->statCache[$fullPath]);
341
+                    });
342
+                case 'a':
343
+                case 'ab':
344
+                case 'r+':
345
+                case 'w+':
346
+                case 'wb+':
347
+                case 'a+':
348
+                case 'x':
349
+                case 'x+':
350
+                case 'c':
351
+                case 'c+':
352
+                    //emulate these
353
+                    if (strrpos($path, '.') !== false) {
354
+                        $ext = substr($path, strrpos($path, '.'));
355
+                    } else {
356
+                        $ext = '';
357
+                    }
358
+                    if ($this->file_exists($path)) {
359
+                        if (!$this->isUpdatable($path)) {
360
+                            return false;
361
+                        }
362
+                        $tmpFile = $this->getCachedFile($path);
363
+                    } else {
364
+                        if (!$this->isCreatable(dirname($path))) {
365
+                            return false;
366
+                        }
367
+                        $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
368
+                    }
369
+                    $source = fopen($tmpFile, $mode);
370
+                    $share = $this->share;
371
+                    return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
372
+                        unset($this->statCache[$fullPath]);
373
+                        $share->put($tmpFile, $fullPath);
374
+                        unlink($tmpFile);
375
+                    });
376
+            }
377
+            return false;
378
+        } catch (NotFoundException $e) {
379
+            return false;
380
+        } catch (ForbiddenException $e) {
381
+            return false;
382
+        } catch (ConnectException $e) {
383
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
384
+        }
385
+    }
386
+
387
+    public function rmdir($path) {
388
+        if ($this->isRootDir($path)) {
389
+            return false;
390
+        }
391
+
392
+        try {
393
+            $this->statCache = array();
394
+            $content = $this->share->dir($this->buildPath($path));
395
+            foreach ($content as $file) {
396
+                if ($file->isDirectory()) {
397
+                    $this->rmdir($path . '/' . $file->getName());
398
+                } else {
399
+                    $this->share->del($file->getPath());
400
+                }
401
+            }
402
+            $this->share->rmdir($this->buildPath($path));
403
+            return true;
404
+        } catch (NotFoundException $e) {
405
+            return false;
406
+        } catch (ForbiddenException $e) {
407
+            return false;
408
+        } catch (ConnectException $e) {
409
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
410
+        }
411
+    }
412
+
413
+    public function touch($path, $time = null) {
414
+        try {
415
+            if (!$this->file_exists($path)) {
416
+                $fh = $this->share->write($this->buildPath($path));
417
+                fclose($fh);
418
+                return true;
419
+            }
420
+            return false;
421
+        } catch (ConnectException $e) {
422
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
423
+        }
424
+    }
425
+
426
+    public function opendir($path) {
427
+        try {
428
+            $files = $this->getFolderContents($path);
429
+        } catch (NotFoundException $e) {
430
+            return false;
431
+        } catch (ForbiddenException $e) {
432
+            return false;
433
+        }
434
+        $names = array_map(function ($info) {
435
+            /** @var \Icewind\SMB\IFileInfo $info */
436
+            return $info->getName();
437
+        }, $files);
438
+        return IteratorDirectory::wrap($names);
439
+    }
440
+
441
+    public function filetype($path) {
442
+        try {
443
+            return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
444
+        } catch (NotFoundException $e) {
445
+            return false;
446
+        } catch (ForbiddenException $e) {
447
+            return false;
448
+        }
449
+    }
450
+
451
+    public function mkdir($path) {
452
+        $path = $this->buildPath($path);
453
+        try {
454
+            $this->share->mkdir($path);
455
+            return true;
456
+        } catch (ConnectException $e) {
457
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
458
+        } catch (Exception $e) {
459
+            return false;
460
+        }
461
+    }
462
+
463
+    public function file_exists($path) {
464
+        try {
465
+            $this->getFileInfo($path);
466
+            return true;
467
+        } catch (NotFoundException $e) {
468
+            return false;
469
+        } catch (ForbiddenException $e) {
470
+            return false;
471
+        } catch (ConnectException $e) {
472
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
473
+        }
474
+    }
475
+
476
+    public function isReadable($path) {
477
+        try {
478
+            $info = $this->getFileInfo($path);
479
+            return !$info->isHidden();
480
+        } catch (NotFoundException $e) {
481
+            return false;
482
+        } catch (ForbiddenException $e) {
483
+            return false;
484
+        }
485
+    }
486
+
487
+    public function isUpdatable($path) {
488
+        try {
489
+            $info = $this->getFileInfo($path);
490
+            // following windows behaviour for read-only folders: they can be written into
491
+            // (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
492
+            return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
493
+        } catch (NotFoundException $e) {
494
+            return false;
495
+        } catch (ForbiddenException $e) {
496
+            return false;
497
+        }
498
+    }
499
+
500
+    public function isDeletable($path) {
501
+        try {
502
+            $info = $this->getFileInfo($path);
503
+            return !$info->isHidden() && !$info->isReadOnly();
504
+        } catch (NotFoundException $e) {
505
+            return false;
506
+        } catch (ForbiddenException $e) {
507
+            return false;
508
+        }
509
+    }
510
+
511
+    /**
512
+     * check if smbclient is installed
513
+     */
514
+    public static function checkDependencies() {
515
+        return (
516
+            (bool)\OC_Helper::findBinaryPath('smbclient')
517
+            || NativeServer::available(new System())
518
+        ) ? true : ['smbclient'];
519
+    }
520
+
521
+    /**
522
+     * Test a storage for availability
523
+     *
524
+     * @return bool
525
+     */
526
+    public function test() {
527
+        try {
528
+            return parent::test();
529
+        } catch (Exception $e) {
530
+            return false;
531
+        }
532
+    }
533
+
534
+    public function listen($path, callable $callback) {
535
+        $this->notify($path)->listen(function (IChange $change) use ($callback) {
536
+            if ($change instanceof IRenameChange) {
537
+                return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
538
+            } else {
539
+                return $callback($change->getType(), $change->getPath());
540
+            }
541
+        });
542
+    }
543
+
544
+    public function notify($path) {
545
+        $path = '/' . ltrim($path, '/');
546
+        $shareNotifyHandler = $this->share->notify($this->buildPath($path));
547
+        return new SMBNotifyHandler($shareNotifyHandler, $this->root);
548
+    }
549 549
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -86,10 +86,10 @@  discard block
 block discarded – undo
86 86
 			$this->share = $this->server->getShare(trim($params['share'], '/'));
87 87
 
88 88
 			$this->root = $params['root'] ?? '/';
89
-			$this->root = '/' . ltrim($this->root, '/');
90
-			$this->root = rtrim($this->root, '/') . '/';
89
+			$this->root = '/'.ltrim($this->root, '/');
90
+			$this->root = rtrim($this->root, '/').'/';
91 91
 		} else {
92
-			throw new \Exception('Invalid configuration: ' . json_encode($params));
92
+			throw new \Exception('Invalid configuration: '.json_encode($params));
93 93
 		}
94 94
 		$this->statCache = new CappedMemoryCache();
95 95
 		parent::__construct($params);
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 		// FIXME: double slash to keep compatible with the old storage ids,
113 113
 		// failure to do so will lead to creation of a new storage id and
114 114
 		// loss of shares from the storage
115
-		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
115
+		return 'smb::'.$this->server->getAuth()->getUsername().'@'.$this->server->getHost().'//'.$this->share->getName().'/'.$this->root;
116 116
 	}
117 117
 
118 118
 	/**
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 	 * @return string
121 121
 	 */
122 122
 	protected function buildPath($path) {
123
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
123
+		return Filesystem::normalizePath($this->root.'/'.$path, true, false, true);
124 124
 	}
125 125
 
126 126
 	protected function relativePath($fullPath) {
@@ -160,9 +160,9 @@  discard block
 block discarded – undo
160 160
 			$path = $this->buildPath($path);
161 161
 			$files = $this->share->dir($path);
162 162
 			foreach ($files as $file) {
163
-				$this->statCache[$path . '/' . $file->getName()] = $file;
163
+				$this->statCache[$path.'/'.$file->getName()] = $file;
164 164
 			}
165
-			return array_filter($files, function (IFileInfo $file) {
165
+			return array_filter($files, function(IFileInfo $file) {
166 166
 				try {
167 167
 					return !$file->isHidden();
168 168
 				} catch (ForbiddenException $e) {
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
 				case 'w':
337 337
 				case 'wb':
338 338
 					$source = $this->share->write($fullPath);
339
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
339
+					return CallBackWrapper::wrap($source, null, null, function() use ($fullPath) {
340 340
 						unset($this->statCache[$fullPath]);
341 341
 					});
342 342
 				case 'a':
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 					}
369 369
 					$source = fopen($tmpFile, $mode);
370 370
 					$share = $this->share;
371
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
371
+					return CallbackWrapper::wrap($source, null, null, function() use ($tmpFile, $fullPath, $share) {
372 372
 						unset($this->statCache[$fullPath]);
373 373
 						$share->put($tmpFile, $fullPath);
374 374
 						unlink($tmpFile);
@@ -394,7 +394,7 @@  discard block
 block discarded – undo
394 394
 			$content = $this->share->dir($this->buildPath($path));
395 395
 			foreach ($content as $file) {
396 396
 				if ($file->isDirectory()) {
397
-					$this->rmdir($path . '/' . $file->getName());
397
+					$this->rmdir($path.'/'.$file->getName());
398 398
 				} else {
399 399
 					$this->share->del($file->getPath());
400 400
 				}
@@ -431,7 +431,7 @@  discard block
 block discarded – undo
431 431
 		} catch (ForbiddenException $e) {
432 432
 			return false;
433 433
 		}
434
-		$names = array_map(function ($info) {
434
+		$names = array_map(function($info) {
435 435
 			/** @var \Icewind\SMB\IFileInfo $info */
436 436
 			return $info->getName();
437 437
 		}, $files);
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
 	 */
514 514
 	public static function checkDependencies() {
515 515
 		return (
516
-			(bool)\OC_Helper::findBinaryPath('smbclient')
516
+			(bool) \OC_Helper::findBinaryPath('smbclient')
517 517
 			|| NativeServer::available(new System())
518 518
 		) ? true : ['smbclient'];
519 519
 	}
@@ -532,7 +532,7 @@  discard block
 block discarded – undo
532 532
 	}
533 533
 
534 534
 	public function listen($path, callable $callback) {
535
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
535
+		$this->notify($path)->listen(function(IChange $change) use ($callback) {
536 536
 			if ($change instanceof IRenameChange) {
537 537
 				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
538 538
 			} else {
@@ -542,7 +542,7 @@  discard block
 block discarded – undo
542 542
 	}
543 543
 
544 544
 	public function notify($path) {
545
-		$path = '/' . ltrim($path, '/');
545
+		$path = '/'.ltrim($path, '/');
546 546
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
547 547
 		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
548 548
 	}
Please login to merge, or discard this patch.
apps/files_external/lib/Service/StoragesService.php 2 patches
Indentation   +492 added lines, -492 removed lines patch added patch discarded remove patch
@@ -44,496 +44,496 @@
 block discarded – undo
44 44
  */
45 45
 abstract class StoragesService {
46 46
 
47
-	/** @var BackendService */
48
-	protected $backendService;
49
-
50
-	/**
51
-	 * @var DBConfigService
52
-	 */
53
-	protected $dbConfig;
54
-
55
-	/**
56
-	 * @var IUserMountCache
57
-	 */
58
-	protected $userMountCache;
59
-
60
-	/**
61
-	 * @param BackendService $backendService
62
-	 * @param DBConfigService $dbConfigService
63
-	 * @param IUserMountCache $userMountCache
64
-	 */
65
-	public function __construct(BackendService $backendService, DBConfigService $dbConfigService, IUserMountCache $userMountCache) {
66
-		$this->backendService = $backendService;
67
-		$this->dbConfig = $dbConfigService;
68
-		$this->userMountCache = $userMountCache;
69
-	}
70
-
71
-	protected function readDBConfig() {
72
-		return $this->dbConfig->getAdminMounts();
73
-	}
74
-
75
-	protected function getStorageConfigFromDBMount(array $mount) {
76
-		$applicableUsers = array_filter($mount['applicable'], function ($applicable) {
77
-			return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER;
78
-		});
79
-		$applicableUsers = array_map(function ($applicable) {
80
-			return $applicable['value'];
81
-		}, $applicableUsers);
82
-
83
-		$applicableGroups = array_filter($mount['applicable'], function ($applicable) {
84
-			return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP;
85
-		});
86
-		$applicableGroups = array_map(function ($applicable) {
87
-			return $applicable['value'];
88
-		}, $applicableGroups);
89
-
90
-		try {
91
-			$config = $this->createStorage(
92
-				$mount['mount_point'],
93
-				$mount['storage_backend'],
94
-				$mount['auth_backend'],
95
-				$mount['config'],
96
-				$mount['options'],
97
-				array_values($applicableUsers),
98
-				array_values($applicableGroups),
99
-				$mount['priority']
100
-			);
101
-			$config->setType($mount['type']);
102
-			$config->setId((int)$mount['mount_id']);
103
-			return $config;
104
-		} catch (\UnexpectedValueException $e) {
105
-			// don't die if a storage backend doesn't exist
106
-			\OC::$server->getLogger()->logException($e, [
107
-				'message' => 'Could not load storage.',
108
-				'level' => ILogger::ERROR,
109
-				'app' => 'files_external',
110
-			]);
111
-			return null;
112
-		} catch (\InvalidArgumentException $e) {
113
-			\OC::$server->getLogger()->logException($e, [
114
-				'message' => 'Could not load storage.',
115
-				'level' => ILogger::ERROR,
116
-				'app' => 'files_external',
117
-			]);
118
-			return null;
119
-		}
120
-	}
121
-
122
-	/**
123
-	 * Read the external storages config
124
-	 *
125
-	 * @return array map of storage id to storage config
126
-	 */
127
-	protected function readConfig() {
128
-		$mounts = $this->readDBConfig();
129
-		$configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
130
-		$configs = array_filter($configs, function ($config) {
131
-			return $config instanceof StorageConfig;
132
-		});
133
-
134
-		$keys = array_map(function (StorageConfig $config) {
135
-			return $config->getId();
136
-		}, $configs);
137
-
138
-		return array_combine($keys, $configs);
139
-	}
140
-
141
-	/**
142
-	 * Get a storage with status
143
-	 *
144
-	 * @param int $id storage id
145
-	 *
146
-	 * @return StorageConfig
147
-	 * @throws NotFoundException if the storage with the given id was not found
148
-	 */
149
-	public function getStorage($id) {
150
-		$mount = $this->dbConfig->getMountById($id);
151
-
152
-		if (!is_array($mount)) {
153
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
154
-		}
155
-
156
-		$config = $this->getStorageConfigFromDBMount($mount);
157
-		if ($this->isApplicable($config)) {
158
-			return $config;
159
-		} else {
160
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
161
-		}
162
-	}
163
-
164
-	/**
165
-	 * Check whether this storage service should provide access to a storage
166
-	 *
167
-	 * @param StorageConfig $config
168
-	 * @return bool
169
-	 */
170
-	abstract protected function isApplicable(StorageConfig $config);
171
-
172
-	/**
173
-	 * Gets all storages, valid or not
174
-	 *
175
-	 * @return StorageConfig[] array of storage configs
176
-	 */
177
-	public function getAllStorages() {
178
-		return $this->readConfig();
179
-	}
180
-
181
-	/**
182
-	 * Gets all valid storages
183
-	 *
184
-	 * @return StorageConfig[]
185
-	 */
186
-	public function getStorages() {
187
-		return array_filter($this->getAllStorages(), [$this, 'validateStorage']);
188
-	}
189
-
190
-	/**
191
-	 * Validate storage
192
-	 * FIXME: De-duplicate with StoragesController::validate()
193
-	 *
194
-	 * @param StorageConfig $storage
195
-	 * @return bool
196
-	 */
197
-	protected function validateStorage(StorageConfig $storage) {
198
-		/** @var Backend */
199
-		$backend = $storage->getBackend();
200
-		/** @var AuthMechanism */
201
-		$authMechanism = $storage->getAuthMechanism();
202
-
203
-		if (!$backend->isVisibleFor($this->getVisibilityType())) {
204
-			// not permitted to use backend
205
-			return false;
206
-		}
207
-		if (!$authMechanism->isVisibleFor($this->getVisibilityType())) {
208
-			// not permitted to use auth mechanism
209
-			return false;
210
-		}
211
-
212
-		return true;
213
-	}
214
-
215
-	/**
216
-	 * Get the visibility type for this controller, used in validation
217
-	 *
218
-	 * @return string BackendService::VISIBILITY_* constants
219
-	 */
220
-	abstract public function getVisibilityType();
221
-
222
-	/**
223
-	 * @return integer
224
-	 */
225
-	protected function getType() {
226
-		return DBConfigService::MOUNT_TYPE_ADMIN;
227
-	}
228
-
229
-	/**
230
-	 * Add new storage to the configuration
231
-	 *
232
-	 * @param StorageConfig $newStorage storage attributes
233
-	 *
234
-	 * @return StorageConfig storage config, with added id
235
-	 */
236
-	public function addStorage(StorageConfig $newStorage) {
237
-		$allStorages = $this->readConfig();
238
-
239
-		$configId = $this->dbConfig->addMount(
240
-			$newStorage->getMountPoint(),
241
-			$newStorage->getBackend()->getIdentifier(),
242
-			$newStorage->getAuthMechanism()->getIdentifier(),
243
-			$newStorage->getPriority(),
244
-			$this->getType()
245
-		);
246
-
247
-		$newStorage->setId($configId);
248
-
249
-		foreach ($newStorage->getApplicableUsers() as $user) {
250
-			$this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_USER, $user);
251
-		}
252
-		foreach ($newStorage->getApplicableGroups() as $group) {
253
-			$this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
254
-		}
255
-		foreach ($newStorage->getBackendOptions() as $key => $value) {
256
-			$this->dbConfig->setConfig($configId, $key, $value);
257
-		}
258
-		foreach ($newStorage->getMountOptions() as $key => $value) {
259
-			$this->dbConfig->setOption($configId, $key, $value);
260
-		}
261
-
262
-		if (count($newStorage->getApplicableUsers()) === 0 && count($newStorage->getApplicableGroups()) === 0) {
263
-			$this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
264
-		}
265
-
266
-		// add new storage
267
-		$allStorages[$configId] = $newStorage;
268
-
269
-		$this->triggerHooks($newStorage, Filesystem::signal_create_mount);
270
-
271
-		$newStorage->setStatus(StorageNotAvailableException::STATUS_SUCCESS);
272
-		return $newStorage;
273
-	}
274
-
275
-	/**
276
-	 * Create a storage from its parameters
277
-	 *
278
-	 * @param string $mountPoint storage mount point
279
-	 * @param string $backendIdentifier backend identifier
280
-	 * @param string $authMechanismIdentifier authentication mechanism identifier
281
-	 * @param array $backendOptions backend-specific options
282
-	 * @param array|null $mountOptions mount-specific options
283
-	 * @param array|null $applicableUsers users for which to mount the storage
284
-	 * @param array|null $applicableGroups groups for which to mount the storage
285
-	 * @param int|null $priority priority
286
-	 *
287
-	 * @return StorageConfig
288
-	 */
289
-	public function createStorage(
290
-		$mountPoint,
291
-		$backendIdentifier,
292
-		$authMechanismIdentifier,
293
-		$backendOptions,
294
-		$mountOptions = null,
295
-		$applicableUsers = null,
296
-		$applicableGroups = null,
297
-		$priority = null
298
-	) {
299
-		$backend = $this->backendService->getBackend($backendIdentifier);
300
-		if (!$backend) {
301
-			$backend = new InvalidBackend($backendIdentifier);
302
-		}
303
-		$authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier);
304
-		if (!$authMechanism) {
305
-			$authMechanism = new InvalidAuth($authMechanismIdentifier);
306
-		}
307
-		$newStorage = new StorageConfig();
308
-		$newStorage->setMountPoint($mountPoint);
309
-		$newStorage->setBackend($backend);
310
-		$newStorage->setAuthMechanism($authMechanism);
311
-		$newStorage->setBackendOptions($backendOptions);
312
-		if (isset($mountOptions)) {
313
-			$newStorage->setMountOptions($mountOptions);
314
-		}
315
-		if (isset($applicableUsers)) {
316
-			$newStorage->setApplicableUsers($applicableUsers);
317
-		}
318
-		if (isset($applicableGroups)) {
319
-			$newStorage->setApplicableGroups($applicableGroups);
320
-		}
321
-		if (isset($priority)) {
322
-			$newStorage->setPriority($priority);
323
-		}
324
-
325
-		return $newStorage;
326
-	}
327
-
328
-	/**
329
-	 * Triggers the given hook signal for all the applicables given
330
-	 *
331
-	 * @param string $signal signal
332
-	 * @param string $mountPoint hook mount pount param
333
-	 * @param string $mountType hook mount type param
334
-	 * @param array $applicableArray array of applicable users/groups for which to trigger the hook
335
-	 */
336
-	protected function triggerApplicableHooks($signal, $mountPoint, $mountType, $applicableArray) {
337
-		foreach ($applicableArray as $applicable) {
338
-			\OCP\Util::emitHook(
339
-				Filesystem::CLASSNAME,
340
-				$signal,
341
-				[
342
-					Filesystem::signal_param_path => $mountPoint,
343
-					Filesystem::signal_param_mount_type => $mountType,
344
-					Filesystem::signal_param_users => $applicable,
345
-				]
346
-			);
347
-		}
348
-	}
349
-
350
-	/**
351
-	 * Triggers $signal for all applicable users of the given
352
-	 * storage
353
-	 *
354
-	 * @param StorageConfig $storage storage data
355
-	 * @param string $signal signal to trigger
356
-	 */
357
-	abstract protected function triggerHooks(StorageConfig $storage, $signal);
358
-
359
-	/**
360
-	 * Triggers signal_create_mount or signal_delete_mount to
361
-	 * accommodate for additions/deletions in applicableUsers
362
-	 * and applicableGroups fields.
363
-	 *
364
-	 * @param StorageConfig $oldStorage old storage data
365
-	 * @param StorageConfig $newStorage new storage data
366
-	 */
367
-	abstract protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage);
368
-
369
-	/**
370
-	 * Update storage to the configuration
371
-	 *
372
-	 * @param StorageConfig $updatedStorage storage attributes
373
-	 *
374
-	 * @return StorageConfig storage config
375
-	 * @throws NotFoundException if the given storage does not exist in the config
376
-	 */
377
-	public function updateStorage(StorageConfig $updatedStorage) {
378
-		$id = $updatedStorage->getId();
379
-
380
-		$existingMount = $this->dbConfig->getMountById($id);
381
-
382
-		if (!is_array($existingMount)) {
383
-			throw new NotFoundException('Storage with ID "' . $id . '" not found while updating storage');
384
-		}
385
-
386
-		$oldStorage = $this->getStorageConfigFromDBMount($existingMount);
387
-
388
-		if ($oldStorage->getBackend() instanceof InvalidBackend) {
389
-			throw new NotFoundException('Storage with id "' . $id . '" cannot be edited due to missing backend');
390
-		}
391
-
392
-		$removedUsers = array_diff($oldStorage->getApplicableUsers(), $updatedStorage->getApplicableUsers());
393
-		$removedGroups = array_diff($oldStorage->getApplicableGroups(), $updatedStorage->getApplicableGroups());
394
-		$addedUsers = array_diff($updatedStorage->getApplicableUsers(), $oldStorage->getApplicableUsers());
395
-		$addedGroups = array_diff($updatedStorage->getApplicableGroups(), $oldStorage->getApplicableGroups());
396
-
397
-		$oldUserCount = count($oldStorage->getApplicableUsers());
398
-		$oldGroupCount = count($oldStorage->getApplicableGroups());
399
-		$newUserCount = count($updatedStorage->getApplicableUsers());
400
-		$newGroupCount = count($updatedStorage->getApplicableGroups());
401
-		$wasGlobal = ($oldUserCount + $oldGroupCount) === 0;
402
-		$isGlobal = ($newUserCount + $newGroupCount) === 0;
403
-
404
-		foreach ($removedUsers as $user) {
405
-			$this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
406
-		}
407
-		foreach ($removedGroups as $group) {
408
-			$this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
409
-		}
410
-		foreach ($addedUsers as $user) {
411
-			$this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
412
-		}
413
-		foreach ($addedGroups as $group) {
414
-			$this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
415
-		}
416
-
417
-		if ($wasGlobal && !$isGlobal) {
418
-			$this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
419
-		} else if (!$wasGlobal && $isGlobal) {
420
-			$this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
421
-		}
422
-
423
-		$changedConfig = array_diff_assoc($updatedStorage->getBackendOptions(), $oldStorage->getBackendOptions());
424
-		$changedOptions = array_diff_assoc($updatedStorage->getMountOptions(), $oldStorage->getMountOptions());
425
-
426
-		foreach ($changedConfig as $key => $value) {
427
-			$this->dbConfig->setConfig($id, $key, $value);
428
-		}
429
-		foreach ($changedOptions as $key => $value) {
430
-			$this->dbConfig->setOption($id, $key, $value);
431
-		}
432
-
433
-		if ($updatedStorage->getMountPoint() !== $oldStorage->getMountPoint()) {
434
-			$this->dbConfig->setMountPoint($id, $updatedStorage->getMountPoint());
435
-		}
436
-
437
-		if ($updatedStorage->getAuthMechanism()->getIdentifier() !== $oldStorage->getAuthMechanism()->getIdentifier()) {
438
-			$this->dbConfig->setAuthBackend($id, $updatedStorage->getAuthMechanism()->getIdentifier());
439
-		}
440
-
441
-		$this->triggerChangeHooks($oldStorage, $updatedStorage);
442
-
443
-		if (($wasGlobal && !$isGlobal) || count($removedGroups) > 0) { // to expensive to properly handle these on the fly
444
-			$this->userMountCache->remoteStorageMounts($this->getStorageId($updatedStorage));
445
-		} else {
446
-			$storageId = $this->getStorageId($updatedStorage);
447
-			foreach ($removedUsers as $userId) {
448
-				$this->userMountCache->removeUserStorageMount($storageId, $userId);
449
-			}
450
-		}
451
-
452
-		return $this->getStorage($id);
453
-	}
454
-
455
-	/**
456
-	 * Delete the storage with the given id.
457
-	 *
458
-	 * @param int $id storage id
459
-	 *
460
-	 * @throws NotFoundException if no storage was found with the given id
461
-	 */
462
-	public function removeStorage($id) {
463
-		$existingMount = $this->dbConfig->getMountById($id);
464
-		var_dump($id);
465
-
466
-		if (!is_array($existingMount)) {
467
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
468
-		}
469
-
470
-		$this->dbConfig->removeMount($id);
471
-
472
-		$deletedStorage = $this->getStorageConfigFromDBMount($existingMount);
473
-		$this->triggerHooks($deletedStorage, Filesystem::signal_delete_mount);
474
-
475
-		// delete oc_storages entries and oc_filecache
476
-		try {
477
-			$rustyStorageId = $this->getRustyStorageIdFromConfig($deletedStorage);
478
-			error_log($rustyStorageId);
479
-			\OC\Files\Cache\Storage::remove($rustyStorageId);
480
-		} catch (\Exception $e) {
481
-			// can happen either for invalid configs where the storage could not
482
-			// be instantiated or whenever $user vars where used, in which case
483
-			// the storage id could not be computed
484
-			var_dump($e);
485
-			\OC::$server->getLogger()->logException($e, [
486
-				'level' => ILogger::ERROR,
487
-				'app' => 'files_external',
488
-			]);
489
-		}
490
-	}
491
-
492
-	/**
493
-	 * Returns the rusty storage id from oc_storages from the given storage config.
494
-	 *
495
-	 * @param StorageConfig $storageConfig
496
-	 * @return string rusty storage id
497
-	 */
498
-	private function getRustyStorageIdFromConfig(StorageConfig $storageConfig) {
499
-		// if any of the storage options contains $user, it is not possible
500
-		// to compute the possible storage id as we don't know which users
501
-		// mounted it already (and we certainly don't want to iterate over ALL users)
502
-		foreach ($storageConfig->getBackendOptions() as $value) {
503
-			if (strpos($value, '$user') !== false) {
504
-				throw new \Exception('Cannot compute storage id for deletion due to $user vars in the configuration');
505
-			}
506
-		}
507
-
508
-		// note: similar to ConfigAdapter->prepateStorageConfig()
509
-		$storageConfig->getAuthMechanism()->manipulateStorageConfig($storageConfig);
510
-		$storageConfig->getBackend()->manipulateStorageConfig($storageConfig);
511
-
512
-		$class = $storageConfig->getBackend()->getStorageClass();
513
-		$storageImpl = new $class($storageConfig->getBackendOptions());
514
-
515
-		return $storageImpl->getId();
516
-	}
517
-
518
-	/**
519
-	 * Construct the storage implementation
520
-	 *
521
-	 * @param StorageConfig $storageConfig
522
-	 * @return int
523
-	 */
524
-	private function getStorageId(StorageConfig $storageConfig) {
525
-		try {
526
-			$class = $storageConfig->getBackend()->getStorageClass();
527
-			/** @var \OC\Files\Storage\Storage $storage */
528
-			$storage = new $class($storageConfig->getBackendOptions());
529
-
530
-			// auth mechanism should fire first
531
-			$storage = $storageConfig->getBackend()->wrapStorage($storage);
532
-			$storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
533
-
534
-			return $storage->getStorageCache()->getNumericId();
535
-		} catch (\Exception $e) {
536
-			return -1;
537
-		}
538
-	}
47
+    /** @var BackendService */
48
+    protected $backendService;
49
+
50
+    /**
51
+     * @var DBConfigService
52
+     */
53
+    protected $dbConfig;
54
+
55
+    /**
56
+     * @var IUserMountCache
57
+     */
58
+    protected $userMountCache;
59
+
60
+    /**
61
+     * @param BackendService $backendService
62
+     * @param DBConfigService $dbConfigService
63
+     * @param IUserMountCache $userMountCache
64
+     */
65
+    public function __construct(BackendService $backendService, DBConfigService $dbConfigService, IUserMountCache $userMountCache) {
66
+        $this->backendService = $backendService;
67
+        $this->dbConfig = $dbConfigService;
68
+        $this->userMountCache = $userMountCache;
69
+    }
70
+
71
+    protected function readDBConfig() {
72
+        return $this->dbConfig->getAdminMounts();
73
+    }
74
+
75
+    protected function getStorageConfigFromDBMount(array $mount) {
76
+        $applicableUsers = array_filter($mount['applicable'], function ($applicable) {
77
+            return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER;
78
+        });
79
+        $applicableUsers = array_map(function ($applicable) {
80
+            return $applicable['value'];
81
+        }, $applicableUsers);
82
+
83
+        $applicableGroups = array_filter($mount['applicable'], function ($applicable) {
84
+            return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP;
85
+        });
86
+        $applicableGroups = array_map(function ($applicable) {
87
+            return $applicable['value'];
88
+        }, $applicableGroups);
89
+
90
+        try {
91
+            $config = $this->createStorage(
92
+                $mount['mount_point'],
93
+                $mount['storage_backend'],
94
+                $mount['auth_backend'],
95
+                $mount['config'],
96
+                $mount['options'],
97
+                array_values($applicableUsers),
98
+                array_values($applicableGroups),
99
+                $mount['priority']
100
+            );
101
+            $config->setType($mount['type']);
102
+            $config->setId((int)$mount['mount_id']);
103
+            return $config;
104
+        } catch (\UnexpectedValueException $e) {
105
+            // don't die if a storage backend doesn't exist
106
+            \OC::$server->getLogger()->logException($e, [
107
+                'message' => 'Could not load storage.',
108
+                'level' => ILogger::ERROR,
109
+                'app' => 'files_external',
110
+            ]);
111
+            return null;
112
+        } catch (\InvalidArgumentException $e) {
113
+            \OC::$server->getLogger()->logException($e, [
114
+                'message' => 'Could not load storage.',
115
+                'level' => ILogger::ERROR,
116
+                'app' => 'files_external',
117
+            ]);
118
+            return null;
119
+        }
120
+    }
121
+
122
+    /**
123
+     * Read the external storages config
124
+     *
125
+     * @return array map of storage id to storage config
126
+     */
127
+    protected function readConfig() {
128
+        $mounts = $this->readDBConfig();
129
+        $configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
130
+        $configs = array_filter($configs, function ($config) {
131
+            return $config instanceof StorageConfig;
132
+        });
133
+
134
+        $keys = array_map(function (StorageConfig $config) {
135
+            return $config->getId();
136
+        }, $configs);
137
+
138
+        return array_combine($keys, $configs);
139
+    }
140
+
141
+    /**
142
+     * Get a storage with status
143
+     *
144
+     * @param int $id storage id
145
+     *
146
+     * @return StorageConfig
147
+     * @throws NotFoundException if the storage with the given id was not found
148
+     */
149
+    public function getStorage($id) {
150
+        $mount = $this->dbConfig->getMountById($id);
151
+
152
+        if (!is_array($mount)) {
153
+            throw new NotFoundException('Storage with ID "' . $id . '" not found');
154
+        }
155
+
156
+        $config = $this->getStorageConfigFromDBMount($mount);
157
+        if ($this->isApplicable($config)) {
158
+            return $config;
159
+        } else {
160
+            throw new NotFoundException('Storage with ID "' . $id . '" not found');
161
+        }
162
+    }
163
+
164
+    /**
165
+     * Check whether this storage service should provide access to a storage
166
+     *
167
+     * @param StorageConfig $config
168
+     * @return bool
169
+     */
170
+    abstract protected function isApplicable(StorageConfig $config);
171
+
172
+    /**
173
+     * Gets all storages, valid or not
174
+     *
175
+     * @return StorageConfig[] array of storage configs
176
+     */
177
+    public function getAllStorages() {
178
+        return $this->readConfig();
179
+    }
180
+
181
+    /**
182
+     * Gets all valid storages
183
+     *
184
+     * @return StorageConfig[]
185
+     */
186
+    public function getStorages() {
187
+        return array_filter($this->getAllStorages(), [$this, 'validateStorage']);
188
+    }
189
+
190
+    /**
191
+     * Validate storage
192
+     * FIXME: De-duplicate with StoragesController::validate()
193
+     *
194
+     * @param StorageConfig $storage
195
+     * @return bool
196
+     */
197
+    protected function validateStorage(StorageConfig $storage) {
198
+        /** @var Backend */
199
+        $backend = $storage->getBackend();
200
+        /** @var AuthMechanism */
201
+        $authMechanism = $storage->getAuthMechanism();
202
+
203
+        if (!$backend->isVisibleFor($this->getVisibilityType())) {
204
+            // not permitted to use backend
205
+            return false;
206
+        }
207
+        if (!$authMechanism->isVisibleFor($this->getVisibilityType())) {
208
+            // not permitted to use auth mechanism
209
+            return false;
210
+        }
211
+
212
+        return true;
213
+    }
214
+
215
+    /**
216
+     * Get the visibility type for this controller, used in validation
217
+     *
218
+     * @return string BackendService::VISIBILITY_* constants
219
+     */
220
+    abstract public function getVisibilityType();
221
+
222
+    /**
223
+     * @return integer
224
+     */
225
+    protected function getType() {
226
+        return DBConfigService::MOUNT_TYPE_ADMIN;
227
+    }
228
+
229
+    /**
230
+     * Add new storage to the configuration
231
+     *
232
+     * @param StorageConfig $newStorage storage attributes
233
+     *
234
+     * @return StorageConfig storage config, with added id
235
+     */
236
+    public function addStorage(StorageConfig $newStorage) {
237
+        $allStorages = $this->readConfig();
238
+
239
+        $configId = $this->dbConfig->addMount(
240
+            $newStorage->getMountPoint(),
241
+            $newStorage->getBackend()->getIdentifier(),
242
+            $newStorage->getAuthMechanism()->getIdentifier(),
243
+            $newStorage->getPriority(),
244
+            $this->getType()
245
+        );
246
+
247
+        $newStorage->setId($configId);
248
+
249
+        foreach ($newStorage->getApplicableUsers() as $user) {
250
+            $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_USER, $user);
251
+        }
252
+        foreach ($newStorage->getApplicableGroups() as $group) {
253
+            $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
254
+        }
255
+        foreach ($newStorage->getBackendOptions() as $key => $value) {
256
+            $this->dbConfig->setConfig($configId, $key, $value);
257
+        }
258
+        foreach ($newStorage->getMountOptions() as $key => $value) {
259
+            $this->dbConfig->setOption($configId, $key, $value);
260
+        }
261
+
262
+        if (count($newStorage->getApplicableUsers()) === 0 && count($newStorage->getApplicableGroups()) === 0) {
263
+            $this->dbConfig->addApplicable($configId, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
264
+        }
265
+
266
+        // add new storage
267
+        $allStorages[$configId] = $newStorage;
268
+
269
+        $this->triggerHooks($newStorage, Filesystem::signal_create_mount);
270
+
271
+        $newStorage->setStatus(StorageNotAvailableException::STATUS_SUCCESS);
272
+        return $newStorage;
273
+    }
274
+
275
+    /**
276
+     * Create a storage from its parameters
277
+     *
278
+     * @param string $mountPoint storage mount point
279
+     * @param string $backendIdentifier backend identifier
280
+     * @param string $authMechanismIdentifier authentication mechanism identifier
281
+     * @param array $backendOptions backend-specific options
282
+     * @param array|null $mountOptions mount-specific options
283
+     * @param array|null $applicableUsers users for which to mount the storage
284
+     * @param array|null $applicableGroups groups for which to mount the storage
285
+     * @param int|null $priority priority
286
+     *
287
+     * @return StorageConfig
288
+     */
289
+    public function createStorage(
290
+        $mountPoint,
291
+        $backendIdentifier,
292
+        $authMechanismIdentifier,
293
+        $backendOptions,
294
+        $mountOptions = null,
295
+        $applicableUsers = null,
296
+        $applicableGroups = null,
297
+        $priority = null
298
+    ) {
299
+        $backend = $this->backendService->getBackend($backendIdentifier);
300
+        if (!$backend) {
301
+            $backend = new InvalidBackend($backendIdentifier);
302
+        }
303
+        $authMechanism = $this->backendService->getAuthMechanism($authMechanismIdentifier);
304
+        if (!$authMechanism) {
305
+            $authMechanism = new InvalidAuth($authMechanismIdentifier);
306
+        }
307
+        $newStorage = new StorageConfig();
308
+        $newStorage->setMountPoint($mountPoint);
309
+        $newStorage->setBackend($backend);
310
+        $newStorage->setAuthMechanism($authMechanism);
311
+        $newStorage->setBackendOptions($backendOptions);
312
+        if (isset($mountOptions)) {
313
+            $newStorage->setMountOptions($mountOptions);
314
+        }
315
+        if (isset($applicableUsers)) {
316
+            $newStorage->setApplicableUsers($applicableUsers);
317
+        }
318
+        if (isset($applicableGroups)) {
319
+            $newStorage->setApplicableGroups($applicableGroups);
320
+        }
321
+        if (isset($priority)) {
322
+            $newStorage->setPriority($priority);
323
+        }
324
+
325
+        return $newStorage;
326
+    }
327
+
328
+    /**
329
+     * Triggers the given hook signal for all the applicables given
330
+     *
331
+     * @param string $signal signal
332
+     * @param string $mountPoint hook mount pount param
333
+     * @param string $mountType hook mount type param
334
+     * @param array $applicableArray array of applicable users/groups for which to trigger the hook
335
+     */
336
+    protected function triggerApplicableHooks($signal, $mountPoint, $mountType, $applicableArray) {
337
+        foreach ($applicableArray as $applicable) {
338
+            \OCP\Util::emitHook(
339
+                Filesystem::CLASSNAME,
340
+                $signal,
341
+                [
342
+                    Filesystem::signal_param_path => $mountPoint,
343
+                    Filesystem::signal_param_mount_type => $mountType,
344
+                    Filesystem::signal_param_users => $applicable,
345
+                ]
346
+            );
347
+        }
348
+    }
349
+
350
+    /**
351
+     * Triggers $signal for all applicable users of the given
352
+     * storage
353
+     *
354
+     * @param StorageConfig $storage storage data
355
+     * @param string $signal signal to trigger
356
+     */
357
+    abstract protected function triggerHooks(StorageConfig $storage, $signal);
358
+
359
+    /**
360
+     * Triggers signal_create_mount or signal_delete_mount to
361
+     * accommodate for additions/deletions in applicableUsers
362
+     * and applicableGroups fields.
363
+     *
364
+     * @param StorageConfig $oldStorage old storage data
365
+     * @param StorageConfig $newStorage new storage data
366
+     */
367
+    abstract protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage);
368
+
369
+    /**
370
+     * Update storage to the configuration
371
+     *
372
+     * @param StorageConfig $updatedStorage storage attributes
373
+     *
374
+     * @return StorageConfig storage config
375
+     * @throws NotFoundException if the given storage does not exist in the config
376
+     */
377
+    public function updateStorage(StorageConfig $updatedStorage) {
378
+        $id = $updatedStorage->getId();
379
+
380
+        $existingMount = $this->dbConfig->getMountById($id);
381
+
382
+        if (!is_array($existingMount)) {
383
+            throw new NotFoundException('Storage with ID "' . $id . '" not found while updating storage');
384
+        }
385
+
386
+        $oldStorage = $this->getStorageConfigFromDBMount($existingMount);
387
+
388
+        if ($oldStorage->getBackend() instanceof InvalidBackend) {
389
+            throw new NotFoundException('Storage with id "' . $id . '" cannot be edited due to missing backend');
390
+        }
391
+
392
+        $removedUsers = array_diff($oldStorage->getApplicableUsers(), $updatedStorage->getApplicableUsers());
393
+        $removedGroups = array_diff($oldStorage->getApplicableGroups(), $updatedStorage->getApplicableGroups());
394
+        $addedUsers = array_diff($updatedStorage->getApplicableUsers(), $oldStorage->getApplicableUsers());
395
+        $addedGroups = array_diff($updatedStorage->getApplicableGroups(), $oldStorage->getApplicableGroups());
396
+
397
+        $oldUserCount = count($oldStorage->getApplicableUsers());
398
+        $oldGroupCount = count($oldStorage->getApplicableGroups());
399
+        $newUserCount = count($updatedStorage->getApplicableUsers());
400
+        $newGroupCount = count($updatedStorage->getApplicableGroups());
401
+        $wasGlobal = ($oldUserCount + $oldGroupCount) === 0;
402
+        $isGlobal = ($newUserCount + $newGroupCount) === 0;
403
+
404
+        foreach ($removedUsers as $user) {
405
+            $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
406
+        }
407
+        foreach ($removedGroups as $group) {
408
+            $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
409
+        }
410
+        foreach ($addedUsers as $user) {
411
+            $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_USER, $user);
412
+        }
413
+        foreach ($addedGroups as $group) {
414
+            $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GROUP, $group);
415
+        }
416
+
417
+        if ($wasGlobal && !$isGlobal) {
418
+            $this->dbConfig->removeApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
419
+        } else if (!$wasGlobal && $isGlobal) {
420
+            $this->dbConfig->addApplicable($id, DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
421
+        }
422
+
423
+        $changedConfig = array_diff_assoc($updatedStorage->getBackendOptions(), $oldStorage->getBackendOptions());
424
+        $changedOptions = array_diff_assoc($updatedStorage->getMountOptions(), $oldStorage->getMountOptions());
425
+
426
+        foreach ($changedConfig as $key => $value) {
427
+            $this->dbConfig->setConfig($id, $key, $value);
428
+        }
429
+        foreach ($changedOptions as $key => $value) {
430
+            $this->dbConfig->setOption($id, $key, $value);
431
+        }
432
+
433
+        if ($updatedStorage->getMountPoint() !== $oldStorage->getMountPoint()) {
434
+            $this->dbConfig->setMountPoint($id, $updatedStorage->getMountPoint());
435
+        }
436
+
437
+        if ($updatedStorage->getAuthMechanism()->getIdentifier() !== $oldStorage->getAuthMechanism()->getIdentifier()) {
438
+            $this->dbConfig->setAuthBackend($id, $updatedStorage->getAuthMechanism()->getIdentifier());
439
+        }
440
+
441
+        $this->triggerChangeHooks($oldStorage, $updatedStorage);
442
+
443
+        if (($wasGlobal && !$isGlobal) || count($removedGroups) > 0) { // to expensive to properly handle these on the fly
444
+            $this->userMountCache->remoteStorageMounts($this->getStorageId($updatedStorage));
445
+        } else {
446
+            $storageId = $this->getStorageId($updatedStorage);
447
+            foreach ($removedUsers as $userId) {
448
+                $this->userMountCache->removeUserStorageMount($storageId, $userId);
449
+            }
450
+        }
451
+
452
+        return $this->getStorage($id);
453
+    }
454
+
455
+    /**
456
+     * Delete the storage with the given id.
457
+     *
458
+     * @param int $id storage id
459
+     *
460
+     * @throws NotFoundException if no storage was found with the given id
461
+     */
462
+    public function removeStorage($id) {
463
+        $existingMount = $this->dbConfig->getMountById($id);
464
+        var_dump($id);
465
+
466
+        if (!is_array($existingMount)) {
467
+            throw new NotFoundException('Storage with ID "' . $id . '" not found');
468
+        }
469
+
470
+        $this->dbConfig->removeMount($id);
471
+
472
+        $deletedStorage = $this->getStorageConfigFromDBMount($existingMount);
473
+        $this->triggerHooks($deletedStorage, Filesystem::signal_delete_mount);
474
+
475
+        // delete oc_storages entries and oc_filecache
476
+        try {
477
+            $rustyStorageId = $this->getRustyStorageIdFromConfig($deletedStorage);
478
+            error_log($rustyStorageId);
479
+            \OC\Files\Cache\Storage::remove($rustyStorageId);
480
+        } catch (\Exception $e) {
481
+            // can happen either for invalid configs where the storage could not
482
+            // be instantiated or whenever $user vars where used, in which case
483
+            // the storage id could not be computed
484
+            var_dump($e);
485
+            \OC::$server->getLogger()->logException($e, [
486
+                'level' => ILogger::ERROR,
487
+                'app' => 'files_external',
488
+            ]);
489
+        }
490
+    }
491
+
492
+    /**
493
+     * Returns the rusty storage id from oc_storages from the given storage config.
494
+     *
495
+     * @param StorageConfig $storageConfig
496
+     * @return string rusty storage id
497
+     */
498
+    private function getRustyStorageIdFromConfig(StorageConfig $storageConfig) {
499
+        // if any of the storage options contains $user, it is not possible
500
+        // to compute the possible storage id as we don't know which users
501
+        // mounted it already (and we certainly don't want to iterate over ALL users)
502
+        foreach ($storageConfig->getBackendOptions() as $value) {
503
+            if (strpos($value, '$user') !== false) {
504
+                throw new \Exception('Cannot compute storage id for deletion due to $user vars in the configuration');
505
+            }
506
+        }
507
+
508
+        // note: similar to ConfigAdapter->prepateStorageConfig()
509
+        $storageConfig->getAuthMechanism()->manipulateStorageConfig($storageConfig);
510
+        $storageConfig->getBackend()->manipulateStorageConfig($storageConfig);
511
+
512
+        $class = $storageConfig->getBackend()->getStorageClass();
513
+        $storageImpl = new $class($storageConfig->getBackendOptions());
514
+
515
+        return $storageImpl->getId();
516
+    }
517
+
518
+    /**
519
+     * Construct the storage implementation
520
+     *
521
+     * @param StorageConfig $storageConfig
522
+     * @return int
523
+     */
524
+    private function getStorageId(StorageConfig $storageConfig) {
525
+        try {
526
+            $class = $storageConfig->getBackend()->getStorageClass();
527
+            /** @var \OC\Files\Storage\Storage $storage */
528
+            $storage = new $class($storageConfig->getBackendOptions());
529
+
530
+            // auth mechanism should fire first
531
+            $storage = $storageConfig->getBackend()->wrapStorage($storage);
532
+            $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
533
+
534
+            return $storage->getStorageCache()->getNumericId();
535
+        } catch (\Exception $e) {
536
+            return -1;
537
+        }
538
+    }
539 539
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -73,17 +73,17 @@  discard block
 block discarded – undo
73 73
 	}
74 74
 
75 75
 	protected function getStorageConfigFromDBMount(array $mount) {
76
-		$applicableUsers = array_filter($mount['applicable'], function ($applicable) {
76
+		$applicableUsers = array_filter($mount['applicable'], function($applicable) {
77 77
 			return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER;
78 78
 		});
79
-		$applicableUsers = array_map(function ($applicable) {
79
+		$applicableUsers = array_map(function($applicable) {
80 80
 			return $applicable['value'];
81 81
 		}, $applicableUsers);
82 82
 
83
-		$applicableGroups = array_filter($mount['applicable'], function ($applicable) {
83
+		$applicableGroups = array_filter($mount['applicable'], function($applicable) {
84 84
 			return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP;
85 85
 		});
86
-		$applicableGroups = array_map(function ($applicable) {
86
+		$applicableGroups = array_map(function($applicable) {
87 87
 			return $applicable['value'];
88 88
 		}, $applicableGroups);
89 89
 
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
 				$mount['priority']
100 100
 			);
101 101
 			$config->setType($mount['type']);
102
-			$config->setId((int)$mount['mount_id']);
102
+			$config->setId((int) $mount['mount_id']);
103 103
 			return $config;
104 104
 		} catch (\UnexpectedValueException $e) {
105 105
 			// don't die if a storage backend doesn't exist
@@ -127,11 +127,11 @@  discard block
 block discarded – undo
127 127
 	protected function readConfig() {
128 128
 		$mounts = $this->readDBConfig();
129 129
 		$configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
130
-		$configs = array_filter($configs, function ($config) {
130
+		$configs = array_filter($configs, function($config) {
131 131
 			return $config instanceof StorageConfig;
132 132
 		});
133 133
 
134
-		$keys = array_map(function (StorageConfig $config) {
134
+		$keys = array_map(function(StorageConfig $config) {
135 135
 			return $config->getId();
136 136
 		}, $configs);
137 137
 
@@ -150,14 +150,14 @@  discard block
 block discarded – undo
150 150
 		$mount = $this->dbConfig->getMountById($id);
151 151
 
152 152
 		if (!is_array($mount)) {
153
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
153
+			throw new NotFoundException('Storage with ID "'.$id.'" not found');
154 154
 		}
155 155
 
156 156
 		$config = $this->getStorageConfigFromDBMount($mount);
157 157
 		if ($this->isApplicable($config)) {
158 158
 			return $config;
159 159
 		} else {
160
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
160
+			throw new NotFoundException('Storage with ID "'.$id.'" not found');
161 161
 		}
162 162
 	}
163 163
 
@@ -380,13 +380,13 @@  discard block
 block discarded – undo
380 380
 		$existingMount = $this->dbConfig->getMountById($id);
381 381
 
382 382
 		if (!is_array($existingMount)) {
383
-			throw new NotFoundException('Storage with ID "' . $id . '" not found while updating storage');
383
+			throw new NotFoundException('Storage with ID "'.$id.'" not found while updating storage');
384 384
 		}
385 385
 
386 386
 		$oldStorage = $this->getStorageConfigFromDBMount($existingMount);
387 387
 
388 388
 		if ($oldStorage->getBackend() instanceof InvalidBackend) {
389
-			throw new NotFoundException('Storage with id "' . $id . '" cannot be edited due to missing backend');
389
+			throw new NotFoundException('Storage with id "'.$id.'" cannot be edited due to missing backend');
390 390
 		}
391 391
 
392 392
 		$removedUsers = array_diff($oldStorage->getApplicableUsers(), $updatedStorage->getApplicableUsers());
@@ -464,7 +464,7 @@  discard block
 block discarded – undo
464 464
 		var_dump($id);
465 465
 
466 466
 		if (!is_array($existingMount)) {
467
-			throw new NotFoundException('Storage with ID "' . $id . '" not found');
467
+			throw new NotFoundException('Storage with ID "'.$id.'" not found');
468 468
 		}
469 469
 
470 470
 		$this->dbConfig->removeMount($id);
Please login to merge, or discard this patch.