Passed
Push — master ( d92c5f...e8ae44 )
by
unknown
23:17 queued 12s
created
apps/dav/lib/Connector/Sabre/Directory.php 1 patch
Indentation   +425 added lines, -425 removed lines patch added patch discarded remove patch
@@ -58,429 +58,429 @@
 block discarded – undo
58 58
 
59 59
 class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota, \Sabre\DAV\IMoveTarget, \Sabre\DAV\ICopyTarget {
60 60
 
61
-	/**
62
-	 * Cached directory content
63
-	 * @var \OCP\Files\FileInfo[]
64
-	 */
65
-	private ?array $dirContent = null;
66
-
67
-	/** Cached quota info */
68
-	private ?array $quotaInfo = null;
69
-	private ?CachingTree $tree = null;
70
-
71
-	/** @var array<string, array<int, FileMetadata>> */
72
-	private array $metadata = [];
73
-
74
-	/**
75
-	 * Sets up the node, expects a full path name
76
-	 */
77
-	public function __construct(View $view, FileInfo $info, ?CachingTree $tree = null, IShareManager $shareManager = null) {
78
-		parent::__construct($view, $info, $shareManager);
79
-		$this->tree = $tree;
80
-	}
81
-
82
-	/**
83
-	 * Creates a new file in the directory
84
-	 *
85
-	 * Data will either be supplied as a stream resource, or in certain cases
86
-	 * as a string. Keep in mind that you may have to support either.
87
-	 *
88
-	 * After successful creation of the file, you may choose to return the ETag
89
-	 * of the new file here.
90
-	 *
91
-	 * The returned ETag must be surrounded by double-quotes (The quotes should
92
-	 * be part of the actual string).
93
-	 *
94
-	 * If you cannot accurately determine the ETag, you should not return it.
95
-	 * If you don't store the file exactly as-is (you're transforming it
96
-	 * somehow) you should also not return an ETag.
97
-	 *
98
-	 * This means that if a subsequent GET to this new file does not exactly
99
-	 * return the same contents of what was submitted here, you are strongly
100
-	 * recommended to omit the ETag.
101
-	 *
102
-	 * @param string $name Name of the file
103
-	 * @param resource|string $data Initial payload
104
-	 * @return null|string
105
-	 * @throws Exception\EntityTooLarge
106
-	 * @throws Exception\UnsupportedMediaType
107
-	 * @throws FileLocked
108
-	 * @throws InvalidPath
109
-	 * @throws \Sabre\DAV\Exception
110
-	 * @throws \Sabre\DAV\Exception\BadRequest
111
-	 * @throws \Sabre\DAV\Exception\Forbidden
112
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
113
-	 */
114
-	public function createFile($name, $data = null) {
115
-		try {
116
-			// for chunked upload also updating a existing file is a "createFile"
117
-			// because we create all the chunks before re-assemble them to the existing file.
118
-			if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
119
-
120
-				// exit if we can't create a new file and we don't updatable existing file
121
-				$chunkInfo = \OC_FileChunking::decodeName($name);
122
-				if (!$this->fileView->isCreatable($this->path) &&
123
-					!$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name'])
124
-				) {
125
-					throw new \Sabre\DAV\Exception\Forbidden();
126
-				}
127
-			} else {
128
-				// For non-chunked upload it is enough to check if we can create a new file
129
-				if (!$this->fileView->isCreatable($this->path)) {
130
-					throw new \Sabre\DAV\Exception\Forbidden();
131
-				}
132
-			}
133
-
134
-			$this->fileView->verifyPath($this->path, $name);
135
-
136
-			$path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
137
-			// in case the file already exists/overwriting
138
-			$info = $this->fileView->getFileInfo($this->path . '/' . $name);
139
-			if (!$info) {
140
-				// use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
141
-				$info = new \OC\Files\FileInfo($path, null, null, [
142
-					'type' => FileInfo::TYPE_FILE
143
-				], null);
144
-			}
145
-			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
146
-
147
-			// only allow 1 process to upload a file at once but still allow reading the file while writing the part file
148
-			$node->acquireLock(ILockingProvider::LOCK_SHARED);
149
-			$this->fileView->lockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
150
-
151
-			$result = $node->put($data);
152
-
153
-			$this->fileView->unlockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
154
-			$node->releaseLock(ILockingProvider::LOCK_SHARED);
155
-			return $result;
156
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
157
-			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e);
158
-		} catch (InvalidPathException $ex) {
159
-			throw new InvalidPath($ex->getMessage(), false, $ex);
160
-		} catch (ForbiddenException $ex) {
161
-			throw new Forbidden($ex->getMessage(), $ex->getRetry(), $ex);
162
-		} catch (LockedException $e) {
163
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
164
-		}
165
-	}
166
-
167
-	/**
168
-	 * Creates a new subdirectory
169
-	 *
170
-	 * @param string $name
171
-	 * @throws FileLocked
172
-	 * @throws InvalidPath
173
-	 * @throws \Sabre\DAV\Exception\Forbidden
174
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
175
-	 */
176
-	public function createDirectory($name) {
177
-		try {
178
-			if (!$this->info->isCreatable()) {
179
-				throw new \Sabre\DAV\Exception\Forbidden();
180
-			}
181
-
182
-			$this->fileView->verifyPath($this->path, $name);
183
-			$newPath = $this->path . '/' . $name;
184
-			if (!$this->fileView->mkdir($newPath)) {
185
-				throw new \Sabre\DAV\Exception\Forbidden('Could not create directory ' . $newPath);
186
-			}
187
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
188
-			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
189
-		} catch (InvalidPathException $ex) {
190
-			throw new InvalidPath($ex->getMessage());
191
-		} catch (ForbiddenException $ex) {
192
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
193
-		} catch (LockedException $e) {
194
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
195
-		}
196
-	}
197
-
198
-	/**
199
-	 * Returns a specific child node, referenced by its name
200
-	 *
201
-	 * @param string $name
202
-	 * @param \OCP\Files\FileInfo $info
203
-	 * @return \Sabre\DAV\INode
204
-	 * @throws InvalidPath
205
-	 * @throws \Sabre\DAV\Exception\NotFound
206
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
207
-	 */
208
-	public function getChild($name, $info = null) {
209
-		if (!$this->info->isReadable()) {
210
-			// avoid detecting files through this way
211
-			throw new NotFound();
212
-		}
213
-
214
-		$path = $this->path . '/' . $name;
215
-		if (is_null($info)) {
216
-			try {
217
-				$this->fileView->verifyPath($this->path, $name);
218
-				$info = $this->fileView->getFileInfo($path);
219
-			} catch (\OCP\Files\StorageNotAvailableException $e) {
220
-				throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
221
-			} catch (InvalidPathException $ex) {
222
-				throw new InvalidPath($ex->getMessage());
223
-			} catch (ForbiddenException $e) {
224
-				throw new \Sabre\DAV\Exception\Forbidden();
225
-			}
226
-		}
227
-
228
-		if (!$info) {
229
-			throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
230
-		}
231
-
232
-		if ($info->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
233
-			$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
234
-		} else {
235
-			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager);
236
-		}
237
-		if ($this->tree) {
238
-			$this->tree->cacheNode($node);
239
-		}
240
-		return $node;
241
-	}
242
-
243
-	/**
244
-	 * Returns an array with all the child nodes
245
-	 *
246
-	 * @return \Sabre\DAV\INode[]
247
-	 * @throws \Sabre\DAV\Exception\Locked
248
-	 * @throws \OCA\DAV\Connector\Sabre\Exception\Forbidden
249
-	 */
250
-	public function getChildren() {
251
-		if (!is_null($this->dirContent)) {
252
-			return $this->dirContent;
253
-		}
254
-		try {
255
-			if (!$this->info->isReadable()) {
256
-				// return 403 instead of 404 because a 404 would make
257
-				// the caller believe that the collection itself does not exist
258
-				if (\OCP\Server::get(\OCP\App\IAppManager::class)->isInstalled('files_accesscontrol')) {
259
-					throw new Forbidden('No read permissions. This might be caused by files_accesscontrol, check your configured rules');
260
-				} else {
261
-					throw new Forbidden('No read permissions');
262
-				}
263
-			}
264
-			$folderContent = $this->getNode()->getDirectoryListing();
265
-		} catch (LockedException $e) {
266
-			throw new Locked();
267
-		}
268
-
269
-		$nodes = [];
270
-		foreach ($folderContent as $info) {
271
-			$node = $this->getChild($info->getName(), $info);
272
-			$nodes[] = $node;
273
-		}
274
-		$this->dirContent = $nodes;
275
-		return $this->dirContent;
276
-	}
277
-
278
-	/**
279
-	 * Checks if a child exists.
280
-	 *
281
-	 * @param string $name
282
-	 * @return bool
283
-	 */
284
-	public function childExists($name) {
285
-		// note: here we do NOT resolve the chunk file name to the real file name
286
-		// to make sure we return false when checking for file existence with a chunk
287
-		// file name.
288
-		// This is to make sure that "createFile" is still triggered
289
-		// (required old code) instead of "updateFile".
290
-		//
291
-		// TODO: resolve chunk file name here and implement "updateFile"
292
-		$path = $this->path . '/' . $name;
293
-		return $this->fileView->file_exists($path);
294
-	}
295
-
296
-	/**
297
-	 * Deletes all files in this directory, and then itself
298
-	 *
299
-	 * @return void
300
-	 * @throws FileLocked
301
-	 * @throws \Sabre\DAV\Exception\Forbidden
302
-	 */
303
-	public function delete() {
304
-		if ($this->path === '' || $this->path === '/' || !$this->info->isDeletable()) {
305
-			throw new \Sabre\DAV\Exception\Forbidden();
306
-		}
307
-
308
-		try {
309
-			if (!$this->fileView->rmdir($this->path)) {
310
-				// assume it wasn't possible to remove due to permission issue
311
-				throw new \Sabre\DAV\Exception\Forbidden();
312
-			}
313
-		} catch (ForbiddenException $ex) {
314
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
315
-		} catch (LockedException $e) {
316
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
317
-		}
318
-	}
319
-
320
-	/**
321
-	 * Returns available diskspace information
322
-	 *
323
-	 * @return array
324
-	 */
325
-	public function getQuotaInfo() {
326
-		/** @var LoggerInterface $logger */
327
-		$logger = \OC::$server->get(LoggerInterface::class);
328
-		if ($this->quotaInfo) {
329
-			return $this->quotaInfo;
330
-		}
331
-		try {
332
-			$storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info, false);
333
-			if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
334
-				$free = \OCP\Files\FileInfo::SPACE_UNLIMITED;
335
-			} else {
336
-				$free = $storageInfo['free'];
337
-			}
338
-			$this->quotaInfo = [
339
-				$storageInfo['used'],
340
-				$free
341
-			];
342
-			return $this->quotaInfo;
343
-		} catch (\OCP\Files\NotFoundException $e) {
344
-			$logger->warning("error while getting quota into", ['exception' => $e]);
345
-			return [0, 0];
346
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
347
-			$logger->warning("error while getting quota into", ['exception' => $e]);
348
-			return [0, 0];
349
-		} catch (NotPermittedException $e) {
350
-			$logger->warning("error while getting quota into", ['exception' => $e]);
351
-			return [0, 0];
352
-		}
353
-	}
354
-
355
-	/**
356
-	 * Moves a node into this collection.
357
-	 *
358
-	 * It is up to the implementors to:
359
-	 *   1. Create the new resource.
360
-	 *   2. Remove the old resource.
361
-	 *   3. Transfer any properties or other data.
362
-	 *
363
-	 * Generally you should make very sure that your collection can easily move
364
-	 * the move.
365
-	 *
366
-	 * If you don't, just return false, which will trigger sabre/dav to handle
367
-	 * the move itself. If you return true from this function, the assumption
368
-	 * is that the move was successful.
369
-	 *
370
-	 * @param string $targetName New local file/collection name.
371
-	 * @param string $fullSourcePath Full path to source node
372
-	 * @param INode $sourceNode Source node itself
373
-	 * @return bool
374
-	 * @throws BadRequest
375
-	 * @throws ServiceUnavailable
376
-	 * @throws Forbidden
377
-	 * @throws FileLocked
378
-	 * @throws \Sabre\DAV\Exception\Forbidden
379
-	 */
380
-	public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
381
-		if (!$sourceNode instanceof Node) {
382
-			// it's a file of another kind, like FutureFile
383
-			if ($sourceNode instanceof IFile) {
384
-				// fallback to default copy+delete handling
385
-				return false;
386
-			}
387
-			throw new BadRequest('Incompatible node types');
388
-		}
389
-
390
-		if (!$this->fileView) {
391
-			throw new ServiceUnavailable('filesystem not setup');
392
-		}
393
-
394
-		$destinationPath = $this->getPath() . '/' . $targetName;
395
-
396
-
397
-		$targetNodeExists = $this->childExists($targetName);
398
-
399
-		// at getNodeForPath we also check the path for isForbiddenFileOrDir
400
-		// with that we have covered both source and destination
401
-		if ($sourceNode instanceof Directory && $targetNodeExists) {
402
-			throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
403
-		}
404
-
405
-		[$sourceDir,] = \Sabre\Uri\split($sourceNode->getPath());
406
-		$destinationDir = $this->getPath();
407
-
408
-		$sourcePath = $sourceNode->getPath();
409
-
410
-		$isMovableMount = false;
411
-		$sourceMount = \OC::$server->getMountManager()->find($this->fileView->getAbsolutePath($sourcePath));
412
-		$internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
413
-		if ($sourceMount instanceof MoveableMount && $internalPath === '') {
414
-			$isMovableMount = true;
415
-		}
416
-
417
-		try {
418
-			$sameFolder = ($sourceDir === $destinationDir);
419
-			// if we're overwriting or same folder
420
-			if ($targetNodeExists || $sameFolder) {
421
-				// note that renaming a share mount point is always allowed
422
-				if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
423
-					throw new \Sabre\DAV\Exception\Forbidden();
424
-				}
425
-			} else {
426
-				if (!$this->fileView->isCreatable($destinationDir)) {
427
-					throw new \Sabre\DAV\Exception\Forbidden();
428
-				}
429
-			}
430
-
431
-			if (!$sameFolder) {
432
-				// moving to a different folder, source will be gone, like a deletion
433
-				// note that moving a share mount point is always allowed
434
-				if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
435
-					throw new \Sabre\DAV\Exception\Forbidden();
436
-				}
437
-			}
438
-
439
-			$fileName = basename($destinationPath);
440
-			try {
441
-				$this->fileView->verifyPath($destinationDir, $fileName);
442
-			} catch (InvalidPathException $ex) {
443
-				throw new InvalidPath($ex->getMessage());
444
-			}
445
-
446
-			$renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
447
-			if (!$renameOkay) {
448
-				throw new \Sabre\DAV\Exception\Forbidden('');
449
-			}
450
-		} catch (StorageNotAvailableException $e) {
451
-			throw new ServiceUnavailable($e->getMessage());
452
-		} catch (ForbiddenException $ex) {
453
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
454
-		} catch (LockedException $e) {
455
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
456
-		}
457
-
458
-		return true;
459
-	}
460
-
461
-
462
-	public function copyInto($targetName, $sourcePath, INode $sourceNode) {
463
-		if ($sourceNode instanceof File || $sourceNode instanceof Directory) {
464
-			$destinationPath = $this->getPath() . '/' . $targetName;
465
-			$sourcePath = $sourceNode->getPath();
466
-
467
-			if (!$this->fileView->isCreatable($this->getPath())) {
468
-				throw new \Sabre\DAV\Exception\Forbidden();
469
-			}
470
-
471
-			try {
472
-				$this->fileView->verifyPath($this->getPath(), $targetName);
473
-			} catch (InvalidPathException $ex) {
474
-				throw new InvalidPath($ex->getMessage());
475
-			}
476
-
477
-			return $this->fileView->copy($sourcePath, $destinationPath);
478
-		}
479
-
480
-		return false;
481
-	}
482
-
483
-	public function getNode(): Folder {
484
-		return $this->node;
485
-	}
61
+    /**
62
+     * Cached directory content
63
+     * @var \OCP\Files\FileInfo[]
64
+     */
65
+    private ?array $dirContent = null;
66
+
67
+    /** Cached quota info */
68
+    private ?array $quotaInfo = null;
69
+    private ?CachingTree $tree = null;
70
+
71
+    /** @var array<string, array<int, FileMetadata>> */
72
+    private array $metadata = [];
73
+
74
+    /**
75
+     * Sets up the node, expects a full path name
76
+     */
77
+    public function __construct(View $view, FileInfo $info, ?CachingTree $tree = null, IShareManager $shareManager = null) {
78
+        parent::__construct($view, $info, $shareManager);
79
+        $this->tree = $tree;
80
+    }
81
+
82
+    /**
83
+     * Creates a new file in the directory
84
+     *
85
+     * Data will either be supplied as a stream resource, or in certain cases
86
+     * as a string. Keep in mind that you may have to support either.
87
+     *
88
+     * After successful creation of the file, you may choose to return the ETag
89
+     * of the new file here.
90
+     *
91
+     * The returned ETag must be surrounded by double-quotes (The quotes should
92
+     * be part of the actual string).
93
+     *
94
+     * If you cannot accurately determine the ETag, you should not return it.
95
+     * If you don't store the file exactly as-is (you're transforming it
96
+     * somehow) you should also not return an ETag.
97
+     *
98
+     * This means that if a subsequent GET to this new file does not exactly
99
+     * return the same contents of what was submitted here, you are strongly
100
+     * recommended to omit the ETag.
101
+     *
102
+     * @param string $name Name of the file
103
+     * @param resource|string $data Initial payload
104
+     * @return null|string
105
+     * @throws Exception\EntityTooLarge
106
+     * @throws Exception\UnsupportedMediaType
107
+     * @throws FileLocked
108
+     * @throws InvalidPath
109
+     * @throws \Sabre\DAV\Exception
110
+     * @throws \Sabre\DAV\Exception\BadRequest
111
+     * @throws \Sabre\DAV\Exception\Forbidden
112
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
113
+     */
114
+    public function createFile($name, $data = null) {
115
+        try {
116
+            // for chunked upload also updating a existing file is a "createFile"
117
+            // because we create all the chunks before re-assemble them to the existing file.
118
+            if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
119
+
120
+                // exit if we can't create a new file and we don't updatable existing file
121
+                $chunkInfo = \OC_FileChunking::decodeName($name);
122
+                if (!$this->fileView->isCreatable($this->path) &&
123
+                    !$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name'])
124
+                ) {
125
+                    throw new \Sabre\DAV\Exception\Forbidden();
126
+                }
127
+            } else {
128
+                // For non-chunked upload it is enough to check if we can create a new file
129
+                if (!$this->fileView->isCreatable($this->path)) {
130
+                    throw new \Sabre\DAV\Exception\Forbidden();
131
+                }
132
+            }
133
+
134
+            $this->fileView->verifyPath($this->path, $name);
135
+
136
+            $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
137
+            // in case the file already exists/overwriting
138
+            $info = $this->fileView->getFileInfo($this->path . '/' . $name);
139
+            if (!$info) {
140
+                // use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
141
+                $info = new \OC\Files\FileInfo($path, null, null, [
142
+                    'type' => FileInfo::TYPE_FILE
143
+                ], null);
144
+            }
145
+            $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
146
+
147
+            // only allow 1 process to upload a file at once but still allow reading the file while writing the part file
148
+            $node->acquireLock(ILockingProvider::LOCK_SHARED);
149
+            $this->fileView->lockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
150
+
151
+            $result = $node->put($data);
152
+
153
+            $this->fileView->unlockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
154
+            $node->releaseLock(ILockingProvider::LOCK_SHARED);
155
+            return $result;
156
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
157
+            throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e);
158
+        } catch (InvalidPathException $ex) {
159
+            throw new InvalidPath($ex->getMessage(), false, $ex);
160
+        } catch (ForbiddenException $ex) {
161
+            throw new Forbidden($ex->getMessage(), $ex->getRetry(), $ex);
162
+        } catch (LockedException $e) {
163
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
164
+        }
165
+    }
166
+
167
+    /**
168
+     * Creates a new subdirectory
169
+     *
170
+     * @param string $name
171
+     * @throws FileLocked
172
+     * @throws InvalidPath
173
+     * @throws \Sabre\DAV\Exception\Forbidden
174
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
175
+     */
176
+    public function createDirectory($name) {
177
+        try {
178
+            if (!$this->info->isCreatable()) {
179
+                throw new \Sabre\DAV\Exception\Forbidden();
180
+            }
181
+
182
+            $this->fileView->verifyPath($this->path, $name);
183
+            $newPath = $this->path . '/' . $name;
184
+            if (!$this->fileView->mkdir($newPath)) {
185
+                throw new \Sabre\DAV\Exception\Forbidden('Could not create directory ' . $newPath);
186
+            }
187
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
188
+            throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
189
+        } catch (InvalidPathException $ex) {
190
+            throw new InvalidPath($ex->getMessage());
191
+        } catch (ForbiddenException $ex) {
192
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
193
+        } catch (LockedException $e) {
194
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
195
+        }
196
+    }
197
+
198
+    /**
199
+     * Returns a specific child node, referenced by its name
200
+     *
201
+     * @param string $name
202
+     * @param \OCP\Files\FileInfo $info
203
+     * @return \Sabre\DAV\INode
204
+     * @throws InvalidPath
205
+     * @throws \Sabre\DAV\Exception\NotFound
206
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
207
+     */
208
+    public function getChild($name, $info = null) {
209
+        if (!$this->info->isReadable()) {
210
+            // avoid detecting files through this way
211
+            throw new NotFound();
212
+        }
213
+
214
+        $path = $this->path . '/' . $name;
215
+        if (is_null($info)) {
216
+            try {
217
+                $this->fileView->verifyPath($this->path, $name);
218
+                $info = $this->fileView->getFileInfo($path);
219
+            } catch (\OCP\Files\StorageNotAvailableException $e) {
220
+                throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
221
+            } catch (InvalidPathException $ex) {
222
+                throw new InvalidPath($ex->getMessage());
223
+            } catch (ForbiddenException $e) {
224
+                throw new \Sabre\DAV\Exception\Forbidden();
225
+            }
226
+        }
227
+
228
+        if (!$info) {
229
+            throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
230
+        }
231
+
232
+        if ($info->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
233
+            $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
234
+        } else {
235
+            $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager);
236
+        }
237
+        if ($this->tree) {
238
+            $this->tree->cacheNode($node);
239
+        }
240
+        return $node;
241
+    }
242
+
243
+    /**
244
+     * Returns an array with all the child nodes
245
+     *
246
+     * @return \Sabre\DAV\INode[]
247
+     * @throws \Sabre\DAV\Exception\Locked
248
+     * @throws \OCA\DAV\Connector\Sabre\Exception\Forbidden
249
+     */
250
+    public function getChildren() {
251
+        if (!is_null($this->dirContent)) {
252
+            return $this->dirContent;
253
+        }
254
+        try {
255
+            if (!$this->info->isReadable()) {
256
+                // return 403 instead of 404 because a 404 would make
257
+                // the caller believe that the collection itself does not exist
258
+                if (\OCP\Server::get(\OCP\App\IAppManager::class)->isInstalled('files_accesscontrol')) {
259
+                    throw new Forbidden('No read permissions. This might be caused by files_accesscontrol, check your configured rules');
260
+                } else {
261
+                    throw new Forbidden('No read permissions');
262
+                }
263
+            }
264
+            $folderContent = $this->getNode()->getDirectoryListing();
265
+        } catch (LockedException $e) {
266
+            throw new Locked();
267
+        }
268
+
269
+        $nodes = [];
270
+        foreach ($folderContent as $info) {
271
+            $node = $this->getChild($info->getName(), $info);
272
+            $nodes[] = $node;
273
+        }
274
+        $this->dirContent = $nodes;
275
+        return $this->dirContent;
276
+    }
277
+
278
+    /**
279
+     * Checks if a child exists.
280
+     *
281
+     * @param string $name
282
+     * @return bool
283
+     */
284
+    public function childExists($name) {
285
+        // note: here we do NOT resolve the chunk file name to the real file name
286
+        // to make sure we return false when checking for file existence with a chunk
287
+        // file name.
288
+        // This is to make sure that "createFile" is still triggered
289
+        // (required old code) instead of "updateFile".
290
+        //
291
+        // TODO: resolve chunk file name here and implement "updateFile"
292
+        $path = $this->path . '/' . $name;
293
+        return $this->fileView->file_exists($path);
294
+    }
295
+
296
+    /**
297
+     * Deletes all files in this directory, and then itself
298
+     *
299
+     * @return void
300
+     * @throws FileLocked
301
+     * @throws \Sabre\DAV\Exception\Forbidden
302
+     */
303
+    public function delete() {
304
+        if ($this->path === '' || $this->path === '/' || !$this->info->isDeletable()) {
305
+            throw new \Sabre\DAV\Exception\Forbidden();
306
+        }
307
+
308
+        try {
309
+            if (!$this->fileView->rmdir($this->path)) {
310
+                // assume it wasn't possible to remove due to permission issue
311
+                throw new \Sabre\DAV\Exception\Forbidden();
312
+            }
313
+        } catch (ForbiddenException $ex) {
314
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
315
+        } catch (LockedException $e) {
316
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
317
+        }
318
+    }
319
+
320
+    /**
321
+     * Returns available diskspace information
322
+     *
323
+     * @return array
324
+     */
325
+    public function getQuotaInfo() {
326
+        /** @var LoggerInterface $logger */
327
+        $logger = \OC::$server->get(LoggerInterface::class);
328
+        if ($this->quotaInfo) {
329
+            return $this->quotaInfo;
330
+        }
331
+        try {
332
+            $storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info, false);
333
+            if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
334
+                $free = \OCP\Files\FileInfo::SPACE_UNLIMITED;
335
+            } else {
336
+                $free = $storageInfo['free'];
337
+            }
338
+            $this->quotaInfo = [
339
+                $storageInfo['used'],
340
+                $free
341
+            ];
342
+            return $this->quotaInfo;
343
+        } catch (\OCP\Files\NotFoundException $e) {
344
+            $logger->warning("error while getting quota into", ['exception' => $e]);
345
+            return [0, 0];
346
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
347
+            $logger->warning("error while getting quota into", ['exception' => $e]);
348
+            return [0, 0];
349
+        } catch (NotPermittedException $e) {
350
+            $logger->warning("error while getting quota into", ['exception' => $e]);
351
+            return [0, 0];
352
+        }
353
+    }
354
+
355
+    /**
356
+     * Moves a node into this collection.
357
+     *
358
+     * It is up to the implementors to:
359
+     *   1. Create the new resource.
360
+     *   2. Remove the old resource.
361
+     *   3. Transfer any properties or other data.
362
+     *
363
+     * Generally you should make very sure that your collection can easily move
364
+     * the move.
365
+     *
366
+     * If you don't, just return false, which will trigger sabre/dav to handle
367
+     * the move itself. If you return true from this function, the assumption
368
+     * is that the move was successful.
369
+     *
370
+     * @param string $targetName New local file/collection name.
371
+     * @param string $fullSourcePath Full path to source node
372
+     * @param INode $sourceNode Source node itself
373
+     * @return bool
374
+     * @throws BadRequest
375
+     * @throws ServiceUnavailable
376
+     * @throws Forbidden
377
+     * @throws FileLocked
378
+     * @throws \Sabre\DAV\Exception\Forbidden
379
+     */
380
+    public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
381
+        if (!$sourceNode instanceof Node) {
382
+            // it's a file of another kind, like FutureFile
383
+            if ($sourceNode instanceof IFile) {
384
+                // fallback to default copy+delete handling
385
+                return false;
386
+            }
387
+            throw new BadRequest('Incompatible node types');
388
+        }
389
+
390
+        if (!$this->fileView) {
391
+            throw new ServiceUnavailable('filesystem not setup');
392
+        }
393
+
394
+        $destinationPath = $this->getPath() . '/' . $targetName;
395
+
396
+
397
+        $targetNodeExists = $this->childExists($targetName);
398
+
399
+        // at getNodeForPath we also check the path for isForbiddenFileOrDir
400
+        // with that we have covered both source and destination
401
+        if ($sourceNode instanceof Directory && $targetNodeExists) {
402
+            throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
403
+        }
404
+
405
+        [$sourceDir,] = \Sabre\Uri\split($sourceNode->getPath());
406
+        $destinationDir = $this->getPath();
407
+
408
+        $sourcePath = $sourceNode->getPath();
409
+
410
+        $isMovableMount = false;
411
+        $sourceMount = \OC::$server->getMountManager()->find($this->fileView->getAbsolutePath($sourcePath));
412
+        $internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
413
+        if ($sourceMount instanceof MoveableMount && $internalPath === '') {
414
+            $isMovableMount = true;
415
+        }
416
+
417
+        try {
418
+            $sameFolder = ($sourceDir === $destinationDir);
419
+            // if we're overwriting or same folder
420
+            if ($targetNodeExists || $sameFolder) {
421
+                // note that renaming a share mount point is always allowed
422
+                if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
423
+                    throw new \Sabre\DAV\Exception\Forbidden();
424
+                }
425
+            } else {
426
+                if (!$this->fileView->isCreatable($destinationDir)) {
427
+                    throw new \Sabre\DAV\Exception\Forbidden();
428
+                }
429
+            }
430
+
431
+            if (!$sameFolder) {
432
+                // moving to a different folder, source will be gone, like a deletion
433
+                // note that moving a share mount point is always allowed
434
+                if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
435
+                    throw new \Sabre\DAV\Exception\Forbidden();
436
+                }
437
+            }
438
+
439
+            $fileName = basename($destinationPath);
440
+            try {
441
+                $this->fileView->verifyPath($destinationDir, $fileName);
442
+            } catch (InvalidPathException $ex) {
443
+                throw new InvalidPath($ex->getMessage());
444
+            }
445
+
446
+            $renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
447
+            if (!$renameOkay) {
448
+                throw new \Sabre\DAV\Exception\Forbidden('');
449
+            }
450
+        } catch (StorageNotAvailableException $e) {
451
+            throw new ServiceUnavailable($e->getMessage());
452
+        } catch (ForbiddenException $ex) {
453
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
454
+        } catch (LockedException $e) {
455
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
456
+        }
457
+
458
+        return true;
459
+    }
460
+
461
+
462
+    public function copyInto($targetName, $sourcePath, INode $sourceNode) {
463
+        if ($sourceNode instanceof File || $sourceNode instanceof Directory) {
464
+            $destinationPath = $this->getPath() . '/' . $targetName;
465
+            $sourcePath = $sourceNode->getPath();
466
+
467
+            if (!$this->fileView->isCreatable($this->getPath())) {
468
+                throw new \Sabre\DAV\Exception\Forbidden();
469
+            }
470
+
471
+            try {
472
+                $this->fileView->verifyPath($this->getPath(), $targetName);
473
+            } catch (InvalidPathException $ex) {
474
+                throw new InvalidPath($ex->getMessage());
475
+            }
476
+
477
+            return $this->fileView->copy($sourcePath, $destinationPath);
478
+        }
479
+
480
+        return false;
481
+    }
482
+
483
+    public function getNode(): Folder {
484
+        return $this->node;
485
+    }
486 486
 }
Please login to merge, or discard this patch.