. require_once("../project/cache_parameters.inc"); // If we can't see request headers, don't do caching // $no_cache = false; if (!function_exists("apache_request_headers")) { $no_cache = true; } // mechanism for caching commonly-accessed pages function make_cache_dirs() { if (!@filemtime("../cache")) { mkdir("../cache", 0770); chmod("../cache", 0770); } for ($i=0;$i<256;$i++) { $j=sprintf("%02x",$i); if (!@filemtime("../cache/$j")) { mkdir("../cache/$j", 0770); chmod("../cache/$j", 0770); } } } function get_path($params, $phpfile=null) { if (!@filemtime("../cache/00")) make_cache_dirs(); if ($phpfile) { $z = $phpfile; } else { $y = pathinfo($_SERVER["PHP_SELF"]); $z = $y["basename"]; } // add a layer of subdirectories for reducing file lookup time $sz = substr(md5($z."_".urlencode($params)),1,2); $path = "../cache/".$sz."/".$z; if ($params) { $path = $path."_".urlencode($params); } return $path; } function disk_usage($dir) { $usage=0; if ($handle=@opendir($dir)) { while ($file=readdir($handle)) { if (($file != ".") && ($file != "..")) { if (@is_dir($dir."/".$file)) { $usage+=disk_usage($dir."/".$file); } else { $usage+=@filesize($dir."/".$file); } } } @closedir($handle); } return $usage; } function clean_cache($max_age, $dir) { $start_dir = getcwd(); if (!chdir($dir)) { return; } if ($handle=@opendir(".")) { while ($file=readdir($handle)) { if ($file == ".") continue; if ($file == "..") continue; // don't let hackers trick us into deleting other files! if (strstr($file, "..")) { continue; } if (@is_dir($file)) { clean_cache($max_age, $file); } else { if ((time()-@filemtime($file))>$max_age) { //echo "unlinking ".getcwd()."/$file\n"; @unlink($file); } } } @closedir($handle); } chdir($start_dir); } // check free disk space every once in a while function cache_check_diskspace(){ if (!(rand() % CACHE_SIZE_CHECK_FREQ)) { set_time_limit(0); // this may take a while $max_age = 86400; while ((disk_free_space("../cache") < MIN_FREE_SPACE) || (disk_usage("../cache") > MAX_CACHE_USAGE) ) { clean_cache($max_age, "../cache"); $max_age/=2; } } } function cache_need_to_regenerate($path, $max_age){ $regenerate = false; $request = apache_request_headers(); clearstatcache(); $lastmodified = @filemtime($path); if ($lastmodified) { // Check to see if this is a conditional fetch. // $if_modified_since = isset($request['If-Modified-Since']) ? (explode(';',$request['If-Modified-Since'])) : false; if ($if_modified_since) { $if_modified_since=strtotime($if_modified_since[0]); } if ($if_modified_since && ($if_modified_since == $lastmodified)) { Header("Last-Modified: " . gmdate("D, d M Y H:i:s",$lastmodified) . " GMT"); Header('HTTP/1.0 304 Not Modified'); exit; } // See if cached copy is too old. // If so regenerate, // and touch the cached copy so other processes // don't regenerate at the same time // if ($lastmodifiedget($path); if ($cache) { return $cache->content; } } else { cache_check_diskspace(); $regenerate=cache_need_to_regenerate($path, $max_age); if (!$regenerate) { return file_get_contents($path); } } } return false; //No data was cached, just return } function start_cache($max_age, $params=""){ global $no_cache, $caching, $memcache; if ($no_cache) return; $caching = true; if ($max_age) { $path = get_path($params); if (defined('MEMCACHE_SERVERS')) { $cache = BoincMemcache::get()->get($path); if ($cache) { $regenerate = false; $lastmodified = abs($cache->timestamp); } else { $regenerate = true; } } else { $lastmodified = @filemtime($path); cache_check_diskspace(); //Check free disk space once in a while $regenerate = cache_need_to_regenerate($path, $max_age); } //Is the stored version too old, do we need to regenerate it? if ($regenerate){ // If cached version is too old (or non-existent) // generate the page and write to cache // ob_start(); ob_implicit_flush(0); Header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); Header("Expires: " . gmdate("D, d M Y H:i:s",time()+$max_age) . " GMT"); Header("Cache-Control: public, max-age=" . $max_age); // allow the calling page to see cache period // global $cached_max_age; $cached_max_age = $max_age; } else { // Otherwise serve the cached version and exit // if (strstr($params, "format=xml")) { header('Content-type: text/xml'); } Header("Last-Modified: " . gmdate("D, d M Y H:i:s",$lastmodified) . " GMT"); Header("Expires: " . gmdate("D, d M Y H:i:s",$lastmodified+$max_age) . " GMT"); Header("Cache-Control: public, max-age=" . $max_age ); if ($cache->content) { echo $cache->content; exit; } if (!@readfile($path)) { //echo "can't read $path; lastmod $lastmodified\n"; @unlink($path); //Proceed to regenerate content } else { exit; } } } } // write output buffer both to client and to cache // function end_cache($max_age,$params=""){ global $no_cache; if ($no_cache) return; // for the benefit of hackers if (strstr($params, "..")) { return; } if ($max_age) { $path = get_path($params); if (defined('MEMCACHE_SERVERS')) { $cache = array('content' => ob_get_contents(), 'timestamp' => time()); ob_end_flush(); $cache = BoincMemcache::get()->set($path, $cache, $max_age); } else { $fhandle=fopen($path, "w"); $page=ob_get_contents(); ob_end_flush(); fwrite($fhandle, $page); fclose($fhandle); } } } function set_cached_data($max_age, $data, $params=""){ // for the benefit of hackers if (strstr($params, "..")) { return; } $path = get_path($params); if (defined('MEMCACHE_SERVERS')) { $cache = array('content' => $data, 'timestamp' => time()); ob_end_flush(); $cache = BoincMemcache::get()->set($path, $cache, $max_age); } else { $fhandle = fopen($path, "w"); fwrite($fhandle, $data); fclose($fhandle); } } function clear_cache_entry($phpfile, $params) { if (strstr($phpfile, "..")) { return; } if (strstr($params, "..")) { return; } $path = get_path($params, $phpfile); unlink($path); } // Memcached class class BoincMemcache { static $instance; static function get() { self::$instance = new Memcached; if (defined('MEMCACHE_PREFIX')) { self::$instance->setOption(Memcached::OPT_PREFIX_KEY, MEMCACHE_PREFIX); } $servers = explode('|', MEMCACHE_SERVERS); foreach($servers as &$server) { list($ip, $port) = explode(':', $server); if (!$port) { $port = 11211; } $server = array($ip, $port); } self::$instance->addServers($servers); return self::$instance; } } ?>