Skip to content

Instantly share code, notes, and snippets.

@DaveRandom
Last active July 17, 2018 15:11
Show Gist options
  • Save DaveRandom/234e9e04f238eb4a584724295c94ad03 to your computer and use it in GitHub Desktop.
Save DaveRandom/234e9e04f238eb4a584724295c94ad03 to your computer and use it in GitHub Desktop.
<?php
// This is the absolute path under which all the files that are allowed for download are stored
const FILES_ROOT_DIR = __DIR__;
// This is the path that was specified by the user
$userPath = $_GET['file'];
// First get the normalised canonical path of the base dir
$baseDir = realpath(FILES_ROOT_DIR);
if ($baseDir === false) {
header('HTTP/1.1 500 Internal Server Error');
exit("The configured root path does not exist or is not accessible");
}
// Next, try to resolve the user-supplied path against the root dir
$target = realpath($baseDir . '/' . ltrim($userPath, '/'));
if ($target === false || !is_file($target)) {
header('HTTP/1.1 404 Not Found');
exit("The specified path does not exist or is not a file");
}
// extract the start of the resolved path that should match the root dir
// get 1 more character, we need to check it's a slash
$targetBaseDirPath = substr($target, 0, strlen($baseDir) + 1);
// Now check that the resolved target path starts with the exact string of the base path
if ($targetBaseDirPath !== $baseDir . '/') {
header('HTTP/1.1 400 Bad Request');
exit("The specified path is outside the root path");
}
// Everything OK, output the file
// Load the files from the Resume lib (this should really be done with composer and autoloading)
require_once __DIR__ . '/Resume/DefaultOutputWriter.php';
require_once __DIR__ . '/Resume/FileResource.php';
require_once __DIR__ . '/Resume/HeaderSet.php';
require_once __DIR__ . '/Resume/IncompatibleRangesException.php';
require_once __DIR__ . '/Resume/InvalidRangeException.php';
require_once __DIR__ . '/Resume/InvalidRangeHeaderException.php';
require_once __DIR__ . '/Resume/LengthNotAvailableException.php';
require_once __DIR__ . '/Resume/LogicException.php';
require_once __DIR__ . '/Resume/NonExistentFileException.php';
require_once __DIR__ . '/Resume/OutputWriter.php';
require_once __DIR__ . '/Resume/Range.php';
require_once __DIR__ . '/Resume/RangeNotApplicableException.php';
require_once __DIR__ . '/Resume/RangeSet.php';
require_once __DIR__ . '/Resume/RangeUnitProvider.php';
require_once __DIR__ . '/Resume/Resource.php';
require_once __DIR__ . '/Resume/ResourceServlet.php';
require_once __DIR__ . '/Resume/RuntimeException.php';
require_once __DIR__ . '/Resume/SendFileFailureException.php';
require_once __DIR__ . '/Resume/UnreadableFileException.php';
require_once __DIR__ . '/Resume/UnsatisfiableRangeException.php';
require_once __DIR__ . '/Resume/functions.php';
try {
$resource = new \DaveRandom\Resume\FileResource($target, 'video/mp4');
// Note that this construct will still work if the client did not specify a Range: header
$rangeHeader = \DaveRandom\Resume\get_request_header('Range');
$rangeSet = \DaveRandom\Resume\RangeSet::createFromHeader($rangeHeader);
// Discard any buffered output, and disable output buffering
while (ob_get_level()) {
ob_end_clean();
}
(new \DaveRandom\Resume\ResourceServlet($resource))
->sendResource($rangeSet);
} catch (\DaveRandom\Resume\InvalidRangeHeaderException $e) {
\header("HTTP/1.1 400 Bad Request");
} catch (\DaveRandom\Resume\UnsatisfiableRangeException $e) {
\header("HTTP/1.1 416 Range Not Satisfiable");
} catch (NonExistentFileException $e) {
\header("HTTP/1.1 404 Not Found");
} catch (\DaveRandom\Resume\UnreadableFileException $e) {
\header("HTTP/1.1 500 Internal Server Error");
} catch (\DaveRandom\Resume\SendFileFailureException $e) {
if (!\headers_sent()) {
\header("HTTP/1.1 500 Internal Server Error");
}
echo "An error occurred while attempting to send the requested resource: {$e->getMessage()}";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment