23e484983a1b1396483d8f79bc3fd650c97a76e2
[moodle.git] / file.php
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;
25     }
27     // disable moodle specific debug messages
28     disable_debugging();
30     $relativepath = get_file_argument('file.php');
31     $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
32     
33     // relative path must start with '/', because of backup/restore!!!
34     if (!$relativepath) {
35         print_error('invalidargorconf');
36     } else if ($relativepath{0} != '/') {
37         print_error('pathdoesnotstartslash');
38     }
40     $pathname = $CFG->dataroot.$relativepath;
42     // extract relative path components
43     $args = explode('/', trim($relativepath, '/'));
44     if (count($args) == 0) { // always at least courseid, may search for index.html in course root
45         print_error('invalidarguments');
46     }
47   
48     // security: limit access to existing course subdirectories
49     if (($args[0]!='blog') and (!$course = $DB->get_record_sql("SELECT * FROM {course} WHERE id=?", array((int)$args[0])))) {
50         print_error('invalidcourseid');
51     }
53     // security: prevent access to "000" or "1 something" directories
54     // hack for blogs, needs proper security check too
55     if (($args[0] != 'blog') and ($args[0] != $course->id)) {
56         print_error('invalidcourseid');
57     }
59     // security: login to course if necessary
60     // Note: file.php always calls require_login() with $setwantsurltome=false
61     //       in order to avoid messing redirects. MDL-14495
62     if ($args[0] == 'blog') {
63         if (empty($CFG->bloglevel)) {
64             print_error('blogdisable', 'blog');
65         } else if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
66             require_login(0, true, null, false);
67         } else if ($CFG->forcelogin) {
68             require_login(0, true, null, false);
69         }
70     } else if ($course->id != SITEID) {
71         require_login($course->id, true, null, false);
72     } else if ($CFG->forcelogin) {
73         if (!empty($CFG->sitepolicy)
74             and ($CFG->sitepolicy == $CFG->wwwroot.'/file.php'.$relativepath
75                  or $CFG->sitepolicy == $CFG->wwwroot.'/file.php?file='.$relativepath)) {
76             //do not require login for policy file
77         } else {
78             require_login(0, true, null, false);
79         }
80     }
82     // security: only editing teachers can access backups
83     if ((count($args) >= 2) and (strtolower($args[1]) == 'backupdata')) {
84         if (!has_capability('moodle/site:backup', get_context_instance(CONTEXT_COURSE, $course->id))) {
85             print_error('nopermissions');
86         } else {
87             $lifetime = 0; //disable browser caching for backups 
88         }
89     }
91     if (is_dir($pathname)) {
92         if (file_exists($pathname.'/index.html')) {
93             $pathname = rtrim($pathname, '/').'/index.html';
94             $args[] = 'index.html';
95         } else if (file_exists($pathname.'/index.htm')) {
96             $pathname = rtrim($pathname, '/').'/index.htm';
97             $args[] = 'index.htm';
98         } else if (file_exists($pathname.'/Default.htm')) {
99             $pathname = rtrim($pathname, '/').'/Default.htm';
100             $args[] = 'Default.htm';
101         } else {
102             // security: do not return directory node!
103             not_found($course->id);
104         }
105     }
107     // security: teachers can view all assignments, students only their own
108     if ((count($args) >= 3)
109         and (strtolower($args[1]) == 'moddata')
110         and (strtolower($args[2]) == 'assignment')) {
112         $lifetime = 0;  // do not cache assignments, students may reupload them
113         if ($args[4] == $USER->id) {
114             //can view own assignemnt submissions
115         } else {
116             $instance = (int)$args[3];
117             if (!$cm = get_coursemodule_from_instance('assignment', $instance, $course->id)) {
118                 not_found($course->id);
119             }
120             if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_MODULE, $cm->id))) {
121                 print_error('nopermissions');
122             }
123         } 
124     }
126     // security: force download of all attachments submitted by students
127     if ((count($args) >= 3)
128         and (strtolower($args[1]) == 'moddata')
129         and ((strtolower($args[2]) == 'forum')
130             or (strtolower($args[2]) == 'assignment')
131             or (strtolower($args[2]) == 'data')
132             or (strtolower($args[2]) == 'glossary')
133             or (strtolower($args[2]) == 'wiki')
134             or (strtolower($args[2]) == 'exercise')
135             or (strtolower($args[2]) == 'workshop')
136             )) {
137         $forcedownload  = 1; // force download of all attachments
138     }
139     if ($args[0] == 'blog') {
140         $forcedownload  = 1; // force download of all attachments
141     }    
143     // security: some protection of hidden resource files
144     // warning: it may break backwards compatibility
145     if ((!empty($CFG->preventaccesstohiddenfiles)) 
146         and (count($args) >= 2)
147         and (!(strtolower($args[1]) == 'moddata' and strtolower($args[2]) != 'resource')) // do not block files from other modules!
148         and (!has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $course->id)))) {
150         $rargs = $args;
151         array_shift($rargs);
152         $reference = implode('/', $rargs);
154         $sql = "SELECT COUNT(r.id)
155                   FROM {resource} r, {course_modules} cm, {modules} m
156                  WHERE r.course        = ?
157                        AND m.name      = 'resource'
158                        AND cm.module   = m.id
159                        AND cm.instance = r.id
160                        AND cm.visible  = 0
161                        AND r.type      = 'file'
162                        AND r.reference = ?";
163         $params = array($course->id, $reference);
165         if ($DB->count_records_sql($sql, $params)) {
166            print_error('nopermissions');
167         }
168     }
170     // check that file exists
171     if (!file_exists($pathname)) {
172         not_found($course->id);
173     }
175     // ========================================
176     // finally send the file
177     // ========================================
178     session_write_close(); // unlock session during fileserving
179     $filename = $args[count($args)-1];
180     send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
182     function not_found($courseid) {
183         global $CFG;
184         header('HTTP/1.0 404 not found');
185         print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$courseid); //this is not displayed on IIS??
186     }
187 ?>