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 $local = getCacheName(strtolower($url),".media.$ext"); 208 $mtime = @filemtime($local); // 0 if not exists 209 210 //decide if download needed: 211 if( $cache == 0 || // never cache 212 ($mtime != 0 && $cache != -1) || // exists but no endless cache 213 ($mtime == 0) || // not exists 214 ($cache != -1 && $mtime < time()-$cache) // expired 215 ){ 216 if(io_download($url,$local)){ 217 return $local; 218 }else{ 219 return false; 220 } 221 } 222 223 //if cache exists use it else 224 if($mtime) return $local; 225 226 //else return false 227 return false; 228} 229 230/** 231 * resize images using external ImageMagick convert program 232 * 233 * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 234 * @author Andreas Gohr <andi@splitbrain.org> 235 */ 236function resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 237 global $conf; 238 239 // check if convert is configured 240 if(!$conf['im_convert']) return false; 241 242 // prepare command 243 $cmd = $conf['im_convert']; 244 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 245 $cmd .= " $from $to"; 246 247 @exec($cmd,$out,$retval); 248 if ($retval == 0) return true; 249 250 return false; 251} 252 253/** 254 * resize images using PHP's libGD support 255 * 256 * @author Andreas Gohr <andi@splitbrain.org> 257 */ 258function resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){ 259 global $conf; 260 261 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted 262 263 // check available memory 264 if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){ 265 return false; 266 } 267 268 // create an image of the given filetype 269 if ($ext == 'jpg' || $ext == 'jpeg'){ 270 if(!function_exists("imagecreatefromjpeg")) return false; 271 $image = @imagecreatefromjpeg($from); 272 }elseif($ext == 'png') { 273 if(!function_exists("imagecreatefrompng")) return false; 274 $image = @imagecreatefrompng($from); 275 276 }elseif($ext == 'gif') { 277 if(!function_exists("imagecreatefromgif")) return false; 278 $image = @imagecreatefromgif($from); 279 } 280 if(!$image) return false; 281 282 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor")){ 283 $newimg = @imagecreatetruecolor ($to_w, $to_h); 284 } 285 if(!$newimg) $newimg = @imagecreate($to_w, $to_h); 286 if(!$newimg){ 287 imagedestroy($image); 288 return false; 289 } 290 291 //keep png alpha channel if possible 292 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){ 293 imagealphablending($newimg, false); 294 imagesavealpha($newimg,true); 295 } 296 297 //try resampling first 298 if(function_exists("imagecopyresampled")){ 299 if(!@imagecopyresampled($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h)) { 300 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 301 } 302 }else{ 303 imagecopyresized($newimg, $image, 0, 0, 0, 0, $to_w, $to_h, $from_w, $from_h); 304 } 305 306 $okay = false; 307 if ($ext == 'jpg' || $ext == 'jpeg'){ 308 if(!function_exists('imagejpeg')){ 309 $okay = false; 310 }else{ 311 $okay = imagejpeg($newimg, $to, 70); 312 } 313 }elseif($ext == 'png') { 314 if(!function_exists('imagepng')){ 315 $okay = false; 316 }else{ 317 $okay = imagepng($newimg, $to); 318 } 319 }elseif($ext == 'gif') { 320 if(!function_exists('imagegif')){ 321 $okay = false; 322 }else{ 323 $okay = imagegif($newimg, $to); 324 } 325 } 326 327 // destroy GD image ressources 328 if($image) imagedestroy($image); 329 if($newimg) imagedestroy($newimg); 330 331 return $okay; 332} 333 334/** 335 * Checks if the given amount of memory is available 336 * 337 * If the memory_get_usage() function is not available the 338 * function just assumes $used bytes of already allocated memory 339 * 340 * @param int $mem Size of memory you want to allocate in bytes 341 * @param int $used already allocated memory (see above) 342 * @author Filip Oscadal <webmaster@illusionsoftworks.cz> 343 * @author Andreas Gohr <andi@splitbrain.org> 344 */ 345function is_mem_available($mem,$bytes=1048576){ 346 $limit = trim(ini_get('memory_limit')); 347 if(empty($limit)) return true; // no limit set! 348 349 // parse limit to bytes 350 $unit = strtolower(substr($limit,-1)); 351 switch($unit){ 352 case 'g': 353 $limit = substr($limit,0,-1); 354 $limit *= 1024*1024*1024; 355 break; 356 case 'm': 357 $limit = substr($limit,0,-1); 358 $limit *= 1024*1024; 359 break; 360 case 'k': 361 $limit = substr($limit,0,-1); 362 $limit *= 1024; 363 break; 364 } 365 366 // get used memory if possible 367 if(function_exists('memory_get_usage')){ 368 $used = memory_get_usage(); 369 } 370 371 372 if($used+$mem > $limit){ 373 return false; 374 } 375 376 return true; 377} 378 379//Setup VIM: ex: et ts=2 enc=utf-8 : 380?> 381