diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/Streamer.php | 43 | ||||
-rw-r--r-- | lib/private/legacy/files.php | 46 |
2 files changed, 80 insertions, 9 deletions
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index 7b178fda652..51c2c923c23 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -24,6 +24,7 @@ namespace OC; +use OCP\IRequest; use ownCloud\TarStreamer\TarStreamer; use ZipStreamer\ZipStreamer; @@ -33,12 +34,42 @@ class Streamer { // streamer instance private $streamerInstance; - - public function __construct(){ - /** @var \OCP\IRequest */ - $request = \OC::$server->getRequest(); - - if ($request->isUserAgent($this->preferTarFor)) { + + /** + * Streamer constructor. + * + * @param IRequest $request + * @param int $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 + */ + public function __construct(IRequest $request, int $size, int $numberOfFiles){ + + /** + * zip32 constraints for a basic (without compression, volumes nor + * encryption) zip file according to the Zip specification: + * - No file size is larger than 4 bytes (file size < 4294967296); see + * 4.4.9 uncompressed size + * - The size of all files plus their local headers is not larger than + * 4 bytes; see 4.4.16 relative offset of local header and 4.4.24 + * offset of start of central directory with respect to the starting + * disk number + * - The total number of entries (files and directories) in the zip file + * is not larger than 2 bytes (number of entries < 65536); see 4.4.22 + * total number of entries in the central dir + * - The size of the central directory is not larger than 4 bytes; see + * 4.4.23 size of the central directory + * + * Due to all that, zip32 is used if the size is below 4GB and there are + * less than 65536 files; the margin between 4*1000^3 and 4*1024^3 + * should give enough room for the extra zip metadata. Technically, it + * would still be possible to create an invalid zip32 file (for example, + * a zip file from files smaller than 4GB with a central directory + * larger than 4GiB), but it should not happen in the real world. + */ + if ($size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) { + $this->streamerInstance = new ZipStreamer(['zip64' => false]); + } else if ($request->isUserAgent($this->preferTarFor)) { $this->streamerInstance = new TarStreamer(); } else { $this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]); diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php index def9f82fab9..9281c1f7da4 100644 --- a/lib/private/legacy/files.php +++ b/lib/private/legacy/files.php @@ -144,17 +144,34 @@ class OC_Files { } } - $streamer = new Streamer(); - OC_Util::obEnd(); - self::lockFiles($view, $dir, $files); + /* Calculate filesize and number of files */ + if ($getType === self::ZIP_FILES) { + $fileInfos = array(); + $fileSize = 0; + foreach ($files as $file) { + $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); + $fileSize += $fileInfo->getSize(); + $fileInfos[] = $fileInfo; + } + $numberOfFiles = self::getNumberOfFiles($fileInfos); + } elseif ($getType === self::ZIP_DIR) { + $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); + $fileSize = $fileInfo->getSize(); + $numberOfFiles = self::getNumberOfFiles(array($fileInfo)); + } + + $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); + OC_Util::obEnd(); + $streamer->sendHeaders($name); $executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time'); if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit(0); } ignore_user_abort(true); + if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; @@ -314,6 +331,29 @@ class OC_Files { } /** + * 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 |