diff options
Diffstat (limited to 'lib/private/Streamer.php')
-rw-r--r-- | lib/private/Streamer.php | 86 |
1 files changed, 45 insertions, 41 deletions
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index f96646e3365..e579c42c0d7 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author szaimen <szaimen@e.mail.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -32,28 +11,40 @@ use OC\Files\Filesystem; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IRequest; use ownCloud\TarStreamer\TarStreamer; +use Psr\Log\LoggerInterface; use ZipStreamer\ZipStreamer; class Streamer { // array of regexp. Matching user agents will get tar instead of zip - private $preferTarFor = [ '/macintosh|mac os x/i' ]; + private const UA_PREFERS_TAR = [ '/macintosh|mac os x/i' ]; // streamer instance private $streamerInstance; + public static function isUserAgentPreferTar(IRequest $request): bool { + return $request->isUserAgent(self::UA_PREFERS_TAR); + } + /** * Streamer constructor. * - * @param IRequest $request - * @param int $size The size of the files in bytes + * @param bool|IRequest $preferTar If true a tar stream is used. + * For legacy reasons also a IRequest can be passed to detect this preference by user agent, + * please migrate to `Streamer::isUserAgentPreferTar()` instead. + * @param int|float $size The size of the files in bytes * @param int $numberOfFiles The number of files (and directories) that will - * be included in the streamed file + * be included in the streamed file */ - public function __construct(IRequest $request, int $size, int $numberOfFiles) { + public function __construct(IRequest|bool $preferTar, int|float $size, int $numberOfFiles) { + if ($preferTar instanceof IRequest) { + $preferTar = self::isUserAgentPreferTar($preferTar); + } + /** * zip32 constraints for a basic (without compression, volumes nor * encryption) zip file according to the Zip specification: @@ -80,10 +71,11 @@ class Streamer { * from not fully scanned external storage. And then things fall apart * if somebody tries to package to much. */ - if ($size > 0 && $size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) { - $this->streamerInstance = new ZipStreamer(['zip64' => false]); - } elseif ($request->isUserAgent($this->preferTarFor)) { + if ($preferTar) { + // If TAR ball is preferred use it $this->streamerInstance = new TarStreamer(); + } elseif ($size > 0 && $size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) { + $this->streamerInstance = new ZipStreamer(['zip64' => false]); } else { $this->streamerInstance = new ZipStreamer(['zip64' => true]); } @@ -103,6 +95,7 @@ class Streamer { /** * Stream directory recursively * + * @param string $dir Directory path relative to root of current user * @throws NotFoundException * @throws NotPermittedException * @throws InvalidPathException @@ -117,15 +110,21 @@ class Streamer { // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $userFolder = \OC::$server->getRootFolder()->get(Filesystem::getRoot()); + $userFolder = \OC::$server->get(IRootFolder::class)->get(Filesystem::getRoot()); /** @var Folder $dirNode */ $dirNode = $userFolder->get($dir); $files = $dirNode->getDirectoryListing(); + /** @var LoggerInterface $logger */ + $logger = \OC::$server->query(LoggerInterface::class); foreach ($files as $file) { if ($file instanceof File) { try { $fh = $file->fopen('r'); + if ($fh === false) { + $logger->error('Unable to open file for stream: ' . print_r($file, true)); + continue; + } } catch (NotPermittedException $e) { continue; } @@ -149,11 +148,11 @@ class Streamer { * * @param resource $stream Stream to read data from * @param string $internalName Filepath and name to be used in the archive. - * @param int $size Filesize - * @param int|bool $time File mtime as int, or false + * @param int|float $size Filesize + * @param int|false $time File mtime as int, or false * @return bool $success */ - public function addFileFromStream($stream, string $internalName, int $size, $time): bool { + public function addFileFromStream($stream, string $internalName, int|float $size, $time): bool { $options = []; if ($time) { $options = [ @@ -171,11 +170,16 @@ class Streamer { /** * Add an empty directory entry to the archive. * - * @param string $dirName Directory Path and name to be added to the archive. - * @return bool $success + * @param $dirName - Directory Path and name to be added to the archive. + * @param $timestamp - Modification time of the directory (defaults to current time) */ - public function addEmptyDir($dirName) { - return $this->streamerInstance->addEmptyDir($dirName); + public function addEmptyDir(string $dirName, int $timestamp = 0): bool { + $options = null; + if ($timestamp > 0) { + $options = ['timestamp' => $timestamp]; + } + + return $this->streamerInstance->addEmptyDir($dirName, $options); } /** |