diff options
author | Thomas Müller <thomas.mueller@tmit.eu> | 2015-10-12 11:58:03 +0200 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2015-10-12 11:58:03 +0200 |
commit | 07f903a1e3204318346bb0c011ee373e879c5cfb (patch) | |
tree | 66de21d31b4ffd70882b5f13d0bc0444ac2b8c49 /lib | |
parent | 4c13a648bbdef70fdc318709bd712b41cdcdb944 (diff) | |
parent | 045c491be8e586c213306c6600e703b375a53945 (diff) | |
download | nextcloud-server-07f903a1e3204318346bb0c011ee373e879c5cfb.tar.gz nextcloud-server-07f903a1e3204318346bb0c011ee373e879c5cfb.zip |
Merge pull request #19466 from owncloud/tarstreamer-stable8.1
[stable8.1] backport Tarstreamer
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/files.php | 71 | ||||
-rw-r--r-- | lib/private/streamer.php | 124 |
2 files changed, 158 insertions, 37 deletions
diff --git a/lib/private/files.php b/lib/private/files.php index 17e2e5a398f..6f6aca5806e 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -39,10 +39,8 @@ * */ -// TODO: get rid of this using proper composer packages -require_once 'mcnetic/phpzipstreamer/ZipStreamer.php'; - use OC\Lock\NoopLockingProvider; +use OC\Streamer; use OCP\Lock\ILockingProvider; /** @@ -59,20 +57,15 @@ class OC_Files { /** * @param string $filename * @param string $name - * @param bool $zip */ - private static function sendHeaders($filename, $name, $zip = false) { + private static function sendHeaders($filename, $name) { OC_Response::setContentDispositionHeader($name, 'attachment'); header('Content-Transfer-Encoding: binary'); OC_Response::disableCaching(); - if ($zip) { - header('Content-Type: application/zip'); - } else { - $filesize = \OC\Files\Filesystem::filesize($filename); - header('Content-Type: '.\OC_Helper::getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename))); - if ($filesize > -1) { - OC_Response::setContentLengthHeader($filesize); - } + $filesize = \OC\Files\Filesystem::filesize($filename); + header('Content-Type: '.\OC_Helper::getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename))); + if ($filesize > -1) { + OC_Response::setContentLengthHeader($filesize); } } @@ -81,9 +74,9 @@ class OC_Files { * * @param string $dir * @param string $files ; separated list of files to download - * @param boolean $only_header ; boolean to only send header of the request + * @param boolean $onlyHeader ; boolean to only send header of the request */ - public static function get($dir, $files, $only_header = false) { + public static function get($dir, $files, $onlyHeader = false) { $view = \OC\Files\Filesystem::getView(); $xsendfile = false; if (\OC::$server->getLockingProvider() instanceof NoopLockingProvider) { @@ -100,48 +93,51 @@ class OC_Files { } if (is_array($files)) { - $get_type = self::ZIP_FILES; + $getType = self::ZIP_FILES; $basename = basename($dir); if ($basename) { - $name = $basename . '.zip'; + $name = $basename; } else { - $name = 'download.zip'; + $name = 'download'; } $filename = $dir . '/' . $name; } else { $filename = $dir . '/' . $files; if (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { - $get_type = self::ZIP_DIR; + $getType = self::ZIP_DIR; // downloading root ? if ($files === '') { - $name = 'download.zip'; + $name = 'download'; } else { - $name = $files . '.zip'; + $name = $files; } } else { - $get_type = self::FILE; + $getType = self::FILE; $name = $files; } } - if ($get_type === self::FILE) { - $zip = false; + if ($getType === self::FILE) { + $streamer = false; if ($xsendfile && \OC::$server->getEncryptionManager()->isEnabled()) { $xsendfile = false; } } else { - $zip = new ZipStreamer(false); + $streamer = new Streamer(); } OC_Util::obEnd(); try { - if ($get_type === self::FILE) { + if ($getType === self::FILE) { $view->lockFile($filename, ILockingProvider::LOCK_SHARED); } - if ($zip or \OC\Files\Filesystem::isReadable($filename)) { - self::sendHeaders($filename, $name, $zip); + + if ($streamer) { + $streamer->sendHeaders($name); + } elseif (\OC\Files\Filesystem::isReadable($filename)) { + self::sendHeaders($filename, $name); } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); @@ -151,28 +147,29 @@ class OC_Files { header("HTTP/1.0 403 Forbidden"); die('403 Forbidden'); } - if ($only_header) { + if ($onlyHeader) { return; } - if ($zip) { + if ($streamer) { $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); - if ($get_type === self::ZIP_FILES) { + if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; if (\OC\Files\Filesystem::is_file($file)) { + $fileSize = \OC\Files\Filesystem::filesize($file); $fh = \OC\Files\Filesystem::fopen($file, 'r'); - $zip->addFileFromStream($fh, basename($file)); + $streamer->addFileFromStream($fh, basename($file), $fileSize); fclose($fh); } elseif (\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file, $zip); + $streamer->addDirRecursive($file); } } - } elseif ($get_type === self::ZIP_DIR) { + } elseif ($getType === self::ZIP_DIR) { $file = $dir . '/' . $files; - self::zipAddDir($file, $zip); + $streamer->addDirRecursive($file); } - $zip->finalize(); + $streamer->finalize(); set_time_limit($executionTime); } else { if ($xsendfile) { @@ -187,7 +184,7 @@ class OC_Files { \OC\Files\Filesystem::readfile($filename); } } - if ($get_type === self::FILE) { + if ($getType === self::FILE) { $view->unlockFile($filename, ILockingProvider::LOCK_SHARED); } } catch (\OCP\Lock\LockedException $ex) { diff --git a/lib/private/streamer.php b/lib/private/streamer.php new file mode 100644 index 00000000000..5b7c0bafd12 --- /dev/null +++ b/lib/private/streamer.php @@ -0,0 +1,124 @@ +<?php +/** + * @author Victor Dubiniuk <dubiniuk@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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/> + * + */ + +namespace OC; + +use ZipStreamer\ZipStreamer; +use ownCloud\TarStreamer\TarStreamer; + +class Streamer { + // array of regexp. Matching user agents will get tar instead of zip + private $preferTarFor = [ '/macintosh|mac os x/i' ]; + + // streamer instance + private $streamerInstance; + + public function __construct(){ + /** @var \OCP\IRequest */ + $request = \OC::$server->getRequest(); + + if ($request->isUserAgent($this->preferTarFor)) { + $this->streamerInstance = new TarStreamer(); + } else { + $this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]); + } + } + + /** + * Send HTTP headers + * @param string $name + */ + public function sendHeaders($name){ + $extension = $this->streamerInstance instanceof ZipStreamer ? '.zip' : '.tar'; + $fullName = $name . $extension; + // ZipStreamer does not escape name in Content-Disposition atm + if ($this->streamerInstance instanceof ZipStreamer) { + $fullName = rawurlencode($fullName); + } + $this->streamerInstance->sendHeaders($fullName); + } + + /** + * Stream directory recursively + * @param string $dir + * @param string $internalDir + */ + public function addDirRecursive($dir, $internalDir='') { + $dirname = basename($dir); + $rootDir = $internalDir . $dirname; + if (!empty($rootDir)) { + $this->streamerInstance->addEmptyDir($rootDir); + } + $internalDir .= $dirname . '/'; + // prevent absolute dirs + $internalDir = ltrim($internalDir, '/'); + + $files= \OC\Files\Filesystem::getDirectoryContent($dir); + foreach($files as $file) { + $filename = $file['name']; + $file = $dir . '/' . $filename; + if(\OC\Files\Filesystem::is_file($file)) { + $filesize = \OC\Files\Filesystem::filesize($file); + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $this->addFileFromStream($fh, $internalDir . $filename, $filesize); + fclose($fh); + }elseif(\OC\Files\Filesystem::is_dir($file)) { + $this->addDirRecursive($file, $internalDir); + } + } + } + + /** + * Add a file to the archive at the specified location and file name. + * + * @param string $stream Stream to read data from + * @param string $internalName Filepath and name to be used in the archive. + * @param int $size Filesize + * @return bool $success + */ + public function addFileFromStream($stream, $internalName, $size){ + if ($this->streamerInstance instanceof ZipStreamer) { + return $this->streamerInstance->addFileFromStream($stream, $internalName); + } else { + return $this->streamerInstance->addFileFromStream($stream, $internalName, $size); + } + } + + /** + * Add an empty directory entry to the archive. + * + * @param string $directoryPath Directory Path and name to be added to the archive. + * @return bool $success + */ + public function addEmptyDir($dirName){ + return $this->streamerInstance->addEmptyDir($dirName); + } + + /** + * Close the archive. + * A closed archive can no longer have new files added to it. After + * closing, the file is completely written to the output stream. + * @return bool $success + */ + public function finalize(){ + return $this->streamerInstance->finalize(); + } +} |