Beispiel #1
0
/**
 * Enhanced readfile() with optional acceleration.
 * @param string|stored_file $file
 * @param string $mimetype
 * @param bool $accelerate
 * @return void
 */
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);
    }
    $lastmodified = is_object($file) ? $file->get_timemodified() : filemtime($file);
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastmodified) . ' GMT');
    if (is_object($file)) {
        header('Etag: "' . $file->get_contenthash() . '"');
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $file->get_contenthash()) {
            header('HTTP/1.1 304 Not Modified');
            return;
        }
    }
    // 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;
        }
    }
    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_allow_large($file, $filesize);
    }
}
Beispiel #2
0
/**
 * Serves a file from dataroot.
 *
 * This function checks that the file is inside dataroot, but does not perform
 * any other checks. Authors using this function should make sure that their
 * scripts perform appropriate authentication.
 *
 * As an example: If the file is an artefact, you could ask for an artefact and
 * view ID, and check that the artefact is in the view and that the user can
 * view the view.
 *
 * @param string $path     The file to send. Must include the dataroot path.
 * @param string $filename The name of the file as the browser should use to
 *                         serve it.
 * @param string $mimetype Mime type to be sent in header
 * @param array  $options  Any options to use when serving the file. Currently
 *                         lifetime = 0 for no cache
 *                         forcedownload - force application rather than inline
 *                         overridecontenttype - send this instead of the mimetype
 *                         there are none.
 */
function serve_file($path, $filename, $mimetype, $options = array())
{
    $dataroot = realpath(get_config('dataroot'));
    $path = realpath($path);
    $options = array_merge(array('lifetime' => 86400), $options);
    if (!get_config('insecuredataroot') && substr($path, 0, strlen($dataroot)) != $dataroot) {
        throw new AccessDeniedException();
    }
    if (!file_exists($path)) {
        throw new NotFoundException();
    }
    session_write_close();
    // unlock session during fileserving
    $lastmodified = filemtime($path);
    $filesize = filesize($path);
    if ($mimetype == 'text/html' || $mimetype == 'text/xml' || $mimetype == 'application/xml' || $mimetype == 'application/xhtml+xml' || $mimetype == 'image/svg+xml') {
        if (isset($options['downloadurl']) && $filesize < 1024 * 1024) {
            display_cleaned_html(file_get_contents($path), $filename, $options);
            exit;
        }
        $options['forcedownload'] = true;
        $mimetype = 'application/octet-stream';
    }
    if (!$mimetype) {
        $mimetype = 'application/forcedownload';
    }
    if (ini_get('zlib.output_compression')) {
        ini_set('zlib.output_compression', 'Off');
    }
    // Try to disable automatic sid rewrite in cookieless mode
    @ini_set('session.use_trans_sid', 'false');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastmodified) . ' GMT');
    // @todo possibly need addslashes on the filename, but I'm unsure on exactly
    // how the browsers will handle it.
    if ($mimetype == 'application/forcedownload' || isset($options['forcedownload'])) {
        header('Content-Disposition: attachment; filename="' . $filename . '"');
    } else {
        header('Content-Disposition: inline; filename="' . $filename . '"');
    }
    header('X-Content-Type-Options: nosniff');
    if ($options['lifetime'] > 0 && !get_config('nocache')) {
        header('Cache-Control: max-age=' . $options['lifetime']);
        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $options['lifetime']) . ' GMT');
        header('Pragma: ');
        if ($mimetype != 'text/plain' && $mimetype != 'text/html' && !isset($fileoutput)) {
            @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($path, $mimetype, $ranges);
                }
            }
        } else {
            // Do not byteserve (disabled, strings, text and html files).
            header('Accept-Ranges: none');
        }
    } else {
        // Do not cache files in proxies and browsers
        if (is_https() === true) {
            //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');
            header('Pragma: ');
        } else {
            //normal http - prevent caching at all cost
            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
            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 ($mimetype == 'text/plain') {
        // Add encoding
        header('Content-Type: Text/plain; charset=utf-8');
    } else {
        if (isset($options['overridecontenttype'])) {
            header('Content-Type: ' . $options['overridecontenttype']);
        } else {
            header('Content-Type: ' . $mimetype);
        }
    }
    header('Content-Length: ' . $filesize);
    while (@ob_end_flush()) {
    }
    //flush the buffers - save memory and disable sid rewrite
    readfile_chunked($path);
    perf_to_log();
    exit;
}
Beispiel #3
0
/**
 * Handles the sending of file data to the user's browser, including support for
 * byteranges etc.
 *
 * @global object
 * @global object
 * @global object
 * @param object $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
 * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
 * @param string $filename Override filename
 * @param bool $dontdie - return control to caller afterwards. this is not recommended and only used for cleanup tasks.
 *                        if this is passed as true, ignore_user_abort is called.  if you don't want your processing to continue on cancel,
 *                        you must detect this case when control is returned using connection_aborted. Please not that session is closed
 *                        and should not be reopened.
 * @return void no return or void, script execution stopped unless $dontdie is true
 */
function send_stored_file($stored_file, $lifetime = 86400, $filter = 0, $forcedownload = false, $filename = null, $dontdie = false)
{
    global $CFG, $COURSE, $SESSION;
    if (!$stored_file or $stored_file->is_directory()) {
        // nothing to serve
        if ($dontdie) {
            return;
        }
        die;
    }
    if ($dontdie) {
        ignore_user_abort(true);
    }
    session_get_instance()->write_close();
    // unlock session during fileserving
    // Use given MIME type if specified, otherwise guess it using mimeinfo.
    // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
    // only Firefox saves all files locally before opening when content-disposition: attachment stated
    $filename = is_null($filename) ? $stored_file->get_filename() : $filename;
    $isFF = check_browser_version('Firefox', '1.5');
    // only FF > 1.5 properly tested
    $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();
    //try to disable automatic sid rewrite in cookieless mode
    @ini_set("session.use_trans_sid", "false");
    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
    //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
    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);
    }
    if ($forcedownload) {
        header('Content-Disposition: attachment; filename="' . $filename . '"');
    } else {
        header('Content-Disposition: inline; filename="' . $filename . '"');
    }
    if ($lifetime > 0) {
        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) && $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');
            header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
            header('Pragma: ');
        } else {
            //normal http - prevent caching at all cost
            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
            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)) {
        $filtered = false;
        if ($mimetype == 'text/html' && !empty($CFG->usesid)) {
            //cookieless mode - rewrite links
            header('Content-Type: text/html');
            $text = $stored_file->get_content();
            $text = sid_ob_rewrite($text);
            $filesize = strlen($text);
            $filtered = true;
        } else {
            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 ($filtered) {
            echo $text;
        } else {
            $stored_file->readfile();
        }
    } else {
        // Try to put the file through filters
        if ($mimetype == 'text/html') {
            $options = new stdClass();
            $options->noclean = true;
            $options->nocache = true;
            // temporary workaround for MDL-5136
            $text = $stored_file->get_content();
            $text = file_modify_html_header($text);
            $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
            if (!empty($CFG->usesid)) {
                //cookieless mode - rewrite links
                $output = sid_ob_rewrite($output);
            }
            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;
        } 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 = $stored_file->get_content();
                $output = '<pre>' . format_text($text, FORMAT_MOODLE, $options, $COURSE->id) . '</pre>';
                if (!empty($CFG->usesid)) {
                    //cookieless mode - rewrite links
                    $output = sid_ob_rewrite($output);
                }
                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();
                // send the contents
                $stored_file->readfile();
            }
        }
    }
    if ($dontdie) {
        return;
    }
    die;
    //no more chars to output!!!
}
/**
 * Handles the sending of file data to the user's browser, including support for
 * byteranges etc.
 * @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)
 * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
 * @param bool $pathisstring If true (default false), $path is the content to send and not the pathname
 * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
 * @param string $mimetype Include to specify the MIME type; leave blank to have it guess the type from $filename
 */
function send_file($path, $filename, $lifetime = 'default', $filter = 0, $pathisstring = false, $forcedownload = false, $mimetype = '')
{
    global $CFG, $COURSE, $SESSION;
    // MDL-11789, apply $CFG->filelifetime here
    if ($lifetime === 'default') {
        if (!empty($CFG->filelifetime)) {
            $lifetime = $CFG->filelifetime;
        } else {
            $lifetime = 86400;
        }
    }
    // Use given MIME type if specified, otherwise guess it using mimeinfo.
    // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
    // only Firefox saves all files locally before opening when content-disposition: attachment stated
    $isFF = check_browser_version('Firefox', '1.5');
    // only FF > 1.5 properly tested
    $mimetype = ($forcedownload and !$isFF) ? 'application/x-forcedownload' : ($mimetype ? $mimetype : mimeinfo('type', $filename));
    // If the file is a Flash file and that the user flash player is outdated return a flash upgrader MDL-20841
    if (!empty($CFG->excludeoldflashclients) && $mimetype == 'application/x-shockwave-flash' && !empty($SESSION->flashversion)) {
        $userplayerversion = explode('.', $SESSION->flashversion);
        $requiredplayerversion = explode('.', $CFG->excludeoldflashclients);
        if ($userplayerversion[0] < $requiredplayerversion[0] || $userplayerversion[0] == $requiredplayerversion[0] && $userplayerversion[1] < $requiredplayerversion[1] || $userplayerversion[0] == $requiredplayerversion[0] && $userplayerversion[1] == $requiredplayerversion[1] && $userplayerversion[2] < $requiredplayerversion[2]) {
            $path = $CFG->dirroot . "/lib/flashdetect/flashupgrade.swf";
            // Alternate content asking user to upgrade Flash
            $filename = "flashupgrade.swf";
            $lifetime = 0;
            // Do not cache
        }
    }
    $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
            }
        }
    */
    //IE compatibiltiy HACK!
    if (ini_get('zlib.output_compression')) {
        ini_set('zlib.output_compression', 'Off');
    }
    //try to disable automatic sid rewrite in cookieless mode
    @ini_set("session.use_trans_sid", "false");
    //do not put '@' before the next header to detect incorrect moodle configurations,
    //error should be better than "weird" empty lines for admins/users
    //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
    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);
    }
    if ($forcedownload) {
        @header('Content-Disposition: attachment; filename="' . $filename . '"');
    } else {
        @header('Content-Disposition: inline; filename="' . $filename . '"');
    }
    if ($lifetime > 0) {
        @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) {
                    byteserving_send_file($path, $mimetype, $ranges);
                }
            }
        } 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');
            @header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
            @header('Pragma: ');
        } else {
            //normal http - prevent caching at all cost
            @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
            @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/html' && !empty($CFG->usesid) && empty($_COOKIE['MoodleSession' . $CFG->sessioncookie])) {
            //cookieless mode - rewrite links
            @header('Content-Type: text/html');
            $path = $pathisstring ? $path : implode('', file($path));
            $path = sid_ob_rewrite($path);
            $filesize = strlen($path);
            $pathisstring = true;
        } else {
            if ($mimetype == 'text/plain') {
                @header('Content-Type: Text/plain; charset=utf-8');
                //add encoding
            } else {
                @header('Content-Type: ' . $mimetype);
            }
        }
        @header('Content-Length: ' . $filesize);
        while (@ob_end_flush()) {
        }
        //flush the buffers - save memory and disable sid rewrite
        if ($pathisstring) {
            echo $path;
        } else {
            readfile_chunked($path);
        }
    } else {
        // Try to put the file through filters
        if ($mimetype == 'text/html') {
            $options = new object();
            $options->noclean = true;
            $options->nocache = true;
            // temporary workaround for MDL-5136
            $text = $pathisstring ? $path : implode('', file($path));
            $text = file_modify_html_header($text);
            $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
            if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession' . $CFG->sessioncookie])) {
                //cookieless mode - rewrite links
                $output = sid_ob_rewrite($output);
            }
            @header('Content-Length: ' . strlen($output));
            @header('Content-Type: text/html');
            while (@ob_end_flush()) {
            }
            //flush the buffers - save memory and disable sid rewrite
            echo $output;
            // only filter text if filter all files is selected
        } else {
            if ($mimetype == 'text/plain' and $filter == 1) {
                $options = new object();
                $options->newlines = false;
                $options->noclean = true;
                $text = htmlentities($pathisstring ? $path : implode('', file($path)));
                $output = '<pre>' . format_text($text, FORMAT_MOODLE, $options, $COURSE->id) . '</pre>';
                if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession' . $CFG->sessioncookie])) {
                    //cookieless mode - rewrite links
                    $output = sid_ob_rewrite($output);
                }
                @header('Content-Length: ' . strlen($output));
                @header('Content-Type: text/html; charset=utf-8');
                //add encoding
                while (@ob_end_flush()) {
                }
                //flush the buffers - save memory and disable sid rewrite
                echo $output;
            } else {
                // Just send it out raw
                @header('Content-Length: ' . $filesize);
                @header('Content-Type: ' . $mimetype);
                while (@ob_end_flush()) {
                }
                //flush the buffers - save memory and disable sid rewrite
                if ($pathisstring) {
                    echo $path;
                } else {
                    readfile_chunked($path);
                }
            }
        }
    }
    die;
    //no more chars to output!!!
}