weekly release 1.9.12+
[moodle.git] / pix / smartpix.php
blob7deb186dcdd6e91a3a8cbd588728cb564c35c5ee
1 <?php
2 // Outputs pictures from theme or core pix folder. Only used if $CFG->smartpix is
3 // turned on.
5 $matches=array(); // Reusable array variable for preg_match
7 // This does NOT use config.php. This is because doing that makes database requests
8 // which cause this to take longer (I benchmarked this at 16ms, 256ms with config.php)
9 // A version using normal Moodle functions is included in comment at end in case we
10 // want to switch to it in future.
12 function error($text,$notfound=false) {
13 header($notfound ? 'HTTP/1.0 404 Not Found' : 'HTTP/1.0 500 Internal Server Error');
14 header('Content-Type: text/plain');
15 print $text;
16 exit;
19 // Nicked from moodlelib clean_param
20 function makesafe($param) {
21 $param = str_replace('\\\'', '\'', $param);
22 $param = str_replace('\\"', '"', $param);
23 $param = str_replace('\\', '/', $param);
24 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
25 $param = ereg_replace('\.\.+', '', $param);
26 $param = ereg_replace('//+', '/', $param);
27 return ereg_replace('/(\./)+', '/', $param);
30 // Nicked from weblib
31 /**
32 * Remove query string from url
34 * Takes in a URL and returns it without the querystring portion
36 * @param string $url the url which may have a query string attached
37 * @return string
39 function strip_querystring($url) {
41 if ($commapos = strpos($url, '?')) {
42 return substr($url, 0, $commapos);
43 } else {
44 return $url;
48 // get query string
49 function get_query($name) {
50 if (!empty($_SERVER['REQUEST_URI'])) {
51 return explode($name, $_SERVER['REQUEST_URI']);
52 } else if (!empty($_SERVER['QUERY_STRING'])) {
53 return array('', '?'. $_SERVER['QUERY_STRING']);
54 } else {
55 return false;
58 // Nicked from weblib then cutdown
59 /**
60 * Extracts file argument either from file parameter or PATH_INFO.
61 * @param string $scriptname name of the calling script
62 * @return string file path (only safe characters)
64 function get_file_argument_limited($scriptname) {
65 $relativepath = FALSE;
67 // first try normal parameter (compatible method == no relative links!)
68 if(isset($_GET['file'])) {
69 return makesafe($_GET['file']);
72 // then try extract file from PATH_INFO (slasharguments method)
73 if (!empty($_SERVER['PATH_INFO'])) {
74 $path_info = $_SERVER['PATH_INFO'];
75 // check that PATH_INFO works == must not contain the script name
76 if (!strpos($path_info, $scriptname)) {
77 return makesafe(rawurldecode($path_info));
81 // now if both fail try the old way
82 // (for compatibility with misconfigured or older buggy php implementations)
83 $arr = get_query($scriptname);
84 if (!empty($arr[1])) {
85 return makesafe(rawurldecode(strip_querystring($arr[1])));
88 error('Unexpected PHP set up. Turn off the smartpix config option.');
91 // We do need to get dirroot from config.php
92 if(!$config=@file_get_contents(dirname(__FILE__).'/../config.php')) {
93 error("Can't open config.php");
95 $configlines=preg_split('/[\r\n]+/',$config);
96 foreach($configlines as $configline) {
97 if(preg_match('/^\s?\$CFG->dirroot\s*=\s*[\'"](.*?)[\'"]\s*;/',$configline,$matches)) {
98 $dirroot=$matches[1];
100 if(preg_match('/^\s?\$CFG->dataroot\s*=\s*[\'"](.*?)[\'"]\s*;/',$configline,$matches)) {
101 $dataroot=$matches[1];
103 if(isset($dirroot) && isset($dataroot)) {
104 break;
107 if(!(isset($dirroot) && isset($dataroot))) {
108 error('No line in config.php like $CFG->dirroot=\'/somewhere/whatever\';');
111 // Split path - starts with theme name, then actual image path inside pix
112 $path=get_file_argument_limited('smartpix.php');
113 $match=array();
114 if(!preg_match('|^/([a-zA-Z0-9_\-.]+)/([a-zA-Z0-9/_\-.]+)$|',$path,$match)) {
115 error('Unexpected request format');
117 list($junk,$theme,$path)=$match;
119 // Check file type
120 if(preg_match('/\.png$/',$path)) {
121 $mimetype='image/png';
122 } else if(preg_match('/\.gif$/',$path)) {
123 $mimetype='image/gif';
124 } else if(preg_match('/\.jpe?g$/',$path)) {
125 $mimetype='image/jpeg';
126 } else {
127 // Note that this is a security feature as well as a lack of mime type
128 // support :) Means this can't accidentally serve files from places it
129 // shouldn't. Without it, you can actually access any file inside the
130 // module code directory.
131 error('Request for non-image file');
134 // Find actual location of image as $file
135 $file=false;
136 if(file_exists($possibility="$dirroot/theme/$theme/pix/$path")) {
137 // Found the file in theme, stop looking
138 $file=$possibility;
139 } else {
140 // Is there a parent theme?
141 while(true) {
142 require("$dirroot/theme/$theme/config.php"); // Sets up $THEME
143 if(!$THEME->parent) {
144 break;
146 $theme=$THEME->parent;
147 if(file_exists($possibility="$dirroot/theme/$theme/pix/$path")) {
148 $file=$possibility;
149 // Found in parent theme
150 break;
153 if(!$file) {
154 if(preg_match('|^mod/|',$path)) {
155 if(!file_exists($possibility="$dirroot/$path")) {
156 error('Requested image not found.',true);
158 } else {
159 if(!file_exists($possibility="$dirroot/pix/$path")) {
160 error('Requested image not found.',true);
163 $file=$possibility;
167 // Now we have a file that exists. Not using send_file since it requires
168 // proper $CFG etc.
170 // Handle If-Modified-Since
171 $filedate=filemtime($file);
172 $ifmodifiedsince=isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
173 if($ifmodifiedsince && strtotime($ifmodifiedsince)>=$filedate) {
174 header('HTTP/1.0 304 Not Modified');
175 exit;
177 header('Last-Modified: '.gmdate('D, d M Y H:i:s',$filedate).' GMT');
179 // As I'm not loading config table from DB, this is hardcoded here; expiry
180 // 4 hours, unless the hacky file reduceimagecache.dat exists in dataroot
181 if(file_exists($reducefile=$dataroot.'/reduceimagecache.dat')) {
182 $lifetime=file_read_contents($reducefile);
183 } else {
184 $lifetime=4*60*60;
187 // Send expire headers
188 header('Cache-Control: max-age='.$lifetime);
189 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
191 // Type
192 header('Content-Type: '.$mimetype);
193 header('Content-Length: '.filesize($file));
195 // Output file
196 $handle=fopen($file,'r');
197 fpassthru($handle);
198 fclose($handle);
200 // Slower Moodle-style version follows:
202 //// Outputs pictures from theme or core pix folder. Only used if $CFG->smartpix is
203 //// turned on.
205 //$nomoodlecookie = true; // Stops it making a session
206 //require_once('../config.php');
207 //require_once('../lib/filelib.php');
208 //global $CFG;
210 //$matches=array(); // Reusable array variable for preg_match
212 //// Split path - starts with theme name, then actual image path inside pix
213 //$path=get_file_argument('smartpix.php');
214 //$match=array();
215 //if(!preg_match('|^/([a-z0-9_\-.]+)/([a-z0-9/_\-.]+)$|',$path,$match)) {
216 // error('Unexpected request format');
218 //list($junk,$theme,$path)=$match;
220 //// Check file type - this is not needed for the MIME types as we could
221 //// get those by the existing function, but it provides an extra layer of security
222 //// as otherwise this script could be used to view all files within dirroot/mod
223 //if(preg_match('/\.png$/',$path)) {
224 // $mimetype='image/png';
225 //} else if(preg_match('/\.gif$/',$path)) {
226 // $mimetype='image/gif';
227 //} else if(preg_match('/\.jpe?g$/',$path)) {
228 // $mimetype='image/jpeg';
229 //} else {
230 // error('Request for non-image file');
233 //// Find actual location of image as $file
234 //$file=false;
235 //if(file_exists($possibility="$CFG->dirroot/theme/$theme/pix/$path")) {
236 // // Found the file in theme, stop looking
237 // $file=$possibility;
238 //} else {
239 // // Is there a parent theme?
240 // while(true) {
241 // require("$CFG->dirroot/theme/$theme/config.php"); // Sets up $THEME
242 // if(!$THEME->parent) {
243 // break;
244 // }
245 // $theme=$THEME->parent;
246 // if(file_exists($possibility="$CFG->dirroot/theme/$theme/pix/$path")) {
247 // $file=$possibility;
248 // // Found in parent theme
249 // break;
250 // }
251 // }
252 // if(!$file) {
253 // if(preg_match('|^mod/|',$path)) {
254 // if(!file_exists($possibility="$CFG->dirroot/$path")) {
255 // error('Requested image not found.');
256 // }
257 // } else {
258 // if(!file_exists($possibility="$CFG->dirroot/pix/$path")) {
259 // error('Requested image not found.');
260 // }
261 // }
262 // $file=$possibility;
263 // }
266 //// Handle If-Modified-Since because send_file doesn't
267 //$filedate=filemtime($file);
268 //$ifmodifiedsince=isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
269 //if($ifmodifiedsince && strtotime($ifmodifiedsince)>=$filedate) {
270 // header('HTTP/1.0 304 Not Modified');
271 // exit;
273 //// Don't need to set last-modified, send_file does that
275 //if (empty($CFG->filelifetime)) {
276 // $lifetime = 86400; // Seconds for files to remain in caches
277 //} else {
278 // $lifetime = $CFG->filelifetime;
280 //send_file($file,preg_replace('|^.*/|','',$file),$lifetime);