1f62ea8a1Sandi<?php 2f62ea8a1Sandi/** 3f62ea8a1Sandi * DokuWiki media passthrough file 4f62ea8a1Sandi * 5f62ea8a1Sandi * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6f62ea8a1Sandi * @author Andreas Gohr <andi@splitbrain.org> 7f62ea8a1Sandi */ 8f62ea8a1Sandi 9d0a27cb0SAndreas Gohr if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../'); 103138b5c7SAndreas Gohr define('DOKU_DISABLE_GZIP_OUTPUT', 1); 11f62ea8a1Sandi require_once(DOKU_INC.'inc/init.php'); 12f62ea8a1Sandi require_once(DOKU_INC.'inc/common.php'); 13*13c08e2fSMichael Klier require_once(DOKU_INC.'inc/media.php'); 14f62ea8a1Sandi require_once(DOKU_INC.'inc/pageutils.php'); 15f62ea8a1Sandi require_once(DOKU_INC.'inc/confutils.php'); 16f62ea8a1Sandi require_once(DOKU_INC.'inc/auth.php'); 178746e727Sandi //close sesseion 188746e727Sandi session_write_close(); 19e935fb4aSAndreas Gohr if(!defined('CHUNK_SIZE')) define('CHUNK_SIZE',16*1024); 208746e727Sandi 21f62ea8a1Sandi $mimetypes = getMimeTypes(); 22f62ea8a1Sandi 23f62ea8a1Sandi //get input 2402b0b681SAndreas Gohr $MEDIA = stripctl(getID('media',false)); // no cleaning except control chars - maybe external 25f62ea8a1Sandi $CACHE = calc_cache($_REQUEST['cache']); 268fcc3410SAndreas Gohr $WIDTH = (int) $_REQUEST['w']; 278fcc3410SAndreas Gohr $HEIGHT = (int) $_REQUEST['h']; 28f62ea8a1Sandi list($EXT,$MIME) = mimetype($MEDIA); 29f62ea8a1Sandi if($EXT === false){ 30f62ea8a1Sandi $EXT = 'unknown'; 31f62ea8a1Sandi $MIME = 'application/octet-stream'; 32f62ea8a1Sandi } 33f62ea8a1Sandi 34f62ea8a1Sandi //media to local file 35d1ed0b61SAndreas Gohr if(preg_match('#^(https?)://#i',$MEDIA)){ 36d1ed0b61SAndreas Gohr //handle external images 37*13c08e2fSMichael Klier if(strncmp($MIME,'image/',6) == 0) $FILE = media_get_from_URL($MEDIA,$EXT,$CACHE); 38f62ea8a1Sandi if(!$FILE){ 39f62ea8a1Sandi //download failed - redirect to original URL 40f62ea8a1Sandi header('Location: '.$MEDIA); 41f62ea8a1Sandi exit; 42f62ea8a1Sandi } 43f62ea8a1Sandi }else{ 44f62ea8a1Sandi $MEDIA = cleanID($MEDIA); 45f62ea8a1Sandi if(empty($MEDIA)){ 46f62ea8a1Sandi header("HTTP/1.0 400 Bad Request"); 47f62ea8a1Sandi print 'Bad request'; 48f62ea8a1Sandi exit; 49f62ea8a1Sandi } 50f62ea8a1Sandi 51f62ea8a1Sandi //check permissions (namespace only) 52f62ea8a1Sandi if(auth_quickaclcheck(getNS($MEDIA).':X') < AUTH_READ){ 53f62ea8a1Sandi header("HTTP/1.0 401 Unauthorized"); 54f62ea8a1Sandi //fixme add some image for imagefiles 55f62ea8a1Sandi print 'Unauthorized'; 56f62ea8a1Sandi exit; 57f62ea8a1Sandi } 58f62ea8a1Sandi $FILE = mediaFN($MEDIA); 59f62ea8a1Sandi } 60f62ea8a1Sandi 61f62ea8a1Sandi //check file existance 62f62ea8a1Sandi if(!@file_exists($FILE)){ 63f62ea8a1Sandi header("HTTP/1.0 404 Not Found"); 64f62ea8a1Sandi //FIXME add some default broken image 65f62ea8a1Sandi print 'Not Found'; 66f62ea8a1Sandi exit; 67f62ea8a1Sandi } 68f62ea8a1Sandi 69b80bedd6SAndreas Gohr $ORIG = $FILE; 70b80bedd6SAndreas Gohr 7120bc86cfSAndreas Gohr //handle image resizing/cropping 72f62ea8a1Sandi if((substr($MIME,0,5) == 'image') && $WIDTH){ 73d52a56e1SAndreas Gohr if($HEIGHT){ 74*13c08e2fSMichael Klier $FILE = media_crop_image($FILE,$EXT,$WIDTH,$HEIGHT); 7520bc86cfSAndreas Gohr }else{ 76*13c08e2fSMichael Klier $FILE = media_resize_image($FILE,$EXT,$WIDTH,$HEIGHT); 77f62ea8a1Sandi } 7820bc86cfSAndreas Gohr } 79f62ea8a1Sandi 80e935fb4aSAndreas Gohr // finally send the file to the client 81b80bedd6SAndreas Gohr $data = array('file' => $FILE, 82b80bedd6SAndreas Gohr 'mime' => $MIME, 83b80bedd6SAndreas Gohr 'cache' => $CACHE, 84b80bedd6SAndreas Gohr 'orig' => $ORIG, 85b80bedd6SAndreas Gohr 'ext' => $EXT, 86b80bedd6SAndreas Gohr 'width' => $WIDTH, 87b80bedd6SAndreas Gohr 'height' => $HEIGHT); 88b80bedd6SAndreas Gohr 89b80bedd6SAndreas Gohr $evt = new Doku_Event('MEDIA_SENDFILE', $data); 90b80bedd6SAndreas Gohr if ($evt->advise_before()) { 91b80bedd6SAndreas Gohr sendFile($data['file'],$data['mime'],$data['cache']); 92b80bedd6SAndreas Gohr } 93f62ea8a1Sandi 94e935fb4aSAndreas Gohr/* ------------------------------------------------------------------------ */ 95f62ea8a1Sandi 96e935fb4aSAndreas Gohr/** 97e935fb4aSAndreas Gohr * Set headers and send the file to the client 98e935fb4aSAndreas Gohr * 99e935fb4aSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 10083730152SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net> 101e935fb4aSAndreas Gohr */ 10283730152SBen Coburnfunction sendFile($file,$mime,$cache){ 10383730152SBen Coburn global $conf; 10409a5b61fSgweissbach $fmtime = @filemtime($file); 105e935fb4aSAndreas Gohr // send headers 106e935fb4aSAndreas Gohr header("Content-Type: $mime"); 10783730152SBen Coburn // smart http caching headers 10883730152SBen Coburn if ($cache==-1) { 10983730152SBen Coburn // cache 11083730152SBen Coburn // cachetime or one hour 11183730152SBen Coburn header('Expires: '.gmdate("D, d M Y H:i:s", time()+max($conf['cachetime'], 3600)).' GMT'); 11283730152SBen Coburn header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); 113e935fb4aSAndreas Gohr header('Pragma: public'); 11483730152SBen Coburn } else if ($cache>0) { 11583730152SBen Coburn // recache 11683730152SBen Coburn // remaining cachetime + 10 seconds so the newly recached media is used 11783730152SBen Coburn header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime+$conf['cachetime']+10).' GMT'); 11883730152SBen Coburn header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime-time()+$conf['cachetime']+10, 0)); 11983730152SBen Coburn header('Pragma: public'); 12083730152SBen Coburn } else if ($cache==0) { 12183730152SBen Coburn // nocache 12283730152SBen Coburn header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); 12383730152SBen Coburn header('Pragma: public'); 12483730152SBen Coburn } 125ff4f5ee7SBen Coburn //send important headers first, script stops here if '304 Not Modified' response 12683730152SBen Coburn http_conditionalRequest($fmtime); 1279a87c72aSAndreas Gohr 128f62ea8a1Sandi 129f62ea8a1Sandi //application mime type is downloadable 130e935fb4aSAndreas Gohr if(substr($mime,0,11) == 'application'){ 131e935fb4aSAndreas Gohr header('Content-Disposition: attachment; filename="'.basename($file).'";'); 132f62ea8a1Sandi } 133f62ea8a1Sandi 1349a87c72aSAndreas Gohr //use x-sendfile header to pass the delivery to compatible webservers 1359a87c72aSAndreas Gohr if($conf['xsendfile'] == 1){ 1369a87c72aSAndreas Gohr header("X-LIGHTTPD-send-file: $file"); 1379a87c72aSAndreas Gohr exit; 1389a87c72aSAndreas Gohr }elseif($conf['xsendfile'] == 2){ 1399a87c72aSAndreas Gohr header("X-Sendfile: $file"); 1409a87c72aSAndreas Gohr exit; 141deec6eb9Spierre.pracht }elseif($conf['xsendfile'] == 3){ 142deec6eb9Spierre.pracht header("X-Accel-Redirect: $file"); 143deec6eb9Spierre.pracht exit; 1449a87c72aSAndreas Gohr } 1459a87c72aSAndreas Gohr 1469a87c72aSAndreas Gohr //support download continueing 1479a87c72aSAndreas Gohr header('Accept-Ranges: bytes'); 1489a87c72aSAndreas Gohr list($start,$len) = http_rangeRequest(filesize($file)); 1499a87c72aSAndreas Gohr 150e935fb4aSAndreas Gohr // send file contents 151e935fb4aSAndreas Gohr $fp = @fopen($file,"rb"); 152f62ea8a1Sandi if($fp){ 153e935fb4aSAndreas Gohr fseek($fp,$start); //seek to start of range 154e935fb4aSAndreas Gohr 155e935fb4aSAndreas Gohr $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 156e935fb4aSAndreas Gohr while (!feof($fp) && $chunk > 0) { 157d6751ba5SAndreas Gohr @set_time_limit(30); // large files can take a lot of time 158e935fb4aSAndreas Gohr print fread($fp, $chunk); 159615a21edSBrian Cowan flush(); 160e935fb4aSAndreas Gohr $len -= $chunk; 161e935fb4aSAndreas Gohr $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len; 162615a21edSBrian Cowan } 163615a21edSBrian Cowan fclose($fp); 164f62ea8a1Sandi }else{ 165f62ea8a1Sandi header("HTTP/1.0 500 Internal Server Error"); 166e935fb4aSAndreas Gohr print "Could not read $file - bad permissions?"; 167e935fb4aSAndreas Gohr } 168f62ea8a1Sandi} 169f62ea8a1Sandi 170e935fb4aSAndreas Gohr/** 171e935fb4aSAndreas Gohr * Checks and sets headers to handle range requets 172e935fb4aSAndreas Gohr * 173e935fb4aSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 174e935fb4aSAndreas Gohr * @returns array The start byte and the amount of bytes to send 175e935fb4aSAndreas Gohr */ 176e935fb4aSAndreas Gohrfunction http_rangeRequest($size){ 177e935fb4aSAndreas Gohr if(!isset($_SERVER['HTTP_RANGE'])){ 178e935fb4aSAndreas Gohr // no range requested - send the whole file 179e935fb4aSAndreas Gohr header("Content-Length: $size"); 180e935fb4aSAndreas Gohr return array(0,$size); 181e935fb4aSAndreas Gohr } 182e935fb4aSAndreas Gohr 183e935fb4aSAndreas Gohr $t = explode('=', $_SERVER['HTTP_RANGE']); 184e935fb4aSAndreas Gohr if (!$t[0]=='bytes') { 185e935fb4aSAndreas Gohr // we only understand byte ranges - send the whole file 186e935fb4aSAndreas Gohr header("Content-Length: $size"); 187e935fb4aSAndreas Gohr return array(0,$size); 188e935fb4aSAndreas Gohr } 189e935fb4aSAndreas Gohr 190e935fb4aSAndreas Gohr $r = explode('-', $t[1]); 191e935fb4aSAndreas Gohr $start = (int)$r[0]; 192e935fb4aSAndreas Gohr $end = (int)$r[1]; 193e935fb4aSAndreas Gohr if (!$end) $end = $size - 1; 194e935fb4aSAndreas Gohr if ($start > $end || $start > $size || $end > $size){ 195e935fb4aSAndreas Gohr header('HTTP/1.1 416 Requested Range Not Satisfiable'); 196e935fb4aSAndreas Gohr print 'Bad Range Request!'; 197e935fb4aSAndreas Gohr exit; 198e935fb4aSAndreas Gohr } 199e935fb4aSAndreas Gohr 200e935fb4aSAndreas Gohr $tot = $end - $start + 1; 201e935fb4aSAndreas Gohr header('HTTP/1.1 206 Partial Content'); 202e935fb4aSAndreas Gohr header("Content-Range: bytes {$start}-{$end}/{$size}"); 203e935fb4aSAndreas Gohr header("Content-Length: $tot"); 204e935fb4aSAndreas Gohr 205e935fb4aSAndreas Gohr return array($start,$tot); 206e935fb4aSAndreas Gohr} 207e935fb4aSAndreas Gohr 208e935fb4aSAndreas Gohr/** 209f62ea8a1Sandi * Returns the wanted cachetime in seconds 210f62ea8a1Sandi * 211f62ea8a1Sandi * Resolves named constants 212f62ea8a1Sandi * 213f62ea8a1Sandi * @author Andreas Gohr <andi@splitbrain.org> 214f62ea8a1Sandi */ 215f62ea8a1Sandifunction calc_cache($cache){ 216f62ea8a1Sandi global $conf; 217f62ea8a1Sandi 218f62ea8a1Sandi if(strtolower($cache) == 'nocache') return 0; //never cache 219f62ea8a1Sandi if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache 220f62ea8a1Sandi return -1; //cache endless 221f62ea8a1Sandi} 222f62ea8a1Sandi 223f62ea8a1Sandi//Setup VIM: ex: et ts=2 enc=utf-8 : 224f62ea8a1Sandi?> 225