public function executeIndex(\lib\HTTPRequest $request) { $fileManager = $this->managers()->getManagerOf('file'); $httpResponse = $this->app->httpResponse(); if (!$request->getExists('index')) { $httpResponse->addHeader('HTTP/1.0 404 Not Found'); throw new \RuntimeException('No icon specified'); } $scalable = $request->getExists('svg') && (int) $request->getData('svg'); $filePath = $this->_getIconPath($request->getData('index'), $scalable); if ($filePath === false) { //Not found $httpResponse->addHeader('HTTP/1.0 404 Not Found'); throw new \RuntimeException('Cannot find specified icon'); } $httpResponse->addHeader('Content-Type: ' . $fileManager->mimetype($filePath)); $fileMtime = $fileManager->mtime($filePath); if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $fileMtime) { $httpResponse->addHeader('HTTP/1.0 304 Not Modified'); return; } $httpResponse->addHeader('Last-Modified: ' . gmdate('D, d M Y H:i:s T', $fileMtime)); $out = $fileManager->read($filePath); $httpResponse->addHeader('Content-Length: ' . strlen($out)); $this->responseContent->output($out); }
public function executeIndex(\lib\HTTPRequest $request) { $fileManager = $this->managers()->getManagerOf('file'); $shareManager = $this->managers()->getManagerOf('sharedFile'); $authManager = $this->managers()->getManagerOf('authorization'); $user = $this->app()->user(); if (!$request->getExists('path')) { $this->app->httpResponse()->addHeader('HTTP/1.0 400 Bad Request'); throw new RuntimeException('No file specified'); } $filePath = $fileManager->beautifyPath($request->getData('path')); //Request parameters $options = array('download' => $request->getExists('dl') && (int) $request->getData('dl'), 'shareKey' => $request->getExists('key') ? $request->getData('key') : null, 'range' => null); // Get the 'Range' header if one was sent // See https://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file/4451376#4451376 if (isset($_SERVER['HTTP_RANGE'])) { $options['range'] = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions } else { if (function_exists('apache_request_headers') && ($apache = apache_request_headers())) { // Try Apache again $headers = array(); foreach ($apache as $header => $val) { if (strtolower($header) == 'range') { $options['range'] = $val; break; } } } } //Authorizations control $userAuths = array(); if ($user->isLogged()) { //Get user's authorizations $userAuths = $authManager->getByUserId($user->id()); } $sharedAccess = false; if (!empty($options['shareKey'])) { $sharedFile = $shareManager->getByKey($options['shareKey'], $fileManager->toInternalPath($filePath)); if (!empty($sharedFile)) { $sharedAccess = true; } } if (!$sharedAccess) { try { $this->guardian->controlArgAuth('file.read', $filePath, $userAuths); } catch (Exception $e) { $this->app->httpResponse()->addHeader('HTTP/1.0 403 Forbidden'); throw $e; } } //Check if the file exists if (!$fileManager->exists($filePath)) { $this->app->httpResponse()->addHeader('HTTP/1.0 404 Not Found'); throw new RuntimeException('The specified file "' . $filePath . '" doesn\'t exist'); } if ($fileManager->isDir($filePath) && !$options['download']) { $this->app->httpResponse()->addHeader('HTTP/1.0 406 Not Acceptable'); throw new RuntimeException('The specified file "' . $filePath . '" is a directory'); } $outputFile = $filePath; $filename = $fileManager->basename($filePath); //If the file is a directory, zip it if ($fileManager->isDir($filePath) && $options['download']) { if (strpos($filePath, '/home/') !== 0) { $this->app->httpResponse()->addHeader('HTTP/1.0 403 Forbidden'); throw new RuntimeException('Downloading files which are not in your home directory is not allowed'); } if (!class_exists('\\ZipArchive')) { $this->app->httpResponse()->addHeader('HTTP/1.0 501 Not Implemented'); throw new RuntimeException('Downloading directories is not available on this system'); } $filesInDir = $fileManager->readDir($filePath, true); //Read recursively the directory $tmpFilePath = $fileManager->tmpfile(); $zip = new ZipArchive(); $zip->open($fileManager->toInternalPath($tmpFilePath), ZipArchive::CREATE); foreach ($filesInDir as $filename => $filepath) { if ($fileManager->isDir($filepath)) { $added = $zip->addEmptyDir($filename); } else { $added = $zip->addFile($fileManager->toInternalPath($filepath), $filename); } if ($added === false) { $this->app->httpResponse()->addHeader('HTTP/1.0 500 Internal Server Error'); throw new RuntimeException('Unable to add "' . $filepath . '" to zip file'); } } $zip->close(); $outputFile = $tmpFilePath; $filename = $fileManager->basename($filePath) . '.zip'; } // Get the data range requested (if any) $isPartial = false; $filesize = $fileManager->size($outputFile); $length = $filesize; if (!empty($options['range'])) { $isPartial = true; list($param, $range) = explode('=', $options['range']); if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes' $this->app->httpResponse()->addHeader('HTTP/1.0 400 Bad Request'); throw new RuntimeException('Invalid range: only ranges in bytes are accepted'); } $range = explode(',', $range); $range = explode('-', $range[0]); // We only deal with the first requested range if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid $this->app->httpResponse()->addHeader('HTTP/1.0 400 Bad Request'); throw new RuntimeException('Invalid range: bytes parameter is not valid'); } if ($range[0] === '') { // First number missing, return last $range[1] bytes $end = $filesize - 1; $start = $end - intval($range[0]); } else { if ($range[1] === '') { // Second number missing, return from byte $range[0] to end $start = intval($range[0]); $end = $filesize - 1; } else { // Both numbers present, return specific range $start = intval($range[0]); $end = intval($range[1]); } } if ($end >= $filesize || !$start && (!$end || $end == $filesize - 1)) { // Invalid range/whole file specified, return whole file $isPartial = false; } $length = $end - $start + 1; } //Send response $httpResponse = $this->app->httpResponse(); $httpResponse->addHeader('Content-Type: ' . $fileManager->mimetype($outputFile)); if ($options['download']) { $httpResponse->addHeader('Content-Description: File Transfer'); $httpResponse->addHeader('Content-Disposition: attachment; filename="' . $filename . '"'); $httpResponse->addHeader('Content-Transfer-Encoding: binary'); } $outputMtime = $fileManager->mtime($outputFile); if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $outputMtime) { $httpResponse->addHeader('HTTP/1.0 304 Not Modified'); return; } $httpResponse->addHeader('Last-Modified: ' . gmdate('D, d M Y H:i:s T', $outputMtime)); $httpResponse->addHeader('Content-Length: ' . $filesize); $httpResponse->addHeader('Accept-Ranges: bytes'); $resp = $this->responseContent; if ($isPartial) { $httpResponse->addHeader('HTTP/1.1 206 Partial Content'); $httpResponse->addHeader('Content-Range: bytes ' . $start . '-' . $end . '/' . $filesize); } if (!($fp = fopen($fileManager->toInternalPath($outputFile), 'r'))) { // Error out if we can't read the file $this->app->httpResponse()->addHeader('HTTP/1.0 500 Internal Server Error'); throw new RuntimeException('Cannot read file "' . $filePath . '"'); } if ($isPartial && $start > 0) { fseek($fp, $start); } while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server $read = $length > 8192 ? 8192 : $length; $length -= $read; $resp->output(fread($fp, $read)); } fclose($fp); }