aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2023-06-22 14:33:18 +0200
committerArthur Schiwon <blizzz@arthur-schiwon.de>2023-07-10 18:31:45 +0200
commit98ec2f023ffa2fe7d2813768d3e4aa9bb4f10f39 (patch)
tree12a1768dabe3edc623a1b984cb03398c1f5e086e /lib/private
parentff86e8c411c8e2edfc648dcc68942a21f65cea7b (diff)
downloadnextcloud-server-98ec2f023ffa2fe7d2813768d3e4aa9bb4f10f39.tar.gz
nextcloud-server-98ec2f023ffa2fe7d2813768d3e4aa9bb4f10f39.zip
implement optimized getDirectoryContent for DAV
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/Files/Storage/DAV.php221
1 files changed, 125 insertions, 96 deletions
diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php
index e2ad2a18f6b..bb1d2ec6efd 100644
--- a/lib/private/Files/Storage/DAV.php
+++ b/lib/private/Files/Storage/DAV.php
@@ -47,6 +47,7 @@ use OCP\Constants;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\FileInfo;
use OCP\Files\ForbiddenException;
+use OCP\Files\IMimeTypeDetector;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
use OCP\Http\Client\IClientService;
@@ -93,10 +94,22 @@ class DAV extends Common {
protected $certManager;
protected LoggerInterface $logger;
protected IEventLogger $eventLogger;
+ protected IMimeTypeDetector $mimeTypeDetector;
/** @var int */
private $timeout;
+ protected const PROPFIND_PROPS = [
+ '{DAV:}getlastmodified',
+ '{DAV:}getcontentlength',
+ '{DAV:}getcontenttype',
+ '{http://owncloud.org/ns}permissions',
+ '{http://open-collaboration-services.org/ns}share-permissions',
+ '{DAV:}resourcetype',
+ '{DAV:}getetag',
+ '{DAV:}quota-available-bytes',
+ ];
+
/**
* @param array $params
* @throws \Exception
@@ -141,6 +154,7 @@ class DAV extends Common {
$this->eventLogger = \OC::$server->get(IEventLogger::class);
// This timeout value will be used for the download and upload of files
$this->timeout = \OC::$server->get(IConfig::class)->getSystemValueInt('davstorage.request_timeout', 30);
+ $this->mimeTypeDetector = \OC::$server->getMimeTypeDetector();
}
protected function init() {
@@ -239,31 +253,12 @@ class DAV extends Common {
$this->init();
$path = $this->cleanPath($path);
try {
- $response = $this->client->propFind(
- $this->encodePath($path),
- ['{DAV:}getetag'],
- 1
- );
- if ($response === false) {
- return false;
- }
- $content = [];
- $files = array_keys($response);
- array_shift($files); //the first entry is the current directory
-
- if (!$this->statCache->hasKey($path)) {
- $this->statCache->set($path, true);
- }
- foreach ($files as $file) {
- $file = urldecode($file);
- // do not store the real entry, we might not have all properties
- if (!$this->statCache->hasKey($path)) {
- $this->statCache->set($file, true);
- }
- $file = basename($file);
- $content[] = $file;
+ $content = $this->getDirectoryContent($path);
+ $files = [];
+ foreach ($content as $child) {
+ $files[] = $child['name'];
}
- return IteratorDirectory::wrap($content);
+ return IteratorDirectory::wrap($files);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -291,16 +286,7 @@ class DAV extends Common {
try {
$response = $this->client->propFind(
$this->encodePath($path),
- [
- '{DAV:}getlastmodified',
- '{DAV:}getcontentlength',
- '{DAV:}getcontenttype',
- '{http://owncloud.org/ns}permissions',
- '{http://open-collaboration-services.org/ns}share-permissions',
- '{DAV:}resourcetype',
- '{DAV:}getetag',
- '{DAV:}quota-available-bytes',
- ]
+ self::PROPFIND_PROPS
);
$this->statCache->set($path, $response);
} catch (ClientHttpException $e) {
@@ -607,53 +593,84 @@ class DAV extends Common {
return false;
}
- /** {@inheritdoc} */
- public function stat($path) {
- try {
- $response = $this->propfind($path);
- if (!$response) {
- return false;
+ public function getMetaData($path) {
+ if (Filesystem::isFileBlacklisted($path)) {
+ throw new ForbiddenException('Invalid path: ' . $path, false);
+ }
+ $response = $this->propfind($path);
+ if (!$response) {
+ return null;
+ } else {
+ return $this->getMetaFromPropfind($path, $response);
+ }
+ }
+ private function getMetaFromPropfind(string $path, array $response): array {
+ if (isset($response['{DAV:}getetag'])) {
+ $etag = trim($response['{DAV:}getetag'], '"');
+ if (strlen($etag) > 40) {
+ $etag = md5($etag);
}
- return [
- 'mtime' => isset($response['{DAV:}getlastmodified']) ? strtotime($response['{DAV:}getlastmodified']) : null,
- 'size' => Util::numericToNumber($response['{DAV:}getcontentlength'] ?? 0),
- ];
- } catch (\Exception $e) {
- $this->convertException($e, $path);
+ } else {
+ $etag = parent::getETag($path);
+ }
+
+ $responseType = [];
+ if (isset($response["{DAV:}resourcetype"])) {
+ /** @var ResourceType[] $response */
+ $responseType = $response["{DAV:}resourcetype"]->getValue();
+ }
+ $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
+ if ($type === 'dir') {
+ $mimeType = 'httpd/unix-directory';
+ } elseif (isset($response['{DAV:}getcontenttype'])) {
+ $mimeType = $response['{DAV:}getcontenttype'];
+ } else {
+ $mimeType = $this->mimeTypeDetector->detectPath($path);
}
- return [];
+
+ if (isset($response['{http://owncloud.org/ns}permissions'])) {
+ $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
+ } elseif ($type === 'dir') {
+ $permissions = Constants::PERMISSION_ALL;
+ } else {
+ $permissions = Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
+ }
+
+ $mtime = isset($response['{DAV:}getlastmodified']) ? strtotime($response['{DAV:}getlastmodified']) : null;
+
+ if ($type === 'dir') {
+ $size = -1;
+ } else {
+ $size = Util::numericToNumber($response['{DAV:}getcontentlength'] ?? 0);
+ }
+
+ return [
+ 'name' => basename($path),
+ 'mtime' => $mtime,
+ 'storage_mtime' => $mtime,
+ 'size' => $size,
+ 'permissions' => $permissions,
+ 'etag' => $etag,
+ 'mimetype' => $mimeType,
+ ];
}
/** {@inheritdoc} */
- public function getMimeType($path) {
- $remoteMimetype = $this->getMimeTypeFromRemote($path);
- if ($remoteMimetype === 'application/octet-stream') {
- return \OC::$server->getMimeTypeDetector()->detectPath($path);
+ public function stat($path) {
+ $meta = $this->getMetaData($path);
+ if (!$meta) {
+ return false;
} else {
- return $remoteMimetype;
+ return $meta;
}
}
- public function getMimeTypeFromRemote($path) {
- try {
- $response = $this->propfind($path);
- if ($response === false) {
- return false;
- }
- $responseType = [];
- if (isset($response["{DAV:}resourcetype"])) {
- /** @var ResourceType[] $response */
- $responseType = $response["{DAV:}resourcetype"]->getValue();
- }
- $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
- if ($type == 'dir') {
- return 'httpd/unix-directory';
- } elseif (isset($response['{DAV:}getcontenttype'])) {
- return $response['{DAV:}getcontenttype'];
- } else {
- return 'application/octet-stream';
- }
- } catch (\Exception $e) {
+ /** {@inheritdoc} */
+ public function getMimeType($path) {
+ $meta = $this->getMetaData($path);
+ if ($meta) {
+ return $meta['mimetype'];
+ } else {
return false;
}
}
@@ -739,18 +756,9 @@ class DAV extends Common {
/** {@inheritdoc} */
public function getPermissions($path) {
- $this->init();
- $path = $this->cleanPath($path);
- $response = $this->propfind($path);
- if ($response === false) {
- return 0;
- }
- if (isset($response['{http://owncloud.org/ns}permissions'])) {
- return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
- } elseif ($this->is_dir($path)) {
- return Constants::PERMISSION_ALL;
- } elseif ($this->file_exists($path)) {
- return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
+ $stat = $this->getMetaData($path);
+ if ($stat) {
+ return $stat['permissions'];
} else {
return 0;
}
@@ -758,20 +766,12 @@ class DAV extends Common {
/** {@inheritdoc} */
public function getETag($path) {
- $this->init();
- $path = $this->cleanPath($path);
- $response = $this->propfind($path);
- if ($response === false) {
+ $meta = $this->getMetaData($path);
+ if ($meta) {
+ return $meta['etag'];
+ } else {
return null;
}
- if (isset($response['{DAV:}getetag'])) {
- $etag = trim($response['{DAV:}getetag'], '"');
- if (strlen($etag) > 40) {
- $etag = md5($etag);
- }
- return $etag;
- }
- return parent::getEtag($path);
}
/**
@@ -901,4 +901,33 @@ class DAV extends Common {
// TODO: only log for now, but in the future need to wrap/rethrow exception
}
+
+ public function getDirectoryContent($directory): \Traversable {
+ $this->init();
+ $directory = $this->cleanPath($directory);
+ try {
+ $responses = $this->client->propFind(
+ $this->encodePath($directory),
+ self::PROPFIND_PROPS,
+ 1
+ );
+ if ($responses === false) {
+ return;
+ }
+
+ array_shift($responses); //the first entry is the current directory
+ if (!$this->statCache->hasKey($directory)) {
+ $this->statCache->set($directory, true);
+ }
+
+ foreach ($responses as $file => $response) {
+ $file = urldecode($file);
+ $file = substr($file, strlen($this->root));
+ $this->statCache->set($file, $response);
+ yield $this->getMetaFromPropfind($file, $response);
+ }
+ } catch (\Exception $e) {
+ $this->convertException($e, $directory);
+ }
+ }
}