aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Streamer.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Streamer.php')
-rw-r--r--lib/private/Streamer.php86
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);
}
/**