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 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 86 header('Pragma: public'); 87 header('Accept-Ranges: bytes'); 88 //send important headers first, script stops here if '304 Not Modified' response 89 http_conditionalRequest(filemtime($file)); 90 list($start,$len) = http_rangeRequest(filesize($file)); 91 92 //application mime type is downloadable 93 if(substr($mime,0,11) == 'application'){ 94 header('Content-Disposition: attachment; filename="'.basename($file).'";'); 95 } 96 97 // send file contents 98 $fp = @fopen($file,"rb"); 99 if($fp){ 100 fseek($fp,$start); //seek to start of range 101 102 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 103 while (!feof($fp) && $chunk > 0) { 104 @set_time_limit(); // large files can take a lot of time 105 print fread($fp, $chunk); 106 flush(); 107 $len -= $chunk; 108 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 109 } 110 fclose($fp); 111 }else{ 112 header("HTTP/1.0 500 Internal Server Error"); 113 print "Could not read $file - bad permissions?"; 114 } 115} 116 117/** 118 * Checks and sets headers to handle range requets 119 * 120 * @author Andreas Gohr <andi@splitbrain.org> 121 * @returns array The start byte and the amount of bytes to send 122 */ 123function http_rangeRequest($size){ 124 if(!isset($_SERVER['HTTP_RANGE'])){ 125 // no range requested - send the whole file 126 header("Content-Length: $size"); 127 return array(0,$size); 128 } 129 130 $t = explode('=', $_SERVER['HTTP_RANGE']); 131 if (!$t[0]=='bytes') { 132 // we only understand byte ranges - send the whole file 133 header("Content-Length: $size"); 134 return array(0,$size); 135 } 136 137 $r = explode('-', $t[1]); 138 $start = (int)$r[0]; 139 $end = (int)$r[1]; 140 if (!$end) $end = $size - 1; 141 if ($start > $end || $start > $size || $end > $size){ 142 header('HTTP/1.1 416 Requested Range Not Satisfiable'); 143 print 'Bad Range Request!'; 144 exit; 145 } 146 147 $tot = $end - $start + 1; 148 header('HTTP/1.1 206 Partial Content'); 149 header("Content-Range: bytes {$start}-{$end}/{$size}"); 150 header("Content-Length: $tot"); 151 152 return array($start,$tot); 153} 154 155/** 156 * Resizes the given image to the given size 157 * 158 * @author Andreas Gohr <andi@splitbrain.org> 159 */ 160function get_resized($file, $ext, $w, $h=0){ 161 global $conf; 162 163 $info = getimagesize($file); 164 if(!$h) $h = round(($w * $info[1]) / $info[0]); 165 166 167 //cache 168 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext); 169 $mtime = @filemtime($local); // 0 if not exists 170 171 if( $mtime > filemtime($file) || 172 resize_imageIM($ext,$file,$info[0],$info[1],$local,$w,$h) || 173 resize_imageGD($ext,$file,$info[0],$info[1],$local,$w,$h) ){ 174 return $local; 175 } 176 //still here? resizing failed 177 return $file; 178} 179 180/** 181 * Returns the wanted cachetime in seconds 182 * 183 * Resolves named constants 184 * 185 * @author Andreas Gohr <andi@splitbrain.org> 186 */ 187function calc_cache($cache){ 188 global $conf; 189 190 if(strtolower($cache) == 'nocache') return 0; //never cache 191 if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache 192 return -1; //cache endless 193} 194 195/** 196 * Download a remote file and return local filename 197 * 198 * returns false if download fails. Uses cached file if available and 199 * wanted 200 * 201 * @author Andreas Gohr <andi@splitbrain.org> 202 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 203 */ 204function get_from_URL($url,$ext,$cache){ 205 global $conf; 206 207 // if 'nocache' just redirect 208 if ($cache==0) { return false; } 209 210 $local = getCacheName(strtolower($url),".media.$ext"); 211 $mtime = @filemtime($local); // 0 if not exists 212 213 //decide if download needed: 214 if( ($mtime == 0) || // cache does not exist 215 ($cache != -1 && $mtime < time()-$cache) // 'recache' and cache has expired 216 ){ 217 if(io_download($url,$local)){ 218 return $local; 219 }else{ 220 return false; 221 } 222 } 223 224 //if cache exists use it else 225 if($mtime) return $local; 226 227 //else return false 228 return false; 229} 230 231/** 232 * resize images using external ImageMagick convert program 233 * 234 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 235 * @author Andreas Gohr <andi@splitbrain.org> 236 */ 237function resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 238 global $conf; 239 240 // check if convert is configured 241 if(!$conf['im_convert']) return false; 242 243 // prepare command 244 $cmd = $conf['im_convert']; 245 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 246 $cmd .= " $from $to"; 247 248 @exec($cmd,$out,$retval); 249 if ($retval == 0) return true; 250 251 return false; 252} 253 254/** 255 * resize images using PHP's libGD support 256 * 257 * @author Andreas Gohr <andi@splitbrain.org> 258 */ 259function resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 260 global $conf; 261 262 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted 263 264 // check available memory 265 if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){ 266 return false; 267 } 268 269 // create an image of the given filetype 270 if ($ext == 'jpg' || $ext == 'jpeg'){ 271 if(!function_exists("imagecreatefromjpeg")) return false; 272 $image = @imagecreatefromjpeg($from); 273 }elseif($ext == 'png') { 274 if(!function_exists("imagecreatefrompng")) return false; 275 $image = @imagecreatefrompng($from); 276 277 }elseif($ext == 'gif') { 278 if(!function_exists("imagecreatefromgif")) return false; 279 $image = @imagecreatefromgif($from); 280 } 281 if(!$image) return false; 282 283 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor")){ 284 $newimg = @imagecreatetruecolor ($to_w, $to_h); 285 } 286 if(!$newimg) $newimg = @imagecreate($to_w, $to_h); 287 if(!$newimg){ 288 imagedestroy($image); 289 return false; 290 } 291 292 //keep png alpha channel if possible 293 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){ 294 imagealphablending($newimg, false); 295 imagesavealpha($newimg,true); 296 } 297 298 //try resampling first 299 if(function_exists("imagecopyresampled")){ 300 if(!@imagecopyresampled($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h)) { 301 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 302 } 303 }else{ 304 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 305 } 306 307 $okay = false; 308 if ($ext == 'jpg' || $ext == 'jpeg'){ 309 if(!function_exists('imagejpeg')){ 310 $okay = false; 311 }else{ 312 $okay = imagejpeg($newimg, $to, 70); 313 } 314 }elseif($ext == 'png') { 315 if(!function_exists('imagepng')){ 316 $okay = false; 317 }else{ 318 $okay = imagepng($newimg, $to); 319 } 320 }elseif($ext == 'gif') { 321 if(!function_exists('imagegif')){ 322 $okay = false; 323 }else{ 324 $okay = imagegif($newimg, $to); 325 } 326 } 327 328 // destroy GD image ressources 329 if($image) imagedestroy($image); 330 if($newimg) imagedestroy($newimg); 331 332 return $okay; 333} 334 335/** 336 * Checks if the given amount of memory is available 337 * 338 * If the memory_get_usage() function is not available the 339 * function just assumes $used bytes of already allocated memory 340 * 341 * @param int $mem Size of memory you want to allocate in bytes 342 * @param int $used already allocated memory (see above) 343 * @author Filip Oscadal <webmaster@illusionsoftworks.cz> 344 * @author Andreas Gohr <andi@splitbrain.org> 345 */ 346function is_mem_available($mem,$bytes=1048576){ 347 $limit = trim(ini_get('memory_limit')); 348 if(empty($limit)) return true; // no limit set! 349 350 // parse limit to bytes 351 $unit = strtolower(substr($limit,-1)); 352 switch($unit){ 353 case 'g': 354 $limit = substr($limit,0,-1); 355 $limit *= 1024*1024*1024; 356 break; 357 case 'm': 358 $limit = substr($limit,0,-1); 359 $limit *= 1024*1024; 360 break; 361 case 'k': 362 $limit = substr($limit,0,-1); 363 $limit *= 1024; 364 break; 365 } 366 367 // get used memory if possible 368 if(function_exists('memory_get_usage')){ 369 $used = memory_get_usage(); 370 } 371 372 373 if($used+$mem > $limit){ 374 return false; 375 } 376 377 return true; 378} 379 380//Setup VIM: ex: et ts=2 enc=utf-8 : 381?> 382