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',dirname(__FILE__).'/../../'); 10 define('DOKU_DISABLE_GZIP_OUTPUT', 1); 11 require_once(DOKU_INC.'inc/init.php'); 12 require_once(DOKU_INC.'inc/common.php'); 13 require_once(DOKU_INC.'inc/media.php'); 14 require_once(DOKU_INC.'inc/pageutils.php'); 15 require_once(DOKU_INC.'inc/confutils.php'); 16 require_once(DOKU_INC.'inc/auth.php'); 17 //close sesseion 18 session_write_close(); 19 if(!defined('CHUNK_SIZE')) define('CHUNK_SIZE',16*1024); 20 21 $mimetypes = getMimeTypes(); 22 23 //get input 24 $MEDIA = stripctl(getID('media',false)); // no cleaning except control chars - maybe external 25 $CACHE = calc_cache($_REQUEST['cache']); 26 $WIDTH = (int) $_REQUEST['w']; 27 $HEIGHT = (int) $_REQUEST['h']; 28 list($EXT,$MIME,$DL) = mimetype($MEDIA); 29 if($EXT === false){ 30 $EXT = 'unknown'; 31 $MIME = 'application/octet-stream'; 32 $DL = true; 33 } 34 35 //media to local file 36 if(preg_match('#^(https?)://#i',$MEDIA)){ 37 //handle external images 38 if(strncmp($MIME,'image/',6) == 0) $FILE = media_get_from_URL($MEDIA,$EXT,$CACHE); 39 if(!$FILE){ 40 //download failed - redirect to original URL 41 header('Location: '.$MEDIA); 42 exit; 43 } 44 }else{ 45 $MEDIA = cleanID($MEDIA); 46 if(empty($MEDIA)){ 47 header("HTTP/1.0 400 Bad Request"); 48 print 'Bad request'; 49 exit; 50 } 51 52 //check permissions (namespace only) 53 if(auth_quickaclcheck(getNS($MEDIA).':X') < AUTH_READ){ 54 header("HTTP/1.0 401 Unauthorized"); 55 //fixme add some image for imagefiles 56 print 'Unauthorized'; 57 exit; 58 } 59 $FILE = mediaFN($MEDIA); 60 } 61 62 //check file existance 63 if(!@file_exists($FILE)){ 64 header("HTTP/1.0 404 Not Found"); 65 //FIXME add some default broken image 66 print 'Not Found'; 67 exit; 68 } 69 70 $ORIG = $FILE; 71 72 //handle image resizing/cropping 73 if((substr($MIME,0,5) == 'image') && $WIDTH){ 74 if($HEIGHT){ 75 $FILE = media_crop_image($FILE,$EXT,$WIDTH,$HEIGHT); 76 }else{ 77 $FILE = media_resize_image($FILE,$EXT,$WIDTH,$HEIGHT); 78 } 79 } 80 81 // finally send the file to the client 82 $data = array('file' => $FILE, 83 'mime' => $MIME, 84 'download' => $DL, 85 'cache' => $CACHE, 86 'orig' => $ORIG, 87 'ext' => $EXT, 88 'width' => $WIDTH, 89 'height' => $HEIGHT); 90 91 $evt = new Doku_Event('MEDIA_SENDFILE', $data); 92 if ($evt->advise_before()) { 93 sendFile($data['file'],$data['mime'],$data['download'],$data['cache']); 94 } 95 96/* ------------------------------------------------------------------------ */ 97 98/** 99 * Set headers and send the file to the client 100 * 101 * @author Andreas Gohr <andi@splitbrain.org> 102 * @author Ben Coburn <btcoburn@silicodon.net> 103 */ 104function sendFile($file,$mime,$dl,$cache){ 105 global $conf; 106 $fmtime = @filemtime($file); 107 // send headers 108 header("Content-Type: $mime"); 109 // smart http caching headers 110 if ($cache==-1) { 111 // cache 112 // cachetime or one hour 113 header('Expires: '.gmdate("D, d M Y H:i:s", time()+max($conf['cachetime'], 3600)).' GMT'); 114 header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); 115 header('Pragma: public'); 116 } else if ($cache>0) { 117 // recache 118 // remaining cachetime + 10 seconds so the newly recached media is used 119 header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime+$conf['cachetime']+10).' GMT'); 120 header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime-time()+$conf['cachetime']+10, 0)); 121 header('Pragma: public'); 122 } else if ($cache==0) { 123 // nocache 124 header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); 125 header('Pragma: public'); 126 } 127 //send important headers first, script stops here if '304 Not Modified' response 128 http_conditionalRequest($fmtime); 129 130 131 //download or display? 132 if($dl){ 133 header('Content-Disposition: attachment; filename="'.basename($file).'";'); 134 }else{ 135 header('Content-Disposition: inline; filename="'.basename($file).'";'); 136 } 137 138 //use x-sendfile header to pass the delivery to compatible webservers 139 if($conf['xsendfile'] == 1){ 140 header("X-LIGHTTPD-send-file: $file"); 141 exit; 142 }elseif($conf['xsendfile'] == 2){ 143 header("X-Sendfile: $file"); 144 exit; 145 }elseif($conf['xsendfile'] == 3){ 146 header("X-Accel-Redirect: $file"); 147 exit; 148 } 149 150 //support download continueing 151 header('Accept-Ranges: bytes'); 152 list($start,$len) = http_rangeRequest(filesize($file)); 153 154 // send file contents 155 $fp = @fopen($file,"rb"); 156 if($fp){ 157 fseek($fp,$start); //seek to start of range 158 159 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 160 while (!feof($fp) && $chunk > 0) { 161 @set_time_limit(30); // large files can take a lot of time 162 print fread($fp, $chunk); 163 flush(); 164 $len -= $chunk; 165 $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 166 } 167 fclose($fp); 168 }else{ 169 header("HTTP/1.0 500 Internal Server Error"); 170 print "Could not read $file - bad permissions?"; 171 } 172} 173 174/** 175 * Checks and sets headers to handle range requets 176 * 177 * @author Andreas Gohr <andi@splitbrain.org> 178 * @returns array The start byte and the amount of bytes to send 179 */ 180function http_rangeRequest($size){ 181 if(!isset($_SERVER['HTTP_RANGE'])){ 182 // no range requested - send the whole file 183 header("Content-Length: $size"); 184 return array(0,$size); 185 } 186 187 $t = explode('=', $_SERVER['HTTP_RANGE']); 188 if (!$t[0]=='bytes') { 189 // we only understand byte ranges - send the whole file 190 header("Content-Length: $size"); 191 return array(0,$size); 192 } 193 194 $r = explode('-', $t[1]); 195 $start = (int)$r[0]; 196 $end = (int)$r[1]; 197 if (!$end) $end = $size - 1; 198 if ($start > $end || $start > $size || $end > $size){ 199 header('HTTP/1.1 416 Requested Range Not Satisfiable'); 200 print 'Bad Range Request!'; 201 exit; 202 } 203 204 $tot = $end - $start + 1; 205 header('HTTP/1.1 206 Partial Content'); 206 header("Content-Range: bytes {$start}-{$end}/{$size}"); 207 header("Content-Length: $tot"); 208 209 return array($start,$tot); 210} 211 212/** 213 * Returns the wanted cachetime in seconds 214 * 215 * Resolves named constants 216 * 217 * @author Andreas Gohr <andi@splitbrain.org> 218 */ 219function calc_cache($cache){ 220 global $conf; 221 222 if(strtolower($cache) == 'nocache') return 0; //never cache 223 if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache 224 return -1; //cache endless 225} 226 227//Setup VIM: ex: et ts=2 enc=utf-8 : 228?> 229