Merge branch 'w18_MDL-26028_m23_xsendfile' of git://github.com/skodak/moodle
This commit is contained in:
@@ -174,11 +174,6 @@ if(empty($serialized)) {
|
||||
die('bad serialization');
|
||||
}
|
||||
|
||||
//IE compatibility HACK!
|
||||
if (ini_get_bool('zlib.output_compression')) {
|
||||
ini_set('zlib.output_compression', 'Off');
|
||||
}
|
||||
|
||||
$filename = 'icalexport.ics';
|
||||
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
|
||||
@@ -220,6 +220,22 @@ $CFG->admin = 'admin';
|
||||
// about students being served outdated versions of uploaded files.
|
||||
// $CFG->filelifetime = 86400;
|
||||
//
|
||||
// Some web servers can offload the file serving from PHP process,
|
||||
// comment out one the following options to enable it in Moodle:
|
||||
// $CFG->xsendfile = 'X-Sendfile'; // Apache {@see https://tn123.org/mod_xsendfile/}
|
||||
// $CFG->xsendfile = 'X-LIGHTTPD-send-file'; // Lighttpd {@see http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file}
|
||||
// $CFG->xsendfile = 'X-Accel-Redirect'; // Nginx {@see http://wiki.nginx.org/XSendfile}
|
||||
// If your X-Sendfile implementation (usually Nginx) uses directory aliases specify them
|
||||
// in the following array setting:
|
||||
// $CFG->xsendfilealiases = array(
|
||||
// '/dataroot/' => $CFG->dataroot,
|
||||
// '/cachedir/' => '/var/www/moodle/cache', // for custom $CFG->cachedir locations
|
||||
// '/tempdir/' => '/var/www/moodle/temp', // for custom $CFG->tempdir locations
|
||||
// '/filedir' => '/var/www/moodle/filedir', // for custom $CFG->filedir locations
|
||||
// );
|
||||
//
|
||||
//
|
||||
//
|
||||
// This setting will prevent the 'My Courses' page being displayed when a student
|
||||
// logs in. The site front page will always show the same (logged-out) view.
|
||||
// $CFG->disablemycourses = true;
|
||||
|
||||
+5
-12
@@ -89,19 +89,12 @@ function min_enable_zlib_compression() {
|
||||
}
|
||||
|
||||
// zlib.output_compression is preferred over ob_gzhandler()
|
||||
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$agent = $_SERVER['HTTP_USER_AGENT'];
|
||||
// try to detect IE6 and prevent gzip because it is extremely buggy browser
|
||||
$parts = explode(';', $agent);
|
||||
if (isset($parts[1])) {
|
||||
$parts = explode(' ', trim($parts[1]));
|
||||
if (count($parts) > 1) {
|
||||
if ($parts[0] === 'MSIE' and (float)$parts[1] < 7) {
|
||||
@ini_set('zlib.output_compression', '0');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!empty($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false) {
|
||||
@ini_set('zlib.output_compression', 'Off');
|
||||
if (function_exists('apache_setenv')) {
|
||||
@apache_setenv('no-gzip', 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ini_set('output_handler', '');
|
||||
|
||||
+189
-279
@@ -1611,38 +1611,159 @@ function send_header_404() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check output buffering settings before sending file.
|
||||
* Please note you should not send any other headers after calling this function.
|
||||
*
|
||||
* To be called only from lib/filelib.php !
|
||||
* Enhanced readfile() with optional acceleration.
|
||||
* @param string|stored_file $file
|
||||
* @param string $mimetype
|
||||
* @param bool $accelerate
|
||||
* @return void
|
||||
*/
|
||||
function prepare_file_content_sending() {
|
||||
// We needed to be able to send headers up until now
|
||||
if (headers_sent()) {
|
||||
throw new file_serving_exception('Headers already sent, can not serve file.');
|
||||
function readfile_accel($file, $mimetype, $accelerate) {
|
||||
global $CFG;
|
||||
|
||||
if ($mimetype === 'text/plain') {
|
||||
// there is no encoding specified in text files, we need something consistent
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
} else {
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
|
||||
$olddebug = error_reporting(0);
|
||||
$lastmodified = is_object($file) ? $file->get_timemodified() : filemtime($file);
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
|
||||
// IE compatibility HACK - it does not like zlib compression much
|
||||
// there is also a problem with the length header in older PHP versions
|
||||
if (ini_get_bool('zlib.output_compression')) {
|
||||
ini_set('zlib.output_compression', 'Off');
|
||||
}
|
||||
|
||||
// flush and close all buffers if possible
|
||||
while(ob_get_level()) {
|
||||
if (!ob_end_flush()) {
|
||||
// prevent infinite loop when buffer can not be closed
|
||||
break;
|
||||
if (is_object($file)) {
|
||||
header('ETag: ' . $file->get_contenthash());
|
||||
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH'] === $file->get_contenthash()) {
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error_reporting($olddebug);
|
||||
// if etag present for stored file rely on it exclusively
|
||||
if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) and (empty($_SERVER['HTTP_IF_NONE_MATCH']) or !is_object($file))) {
|
||||
// get unixtime of request header; clip extra junk off first
|
||||
$since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]));
|
||||
if ($since && $since >= $lastmodified) {
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: we can not reliable test headers_sent() here because
|
||||
// the headers might be sent which trying to close the buffers,
|
||||
// this happens especially if browser does not support gzip or deflate
|
||||
if ($accelerate and !empty($CFG->xsendfile)) {
|
||||
if (empty($CFG->disablebyteserving) and $mimetype !== 'text/plain') {
|
||||
header('Accept-Ranges: bytes');
|
||||
} else {
|
||||
header('Accept-Ranges: none');
|
||||
}
|
||||
|
||||
if (is_object($file)) {
|
||||
$fs = get_file_storage();
|
||||
if ($fs->xsendfile($file->get_contenthash())) {
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
require_once("$CFG->libdir/xsendfilelib.php");
|
||||
if (xsendfile($file)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filesize = is_object($file) ? $file->get_filesize() : filesize($file);
|
||||
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
|
||||
if ($accelerate and empty($CFG->disablebyteserving) and $mimetype !== 'text/plain') {
|
||||
header('Accept-Ranges: bytes');
|
||||
|
||||
if (!empty($_SERVER['HTTP_RANGE']) and strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
|
||||
// byteserving stuff - for acrobat reader and download accelerators
|
||||
// see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
|
||||
// inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
|
||||
$ranges = false;
|
||||
if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
|
||||
foreach ($ranges as $key=>$value) {
|
||||
if ($ranges[$key][1] == '') {
|
||||
//suffix case
|
||||
$ranges[$key][1] = $filesize - $ranges[$key][2];
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
} else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
|
||||
//fix range length
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
}
|
||||
if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
|
||||
//invalid byte-range ==> ignore header
|
||||
$ranges = false;
|
||||
break;
|
||||
}
|
||||
//prepare multipart header
|
||||
$ranges[$key][0] = "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
|
||||
$ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
|
||||
}
|
||||
} else {
|
||||
$ranges = false;
|
||||
}
|
||||
if ($ranges) {
|
||||
if (is_object($file)) {
|
||||
$handle = $file->get_content_file_handle();
|
||||
} else {
|
||||
$handle = fopen($file, 'rb');
|
||||
}
|
||||
byteserving_send_file($handle, $mimetype, $ranges, $filesize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Do not byteserve
|
||||
header('Accept-Ranges: none');
|
||||
}
|
||||
|
||||
header('Content-Length: '.$filesize);
|
||||
|
||||
if ($filesize > 10000000) {
|
||||
// for large files try to flush and close all buffers to conserve memory
|
||||
while(@ob_get_level()) {
|
||||
if (!@ob_end_flush()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send the whole file content
|
||||
if (is_object($file)) {
|
||||
$file->readfile();
|
||||
} else {
|
||||
readfile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to readfile_accel() but designed for strings.
|
||||
* @param string $string
|
||||
* @param string $mimetype
|
||||
* @param bool $accelerate
|
||||
* @return void
|
||||
*/
|
||||
function readstring_accel($string, $mimetype, $accelerate) {
|
||||
global $CFG;
|
||||
|
||||
if ($mimetype === 'text/plain') {
|
||||
// there is no encoding specified in text files, we need something consistent
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
} else {
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Accept-Ranges: none');
|
||||
|
||||
if ($accelerate and !empty($CFG->xsendfile)) {
|
||||
$fs = get_file_storage();
|
||||
if ($fs->xsendfile(sha1($string))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Length: '.strlen($string));
|
||||
echo $string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1656,8 +1777,15 @@ function prepare_file_content_sending() {
|
||||
function send_temp_file($path, $filename, $pathisstring=false) {
|
||||
global $CFG;
|
||||
|
||||
if (check_browser_version('Firefox', '1.5')) {
|
||||
// only FF is known to correctly save to disk before opening...
|
||||
$mimetype = mimeinfo('type', $filename);
|
||||
} else {
|
||||
$mimetype = 'application/x-forcedownload';
|
||||
}
|
||||
|
||||
// close session - not needed anymore
|
||||
@session_get_instance()->write_close();
|
||||
session_get_instance()->write_close();
|
||||
|
||||
if (!$pathisstring) {
|
||||
if (!file_exists($path)) {
|
||||
@@ -1673,10 +1801,7 @@ function send_temp_file($path, $filename, $pathisstring=false) {
|
||||
$filename = urlencode($filename);
|
||||
}
|
||||
|
||||
$filesize = $pathisstring ? strlen($path) : filesize($path);
|
||||
|
||||
header('Content-Disposition: attachment; filename='.$filename);
|
||||
header('Content-Length: '.$filesize);
|
||||
if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
|
||||
header('Cache-Control: max-age=10');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
|
||||
@@ -1686,17 +1811,13 @@ function send_temp_file($path, $filename, $pathisstring=false) {
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
|
||||
header('Pragma: no-cache');
|
||||
}
|
||||
header('Accept-Ranges: none'); // Do not allow byteserving
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
// this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
// send the contents - we can not accelerate this because the file will be deleted asap
|
||||
if ($pathisstring) {
|
||||
echo $path;
|
||||
readstring_accel($path, $mimetype, false);
|
||||
} else {
|
||||
@readfile($path);
|
||||
readfile_accel($path, $mimetype, false);
|
||||
@unlink($path);
|
||||
}
|
||||
|
||||
die; //no more chars to output
|
||||
@@ -1718,9 +1839,6 @@ function send_temp_file_finished($path) {
|
||||
* byteranges etc.
|
||||
*
|
||||
* @category files
|
||||
* @global stdClass $CFG
|
||||
* @global stdClass $COURSE
|
||||
* @global moodle_session $SESSION
|
||||
* @param string $path Path of file on disk (including real filename), or actual content of file as string
|
||||
* @param string $filename Filename to send
|
||||
* @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
|
||||
@@ -1735,7 +1853,7 @@ function send_temp_file_finished($path) {
|
||||
* @return null script execution stopped unless $dontdie is true
|
||||
*/
|
||||
function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathisstring=false, $forcedownload=false, $mimetype='', $dontdie=false) {
|
||||
global $CFG, $COURSE, $SESSION;
|
||||
global $CFG, $COURSE;
|
||||
|
||||
if ($dontdie) {
|
||||
ignore_user_abort(true);
|
||||
@@ -1759,46 +1877,6 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
$mimetype = ($forcedownload and !$isFF) ? 'application/x-forcedownload' :
|
||||
($mimetype ? $mimetype : mimeinfo('type', $filename));
|
||||
|
||||
$lastmodified = $pathisstring ? time() : filemtime($path);
|
||||
$filesize = $pathisstring ? strlen($path) : filesize($path);
|
||||
|
||||
/* - MDL-13949
|
||||
//Adobe Acrobat Reader XSS prevention
|
||||
if ($mimetype=='application/pdf' or mimeinfo('type', $filename)=='application/pdf') {
|
||||
//please note that it prevents opening of pdfs in browser when http referer disabled
|
||||
//or file linked from another site; browser caching of pdfs is now disabled too
|
||||
if (!empty($_SERVER['HTTP_RANGE'])) {
|
||||
//already byteserving
|
||||
$lifetime = 1; // >0 needed for byteserving
|
||||
} else if (empty($_SERVER['HTTP_REFERER']) or strpos($_SERVER['HTTP_REFERER'], $CFG->wwwroot)!==0) {
|
||||
$mimetype = 'application/x-forcedownload';
|
||||
$forcedownload = true;
|
||||
$lifetime = 0;
|
||||
} else {
|
||||
$lifetime = 1; // >0 needed for byteserving
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if ($lifetime > 0 && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
||||
// get unixtime of request header; clip extra junk off first
|
||||
$since = strtotime(preg_replace('/;.*$/','',$_SERVER["HTTP_IF_MODIFIED_SINCE"]));
|
||||
if ($since && $since >= $lastmodified) {
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Content-Type: '.$mimetype);
|
||||
if ($dontdie) {
|
||||
return;
|
||||
}
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
//do not put '@' before the next header to detect incorrect moodle configurations,
|
||||
//error should be better than "weird" empty lines for admins/users
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
|
||||
// if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
|
||||
if (check_browser_version('MSIE')) {
|
||||
$filename = rawurlencode($filename);
|
||||
@@ -1811,51 +1889,13 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
}
|
||||
|
||||
if ($lifetime > 0) {
|
||||
$nobyteserving = false;
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
|
||||
if (empty($CFG->disablebyteserving) && !$pathisstring && $mimetype != 'text/plain' && $mimetype != 'text/html') {
|
||||
|
||||
header('Accept-Ranges: bytes');
|
||||
|
||||
if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
|
||||
// byteserving stuff - for acrobat reader and download accelerators
|
||||
// see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
|
||||
// inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
|
||||
$ranges = false;
|
||||
if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
|
||||
foreach ($ranges as $key=>$value) {
|
||||
if ($ranges[$key][1] == '') {
|
||||
//suffix case
|
||||
$ranges[$key][1] = $filesize - $ranges[$key][2];
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
} else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
|
||||
//fix range length
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
}
|
||||
if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
|
||||
//invalid byte-range ==> ignore header
|
||||
$ranges = false;
|
||||
break;
|
||||
}
|
||||
//prepare multipart header
|
||||
$ranges[$key][0] = "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
|
||||
$ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
|
||||
}
|
||||
} else {
|
||||
$ranges = false;
|
||||
}
|
||||
if ($ranges) {
|
||||
$handle = fopen($path, 'rb');
|
||||
byteserving_send_file($handle, $mimetype, $ranges, $filesize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Do not byteserve (disabled, strings, text and html files).
|
||||
header('Accept-Ranges: none');
|
||||
}
|
||||
} else { // Do not cache files in proxies and browsers
|
||||
$nobyteserving = true;
|
||||
if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
|
||||
header('Cache-Control: max-age=10');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
|
||||
@@ -1865,29 +1905,18 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
|
||||
header('Pragma: no-cache');
|
||||
}
|
||||
header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
|
||||
}
|
||||
|
||||
if (empty($filter)) {
|
||||
if ($mimetype == 'text/plain') {
|
||||
header('Content-Type: Text/plain; charset=utf-8'); //add encoding
|
||||
} else {
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
header('Content-Length: '.$filesize);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
if ($pathisstring) {
|
||||
echo $path;
|
||||
readstring_accel($path, $mimetype, !$dontdie);
|
||||
} else {
|
||||
@readfile($path);
|
||||
readfile_accel($path, $mimetype, !$dontdie);
|
||||
}
|
||||
|
||||
} else { // Try to put the file through filters
|
||||
} else {
|
||||
// Try to put the file through filters
|
||||
if ($mimetype == 'text/html') {
|
||||
$options = new stdClass();
|
||||
$options->noclean = true;
|
||||
@@ -1897,46 +1926,24 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
$text = file_modify_html_header($text);
|
||||
$output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
|
||||
|
||||
header('Content-Length: '.strlen($output));
|
||||
header('Content-Type: text/html');
|
||||
readstring_accel($output, $mimetype, false);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
echo $output;
|
||||
// only filter text if filter all files is selected
|
||||
} else if (($mimetype == 'text/plain') and ($filter == 1)) {
|
||||
// only filter text if filter all files is selected
|
||||
$options = new stdClass();
|
||||
$options->newlines = false;
|
||||
$options->noclean = true;
|
||||
$text = htmlentities($pathisstring ? $path : implode('', file($path)));
|
||||
$output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
|
||||
|
||||
header('Content-Length: '.strlen($output));
|
||||
header('Content-Type: text/html; charset=utf-8'); //add encoding
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
echo $output;
|
||||
|
||||
} else { // Just send it out raw
|
||||
header('Content-Length: '.$filesize);
|
||||
header('Content-Type: '.$mimetype);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
readstring_accel($output, $mimetype, false);
|
||||
|
||||
} else {
|
||||
// send the contents
|
||||
if ($pathisstring) {
|
||||
echo $path;
|
||||
}else {
|
||||
@readfile($path);
|
||||
readstring_accel($path, $mimetype, !$dontdie);
|
||||
} else {
|
||||
readfile_accel($path, $mimetype, !$dontdie);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1959,9 +1966,6 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
* and should not be reopened.
|
||||
*
|
||||
* @category files
|
||||
* @global stdClass $CFG
|
||||
* @global stdClass $COURSE
|
||||
* @global moodle_session $SESSION
|
||||
* @param stored_file $stored_file local file object
|
||||
* @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
|
||||
* @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
|
||||
@@ -1970,7 +1974,7 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
|
||||
* @return null script execution stopped unless $options['dontdie'] is true
|
||||
*/
|
||||
function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownload=false, array $options=array()) {
|
||||
global $CFG, $COURSE, $SESSION;
|
||||
global $CFG, $COURSE;
|
||||
|
||||
if (empty($options['filename'])) {
|
||||
$filename = null;
|
||||
@@ -2023,28 +2027,6 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
|
||||
$mimetype = ($forcedownload and !$isFF) ? 'application/x-forcedownload' :
|
||||
($stored_file->get_mimetype() ? $stored_file->get_mimetype() : mimeinfo('type', $filename));
|
||||
|
||||
$lastmodified = $stored_file->get_timemodified();
|
||||
$filesize = $stored_file->get_filesize();
|
||||
|
||||
if ($lifetime > 0 && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
||||
// get unixtime of request header; clip extra junk off first
|
||||
$since = strtotime(preg_replace('/;.*$/','',$_SERVER["HTTP_IF_MODIFIED_SINCE"]));
|
||||
if ($since && $since >= $lastmodified) {
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Content-Type: '.$mimetype);
|
||||
if ($dontdie) {
|
||||
return;
|
||||
}
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
//do not put '@' before the next header to detect incorrect moodle configurations,
|
||||
//error should be better than "weird" empty lines for admins/users
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
|
||||
// if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
|
||||
if (check_browser_version('MSIE')) {
|
||||
$filename = rawurlencode($filename);
|
||||
@@ -2061,45 +2043,6 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
|
||||
if (empty($CFG->disablebyteserving) && $mimetype != 'text/plain' && $mimetype != 'text/html') {
|
||||
|
||||
header('Accept-Ranges: bytes');
|
||||
|
||||
if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
|
||||
// byteserving stuff - for acrobat reader and download accelerators
|
||||
// see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
|
||||
// inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
|
||||
$ranges = false;
|
||||
if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
|
||||
foreach ($ranges as $key=>$value) {
|
||||
if ($ranges[$key][1] == '') {
|
||||
//suffix case
|
||||
$ranges[$key][1] = $filesize - $ranges[$key][2];
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
} else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
|
||||
//fix range length
|
||||
$ranges[$key][2] = $filesize - 1;
|
||||
}
|
||||
if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
|
||||
//invalid byte-range ==> ignore header
|
||||
$ranges = false;
|
||||
break;
|
||||
}
|
||||
//prepare multipart header
|
||||
$ranges[$key][0] = "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
|
||||
$ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
|
||||
}
|
||||
} else {
|
||||
$ranges = false;
|
||||
}
|
||||
if ($ranges) {
|
||||
byteserving_send_file($stored_file->get_content_file_handle(), $mimetype, $ranges, $filesize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Do not byteserve (disabled, strings, text and html files).
|
||||
header('Accept-Ranges: none');
|
||||
}
|
||||
} else { // Do not cache files in proxies and browsers
|
||||
if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
|
||||
header('Cache-Control: max-age=10');
|
||||
@@ -2110,23 +2053,11 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
|
||||
header('Pragma: no-cache');
|
||||
}
|
||||
header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
|
||||
}
|
||||
|
||||
if (empty($filter)) {
|
||||
if ($mimetype == 'text/plain') {
|
||||
header('Content-Type: Text/plain; charset=utf-8'); //add encoding
|
||||
} else {
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
header('Content-Length: '.$filesize);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
$stored_file->readfile();
|
||||
readfile_accel($stored_file, $mimetype, !$dontdie);
|
||||
|
||||
} else { // Try to put the file through filters
|
||||
if ($mimetype == 'text/html') {
|
||||
@@ -2137,15 +2068,7 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
|
||||
$text = file_modify_html_header($text);
|
||||
$output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
|
||||
|
||||
header('Content-Length: '.strlen($output));
|
||||
header('Content-Type: text/html');
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
echo $output;
|
||||
readstring_accel($output, $mimetype, false);
|
||||
|
||||
} else if (($mimetype == 'text/plain') and ($filter == 1)) {
|
||||
// only filter text if filter all files is selected
|
||||
@@ -2155,26 +2078,10 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
|
||||
$text = $stored_file->get_content();
|
||||
$output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
|
||||
|
||||
header('Content-Length: '.strlen($output));
|
||||
header('Content-Type: text/html; charset=utf-8'); //add encoding
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
echo $output;
|
||||
readstring_accel($output, $mimetype, false);
|
||||
|
||||
} else { // Just send it out raw
|
||||
header('Content-Length: '.$filesize);
|
||||
header('Content-Type: '.$mimetype);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
|
||||
// send the contents
|
||||
$stored_file->readfile();
|
||||
readfile_accel($stored_file, $mimetype, !$dontdie);
|
||||
}
|
||||
}
|
||||
if ($dontdie) {
|
||||
@@ -2353,9 +2260,11 @@ function fulldelete($location) {
|
||||
* @param string $mimetype The mimetype for the output
|
||||
* @param array $ranges An array of ranges to send
|
||||
* @param string $filesize The size of the content if only one range is used
|
||||
* @todo MDL-31088 check if "multipart/x-byteranges" is more compatible with current readers/browsers/servers
|
||||
*/
|
||||
function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
|
||||
// better turn off any kind of compression and buffering
|
||||
@ini_set('zlib.output_compression', 'Off');
|
||||
|
||||
$chunksize = 1*(1024*1024); // 1MB chunks - must be less than 2MB!
|
||||
if ($handle === false) {
|
||||
die;
|
||||
@@ -2367,11 +2276,12 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
|
||||
header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.$filesize);
|
||||
header('Content-Type: '.$mimetype);
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
while(@ob_get_level()) {
|
||||
if (!@ob_end_flush()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$buffer = '';
|
||||
fseek($handle, $ranges[0][1]);
|
||||
while (!feof($handle) && $length > 0) {
|
||||
@set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
|
||||
@@ -2391,16 +2301,16 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
|
||||
header('HTTP/1.1 206 Partial content');
|
||||
header('Content-Length: '.$totallength);
|
||||
header('Content-Type: multipart/byteranges; boundary='.BYTESERVING_BOUNDARY);
|
||||
//TODO: check if "multipart/x-byteranges" is more compatible with current readers/browsers/servers
|
||||
|
||||
//flush the buffers - save memory and disable sid rewrite
|
||||
//this also disables zlib compression
|
||||
prepare_file_content_sending();
|
||||
while(@ob_get_level()) {
|
||||
if (!@ob_end_flush()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($ranges as $range) {
|
||||
$length = $range[2] - $range[1] + 1;
|
||||
echo $range[0];
|
||||
$buffer = '';
|
||||
fseek($handle, $range[1]);
|
||||
while (!feof($handle) && $length > 0) {
|
||||
@set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
|
||||
|
||||
@@ -1294,6 +1294,22 @@ class file_storage {
|
||||
return array($contenthash, $filesize, $newfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve file content using X-Sendfile header.
|
||||
* Please make sure that all headers are already sent
|
||||
* and the all access control checks passed.
|
||||
*
|
||||
* @param string $contenthash sah1 hash of the file content to be served
|
||||
* @return bool success
|
||||
*/
|
||||
public function xsendfile($contenthash) {
|
||||
global $CFG;
|
||||
require_once("$CFG->libdir/xsendfilelib.php");
|
||||
|
||||
$hashpath = $this->path_from_hash($contenthash);
|
||||
return xsendfile("$hashpath/$contenthash");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return path to file with given hash.
|
||||
*
|
||||
|
||||
@@ -891,6 +891,15 @@ if (PHPUNIT_TEST) {
|
||||
|
||||
}
|
||||
|
||||
// // try to detect IE6 and prevent gzip because it is extremely buggy browser
|
||||
if (!empty($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false) {
|
||||
@ini_set('zlib.output_compression', 'Off');
|
||||
if (function_exists('apache_setenv')) {
|
||||
@apache_setenv('no-gzip', 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// note: we can not block non utf-8 installations here, because empty mysql database
|
||||
// might be converted to utf-8 in admin/index.php during installation
|
||||
|
||||
|
||||
@@ -1079,6 +1079,9 @@ function disable_output_buffering() {
|
||||
}
|
||||
}
|
||||
|
||||
// disable any other output handlers
|
||||
ini_set('output_handler', '');
|
||||
|
||||
error_reporting($olddebug);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* X-Sendfile support
|
||||
*
|
||||
* @package core_files
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
//NOTE: do not verify MOODLE_INTERNAL here, this is used from themes too
|
||||
|
||||
/**
|
||||
* Serve file using X-Sendfile header, this needs special server module
|
||||
* or configuration. Please make sure that all headers are already sent
|
||||
* and the all access control checks passed.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return bool success
|
||||
*/
|
||||
function xsendfile($filepath) {
|
||||
global $CFG;
|
||||
|
||||
if (empty($CFG->xsendfile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($filepath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (headers_sent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filepath = realpath($filepath);
|
||||
|
||||
$aliased = false;
|
||||
if (!empty($CFG->xsendfilealiases) and is_array($CFG->xsendfilealiases)) {
|
||||
foreach ($CFG->xsendfilealiases as $alias=>$dir) {
|
||||
$dir = realpath($dir);
|
||||
if ($dir === false) {
|
||||
continue;
|
||||
}
|
||||
if (substr($dir, -1) !== DIRECTORY_SEPARATOR) {
|
||||
// add trailing dir separator
|
||||
$dir .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if (strpos($filepath, $dir) === 0) {
|
||||
$filepath = $alias.substr($filepath, strlen($dir));
|
||||
$aliased = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($CFG->xsendfile === 'X-LIGHTTPD-send-file') {
|
||||
// http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file says 1.4 it does not support byteserving
|
||||
header('Accept-Ranges: none');
|
||||
|
||||
} else if ($CFG->xsendfile === 'X-Accel-Redirect') {
|
||||
// http://wiki.nginx.org/XSendfile
|
||||
// Nginx requires paths relative to aliases, you need to specify them in config.php
|
||||
if (!$aliased) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
header("$CFG->xsendfile: $filepath");
|
||||
|
||||
return true;
|
||||
}
|
||||
+3
-7
@@ -23,13 +23,6 @@ $scoid = required_param('scoid', PARAM_INT); // sco ID
|
||||
$mode = optional_param('mode', '', PARAM_ALPHA); // navigation mode
|
||||
$attempt = required_param('attempt', PARAM_INT); // new attempt
|
||||
|
||||
//IE 6 Bug workaround
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false) {
|
||||
@ini_set('zlib.output_compression', 'Off');
|
||||
@apache_setenv('no-gzip', 1);
|
||||
header( 'Content-Type: application/javascript' );
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
if (! $cm = get_coursemodule_from_id('scorm', $id)) {
|
||||
print_error('invalidcoursemodule');
|
||||
@@ -96,6 +89,9 @@ if (!$sco = scorm_get_sco($scoid)) {
|
||||
if (scorm_version_check($scorm->version, SCORM_13)) {
|
||||
$userdata->{'cmi.scaled_passing_score'} = $DB->get_field('scorm_seq_objective', 'minnormalizedmeasure', array('scoid'=>$scoid));
|
||||
}
|
||||
|
||||
header('Content-Type: text/javascript; charset=UTF-8');
|
||||
|
||||
$scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe
|
||||
if (file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'.js.php')) {
|
||||
include_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'.js.php');
|
||||
|
||||
@@ -145,6 +145,9 @@ add_to_log($course->id, 'scorm', 'launch', 'view.php?id='.$cm->id, $result, $cm-
|
||||
|
||||
// which API are we looking for
|
||||
$LMS_api = (scorm_version_check($scorm->version, SCORM_12) || empty($scorm->version)) ? 'API' : 'API_1484_11';
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@@ -23,11 +23,6 @@ $scoid = required_param('scoid', PARAM_INT); // sco ID
|
||||
$mode = optional_param('mode', '', PARAM_ALPHA); // navigation mode
|
||||
$attempt = required_param('attempt', PARAM_INT); // new attempt
|
||||
|
||||
//IE 6 Bug workaround
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false && ini_get('zlib.output_compression') == 'On') {
|
||||
ini_set('zlib.output_compression', 'Off');
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
if (! $cm = get_coursemodule_from_id('scorm', $id)) {
|
||||
print_error('invalidcoursemodule');
|
||||
@@ -94,6 +89,9 @@ if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
|
||||
if (!$sco = scorm_get_sco($scoid)) {
|
||||
print_error('cannotfindsco', 'scorm');
|
||||
}
|
||||
|
||||
header('Content-Type: text/javascript; charset=UTF-8');
|
||||
|
||||
$scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe
|
||||
if (file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'.js.php')) {
|
||||
include($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'.js.php');
|
||||
|
||||
@@ -28,12 +28,6 @@ $currentorg = optional_param('currentorg', '', PARAM_RAW); // selected organizat
|
||||
$newattempt = optional_param('newattempt', 'off', PARAM_ALPHA); // the user request to start a new attempt
|
||||
$displaymode = optional_param('display','',PARAM_ALPHA);
|
||||
|
||||
//IE 6 Bug workaround
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false) {
|
||||
@ini_set('zlib.output_compression', 'Off');
|
||||
@apache_setenv('no-gzip', 1);
|
||||
}
|
||||
|
||||
// IE 9 workaround for Flash bug: MDL-29213
|
||||
// Note that it's not clear if appending the meta tag via $CFG->additionalhtmlhead
|
||||
// is correct at all, both because of the mechanism itself and because MS says
|
||||
|
||||
@@ -139,6 +139,9 @@ if ($rev > -1) {
|
||||
// parameters to get the best performance.
|
||||
|
||||
function send_cached_image($imagepath, $rev) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
$pathinfo = pathinfo($imagepath);
|
||||
$imagename = $pathinfo['filename'].'.'.$pathinfo['extension'];
|
||||
@@ -155,6 +158,10 @@ function send_cached_image($imagepath, $rev) {
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Content-Length: '.filesize($imagepath));
|
||||
|
||||
if (xsendfile($imagepath)) {
|
||||
die;
|
||||
}
|
||||
|
||||
// no need to gzip already compressed images ;-)
|
||||
|
||||
readfile($imagepath);
|
||||
|
||||
@@ -99,6 +99,9 @@ if ($rev > -1) {
|
||||
// parameters to get the best performance.
|
||||
|
||||
function send_cached_js($jspath) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
|
||||
header('Content-Disposition: inline; filename="javascript.php"');
|
||||
@@ -108,6 +111,11 @@ function send_cached_js($jspath) {
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
|
||||
if (xsendfile($jspath)) {
|
||||
die;
|
||||
}
|
||||
|
||||
if (!min_enable_zlib_compression()) {
|
||||
header('Content-Length: '.filesize($jspath));
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ yui_image_cached($imagepath);
|
||||
|
||||
|
||||
function yui_image_cached($imagepath) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*300; // 300 days === forever
|
||||
$pathinfo = pathinfo($imagepath);
|
||||
$imagename = $pathinfo['filename'].'.'.$pathinfo['extension'];
|
||||
@@ -92,6 +95,10 @@ function yui_image_cached($imagepath) {
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Content-Length: '.filesize($imagepath));
|
||||
|
||||
if (xsendfile($imagepath)) {
|
||||
die;
|
||||
}
|
||||
|
||||
// no need to gzip already compressed images ;-)
|
||||
|
||||
readfile($imagepath);
|
||||
|
||||
Reference in New Issue
Block a user