From 6ced0109ace2fbfae0ced66dc50676441faa7150 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 26 Sep 2024 23:57:10 +0200 Subject: [PATCH] chore: Drop unused legacy `OC_Files` It is replaced with the Sabre `ZipFolderPlugin` and apps should use the `OCP\AppFramework\Http\StreamResponse`. Signed-off-by: Ferdinand Thiessen --- lib/composer/composer/autoload_classmap.php | 1 - lib/composer/composer/autoload_static.php | 1 - lib/private/legacy/OC_Files.php | 428 -------------------- tests/lib/AutoLoaderTest.php | 4 +- 4 files changed, 2 insertions(+), 432 deletions(-) delete mode 100644 lib/private/legacy/OC_Files.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 46957c1f36f3d..8f1ba58c3cdce 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -2008,7 +2008,6 @@ 'OC\\User\\User' => $baseDir . '/lib/private/User/User.php', 'OC_App' => $baseDir . '/lib/private/legacy/OC_App.php', 'OC_Defaults' => $baseDir . '/lib/private/legacy/OC_Defaults.php', - 'OC_Files' => $baseDir . '/lib/private/legacy/OC_Files.php', 'OC_Helper' => $baseDir . '/lib/private/legacy/OC_Helper.php', 'OC_Hook' => $baseDir . '/lib/private/legacy/OC_Hook.php', 'OC_JSON' => $baseDir . '/lib/private/legacy/OC_JSON.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 59245d8818b09..bd6aab1843bb5 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -2041,7 +2041,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\User\\User' => __DIR__ . '/../../..' . '/lib/private/User/User.php', 'OC_App' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_App.php', 'OC_Defaults' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_Defaults.php', - 'OC_Files' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_Files.php', 'OC_Helper' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_Helper.php', 'OC_Hook' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_Hook.php', 'OC_JSON' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_JSON.php', diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php deleted file mode 100644 index f75b89fea67ed..0000000000000 --- a/lib/private/legacy/OC_Files.php +++ /dev/null @@ -1,428 +0,0 @@ -int,'to'=>int), ... - */ - private static function sendHeaders($filename, $name, array $rangeArray): void { - OC_Response::setContentDispositionHeader($name, 'attachment'); - header('Content-Transfer-Encoding: binary', true); - header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - $fileSize = \OC\Files\Filesystem::filesize($filename); - $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); - if ($fileSize > -1) { - if (!empty($rangeArray)) { - http_response_code(206); - header('Accept-Ranges: bytes', true); - if (count($rangeArray) > 1) { - $type = 'multipart/byteranges; boundary=' . self::getBoundary(); - // no Content-Length header here - } else { - header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true); - OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1); - } - } else { - OC_Response::setContentLengthHeader($fileSize); - } - } - header('Content-Type: ' . $type, true); - header('X-Accel-Buffering: no'); - } - - /** - * return the content of a file or return a zip file containing multiple files - * - * @param string $dir - * @param string|array $files ; separated list of files to download - * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header - */ - public static function get($dir, $files, $params = null) { - OC_Util::setupFS(); - $view = \OC\Files\Filesystem::getView(); - $getType = self::FILE; - $filename = $dir; - try { - if (is_array($files) && count($files) === 1) { - $files = $files[0]; - } - - if (!is_array($files)) { - $filename = $dir . '/' . $files; - if (!$view->is_dir($filename)) { - self::getSingleFile($view, $dir, $files, is_null($params) ? [] : $params); - return; - } - } - - $name = 'download'; - if (is_array($files)) { - $getType = self::ZIP_FILES; - $basename = basename($dir); - if ($basename) { - $name = $basename; - } - - $filename = $dir . '/' . $name; - } else { - $filename = $dir . '/' . $files; - $getType = self::ZIP_DIR; - // downloading root ? - if ($files !== '') { - $name = $files; - } - } - - self::lockFiles($view, $dir, $files); - $numberOfFiles = 0; - $fileSize = 0; - - /* Calculate filesize and number of files */ - if ($getType === self::ZIP_FILES) { - $fileInfos = []; - foreach ($files as $file) { - $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); - if ($fileInfo) { - $fileSize += $fileInfo->getSize(); - $fileInfos[] = $fileInfo; - } - } - $numberOfFiles = self::getNumberOfFiles($fileInfos); - } elseif ($getType === self::ZIP_DIR) { - $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); - if ($fileInfo) { - $fileSize = $fileInfo->getSize(); - $numberOfFiles = self::getNumberOfFiles([$fileInfo]); - } - } - - //Dispatch an event to see if any apps have problem with download - $event = new BeforeZipCreatedEvent($dir, is_array($files) ? $files : [$files]); - $dispatcher = \OCP\Server::get(IEventDispatcher::class); - $dispatcher->dispatchTyped($event); - if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) { - throw new \OC\ForbiddenException($event->getErrorMessage()); - } - - $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); - OC_Util::obEnd(); - - $streamer->sendHeaders($name); - $executionTime = (int)OC::$server->get(IniGetWrapper::class)->getNumeric('max_execution_time'); - if (!str_contains(@ini_get('disable_functions'), 'set_time_limit')) { - @set_time_limit(0); - } - ignore_user_abort(true); - - if ($getType === self::ZIP_FILES) { - foreach ($files as $file) { - $file = $dir . '/' . $file; - if (\OC\Files\Filesystem::is_file($file)) { - $userFolder = \OC::$server->get(IRootFolder::class)->get(\OC\Files\Filesystem::getRoot()); - $file = $userFolder->get($file); - if ($file instanceof \OC\Files\Node\File) { - try { - $fh = $file->fopen('r'); - } catch (\OCP\Files\NotPermittedException $e) { - continue; - } - $fileSize = $file->getSize(); - $fileTime = $file->getMTime(); - } else { - // File is not a file? … - \OCP\Server::get(LoggerInterface::class)->debug( - 'File given, but no Node available. Name {file}', - [ 'app' => 'files', 'file' => $file ] - ); - continue; - } - $streamer->addFileFromStream($fh, $file->getName(), $fileSize, $fileTime); - fclose($fh); - } elseif (\OC\Files\Filesystem::is_dir($file)) { - $streamer->addDirRecursive($file); - } - } - } elseif ($getType === self::ZIP_DIR) { - $file = $dir . '/' . $files; - $streamer->addDirRecursive($file); - } - $streamer->finalize(); - set_time_limit($executionTime); - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - } catch (\OCP\Lock\LockedException $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - \OCP\Server::get(LoggerInterface::class)->error('File is currently busy', ['exception' => $ex]); - $l = \OC::$server->getL10N('lib'); - $hint = $ex instanceof HintException ? $ex->getHint() : ''; - \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint, 200); - } catch (\OCP\Files\ForbiddenException $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - \OCP\Server::get(LoggerInterface::class)->error('Cannot download file', ['exception' => $ex]); - $l = \OC::$server->getL10N('lib'); - \OC_Template::printErrorPage($l->t('Cannot download file'), $ex->getMessage(), 200); - } catch (\OCP\Files\ConnectionLostException $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - \OCP\Server::get(LoggerInterface::class)->debug('Connection lost', ['exception' => $ex]); - /* We do not print anything here, the connection is already closed */ - die(); - } catch (\Exception $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - \OCP\Server::get(LoggerInterface::class)->error('Cannot download file', ['exception' => $ex]); - $l = \OC::$server->getL10N('lib'); - $hint = $ex instanceof HintException ? $ex->getHint() : ''; - if ($event && $event->getErrorMessage() !== null) { - $hint .= ' ' . $event->getErrorMessage(); - } - \OC_Template::printErrorPage($l->t('Cannot download file'), $hint, 200); - } - } - - /** - * @param string $rangeHeaderPos - * @param int|float $fileSize - * @return array $rangeArray ('from'=>int,'to'=>int), ... - */ - private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize): array { - $rArray = explode(',', $rangeHeaderPos); - $minOffset = 0; - $ind = 0; - - $rangeArray = []; - - foreach ($rArray as $value) { - $ranges = explode('-', $value); - if (is_numeric($ranges[0])) { - if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999 - $ranges[0] = $minOffset; - } - if ($ind > 0 && $rangeArray[$ind - 1]['to'] + 1 == $ranges[0]) { // case: bytes=500-600,601-999 - $ind--; - $ranges[0] = $rangeArray[$ind]['from']; - } - } - - if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) { - // case: x-x - if ($ranges[1] >= $fileSize) { - $ranges[1] = $fileSize - 1; - } - $rangeArray[$ind++] = [ 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize ]; - $minOffset = $ranges[1] + 1; - if ($minOffset >= $fileSize) { - break; - } - } elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) { - // case: x- - $rangeArray[$ind++] = [ 'from' => $ranges[0], 'to' => $fileSize - 1, 'size' => $fileSize ]; - break; - } elseif (is_numeric($ranges[1])) { - // case: -x - if ($ranges[1] > $fileSize) { - $ranges[1] = $fileSize; - } - $rangeArray[$ind++] = [ 'from' => $fileSize - $ranges[1], 'to' => $fileSize - 1, 'size' => $fileSize ]; - break; - } - } - return $rangeArray; - } - - /** - * @param View $view - * @param string $name - * @param string $dir - * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header - * @throws \OC\ForbiddenException - */ - private static function getSingleFile($view, $dir, $name, $params) { - $filename = $dir . '/' . $name; - $file = null; - - try { - $userFolder = \OC::$server->get(IRootFolder::class)->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 = []; - - if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { - $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize); - } - - $dispatcher = \OCP\Server::get(IEventDispatcher::class); - $event = new BeforeDirectFileDownloadEvent($filename); - $dispatcher->dispatchTyped($event); - - if (!\OC\Files\Filesystem::isReadable($filename) || $event->getErrorMessage()) { - if ($event->getErrorMessage()) { - $msg = $event->getErrorMessage(); - } else { - $msg = 'Access denied'; - } - throw new \OC\ForbiddenException($msg); - } - - self::sendHeaders($filename, $name, $rangeArray); - - if (isset($params['head']) && $params['head']) { - return; - } - - if (!empty($rangeArray)) { - try { - if (count($rangeArray) == 1) { - $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']); - } else { - // check if file is seekable (if not throw UnseekableException) - // we have to check it before body contents - $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']); - - $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); - - foreach ($rangeArray as $range) { - echo "\r\n--" . self::getBoundary() . "\r\n" . - 'Content-type: ' . $type . "\r\n" . - 'Content-range: bytes ' . $range['from'] . '-' . $range['to'] . '/' . $range['size'] . "\r\n\r\n"; - $view->readfilePart($filename, $range['from'], $range['to']); - } - echo "\r\n--" . self::getBoundary() . "--\r\n"; - } - } catch (\OCP\Files\UnseekableException $ex) { - // file is unseekable - header_remove('Accept-Ranges'); - header_remove('Content-Range'); - http_response_code(200); - self::sendHeaders($filename, $name, []); - $view->readfile($filename); - } - } else { - $view->readfile($filename); - } - } - - /** - * Returns the total (recursive) number of files and folders in the given - * FileInfos. - * - * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count - * @return int the total number of files and folders - */ - private static function getNumberOfFiles($fileInfos) { - $numberOfFiles = 0; - - $view = new View(); - - while ($fileInfo = array_pop($fileInfos)) { - $numberOfFiles++; - - if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) { - $fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath())); - } - } - - return $numberOfFiles; - } - - /** - * @param View $view - * @param string $dir - * @param string[]|string $files - */ - public static function lockFiles($view, $dir, $files) { - if (!is_array($files)) { - $file = $dir . '/' . $files; - $files = [$file]; - } - foreach ($files as $file) { - $file = $dir . '/' . $file; - $view->lockFile($file, ILockingProvider::LOCK_SHARED); - if ($view->is_dir($file)) { - $contents = $view->getDirectoryContent($file); - $contents = array_map(function ($fileInfo) use ($file) { - /** @var \OCP\Files\FileInfo $fileInfo */ - return $file . '/' . $fileInfo->getName(); - }, $contents); - self::lockFiles($view, $dir, $contents); - } - } - } - - /** - * @param string $dir - * @param $files - * @param integer $getType - * @param View $view - * @param string $filename - */ - private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) { - if ($getType === self::FILE) { - $view->unlockFile($filename, ILockingProvider::LOCK_SHARED); - } - if ($getType === self::ZIP_FILES) { - foreach ($files as $file) { - $file = $dir . '/' . $file; - $view->unlockFile($file, ILockingProvider::LOCK_SHARED); - } - } - if ($getType === self::ZIP_DIR) { - $file = $dir . '/' . $files; - $view->unlockFile($file, ILockingProvider::LOCK_SHARED); - } - } -} diff --git a/tests/lib/AutoLoaderTest.php b/tests/lib/AutoLoaderTest.php index 2aa8bc2a26ab5..72d4a2e94cf52 100644 --- a/tests/lib/AutoLoaderTest.php +++ b/tests/lib/AutoLoaderTest.php @@ -20,8 +20,8 @@ protected function setUp(): void { public function testLegacyPath(): void { $this->assertEquals([ - \OC::$SERVERROOT . '/lib/private/legacy/files.php', - ], $this->loader->findClass('OC_Files')); + \OC::$SERVERROOT . '/lib/private/legacy/json.php', + ], $this->loader->findClass('OC_JSON')); } public function testLoadTestTestCase(): void {