Example #1
0
/**
 * Handles the sending of file data to the user's browser, including support for
 * byteranges etc.
 *
 * The $options parameter supports the following keys:
 *  (string|null) preview - send the preview of the file (e.g. "thumb" for a thumbnail)
 *  (string|null) filename - overrides the implicit filename
 *  (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
 *  (string|null) cacheability - force the cacheability setting of the HTTP response, "private" or "public",
 *      when $lifetime is greater than 0. Cacheability defaults to "private" when logged in as other than guest; otherwise,
 *      defaults to "public".
 *
 * @category files
 * @param stored_file $stored_file local file object
 * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime)
 * @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 array $options additional options affecting the file serving
 * @return null script execution stopped unless $options['dontdie'] is true
 */
function send_stored_file($stored_file, $lifetime = null, $filter = 0, $forcedownload = false, array $options = array())
{
    global $CFG, $COURSE;
    if (empty($options['filename'])) {
        $filename = null;
    } else {
        $filename = $options['filename'];
    }
    if (empty($options['dontdie'])) {
        $dontdie = false;
    } else {
        $dontdie = true;
    }
    if ($lifetime === 'default' or is_null($lifetime)) {
        $lifetime = $CFG->filelifetime;
    }
    if (!empty($options['preview'])) {
        // replace the file with its preview
        $fs = get_file_storage();
        $preview_file = $fs->get_file_preview($stored_file, $options['preview']);
        if (!$preview_file) {
            // unable to create a preview of the file, send its default mime icon instead
            if ($options['preview'] === 'tinyicon') {
                $size = 24;
            } else {
                if ($options['preview'] === 'thumb') {
                    $size = 90;
                } else {
                    $size = 256;
                }
            }
            $fileicon = file_file_icon($stored_file, $size);
            send_file($CFG->dirroot . '/pix/' . $fileicon . '.png', basename($fileicon) . '.png');
        } else {
            // preview images have fixed cache lifetime and they ignore forced download
            // (they are generated by GD and therefore they are considered reasonably safe).
            $stored_file = $preview_file;
            $lifetime = DAYSECS;
            $filter = 0;
            $forcedownload = false;
        }
    }
    // handle external resource
    if ($stored_file && $stored_file->is_external_file() && !isset($options['sendcachedexternalfile'])) {
        $stored_file->send_file($lifetime, $filter, $forcedownload, $options);
        die;
    }
    if (!$stored_file or $stored_file->is_directory()) {
        // nothing to serve
        if ($dontdie) {
            return;
        }
        die;
    }
    if ($dontdie) {
        ignore_user_abort(true);
    }
    \core\session\manager::write_close();
    // Unlock session during file serving.
    $filename = is_null($filename) ? $stored_file->get_filename() : $filename;
    // Use given MIME type if specified.
    $mimetype = $stored_file->get_mimetype();
    // Otherwise guess it.
    if (!$mimetype || $mimetype === 'document/unknown') {
        $mimetype = get_mimetype_for_sending($filename);
    }
    // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
    if (core_useragent::is_ie()) {
        $filename = rawurlencode($filename);
    }
    if ($forcedownload) {
        header('Content-Disposition: attachment; filename="' . $filename . '"');
    } else {
        if ($mimetype !== 'application/x-shockwave-flash') {
            // If this is an swf don't pass content-disposition with filename as this makes the flash player treat the file
            // as an upload and enforces security that may prevent the file from being loaded.
            header('Content-Disposition: inline; filename="' . $filename . '"');
        }
    }
    if ($lifetime > 0) {
        $cacheability = ' public,';
        if (!empty($options['cacheability']) && $options['cacheability'] === 'public') {
            // This file must be cache-able by both browsers and proxies.
            $cacheability = ' public,';
        } else {
            if (!empty($options['cacheability']) && $options['cacheability'] === 'private') {
                // This file must be cache-able only by browsers.
                $cacheability = ' private,';
            } else {
                if (isloggedin() and !isguestuser()) {
                    $cacheability = ' private,';
                }
            }
        }
        header('Cache-Control:' . $cacheability . ' max-age=' . $lifetime . ', no-transform');
        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT');
        header('Pragma: ');
    } else {
        // Do not cache files in proxies and browsers
        if (is_https()) {
            // HTTPS sites - watch out for IE! KB812935 and KB316431.
            header('Cache-Control: private, max-age=10, no-transform');
            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, no-transform');
            header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
            header('Pragma: no-cache');
        }
    }
    // Allow cross-origin requests only for Web Services.
    // This allow to receive requests done by Web Workers or webapps in different domains.
    if (WS_SERVER) {
        header('Access-Control-Allow-Origin: *');
    }
    if (empty($filter)) {
        // send the contents
        readfile_accel($stored_file, $mimetype, !$dontdie);
    } else {
        // Try to put the file through filters
        if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml') {
            $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);
            readstring_accel($output, $mimetype, false);
        } 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>';
                readstring_accel($output, $mimetype, false);
            } else {
                // Just send it out raw
                readfile_accel($stored_file, $mimetype, !$dontdie);
            }
        }
    }
    if ($dontdie) {
        return;
    }
    die;
    //no more chars to output!!!
}
Example #2
0
 /**
  * Tests for get_mimetype_for_sending function.
  */
 public function test_get_mimetype_for_sending()
 {
     // Without argument.
     $this->assertEquals('application/octet-stream', get_mimetype_for_sending());
     // Argument is null.
     $this->assertEquals('application/octet-stream', get_mimetype_for_sending(null));
     // Filename having no extension.
     $this->assertEquals('application/octet-stream', get_mimetype_for_sending('filenamewithoutextension'));
     // Test using the extensions listed from the get_mimetypes_array function.
     $mimetypes = get_mimetypes_array();
     foreach ($mimetypes as $ext => $info) {
         if ($ext === 'xxx') {
             $this->assertEquals('application/octet-stream', get_mimetype_for_sending('SampleFile.' . $ext));
         } else {
             $this->assertEquals($info['type'], get_mimetype_for_sending('SampleFile.' . $ext));
         }
     }
 }