1<?php 2/** 3 * DokuWiki media passthrough file 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10 require_once(DOKU_INC.'inc/init.php'); 11 require_once(DOKU_INC.'inc/common.php'); 12 require_once(DOKU_INC.'inc/pageutils.php'); 13 require_once(DOKU_INC.'inc/confutils.php'); 14 require_once(DOKU_INC.'inc/auth.php'); 15 //close sesseion 16 session_write_close(); 17 if(!defined('CHUNK_SIZE')) define('CHUNK_SIZE',16*1024); 18 19 $mimetypes = getMimeTypes(); 20 21 //get input 22 $MEDIA = getID('media',false); // no cleaning - maybe external 23 $CACHE = calc_cache($_REQUEST['cache']); 24 $WIDTH = $_REQUEST['w']; 25 $HEIGHT = $_REQUEST['h']; 26 list($EXT,$MIME) = mimetype($MEDIA); 27 if($EXT === false){ 28 $EXT = 'unknown'; 29 $MIME = 'application/octet-stream'; 30 } 31 32 //media to local file 33 if(preg_match('#^(https?|ftp)://#i',$MEDIA)){ 34 //handle external media 35 $FILE = get_from_URL($MEDIA,$EXT,$CACHE); 36 if(!$FILE){ 37 //download failed - redirect to original URL 38 header('Location: '.$MEDIA); 39 exit; 40 } 41 }else{ 42 $MEDIA = cleanID($MEDIA); 43 if(empty($MEDIA)){ 44 header("HTTP/1.0 400 Bad Request"); 45 print 'Bad request'; 46 exit; 47 } 48 49 //check permissions (namespace only) 50 if(auth_quickaclcheck(getNS($MEDIA).':X') < AUTH_READ){ 51 header("HTTP/1.0 401 Unauthorized"); 52 //fixme add some image for imagefiles 53 print 'Unauthorized'; 54 exit; 55 } 56 $FILE = mediaFN($MEDIA); 57 } 58 59 //check file existance 60 if(!@file_exists($FILE)){ 61 header("HTTP/1.0 404 Not Found"); 62 //FIXME add some default broken image 63 print 'Not Found'; 64 exit; 65 } 66 67 //handle image resizing 68 if((substr($MIME,0,5) == 'image') && $WIDTH){ 69 $FILE = get_resized($FILE,$EXT,$WIDTH,$HEIGHT); 70 } 71 72 // finally send the file to the client 73 sendFile($FILE,$MIME); 74 75/* ------------------------------------------------------------------------ */ 76 77/** 78 * Set headers and send the file to the client 79 * 80 * @author Andreas Gohr <andi@splitbrain.org> 81 */ 82function sendFile($file,$mime){ 83 // send headers 84 header("Content-Type: $mime"); 85 http_conditionalRequest(filemtime($file)); 86 list($start,$len) = http_rangeRequest(filesize($file)); 87 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 88 header('Pragma: public'); 89 header('Accept-Ranges: bytes'); 90 91 //application mime type is downloadable 92 if(substr($mime,0,11) == 'application'){ 93 header('Content-Disposition: attachment; filename="'.basename($file).'";'); 94 } 95 96 // send file contents 97 $fp = @fopen($file,"rb"); 98 if($fp){ 99 fseek($fp,$start); //seek to start of range 100 101 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 102 while (!feof($fp) && $chunk > 0) { 103 @set_time_limit(); // large files can take a lot of time 104 print fread($fp, $chunk); 105 flush(); 106 $len -= $chunk; 107 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 108 } 109 fclose($fp); 110 }else{ 111 header("HTTP/1.0 500 Internal Server Error"); 112 print "Could not read $file - bad permissions?"; 113 } 114} 115 116/** 117 * Checks and sets headers to handle range requets 118 * 119 * @author Andreas Gohr <andi@splitbrain.org> 120 * @returns array The start byte and the amount of bytes to send 121 */ 122function http_rangeRequest($size){ 123 if(!isset($_SERVER['HTTP_RANGE'])){ 124 // no range requested - send the whole file 125 header("Content-Length: $size"); 126 return array(0,$size); 127 } 128 129 $t = explode('=', $_SERVER['HTTP_RANGE']); 130 if (!$t[0]=='bytes') { 131 // we only understand byte ranges - send the whole file 132 header("Content-Length: $size"); 133 return array(0,$size); 134 } 135 136 $r = explode('-', $t[1]); 137 $start = (int)$r[0]; 138 $end = (int)$r[1]; 139 if (!$end) $end = $size - 1; 140 if ($start > $end || $start > $size || $end > $size){ 141 header('HTTP/1.1 416 Requested Range Not Satisfiable'); 142 print 'Bad Range Request!'; 143 exit; 144 } 145 146 $tot = $end - $start + 1; 147 header('HTTP/1.1 206 Partial Content'); 148 header("Content-Range: bytes {$start}-{$end}/{$size}"); 149 header("Content-Length: $tot"); 150 151 return array($start,$tot); 152} 153 154/** 155 * Resizes the given image to the given size 156 * 157 * @author Andreas Gohr <andi@splitbrain.org> 158 */ 159function get_resized($file, $ext, $w, $h=0){ 160 global $conf; 161 162 $info = getimagesize($file); 163 if(!$h) $h = round(($w * $info[1]) / $info[0]); 164 165 166 //cache 167 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext); 168 $mtime = @filemtime($local); // 0 if not exists 169 170 if( $mtime > filemtime($file) || 171 resize_imageIM($ext,$file,$info[0],$info[1],$local,$w,$h) || 172 resize_imageGD($ext,$file,$info[0],$info[1],$local,$w,$h) ){ 173 return $local; 174 } 175 //still here? resizing failed 176 return $file; 177} 178 179/** 180 * Returns the wanted cachetime in seconds 181 * 182 * Resolves named constants 183 * 184 * @author Andreas Gohr <andi@splitbrain.org> 185 */ 186function calc_cache($cache){ 187 global $conf; 188 189 if(strtolower($cache) == 'nocache') return 0; //never cache 190 if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache 191 return -1; //cache endless 192} 193 194/** 195 * Download a remote file and return local filename 196 * 197 * returns false if download fails. Uses cached file if available and 198 * wanted 199 * 200 * @author Andreas Gohr <andi@splitbrain.org> 201 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 202 */ 203function get_from_URL($url,$ext,$cache){ 204 global $conf; 205 206 $local = getCacheName(strtolower($url),".media.$ext"); 207 $mtime = @filemtime($local); // 0 if not exists 208 209 //decide if download needed: 210 if( $cache == 0 || // never cache 211 ($mtime != 0 && $cache != -1) || // exists but no endless cache 212 ($mtime == 0) || // not exists 213 ($cache != -1 && $mtime < time()-$cache) // expired 214 ){ 215 if(io_download($url,$local)){ 216 return $local; 217 }else{ 218 return false; 219 } 220 } 221 222 //if cache exists use it else 223 if($mtime) return $local; 224 225 //else return false 226 return false; 227} 228 229/** 230 * resize images using external ImageMagick convert program 231 * 232 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 233 * @author Andreas Gohr <andi@splitbrain.org> 234 */ 235function resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 236 global $conf; 237 238 // check if convert is configured 239 if(!$conf['im_convert']) return false; 240 241 // prepare command 242 $cmd = $conf['im_convert']; 243 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 244 $cmd .= " $from $to"; 245 246 @exec($cmd,$out,$retval); 247 if ($retval == 0) return true; 248 249 return false; 250} 251 252/** 253 * resize images using PHP's libGD support 254 * 255 * @author Andreas Gohr <andi@splitbrain.org> 256 */ 257function resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 258 global $conf; 259 260 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted 261 262 // check available memory 263 if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){ 264 return false; 265 } 266 267 // create an image of the given filetype 268 if ($ext == 'jpg' || $ext == 'jpeg'){ 269 if(!function_exists("imagecreatefromjpeg")) return false; 270 $image = @imagecreatefromjpeg($from); 271 }elseif($ext == 'png') { 272 if(!function_exists("imagecreatefrompng")) return false; 273 $image = @imagecreatefrompng($from); 274 275 }elseif($ext == 'gif') { 276 if(!function_exists("imagecreatefromgif")) return false; 277 $image = @imagecreatefromgif($from); 278 } 279 if(!$image) return false; 280 281 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor")){ 282 $newimg = @imagecreatetruecolor ($to_w, $to_h); 283 } 284 if(!$newimg) $newimg = @imagecreate($to_w, $to_h); 285 if(!$newimg){ 286 imagedestroy($image); 287 return false; 288 } 289 290 //keep png alpha channel if possible 291 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){ 292 imagealphablending($newimg, false); 293 imagesavealpha($newimg,true); 294 } 295 296 //try resampling first 297 if(function_exists("imagecopyresampled")){ 298 if(!@imagecopyresampled($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h)) { 299 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 300 } 301 }else{ 302 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 303 } 304 305 $okay = false; 306 if ($ext == 'jpg' || $ext == 'jpeg'){ 307 if(!function_exists('imagejpeg')){ 308 $okay = false; 309 }else{ 310 $okay = imagejpeg($newimg, $to, 70); 311 } 312 }elseif($ext == 'png') { 313 if(!function_exists('imagepng')){ 314 $okay = false; 315 }else{ 316 $okay = imagepng($newimg, $to); 317 } 318 }elseif($ext == 'gif') { 319 if(!function_exists('imagegif')){ 320 $okay = false; 321 }else{ 322 $okay = imagegif($newimg, $to); 323 } 324 } 325 326 // destroy GD image ressources 327 if($image) imagedestroy($image); 328 if($newimg) imagedestroy($newimg); 329 330 return $okay; 331} 332 333/** 334 * Checks if the given amount of memory is available 335 * 336 * If the memory_get_usage() function is not available the 337 * function just assumes $used bytes of already allocated memory 338 * 339 * @param int $mem Size of memory you want to allocate in bytes 340 * @param int $used already allocated memory (see above) 341 * @author Filip Oscadal <webmaster@illusionsoftworks.cz> 342 * @author Andreas Gohr <andi@splitbrain.org> 343 */ 344function is_mem_available($mem,$bytes=1048576){ 345 $limit = trim(ini_get('memory_limit')); 346 if(empty($limit)) return true; // no limit set! 347 348 // parse limit to bytes 349 $unit = strtolower(substr($limit,-1)); 350 switch($unit){ 351 case 'g': 352 $limit = substr($limit,0,-1); 353 $limit *= 1024*1024*1024; 354 break; 355 case 'm': 356 $limit = substr($limit,0,-1); 357 $limit *= 1024*1024; 358 break; 359 case 'k': 360 $limit = substr($limit,0,-1); 361 $limit *= 1024; 362 break; 363 } 364 365 // get used memory if possible 366 if(function_exists('memory_get_usage')){ 367 $used = memory_get_usage(); 368 } 369 370 371 if($used+$mem > $limit){ 372 return false; 373 } 374 375 return true; 376} 377 378//Setup VIM: ex: et ts=2 enc=utf-8 : 379?> 380