From 38184ca9fde7079cfcdbaf8312cd38dd5cf7db6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Wed, 20 Nov 2019 09:06:47 +0100 Subject: [PATCH 1/3] Use files node API for single file downloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- lib/private/legacy/files.php | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php index 140c02e77b..d8de038160 100644 --- a/lib/private/legacy/files.php +++ b/lib/private/legacy/files.php @@ -284,30 +284,44 @@ class OC_Files { */ private static function getSingleFile($view, $dir, $name, $params) { $filename = $dir . '/' . $name; + $file = null; + + try { + $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); + $file = $userFolder->get($filename); + if(!$file instanceof \OC\Files\Node\File || !$file->isReadable()) { + http_response_code(403); + die('403 Forbidden'); + } + $fileSize = $file->getSize(); + } catch (\OCP\Files\NotPermittedException $e) { + http_response_code(403); + die('403 Forbidden'); + } catch (\OCP\Files\InvalidPathException $e) { + http_response_code(403); + die('403 Forbidden'); + } catch (\OCP\Files\NotFoundException $e) { + http_response_code(404); + $tmpl = new OC_Template('', '404', 'guest'); + $tmpl->printPage(); + exit(); + } + OC_Util::obEnd(); $view->lockFile($filename, ILockingProvider::LOCK_SHARED); $rangeArray = array(); if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { - $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), - \OC\Files\Filesystem::filesize($filename)); - } - - if (\OC\Files\Filesystem::isReadable($filename)) { - self::sendHeaders($filename, $name, $rangeArray); - } elseif (!\OC\Files\Filesystem::file_exists($filename)) { - http_response_code(404); - $tmpl = new OC_Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } else { - http_response_code(403); - die('403 Forbidden'); + $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize); } + + self::sendHeaders($filename, $name, $rangeArray); + if (isset($params['head']) && $params['head']) { return; } + if (!empty($rangeArray)) { try { if (count($rangeArray) == 1) { From 2989732597eb113a7b5d4fc78705315c1fe76bf0 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 12 Dec 2019 22:35:57 +0100 Subject: [PATCH 2/3] use Nodes API for zip streaming Signed-off-by: Arthur Schiwon --- lib/private/Streamer.php | 47 +++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index 51c2c923c2..f56655e55e 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -24,6 +24,12 @@ namespace OC; +use OC\Files\Filesystem; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\InvalidPathException; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; use OCP\IRequest; use ownCloud\TarStreamer\TarStreamer; use ZipStreamer\ZipStreamer; @@ -75,23 +81,25 @@ class Streamer { $this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]); } } - + /** * Send HTTP headers - * @param string $name + * @param string $name */ public function sendHeaders($name){ $extension = $this->streamerInstance instanceof ZipStreamer ? '.zip' : '.tar'; $fullName = $name . $extension; $this->streamerInstance->sendHeaders($fullName); } - + /** * Stream directory recursively - * @param string $dir - * @param string $internalDir + * + * @throws NotFoundException + * @throws NotPermittedException + * @throws InvalidPathException */ - public function addDirRecursive($dir, $internalDir='') { + public function addDirRecursive(string $dir, string $internalDir = ''): void { $dirname = basename($dir); $rootDir = $internalDir . $dirname; if (!empty($rootDir)) { @@ -101,22 +109,27 @@ class Streamer { // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $files= \OC\Files\Filesystem::getDirectoryContent($dir); + $userFolder = \OC::$server->getRootFolder()->get(Filesystem::getRoot()); + /** @var Folder $dirNode */ + $dirNode = $userFolder->get($rootDir); + $files = $dirNode->getDirectoryListing(); + foreach($files as $file) { - $filename = $file['name']; - $file = $dir . '/' . $filename; - if(\OC\Files\Filesystem::is_file($file)) { - $filesize = \OC\Files\Filesystem::filesize($file); - $fileTime = \OC\Files\Filesystem::filemtime($file); - $fh = \OC\Files\Filesystem::fopen($file, 'r'); - $this->addFileFromStream($fh, $internalDir . $filename, $filesize, $fileTime); + if($file instanceof File) { + $fh = $file->fopen('r'); + $this->addFileFromStream( + $fh, + $internalDir . $file->getName(), + $file->getSize(), + $file->getMTime() + ); fclose($fh); - }elseif(\OC\Files\Filesystem::is_dir($file)) { - $this->addDirRecursive($file, $internalDir); + } elseif ($file instanceof Folder) { + $this->addDirRecursive($file->getName(), $internalDir); } } } - + /** * Add a file to the archive at the specified location and file name. * From b67980b52d1245472b98d1b9048622794e79baf5 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 16 Dec 2019 18:51:19 +0100 Subject: [PATCH 3/3] when downloading from web, skip files that are not accessible * avoids a 403, but enables download of resources that are not restricted * single file downloads still cause 403 Signed-off-by: Arthur Schiwon --- lib/private/Streamer.php | 12 +++++++++--- lib/private/legacy/files.php | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index f56655e55e..2443c401c8 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -111,12 +111,16 @@ class Streamer { $userFolder = \OC::$server->getRootFolder()->get(Filesystem::getRoot()); /** @var Folder $dirNode */ - $dirNode = $userFolder->get($rootDir); + $dirNode = $userFolder->get($dir); $files = $dirNode->getDirectoryListing(); foreach($files as $file) { if($file instanceof File) { - $fh = $file->fopen('r'); + try { + $fh = $file->fopen('r'); + } catch (NotPermittedException $e) { + continue; + } $this->addFileFromStream( $fh, $internalDir . $file->getName(), @@ -125,7 +129,9 @@ class Streamer { ); fclose($fh); } elseif ($file instanceof Folder) { - $this->addDirRecursive($file->getName(), $internalDir); + if($file->isReadable()) { + $this->addDirRecursive($dir . '/' . $file->getName(), $internalDir); + } } } } diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php index d8de038160..857bcc9610 100644 --- a/lib/private/legacy/files.php +++ b/lib/private/legacy/files.php @@ -180,7 +180,11 @@ class OC_Files { $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); $file = $userFolder->get($file); if($file instanceof \OC\Files\Node\File) { - $fh = $file->fopen('r'); + try { + $fh = $file->fopen('r'); + } catch (\OCP\Files\NotPermittedException $e) { + continue; + } $fileSize = $file->getSize(); $fileTime = $file->getMTime(); } else { @@ -309,7 +313,7 @@ class OC_Files { OC_Util::obEnd(); $view->lockFile($filename, ILockingProvider::LOCK_SHARED); - + $rangeArray = array(); if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') {