Merge branch 'MDL-27515_m19' of git://github.com/rwijaya/moodle into MOODLE_19_STABLE
[moodle.git] / file.php
blob2b16dcf6d0867242476d69aa403b5fd2dad513d9
1 <?php // $Id$
2 // This script fetches files from the dataroot directory
3 //
4 // You should use the get_file_url() function, available in lib/filelib.php, to link to file.php.
5 // This ensures proper formatting and offers useful options.
6 //
7 // Syntax: file.php/courseid/dir/dir/dir/filename.ext
8 // file.php/courseid/dir/dir/dir/filename.ext?forcedownload=1 (download instead of inline)
9 // file.php/courseid/dir (returns index.html from dir)
10 // Workaround: file.php?file=/courseid/dir/dir/dir/filename.ext
11 // Test: file.php/testslasharguments
14 //TODO: Blog attachments do not have access control implemented - anybody can read them!
15 // It might be better to move the code to separate file because the access
16 // control is quite complex - see bolg/index.php
18 require_once('config.php');
19 require_once('lib/filelib.php');
21 if (!isset($CFG->filelifetime)) {
22 $lifetime = 86400; // Seconds for files to remain in caches
23 } else {
24 $lifetime = $CFG->filelifetime;
27 // disable moodle specific debug messages
28 disable_debugging();
30 $relativepath = get_file_argument('file.php');
31 $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
33 // relative path must start with '/', because of backup/restore!!!
34 if (!$relativepath) {
35 error('No valid arguments supplied or incorrect server configuration');
36 } else if ($relativepath{0} != '/') {
37 error('No valid arguments supplied, path does not start with slash!');
40 $pathname = $CFG->dataroot.$relativepath;
42 // protect imsenterprise plugin data
43 if (strtolower("$CFG->dataroot/1/imsenterprise-enrol.xml") === strtolower(realpath($pathname))
44 or (!empty($CFG->enrol_imsfilelocation) and strtolower($CFG->enrol_imsfilelocation) === strtolower(realpath($pathname)))) {
45 require_login();
46 require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
47 $forcedownload = 1;
50 // extract relative path components
51 $args = explode('/', trim($relativepath, '/'));
52 if (count($args) == 0) { // always at least courseid, may search for index.html in course root
53 error('No valid arguments supplied');
56 // security: limit access to existing course subdirectories
57 if (($args[0]!='blog') and (!$course = get_record_sql("SELECT * FROM {$CFG->prefix}course WHERE id='".(int)$args[0]."'"))) {
58 error('Invalid course ID');
61 // security: prevent access to "000" or "1 something" directories
62 // hack for blogs, needs proper security check too
63 if (($args[0] != 'blog') and ($args[0] != $course->id)) {
64 error('Invalid course ID');
67 // security: login to course if necessary
68 // Note: file.php always calls require_login() with $setwantsurltome=false
69 // in order to avoid messing redirects. MDL-14495
70 if ($args[0] == 'blog') {
71 if (empty($CFG->bloglevel)) {
72 error('Blogging is disabled!');
73 } else if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
74 require_login(0, true, null, false);
75 } else if ($CFG->forcelogin) {
76 require_login(0, true, null, false);
78 } else if ($course->id != SITEID) {
79 require_login($course->id, true, null, false);
80 } else if ($CFG->forcelogin) {
81 if (!empty($CFG->sitepolicy)
82 and ($CFG->sitepolicy == $CFG->wwwroot.'/file.php'.$relativepath
83 or $CFG->sitepolicy == $CFG->wwwroot.'/file.php?file='.$relativepath)) {
84 //do not require login for policy file
85 } else {
86 require_login(0, true, null, false);
90 // security: only editing teachers can access backups
91 if ((count($args) >= 2) and (strtolower($args[1]) == 'backupdata')) {
92 if (!has_capability('moodle/site:backup', get_context_instance(CONTEXT_COURSE, $course->id))) {
93 error('Access not allowed');
94 } else {
95 $lifetime = 0; //disable browser caching for backups
99 if (is_dir($pathname)) {
100 if (file_exists($pathname.'/index.html')) {
101 $pathname = rtrim($pathname, '/').'/index.html';
102 $args[] = 'index.html';
103 } else if (file_exists($pathname.'/index.htm')) {
104 $pathname = rtrim($pathname, '/').'/index.htm';
105 $args[] = 'index.htm';
106 } else if (file_exists($pathname.'/Default.htm')) {
107 $pathname = rtrim($pathname, '/').'/Default.htm';
108 $args[] = 'Default.htm';
109 } else {
110 // security: do not return directory node!
111 not_found($course->id);
115 // security: teachers can view all assignments, students only their own
116 if ((count($args) >= 3)
117 and (strtolower($args[1]) == 'moddata')
118 and (strtolower($args[2]) == 'assignment')) {
120 $lifetime = 0; // do not cache assignments, students may reupload them
121 if ($args[4] == $USER->id) {
122 //can view own assignemnt submissions
123 } else {
124 $instance = (int)$args[3];
125 if (!$cm = get_coursemodule_from_instance('assignment', $instance, $course->id)) {
126 not_found($course->id);
128 if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_MODULE, $cm->id))) {
129 error('Access not allowed');
134 // security: force download of all attachments submitted by students
135 if (count($args) >= 3 and strtolower($args[1]) === 'moddata') {
136 $mod = clean_param($args[2], PARAM_SAFEDIR);
137 if (file_exists("$CFG->dirroot/mod/$mod/lib.php")) {
138 if (!$forcedownload) {
139 require_once("$CFG->dirroot/mod/$mod/lib.php");
140 $trustedfunction = $mod.'_is_moddata_trusted';
141 if (function_exists($trustedfunction)) {
142 // force download of all attachments that are not trusted
143 $forcedownload = !$trustedfunction();
144 } else {
145 $forcedownload = 1;
148 } else {
149 // module is not installed - better not serve file at all
150 not_found($course->id);
153 if ($args[0] == 'blog') {
154 $forcedownload = 1; // force download of all attachments
157 // security: some protection of hidden resource files
158 // warning: it may break backwards compatibility
159 if ((!empty($CFG->preventaccesstohiddenfiles))
160 and (count($args) >= 2)
161 and (!(strtolower($args[1]) == 'moddata' and strtolower($args[2]) != 'resource')) // do not block files from other modules!
162 and (!has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $course->id)))) {
164 $rargs = $args;
165 array_shift($rargs);
166 $reference = implode('/', $rargs);
168 $sql = "SELECT COUNT(r.id) " .
169 "FROM {$CFG->prefix}resource r, " .
170 "{$CFG->prefix}course_modules cm, " .
171 "{$CFG->prefix}modules m " .
172 "WHERE r.course = '{$course->id}' " .
173 "AND m.name = 'resource' " .
174 "AND cm.module = m.id " .
175 "AND cm.instance = r.id " .
176 "AND cm.visible = 0 " .
177 "AND r.type = 'file' " .
178 "AND r.reference = '{$reference}'";
179 if (count_records_sql($sql)) {
180 error('Access not allowed');
184 // check that file exists
185 if (!file_exists($pathname)) {
186 not_found($course->id);
189 // ========================================
190 // finally send the file
191 // ========================================
192 session_write_close(); // unlock session during fileserving
193 $filename = $args[count($args)-1];
194 send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
196 function not_found($courseid) {
197 global $CFG;
198 header('HTTP/1.0 404 not found');
199 print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$courseid); //this is not displayed on IIS??