--- /dev/null
+<?php
+// Outputs pictures from theme or core pix folder. Only used if $CFG->smartpix is
+// turned on.
+
+$matches=array(); // Reusable array variable for preg_match
+
+// This does NOT use config.php. This is because doing that makes database requests
+// which cause this to take longer (I benchmarked this at 16ms, 256ms with config.php)
+// A version using normal Moodle functions is included in comment at end in case we
+// want to switch to it in future.
+
+function error($text,$notfound=false) {
+ header($notfound ? 'HTTP/1.0 404 Not Found' : 'HTTP/1.0 500 Internal Server Error');
+ header('Content-Type: text/plain');
+ print $text;
+ exit;
+}
+
+// Nicked from moodlelib clean_param
+function makesafe($param) {
+ $param = str_replace('\\\'', '\'', $param);
+ $param = str_replace('\\"', '"', $param);
+ $param = str_replace('\\', '/', $param);
+ $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
+ $param = ereg_replace('\.\.+', '', $param);
+ $param = ereg_replace('//+', '/', $param);
+ return ereg_replace('/(\./)+', '/', $param);
+}
+
+// Nicked from weblib
+/**
+ * Remove query string from url
+ *
+ * Takes in a URL and returns it without the querystring portion
+ *
+ * @param string $url the url which may have a query string attached
+ * @return string
+ */
+ function strip_querystring($url) {
+
+ if ($commapos = strpos($url, '?')) {
+ return substr($url, 0, $commapos);
+ } else {
+ return $url;
+ }
+}
+
+// Nicked from weblib then cutdown
+/**
+ * Extracts file argument either from file parameter or PATH_INFO.
+ * @param string $scriptname name of the calling script
+ * @return string file path (only safe characters)
+ */
+function get_file_argument_limited($scriptname) {
+ $relativepath = FALSE;
+
+ // first try normal parameter (compatible method == no relative links!)
+ if(isset($_GET['file'])) {
+ return makesafe($_GET['file']);
+ }
+
+ // then try extract file from PATH_INFO (slasharguments method)
+ if (!empty($_SERVER['PATH_INFO'])) {
+ $path_info = $_SERVER['PATH_INFO'];
+ // check that PATH_INFO works == must not contain the script name
+ if (!strpos($path_info, $scriptname)) {
+ return makesafe(rawurldecode($path_info));
+ }
+ }
+
+ // now if both fail try the old way
+ // (for compatibility with misconfigured or older buggy php implementations)
+ $arr = explode($scriptname, me());
+ if (!empty($arr[1])) {
+ return makesafe(rawurldecode(strip_querystring($arr[1])));
+ }
+
+ error('Unexpected PHP set up. Turn off the smartpix config option.');
+}
+
+// We do need to get dirroot from config.php
+if(!$config=@file_get_contents(dirname(__FILE__).'/../config.php')) {
+ error("Can't open config.php");
+}
+$configlines=preg_split('/[\r\n]+/',$config);
+foreach($configlines as $configline) {
+ if(preg_match('/^\s?\$CFG->dirroot\s*=\s*[\'"](.*?)[\'"]\s*;/',$configline,$matches)) {
+ $dirroot=$matches[1];
+ }
+ if(preg_match('/^\s?\$CFG->dataroot\s*=\s*[\'"](.*?)[\'"]\s*;/',$configline,$matches)) {
+ $dataroot=$matches[1];
+ }
+ if(isset($dirroot) && isset($dataroot)) {
+ break;
+ }
+}
+if(!(isset($dirroot) && isset($dataroot))) {
+ error('No line in config.php like $CFG->dirroot=\'/somewhere/whatever\';');
+}
+
+// Split path - starts with theme name, then actual image path inside pix
+$path=get_file_argument_limited('smartpix.php');
+$match=array();
+if(!preg_match('|^/([a-z0-9_\-.]+)/([a-z0-9/_\-.]+)$|',$path,$match)) {
+ error('Unexpected request format');
+}
+list($junk,$theme,$path)=$match;
+
+// Check file type
+if(preg_match('/\.png$/',$path)) {
+ $mimetype='image/png';
+} else if(preg_match('/\.gif$/',$path)) {
+ $mimetype='image/gif';
+} else if(preg_match('/\.jpe?g$/',$path)) {
+ $mimetype='image/jpeg';
+} else {
+ // Note that this is a security feature as well as a lack of mime type
+ // support :) Means this can't accidentally serve files from places it
+ // shouldn't. Without it, you can actually access any file inside the
+ // module code directory.
+ error('Request for non-image file');
+}
+
+// Find actual location of image as $file
+$file=false;
+if(file_exists($possibility="$dirroot/theme/$theme/pix/$path")) {
+ // Found the file in theme, stop looking
+ $file=$possibility;
+} else {
+ // Is there a parent theme?
+ while(true) {
+ require("$dirroot/theme/$theme/config.php"); // Sets up $THEME
+ if(!$THEME->parent) {
+ break;
+ }
+ $theme=$THEME->parent;
+ if(file_exists($possibility="$dirroot/theme/$theme/pix/$path")) {
+ $file=$possibility;
+ // Found in parent theme
+ break;
+ }
+ }
+ if(!$file) {
+ if(preg_match('|^mod/|',$path)) {
+ if(!file_exists($possibility="$dirroot/$path")) {
+ error('Requested image not found.',true);
+ }
+ } else {
+ if(!file_exists($possibility="$dirroot/pix/$path")) {
+ error('Requested image not found.',true);
+ }
+ }
+ $file=$possibility;
+ }
+}
+
+// Now we have a file that exists. Not using send_file since it requires
+// proper $CFG etc.
+
+// Handle If-Modified-Since
+$filedate=filemtime($file);
+$ifmodifiedsince=isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
+if($ifmodifiedsince && strtotime($ifmodifiedsince)>=$filedate) {
+ header('HTTP/1.0 304 Not Modified');
+ exit;
+}
+header('Last-Modified: '.gmdate('D, d M Y H:i:s',$filedate).' GMT');
+
+// As I'm not loading config table from DB, this is hardcoded here; expiry
+// 4 hours, unless the hacky file reduceimagecache.dat exists in dataroot
+if(file_exists($reducefile=$dataroot.'/reduceimagecache.dat')) {
+ $lifetime=file_read_contents($reducefile);
+} else {
+ $lifetime=4*60*60;
+}
+
+// Send expire headers
+header('Cache-Control: max-age='.$lifetime);
+header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+
+// Type
+header('Content-Type: '.$mimetype);
+header('Content-Length: '.filesize($file));
+
+// Output file
+$handle=fopen($file,'r');
+fpassthru($handle);
+fclose($handle);
+
+// Slower Moodle-style version follows:
+
+//// Outputs pictures from theme or core pix folder. Only used if $CFG->smartpix is
+//// turned on.
+//
+//$nomoodlecookie = true; // Stops it making a session
+//require_once('../config.php');
+//require_once('../lib/filelib.php');
+//global $CFG;
+//
+//$matches=array(); // Reusable array variable for preg_match
+//
+//// Split path - starts with theme name, then actual image path inside pix
+//$path=get_file_argument('smartpix.php');
+//$match=array();
+//if(!preg_match('|^/([a-z0-9_\-.]+)/([a-z0-9/_\-.]+)$|',$path,$match)) {
+// error('Unexpected request format');
+//}
+//list($junk,$theme,$path)=$match;
+//
+//// Check file type - this is not needed for the MIME types as we could
+//// get those by the existing function, but it provides an extra layer of security
+//// as otherwise this script could be used to view all files within dirroot/mod
+//if(preg_match('/\.png$/',$path)) {
+// $mimetype='image/png';
+//} else if(preg_match('/\.gif$/',$path)) {
+// $mimetype='image/gif';
+//} else if(preg_match('/\.jpe?g$/',$path)) {
+// $mimetype='image/jpeg';
+//} else {
+// error('Request for non-image file');
+//}
+//
+//// Find actual location of image as $file
+//$file=false;
+//if(file_exists($possibility="$CFG->dirroot/theme/$theme/pix/$path")) {
+// // Found the file in theme, stop looking
+// $file=$possibility;
+//} else {
+// // Is there a parent theme?
+// while(true) {
+// require("$CFG->dirroot/theme/$theme/config.php"); // Sets up $THEME
+// if(!$THEME->parent) {
+// break;
+// }
+// $theme=$THEME->parent;
+// if(file_exists($possibility="$CFG->dirroot/theme/$theme/pix/$path")) {
+// $file=$possibility;
+// // Found in parent theme
+// break;
+// }
+// }
+// if(!$file) {
+// if(preg_match('|^mod/|',$path)) {
+// if(!file_exists($possibility="$CFG->dirroot/$path")) {
+// error('Requested image not found.');
+// }
+// } else {
+// if(!file_exists($possibility="$CFG->dirroot/pix/$path")) {
+// error('Requested image not found.');
+// }
+// }
+// $file=$possibility;
+// }
+//}
+//
+//// Handle If-Modified-Since because send_file doesn't
+//$filedate=filemtime($file);
+//$ifmodifiedsince=isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
+//if($ifmodifiedsince && strtotime($ifmodifiedsince)>=$filedate) {
+// header('HTTP/1.0 304 Not Modified');
+// exit;
+//}
+//// Don't need to set last-modified, send_file does that
+//
+//if (empty($CFG->filelifetime)) {
+// $lifetime = 86400; // Seconds for files to remain in caches
+//} else {
+// $lifetime = $CFG->filelifetime;
+//}
+//send_file($file,preg_replace('|^.*/|','',$file),$lifetime);
+?>