"MDL-14129,fix print_error"
[moodle.git] / file.php
1 <?php // $Id$
2       // This script fetches files from the dataroot directory
3       // Syntax:      file.php/courseid/dir/dir/dir/filename.ext
4       //              file.php/courseid/dir/dir/dir/filename.ext?forcedownload=1 (download instead of inline)
5       //              file.php/courseid/dir (returns index.html from dir)
6       // Workaround:  file.php?file=/courseid/dir/dir/dir/filename.ext
7       // Test:        file.php/testslasharguments
10       //TODO: Blog attachments do not have access control implemented - anybody can read them!
11       //      It might be better to move the code to separate file because the access
12       //      control is quite complex - see bolg/index.php 
14     require_once('config.php');
15     require_once('lib/filelib.php');
17     if (!isset($CFG->filelifetime)) {
18         $lifetime = 86400;     // Seconds for files to remain in caches
19     } else {
20         $lifetime = $CFG->filelifetime;
21     }
23     // disable moodle specific debug messages
24     disable_debugging();
26     $relativepath = get_file_argument('file.php');
27     $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
28     
29     // relative path must start with '/', because of backup/restore!!!
30     if (!$relativepath) {
31         print_error('invalidargorconf');
32     } else if ($relativepath{0} != '/') {
33         print_error('pathdoesnotstartslash');
34     }
36     $pathname = $CFG->dataroot.$relativepath;
38     // extract relative path components
39     $args = explode('/', trim($relativepath, '/'));
40     if (count($args) == 0) { // always at least courseid, may search for index.html in course root
41         print_error('invalidarguments');
42     }
43   
44     // security: limit access to existing course subdirectories
45     if (($args[0]!='blog') and (!$course = $DB->get_record_sql("SELECT * FROM {course} WHERE id=?", array((int)$args[0])))) {
46         print_error('invalidcourseid');
47     }
49     // security: prevent access to "000" or "1 something" directories
50     // hack for blogs, needs proper security check too
51     if (($args[0] != 'blog') and ($args[0] != $course->id)) {
52         print_error('invalidcourseid');
53     }
55     // security: login to course if necessary
56     // Note: file.php always calls require_login() with $setwantsurltome=false
57     //       in order to avoid messing redirects. MDL-14495
58     if ($args[0] == 'blog') {
59         if (empty($CFG->bloglevel)) {
60             print_error('blogdisable', 'blog');
61         } else if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
62             require_login(0, true, null, false);
63         } else if ($CFG->forcelogin) {
64             require_login(0, true, null, false);
65         }
66     } else if ($course->id != SITEID) {
67         require_login($course->id, true, null, false);
68     } else if ($CFG->forcelogin) {
69         if (!empty($CFG->sitepolicy)
70             and ($CFG->sitepolicy == $CFG->wwwroot.'/file.php'.$relativepath
71                  or $CFG->sitepolicy == $CFG->wwwroot.'/file.php?file='.$relativepath)) {
72             //do not require login for policy file
73         } else {
74             require_login(0, true, null, false);
75         }
76     }
78     // security: only editing teachers can access backups
79     if ((count($args) >= 2) and (strtolower($args[1]) == 'backupdata')) {
80         if (!has_capability('moodle/site:backup', get_context_instance(CONTEXT_COURSE, $course->id))) {
81             print_error('nopermissions');
82         } else {
83             $lifetime = 0; //disable browser caching for backups 
84         }
85     }
87     if (is_dir($pathname)) {
88         if (file_exists($pathname.'/index.html')) {
89             $pathname = rtrim($pathname, '/').'/index.html';
90             $args[] = 'index.html';
91         } else if (file_exists($pathname.'/index.htm')) {
92             $pathname = rtrim($pathname, '/').'/index.htm';
93             $args[] = 'index.htm';
94         } else if (file_exists($pathname.'/Default.htm')) {
95             $pathname = rtrim($pathname, '/').'/Default.htm';
96             $args[] = 'Default.htm';
97         } else {
98             // security: do not return directory node!
99             not_found($course->id);
100         }
101     }
103     // security: teachers can view all assignments, students only their own
104     if ((count($args) >= 3)
105         and (strtolower($args[1]) == 'moddata')
106         and (strtolower($args[2]) == 'assignment')) {
108         $lifetime = 0;  // do not cache assignments, students may reupload them
109         if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_COURSE, $course->id))
110           and $args[4] != $USER->id) {
111            print_error('nopermissions');
112         }
113     }
115     // security: force download of all attachments submitted by students
116     if ((count($args) >= 3)
117         and (strtolower($args[1]) == 'moddata')
118         and ((strtolower($args[2]) == 'forum')
119             or (strtolower($args[2]) == 'assignment')
120             or (strtolower($args[2]) == 'data')
121             or (strtolower($args[2]) == 'glossary')
122             or (strtolower($args[2]) == 'wiki')
123             or (strtolower($args[2]) == 'exercise')
124             or (strtolower($args[2]) == 'workshop')
125             )) {
126         $forcedownload  = 1; // force download of all attachments
127     }
128     if ($args[0] == 'blog') {
129         $forcedownload  = 1; // force download of all attachments
130     }    
132     // security: some protection of hidden resource files
133     // warning: it may break backwards compatibility
134     if ((!empty($CFG->preventaccesstohiddenfiles)) 
135         and (count($args) >= 2)
136         and (!(strtolower($args[1]) == 'moddata' and strtolower($args[2]) != 'resource')) // do not block files from other modules!
137         and (!has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $course->id)))) {
139         $rargs = $args;
140         array_shift($rargs);
141         $reference = implode('/', $rargs);
143         $sql = "SELECT COUNT(r.id)
144                   FROM {resource} r, {course_modules} cm, {modules} m
145                  WHERE r.course        = ?
146                        AND m.name      = 'resource'
147                        AND cm.module   = m.id
148                        AND cm.instance = r.id
149                        AND cm.visible  = 0
150                        AND r.type      = 'file'
151                        AND r.reference = ?";
152         $params = array($course->id, $reference);
154         if ($DB->count_records_sql($sql, $params)) {
155            print_error('nopermissions');
156         }
157     }
159     // check that file exists
160     if (!file_exists($pathname)) {
161         not_found($course->id);
162     }
164     // ========================================
165     // finally send the file
166     // ========================================
167     session_write_close(); // unlock session during fileserving
168     $filename = $args[count($args)-1];
169     send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
171     function not_found($courseid) {
172         global $CFG;
173         header('HTTP/1.0 404 not found');
174         print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$courseid); //this is not displayed on IIS??
175     }
176 ?>