Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 50 additions & 33 deletions apps/dav/lib/Connector/Sabre/QuotaPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
namespace OCA\DAV\Connector\Sabre;

use OCA\DAV\Upload\FutureFile;
use OCA\DAV\Upload\UploadFolder;
use OCP\Files\StorageNotAvailableException;
use Sabre\DAV\Exception\InsufficientStorage;
use Sabre\DAV\Exception\ServiceUnavailable;
Expand Down Expand Up @@ -90,6 +91,19 @@ public function initialize(\Sabre\DAV\Server $server) {
* @param bool $modified modified
*/
public function beforeCreateFile($uri, $data, INode $parent, $modified) {
$request = $this->server->httpRequest;
if ($parent instanceof UploadFolder && $request->getHeader('Destination')) {
// If chunked upload and Total-Length header is set, use that
// value for quota check. This allows us to also check quota while
// uploading chunks and not only when the file is assembled.
$length = $request->getHeader('OC-Total-Length');
$destinationPath = $this->server->calculateUri($request->getHeader('Destination'));

Check notice

Code scanning / Psalm

PossiblyNullArgument

Argument 1 of Sabre\DAV\Server::calculateUri cannot be null, possibly null value provided
$quotaPath = $this->getPathForDestination($destinationPath);
if ($quotaPath && is_numeric($length)) {
return $this->checkQuota($quotaPath, (int)$length);
}
}

if (!$parent instanceof Node) {
return;
}
Expand All @@ -114,29 +128,20 @@ public function beforeWriteContent($uri, INode $node, $data, $modified) {
}

/**
* Check if we're moving a Futurefile in which case we need to check
* Check if we're moving a FutureFile in which case we need to check
* the quota on the target destination.
*
* @param string $source source path
* @param string $destination destination path
*/
public function beforeMove($source, $destination) {
$sourceNode = $this->server->tree->getNodeForPath($source);
public function beforeMove(string $sourcePath, string $destinationPath): bool {
$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
if (!$sourceNode instanceof FutureFile) {
return;
return true;
}

// get target node for proper path conversion
if ($this->server->tree->nodeExists($destination)) {
$destinationNode = $this->server->tree->getNodeForPath($destination);
$path = $destinationNode->getPath();
} else {
$parent = dirname($destination);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
$path = $parentNode->getPath();
try {
// The final path is not known yet, we check the quota on the parent
$path = $this->getPathForDestination($destinationPath);
} catch (\Exception $e) {
return true;
}

return $this->checkQuota($path, $sourceNode->getSize());
Expand All @@ -151,26 +156,36 @@ public function beforeCopy(string $sourcePath, string $destinationPath): bool {
return true;
}

try {
$path = $this->getPathForDestination($destinationPath);
} catch (\Exception $e) {
return true;
}

return $this->checkQuota($path, $sourceNode->getSize());
}

private function getPathForDestination(string $destinationPath): string {
// get target node for proper path conversion
if ($this->server->tree->nodeExists($destinationPath)) {
$destinationNode = $this->server->tree->getNodeForPath($destinationPath);
if (!$destinationNode instanceof Node) {
return true;
}
$path = $destinationNode->getPath();
} else {
$parent = dirname($destinationPath);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof Node) {
return true;
throw new \Exception('Invalid destination node');
}
$path = $parentNode->getPath();
return $destinationNode->getPath();
}

return $this->checkQuota($path, $sourceNode->getSize());
$parent = dirname($destinationPath);
if ($parent === '.') {
$parent = '';
}

$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof Node) {
throw new \Exception('Invalid destination node');
}

return $parentNode->getPath();
}


Expand All @@ -182,7 +197,7 @@ public function beforeCopy(string $sourcePath, string $destinationPath): bool {
* @throws InsufficientStorage
* @return bool
*/
public function checkQuota($path, $length = null) {
public function checkQuota(string $path, $length = null) {
if ($length === null) {
$length = $this->getLength();
}
Expand All @@ -194,7 +209,7 @@ public function checkQuota($path, $length = null) {
}
$req = $this->server->httpRequest;

// If chunked upload
// If LEGACY chunked upload
if ($req->getHeader('OC-Chunked')) {
$info = \OC_FileChunking::decodeName($newName);
$chunkHandler = $this->getFileChunking($info);
Expand All @@ -210,12 +225,14 @@ public function checkQuota($path, $length = null) {

$freeSpace = $this->getFreeSpace($path);
if ($freeSpace >= 0 && $length > $freeSpace) {
// If LEGACY chunked upload, clean up
if (isset($chunkHandler)) {
$chunkHandler->cleanup();
}
throw new InsufficientStorage("Insufficient space in $path, $length required, $freeSpace available");
}
}

return true;
}

Expand Down